Answers:
异步串行(通常称为串行)通信用于将字节从一台设备发送到另一台设备。设备可以是以下一项或多项:
与SPI / USB / I2C不同,串行通信没有时钟信号。采样时钟是商定的采样率(称为波特率)。发送方和接收方都必须配置为使用相同的速率,否则接收方将接收到无意义的数据(由于未以与发送时相同的速率对位进行采样)。
传输是异步的,这基本上意味着可以在任何时间发送字节,并且字节之间的间隙可以变化。此图说明了正在发送的单个字节:
上图显示了正在发送的字母“ F”。在ASCII中,它是0x46(十六进制)或0b01000110(二进制)。所述至少显著(低位)位首先传输,从而在上述图形你看到的顺序到达的位:01100010
。
字节之间的“空闲”时间以连续的“ 1”位发送(有效地,发送线连续保持高电平)。
为了指示字节的开始,总是通过将线拉低来指示开始位,如图所示。接收器看到起始位后,将等待1.5倍的采样时间,然后对数据位进行采样。它等待1.5次,因此:
例如,如果波特率为9600波特,则采样率将为1/9600 = 0.00010416
秒(104.16 µs)。
因此,在9600波特下,接收器在接收到起始位后等待156.25 µs,然后每104.16 µs采样一次。
停止位的目的是确保每个字节之间肯定有1位。没有停止位,如果一个字节以零结尾,那么硬件将无法分辨该位与下一个字节的起始位之间的差异。
要在Uno上产生以上输出,您可以编写以下代码:
void setup()
{
Serial.begin(9600);
Serial.print("F");
}
void loop ()
{
}
为了节省传输时间(以前是heh),允许您指定不同数量的数据位。AtMega硬件支持从5到9的数据位编号。显然,数据位越少,您可以发送的信息就越少,但速度越快。
您可以选择具有奇偶校验位。如果需要,可以通过计算字符中1的数量,然后根据需要将奇偶校验位设置为0或1来确保该数字为奇数或偶数来计算该值。
例如,对于字母“ F”(或0x46或0b01000110),您可以看到那里有3个(在01000110中)。因此,我们已经有了奇偶校验。因此,奇偶校验位将如下所示:
奇偶校验位(如果存在)出现在最后一个数据位之后但在停止位之前。
如果接收器没有获得正确的奇偶校验位,则称为“奇偶校验错误”。这表明存在一些问题。可能将发送方和接收方配置为使用不同的波特率,或者线路上出现了噪声,将零变为零,反之亦然。
一些早期的系统还使用“标记”奇偶校验(其中,奇偶校验位始终与数据无关,始终为1)或“空间”奇偶校验(其中,奇偶校验位与数据无关,始终为0)。
某些通信设备使用9位数据,因此在这些情况下,奇偶校验位变为第9位。发送第9位有特殊的技术(寄存器是8位寄存器,因此第9位必须放在其他位置)。
早期的设备在电子方面趋向于变慢,因此为了给接收者一些时间来处理传入的字节,有时会指定发送者将发送两个停止位。从根本上来说,这将增加更多的时间,在此位置数据线保持高电平(一个位的时间)之后,下一个起始位才能出现。这个额外的位时间使接收器有时间处理最后一个输入字节。
如果在假定停止位时接收器未获得逻辑1,则称为“成帧错误”。这表明存在一些问题。发送方和接收方很可能配置为使用不同的波特率。
通常,串行通信通过告诉您速度,数据位数,奇偶校验类型和停止位数来表示,如下所示:
9600/8-N-1
这是在告诉我们:
发送者和接收者就上述达成一致非常重要,否则通信不太可能成功。
Arduino Uno具有用于硬件串行的数字引脚0和1:
要连接两个Arduino,您需要像这样交换 Tx和Rx:
支持多种速度(请参见下图)。“标准”速度通常是300波特的倍数(例如300/600/1200/2400等)。
可以通过设置适当的寄存器来处理其他“非标准”速度。HardwareSerial类为您执行此操作。例如。
Serial.begin (115200); // set speed to 115200 baud
根据经验法则,假设您使用的是8位数据,则可以通过将波特率除以10(由于起始位和停止位)来估计每秒可以传输的字节数。
因此,在9600波特下,您可以9600 / 10 = 960
每秒传输960字节()。
通过对系统时钟进行分频,然后向上计数至预设数字,可以生成Atmega的波特率。数据表中的此表显示了16 MHz时钟(例如Arduino Uno上的时钟)的寄存器值和错误百分比。
U2Xn位影响时钟速率除数(0 =除以16,1 =除以8)。UBRRn寄存器包含处理器计数的数字。
因此,从上表可以看出,我们从16 MHz时钟中获得了9600波特,如下所示:
16000000 / 16 / 104 = 9615
我们除以104而不是103,因为计数器是零相关的。因此,这里的误差15 / 9600 = 0.0016
接近上表的误差(0.02%)。
您会注意到某些波特率的错误量比其他波特率高。
根据数据手册,8个数据位的最大错误百分比在1.5%至2.0%的范围内(更多信息请参见数据手册)。
Arduino Leonardo和Micro具有不同的串行通信方法,因为它们直接通过USB连接到主机,而不是通过串行端口。
因此,您必须等待串行变为“就绪”状态(因为软件会建立USB连接),需要额外的几行,如下所示:
void setup()
{
Serial.begin(115200);
while (!Serial)
{} // wait for Serial comms to become ready
Serial.print("Fab");
}
void loop ()
{
}
但是,如果您想通过D0和D1引脚(而不是通过USB电缆)进行实际通信,则需要使用Serial1而不是Serial。您是这样的:
void setup()
{
Serial1.begin(115200);
Serial1.print("Fab");
}
void loop ()
{
}
请注意,Arduino使用TTL电平进行串行通信。这意味着它期望:
设计用于插入PC串行端口的较旧的串行设备可能使用RS232电压电平,即:
不仅是相对于TTL电平的这种“反转”(“ 1”比“ 0”更负),Arduino无法处理其输入引脚上的负电压(也不能处理大于5V的正电压)。
因此,您需要一个用于与此类设备通信的接口电路。仅对于输入(到Arduino),一个简单的晶体管,二极管和几个电阻就可以做到:
对于双向通信,您需要能够产生负电压,因此需要更复杂的电路。例如,MAX232芯片将与四个1 µF电容器一起用作电荷泵电路。
有一个名为SoftwareSerial的库,它使您可以在软件而非硬件中进行串行通信(最大程度)。这样的好处是您可以将不同的引脚配置用于串行通信。缺点是在软件中进行串行处理会占用更多的处理器资源,并且更容易出错。有关更多详细信息,请参见软件序列。
Arduino“ Mega”具有3个附加的硬件串行端口。它们在板上标记为Tx1 / Rx1,Tx2 / Rx2,Tx3 / Rx3。如果可能,应优先于SoftwareSerial使用它们。要打开其他端口,请使用名称Serial1,Serial2,Serial3,如下所示:
Serial1.begin (115200); // start hardware serial port Tx1/Rx1
Serial2.begin (115200); // start hardware serial port Tx2/Rx2
Serial3.begin (115200); // start hardware serial port Tx3/Rx3
使用HardwareSerial库发送和接收都使用中断。
当您执行时Serial.print
,尝试打印的数据将放置在内部“发送”缓冲区中。如果您具有1024字节或更多的RAM(例如Uno),则将获得64字节的缓冲区,否则将获得16字节的缓冲区。如果缓冲区有空间,则Serial.print
立即返回,因此不会延迟您的代码。如果没有空间,则它“阻塞”,等待清空缓冲区足以容纳空间。
然后,随着硬件发送每个字节,将调用一个中断(“ USART,数据寄存器为空”中断),并且中断例程将缓冲区中的下一个字节发送到串行端口之外。
当接收到输入数据时,将调用一个中断例程(“ USART Rx完成”中断),并将输入字节放入“接收”缓冲区(与上述发送缓冲区的大小相同)。
调用时,Serial.available
您会发现该“接收”缓冲区中有多少字节可用。调用时,Serial.read
将从接收缓冲区中删除一个字节并返回到您的代码。
在具有1000字节或更多字节RAM的Arduino上,只要您不让它填满,就不会急于从接收缓冲区中删除数据。如果已填满,则将丢弃任何其他传入数据。
请注意,由于此缓冲区的大小,等待大量字节到达毫无意义,例如:
while (Serial.available () < 200)
{ } // wait for 200 bytes to arrive
这将永远无法工作,因为缓冲区无法容纳那么多缓冲区。
读取之前,请务必确保数据可用。例如,这是错误的:
if (Serial.available ())
{
char a = Serial.read ();
char b = Serial.read (); // may not be available
}
该Serial.available
测试仅确保您有一个字节可用,但是代码尝试读取两个字节。如果缓冲区中有两个字节,它可能会起作用,如果没有,您将得到-1的返回值,如果打印出来,它将看起来像“ÿ”。
请注意发送数据需要多长时间。如上所述,在9600波特下,您每秒仅传输960字节,因此尝试以9600波特从模拟端口发送1000读数将不会非常成功。