串口通信

介绍

嵌入式电子都是关于通过电路的互动(处理器或者其他集成电路)来制作一个共生系统。为了让那些单个电路交换信息,它们必须要有一个通讯协议。上百个通讯协议已经被定义为完成数据交换,而且一般每一个会被分为一或两类:并行或者串行。

并行vs串行

并行接口能同时进行多位传输。它们通常要求有八条十六或者更多分线的数据总线。数据以1和0的波形来传输。

1

一个八位的数据线受时钟信号的控制,以每个时钟脉冲发射一个字节的速度传输。这需要用到9条线

串行接口以每单位时间发射一个单字节的速度传输它们的数据。这类接口可以只用一条线进行操作,且通常不超过4条。

1.5

串行接口的例子,每个时钟脉冲传输一个位。仅仅用到两条线

把两种接口想象作车流:并行接口就如8+道的超级高速公路,然而串行接口就像两道的乡村道路。超过一定时间,高速公路就可能使更多人到达目的地,但乡村道路也能到达目的地而且花的建设费更少。

并行接口确实有它的优势。它快,直接而且容易实现。但是它要求更多的输入输出(I/O)线。如果你已经把你的任务从ArduinoUno移到了Mega,你就知道在单片机上的I/O线是多么珍贵而且少。所以我们选用串行接口,尽管牺牲引脚的潜在速度。

异步串行

这些年来,大量的串行协议被制作出来以满足嵌入式系统的特殊需求。USB(通用串行总线)和以太网是一对广为人知串行接口。其他普遍的串行接口包括SPI,I2C以及我们今天说的一些串行标准。所有的这些接口可以分成两类:同步或者异步。

同步串行接口总是数据线伴随着时钟信号,所以所有同步串行总线上的设备享用同一时钟信号。这使得传输更直接并能加快传输,但它要求至少有一条完整的线连接通信设备。同步串行接口的例子有SPI和I2C。

异步意味着数据的传输不需要外部提供时钟信号。这种传输方法能很好的降低线和I/O引脚的需求,但这也意味着我们需要花点精力来注意数据的输入输出。在这片指导里我们将会介绍最为常见的一些异步传输的串行协议。事实上,它是十分普通的,当众人在说‘串行’的时候就是再说这个协议了(你可能会通过这篇指导注意到一些)

无时钟信号的串行协议在嵌入式电子电路中是被广泛利用的。如果你想要加一个GPS模块,蓝牙,无线模块,串口液晶显示屏或者其他额外设备到你的项目里,你可能需要拿出一些串行符。

串行规则

异步串行协议有大量内建的规则——帮助确认无差错数据传输的机制。这些机制让我们脱离了额外的时钟信号,如:

  • 数据位

  • 同步位

  • 奇偶校验位

  • 波特率

尽管这些信令机制种类繁多,但你会发现这没有一个是串行传输数据的方式。协议高度集成。最重要的部分是用来确认串行总线上的设备是否被设置为使用同一套协议

波特率

波特率决定了一条串行线的传输速率。它的单位是每秒位数(bps)。如果你倒置了波特率,你可以知道它多久传输一位。值决定了发射器能维持串行线高电平/低电平的时间或者在什么时期接受设备的信号。

波特率可以使合理范围内的任意值。但要求两端的设备使用同一个波特率。9600bps是一个比较通用的波特率,尤其是对速度要求不严格的东西。其他的一些“标准”波特率包括1200,2400,4800,19200, 38400, 57600和115200

波特率越高,传输的速度就越快,但是会有一些速度的限制。你一般不会看到超过115200的速度——那是单片机的最高传输速率。如果太快,你会在接受结束时看到错误信息,因为时钟信号和采样周期会跟不上。

帧数

每一块(通常为一字节)数据。为我们的数据追加同部位和奇偶校验位从而产生了帧。

2

串行数据帧。一些帧的符号配置了位的长度。

让我们更详细的了解每一块帧。

数据块

每一个串行数据包真正有价值的它承载的数据。我们大概称它为块,因为它的长度并没有特别说明。每一块块的数据可以设置5到9位。当然,标准的数据长度是8位一字节,但是也有其他的长度规则。一个7位的数据块会比8位的更有效率,如果你只传输7位ASCII字符。

在商定了字符长度之后,两个串行设备还要商定数据的字节序。是以最高有效位到最低传输数据,或是反过来?如果没有其他说明,你可以假设是最低有效位先传。

同部位

同部位是每一块数据传输是比较特别的两三位。它们是起始位终止位(多位)。如它们的名字所示,这些位标记了一个包的起始和结束。起始位只有一位,但是终止位可以使一或者两位(虽然一般只有一位)。

起始位总是由空数据线开始从1到0表示,然而终止位会通过把线设为1从而转换为空状态。

奇偶校验位

奇偶校验是一种简单低层次的错误检查方式。它有两种结果:奇数或偶数。为了制作奇偶校验位,所有5-9位的数据字节要被加起来,而总数的均匀度将会决定是否设置位。举个例子,假设奇偶性设置为偶,而要加的数据位是0b01011101,有奇数个1,奇偶校验位就会设为1。反过来,如果奇偶性设为奇,奇偶校验位就会设为0。

奇偶性是可选的,并且利用不是很广。它有助于在嘈杂的媒介进行传输,但它也会降低一位你的数据传输并且要求你的发送器和接收器实施错误处理(通常,接收数据出错要求重新发送。)

9600 8N1(一个例子)

9600 8N1 – 9600波特,8位数据,没有奇偶校验,一个终止位——是最为常用的串行协议之一。所以,一包或者两个9600 8N1是怎样的呢?让我们看一个例子。

一个设备发送‘O’和‘K’的ASCII字符会用到两包数据。(大写的)O的ASCII值是79,转换为8位的二进制值是01001111,然而K的二进制值是01001011。剩下的全是同部位。

虽然没有特别声明,但它的数据传输是从最低有效位开始的。可以注意到两个字节如它从右到左读的顺序来传输的。

3

因为我们以9600 bps的速度传输,所以每一个高低电平的持续时间只有1/(9600 bps)或者104 µs每位。

每有一个字节被传输,实际上已经传输了10位:一位起始位,8位数据位以及一位终止位。所以,在9600 bps的状态下,我们事实上是传输9600位每秒或者说960字节每秒。

现在你已经知道如何构造串行数据包,我们就可以向硬件部分进军了。你可以看到1,0以及波特率是如何在信号电平上运作了。

写入和硬件

一个串行总线仅有两条线组成——一条输出另外一条接收。因此,串行设备需要有两个串行引脚:接收器RX和发射器TX。

4

注意设备上的RX和TX标签是很重要的。设备的RX端要连接到另一设备的TX端,反之亦然。如果你习惯性的像VCC接VCC,GND接GND,MOSI接MOSI会很奇怪的,但你如果思考一下就能想通了,发射器应该需要的接收器而不是另一个发射器。

串行接口上的设备会全双工或者半双工地进行数据发送和接收。全双工就是两端的设备可以同时发送和接收,而半双工是两个串行设备轮流发送接收。

一些串行总线可能只是单个连接发送和接收设备。比方说,我们支持串行的LCD只接收数据而没有任何数据反馈给控制器。这就是我们所知道的单纯的串口通信。你所需要的只是一条线从主设备的TX端连接到接收器的RX端。

硬件实现

我们已经了解了异步串行的概念了,也知道我们需要怎样的线。但究竟串行通信怎样在信号电平里实施呢?事实上有很多方法。这里各种串行信号的标准,一对比较大众的硬件实施串行方法:逻辑电平(TTL)和RS-232.

通常微处理器和其他低电平IC会用TTL(晶体管—晶体管逻辑)电平。TTL串行信号的大小在微处理器电压供应范围内——通常是0到3.3V或者5V。处于VCC电平(3.3V, 5V等)的信号表示空线或者值为1的位或者终止位。0V(GND)信号代表起始位或者值为0的位。

5

RS-232,可以在大部分古老的电脑和外围设备上找到,像TTL翻转了。RS-232信号通常在13V到-13V,尽管规范是允许+/-3V到+/-25V。在这些信号里,低电压(如-5V,-13V)表示空线,终止位或者值为1的位。高电平RS-232信号则表示起始位或者值为0的位。这与TTL串行方式正好相反。

6

这两种串行信号标准里,TTL更容易在嵌入式电路上实现。然而低电压电平更容易受到长线传输亏损的影响。RS-232或者更复杂的标准比如RS-485就更适合长距离串行传输了。

当你把两个串行设备连接在一起时,要确认它们的信号电压是否配对。你不能直接把一个TTL串行设备直接接到RS-232总线上,你必须先转换这些信号

接下来,我们将会探究微处理器把串行接口上的数据转换到并行总线上的工具,UART

完成制作串行包和控制物理硬件的线的最后一块拼图,UART。

通用异步收发器(UART)是一块负责实行串口通信的电路。实际上,UART就相当于串并行接口的媒介。在UART的末端是一个的8条数据线的总线(加了一些控制引脚),在另一边是两条串行线——RX和TX。

7

超简易UART接口。并行在末端,串行在另一边

UART也像独立的IC一样,但是它们常出现在微控制器里,你可以看看你微控制器的数据表是否有UART。有些没有,有些有一个,有些有许多。比如,ArduinoUno——基于“老友”Atmega2560——只有一个UART,然而ArduinoMega——基于Atmega2560——有高达四个UART。

因为R和T是缩写,UART要负责发送和接收串行数据。在发射端,UART需要制作附加同步位和奇偶校验位的数据包——并且把数据包从TX线按精确计时(按照设定的波特率)发送。在接收端,UART需要根据预期的波特率对RX线进行采样,挑出同步位,获得数据。

8

UART块的内部结构图(来自Exar ST16C550的数据表

越来越多先进的UART把它们接收的数据放进缓冲器,数据可以一直存放直到微控制器读取它们。

UART软件

如果微控制器没有UART(或者不够用),串行接口可以位脉冲——直接由处理器控制。这是Arduino库里像 SoftwareSerial做到的。位脉冲是密集型处理而不像 UART那样精确,但它在紧要关头有效 !

常见缺陷

关于串行通信,我想告诉你一些不同经验的工程师都常犯的一些错误:RX接TX,TX接RX

看上去十分简单,但它是一个我已经犯了不止几次的错了。你会想把它们的标签配对起来,所以一定要确认连接两个串行设备上的RX和TX线。

10

          FTDI Basic编译Pro Mini注意RX和TX!

错误配对波特率

波特率就像是串行通信的语言。如果两台设备不用同一个速度说话,数据就会错误输入,或者完全丢失,如果接受的东西在所有接收设备上看都是垃圾,那就确认一下波特率是否相同。

9

数据发射的波特率是9600 bps,但是接收的波特率是19200 bps。波特率错误配对=垃圾

总线争用

串行通信只允许两个设备通过一条串行总线通信,如果多于一台设备尝试在同一串行线上发送数据,就会发生总线争用。

举个例子,如果你接一个GPS模块到你的Arduino上,你可能只连了模块的TX到Arduino的RX上。但是Arduino得RX引脚已经连到了USB转串行的TX引脚上,一个你只要编译Arduino或者用串行监视器就会被用上的东西。这会导致GPS模块和FTDI同时用同一条线发送数据的潜在情况。

11

两个发射器发送信号给接收器会导致总线争用

两个设备想在同一线上同时发送数据是不行的。“最好”的结果是没有设备发送成功。最坏的结果是,两个发射器都坏掉(尽管这很少发生,尽量避免。)

单一发射装置可以连接多个接收器。这不是规范的做法,而且会让硬件工程师苦恼,但它能正常运作。比如,你连接一个串行LCD到Arduino上,最简单的办法就是把LCD模块的RX与Arduino的TX连接。Arduino的TX已经连接到USB编译器的RX了,但仍只有一个设备控制发射线

12

像这样分配TX线仍然会对硬件造成危险,因为你不能选择哪台设备接收信号。LCD会终止接收不属于它的数据,这会使数据进入一个未知状态。

总之,一个串行总线,两个设备。

 


cc

原始文章采用CC BY-SA 4.0,您可以自由地:

  • 演绎 — 修改、转换或以本作品为基础进行创作
  • 在任何用途下,甚至商业目的。

只要你遵守许可协议条款,许可人就无法收回你的这些权利。本文由美国开源硬件厂商Sparkfun(火花快乐)的相关教程翻译,原始教程采用同样的CC BY-SA 4.0协议,为便于理解和方便读者学习使用,部分内容为适应国内使用场景稍有删改或整合,这些行为都是协议允许并鼓励的。

原始文章及相关素材链接:

https://learn.sparkfun.com/tutorials/serial-communication