USART、I2C、SPI、CAN总结
USART、I2C、SPI、CAN属于嵌入式、硬件、单片机等开发时经常用到的通信协议,找工作面试也经常很容易问到,这里整理总结一下。
USART
简介
USART即通用同步/异步收发器(Universal Synchronous/Asynchronous Receiver/Transmitter),它是嵌入式领域中使用十分广泛的一种串行通信协议,大部分MCU都具备USART硬件接口,或者仅UART(即不具备同步功能)。
发展历史
USART协议是由RS-232协议发展而来的。在个人计算机诞生之前就已经存在了串口设备,如电传打字机,工控测量设备,通信调制解调器,最初的串口就是用一根线直连另外一方,一方发一方收,后来厂商为了完善自己的串口又增加了一根线用于做信号位,主要用于判断流是否可用,同时当时的厂商们的设备不同,芯片的工作频率也不同以及电平信号也不同,导致厂商的设备只能跟自己通讯,没有一个统一的标准非常混乱,后来由无线电制造商协会(Radio Manufacturers’ Association:RMA,现美国电子工业协会(EIA))联合贝尔实验室在19世纪70年代一起制定了一个标准,即历史上第一个通用串口协议标准:RS-232,采用DB25针串口,支持异步。
后来随着个人电脑的出现,个人电脑最初的上的接口较小,PCB板子空间也很小,DB25较大,EIA将其中保留的一些针位去除了,形成了后来的DB9针串口,协议依然是RS-232。
由于DB9接口只定义了信号量,但是没有定义与每个引脚的直接关系,导致当时的厂商需要自己去定义每个引脚的作用,这就导致了不同厂商每个引脚的作用不一样,但是功能是一样的,无法相互接在一起,最初有一个比较知名的公司:IBM,它规定了一种定义,后来大多数厂商为了统一都采用IBM的定义。
在这一刻UART才叫UART,这里的U即是通用的意思,不同厂商之间的设备可以通过串口进行通讯了。
特点
USART是一种全双工同步串行通信(UART为异步通信),点对点通信,常见波特率包括9600和115200,通常使用两根线即可进行通信,分别为Tx和Rx,与主端的发送接口和接受接口对应。
电平标准
USART常见不同电平标准包括TTL、RS232、RS485、RS422,其电平区别如下:
| 标准 | 电平(发送端) |
|---|---|
| RS232 | 逻辑1:-3V ~ -15V 逻辑0:+3V ~ +15V |
| TTL | 逻辑1:2.4V ~ 5V 逻辑0:0V ~ 0.5V |
| RS485 | (AB线电压差) 逻辑1:+2V ~ +6V 逻辑0:-2V ~ -6V |
| RS422 | 同RS485 |
传输过程
USART(UART)通信中,每个字节都装载在一个数据帧(10或11位)里,每个数据帧都由起始位、数据位和停止位组成。
起始位标志一个数据帧的开始,固定为低电平。空闲状态为高电平,起始位产生下降沿,来告诉设备要开始发送数据了
数据位有8位或9位,数据位9位是多了一个奇偶校验位,跟在有效载荷(一个字节)后面,1为高电平,0为低电平,低位先行。奇偶校验可以判断数据传输是否出错,如果出错可选择丢弃或者重传。可选择三种方式,无校验、奇校验、偶校验。奇校验,包括校验位在内的9个数据位会出现奇数个1,根据8位数据情况奇校验位补0或1,保证1的个数位奇数,接收方接收数据时,会验证数据位和校验位,检出率不高比如有两位同时出错,只校验奇偶特性是检验不出的。偶校验同理,只能保证一定检出率。如果要更高检出率可以使用CRC校验。
停止位用于数据帧间隔,固定为高电平。也是为下一个起始位做准备(切换到高电平空闲状态)。
I2C
简介
I2C即集成电路总线(Inter-Integrated Circuit),是由Philips半导体公司(现在的NXP半导体公司)在八十年代初设计出来的一种简单、双向、二线制总线标准。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。
特点
I2C是一种半双工同步串行通信。I2C通信由主机启动总线,并产生时钟用于传送数据,此时任何接收数据的器件均被认为是从机。总线上可以连接多个IIC通讯设备,支持多个通信主机及多个通信从机。连接到相同总线的设备数量受到总线的最大电容400pF限制。
一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA),一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。I2C将SCL处于高时SDA拉低的动作作为开始信号,SCL处于高时SDA拉高的动作作为结束信号;传输数据时,SDA在SCL低电平时改变数据,在SCL高电平时保持数据,每个SCL脉冲的高电平传递1位数据。多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
I2C具有三种传输模式:标准模式为100kbit/s,快速模式为400kbit/s,高速模式下可达3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。
传输过程
I2C的协议定义了传输的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
在I2C器件开始通信(传输数据)之前,串行时钟线SCL和串行数据线SDA线由于上拉的原因处于高电平状态,此时I2C总线处于空闲状态。如果主机想开始传输数据,只需在SCL为高电平时将SDA线拉低,产生一个起始信号,从机检测到起始信号后,准备接收数据,当数据传输完成,主机只需产生一个停止信号,告诉从机数据传输结束,停止信号的产生是在SCL为高电平时,SDA从低电平跳变到高电平,从机检测到停止信号后,停止接收数据。I2C整体时序如下图。起始信号之前为空闲状态,起始信号之后到停止信号之前的这一段为数据传输状态,主机可以向从机写数据,也可以读取从机输出的数据,数据的传输由双向数据线(SDA)完成。停止信号产生后,总线再次处于空闲状态。
在起始信号之后,主机开始发送传输的数据;在串行时钟线SCL为低电平状态时,SDA允许改变传输的数据位(1为高电平,0为低电平),在SCL为高电平状态时,SDA要求保持稳定,相当于一个时钟周期传输1位数据,经过8个时钟周期后,传输了8位数据,即一个字节。第8个时钟周期末,主机释放SDA以使从机应答,在第9个时钟周期,从机将SDA拉低以应答;如果第9个时钟周期,SCL为高电平时,SDA未被检测到为低电,视为非应答,表明此次数据传输失败。第9个时钟周期末,从机释放SDA以使主机继续传输数据,如果主机发送停止信号,此次传输结束。我们要注意的是数据以8位即一个字节为单位串行发出,其最先发送的是字节的最高位。
每个I2C器件都有一个器件地址,有些I2C器件的器件地址是固定的,而有些I2C器件的器件地址由一个固定部分和一个可编程的部分构成。当主机想给某个器件发送数据时,只需向总线上发送接收器件的器件地址即可。进行数据传输时,主机首先向总线上发出开始信号,对应开始位S,然后按照从高到低的位序发送器件地址,一般为7位,第8位为读写控制位R/W,该位为0时表示主机对从机进行写操作,当该位为1时表示主机对从机进行读操作,然后接收从机响应。
发送完第一个字节(7位器件地址和一位读写控制位)并收到从机正确的应答后就开始发送字地址(Word Address)。一般而言,每个兼容I2C协议的器件,内部总会有可供读写的寄存器或存储器,例如EEPROM存储器,内部就是一系列顺序编址的存储单元。所以当我们对一个器件中的存储单元(包括寄存器)进行读写时,首先要指定存储单元的地址即字地址,然后再向该地址写入内容。该地址为一个或两个字节长度,具体长度由器件内部的存储单元的数量决定,当存储单元数量不超过一个字节所能表示的最大数量(2^8=256)时,用一个字节表示,超过一个字节所能表示的最大数量时,就需要用两个字节来表示,例如同是EEPROM存储器,AT24C02的存储单元容量为2Kbit=256Byte(一般bit缩写为b,Byte缩写为B),用一个字节地址即可寻址所有的存储单元,而AT24C64的存储单元容量为64Kb=8KB,需要13位(2^13=8KB)的地址位,而I2C又是以字节为单位进行传输的,所以需要用两个字节地址来寻址整个存储单元。
主机发送完字地址,从机正确应答后就把内部的存储单元地址指针指向该单元。如果读写控制位R/W位为“0”即写命令,从机就处于接收数据的状态,此时,主机就开始写数据了。写数据分为单次写(对于EEPROM而言,称为字节写)和连续写(对于EEPROM而言,称为页写),两者的区别在于发送完一字节数据后,是发送结束信号还是继续发送下一字节数据,如果发送的是结束信号,就称为单次写,如果继续发送下一字节数据,就称为连续写。要注意的是,对于EEPROM的页写,是不能发送超过一页的单元容量的数据的,当写完一页的最后一个单元时,地址指针指向该页的开头,如果再写入数据,就会覆盖该页的起始数据。
如果读写控制位R/W位为“1”即读命令,主机就处于接收数据的状态,从机从该地址单元输出数据。读数据有三种方式:当前地址读、随机读和连续读。当前地址读是指在一次读或写操作后发起读操作。由于I2C器件在读写操作后,其内部的地址指针自动加一,因此当前地址读可以读取下一个字地址的数据。也就是说上次读或写操作的单元地址为02时,当前地址读的内容就是地址03处的单元数据。
由于当前地址读极不方便读取任意的地址单元的数据,所以就有了随机读,随机读的时序有点奇怪,发送完器件地址和字地址后,竟然又发送起始信号和器件地址,而且第一次发送器件地址时后面的读写控制位为“0”,也就是写命令,第二次发送器件地址时后面的读写控制位为“1”,也就是读。为什么会有这样奇怪的操作呢?这是因为我们需要使从机内的存储单元地址指针指向我们想要读取的存储单元地址处,所以首先发送了一次Dummy Write也就是虚写操作,之所以称为虚写,是因为我们并不是真的要写数据,而是通过这种虚写操作使地址指针指向虚写操作中字地址的位置,等从机应答后,就可以以当前地址读的方式读数据了,因此随机地址读是没有发送数据的单次写操作和当前地址读操作的结合体。
至于连续读,对应的是当前地址读和随机读都是一次读取一个字节而言的,它是将当前地址读或随机读的主机非应答改成应答,表示继续读取数据。
SPI
简介
SPI即串行外设接口(Serial Peripheral Interface)的缩写,是美国摩托罗拉公司(Motorola)在20世纪80年代中期开发推出的一种同步串行传输规范,SPI主要应用于EEPROM、FLASH、ADC、DAC等芯片,还有数字信号处理器和数字信号解码器之间的于短距离高速通信。
特点
SPI是一种全双工同步通信。SPI有主、从两种模式,通常由一个主机和一个或多个从机组成(SPI不支持多主机),主机选择一个从机进行同步通信,从而完成数据的交换。提供时钟的为主机(Master),接收时钟的为从机(Slave),SPI接口的读写操作,都是由主设备发起,当存在多个从设备时,通过各自的片选信号进行管理。
SPI通信需要至少4根线,单向传输时3根线(如果从机设备允许的话,可直接将CS/SS线固定在低电平),它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)和CS/SS(片选):
SPI通信中有4种不同的操作模式,不同的从机设备可能在出厂时就被设置好了某种模式,并且无法更改。但是SPI通信必须处于同一种模式下才能进行。因此我们应该对自己手里的SPI主机设备进行模式的配置,也就是通过CPOL(时钟极性)和CPHA(时钟相位)来控制SPI主设备的通信模式,具体如下:
时钟极性(CPOL)定义了SCLK时钟线空闲状态时的电平:
- CPOL=0,即SCLK=0,表示SCLK时钟信号线在空闲状态时的电平为低电平,因此有效状态为高电平。
- CPOL=1,即SCLK=1,表示SCLK时钟信号线在空闲状态时的电平为高电平,因此有效状态为低电平。
时钟相位(CPHA)定义了数据位相对于时钟线的时序(即相位):
- CPHA=0,即表示输出(out)端在上一个时钟周期的后沿改变数据,而输入(in)端在时钟周期的前沿(或不久之后)捕获数据。输出端保持数据有效直到当前时钟周期的尾部边缘。对于第一个时钟周期来说,第一位的数据必须在时钟前沿之前出现在MOSI线上。也就是一个CPHA=0的周期包括半个时钟空闲和半个时钟置位的周期。
- CPHA=1,即表示输出(out)端在当前时钟周期的前沿改变数据,而输入(in)端在时钟周期的后沿(或不久之后)捕获数据。输出端保持数据有效直到下一个时钟周期的前沿。对于最后一个时钟周期来说,从机设备在片选信号消失之前保持MISO信号线有效。也就是一个CHPA=1的周期包括半个时钟置位和半个时钟空闲的周期。
此处的前沿和后沿的意思表示在每个周期中第一个出现的边沿和最后一个出现的边沿。总的来说则为:当时钟为正向时钟时,时钟线的上升沿为前沿,时钟的下降沿为后沿;当时钟为反向时钟时,时钟线的下降沿为前沿,时钟的上升沿为后沿。
传输过程
在SPI通信中,SPI主机设备以从机设备支持的频率通过SCLK线给到SPI从机设备,这点也意味着从机是无法主动向主机发送数据的,只能主机轮询向从机发或者从机设备主动通过一个IO口来告知主机数据到达。
在SPI每个时钟周期内,都会进行一次全双工数据的传输。主机通过MOSI线上发送1bit时,从机也会在读取到之后通过MISO线发送1bit数据出去。这说明,即使只进行单工通信,也会保持此通信顺序。
SPI传输通常涉及到两个给定了字长的移位寄存器。例如在主机、从机中的8bit的移位寄存器。它们以虚拟环形拓扑连接,数据通常先从最高有效位被移出。在时钟沿,主机和从机都移出1bit数据从传输线上给到对方。在下一个时钟边沿来到时,双方的接收器再对传输线上的该bit进行采样,并将其设置为移位寄存器的新的最低有效位。在寄存器位被移出和移入后,主机和从机交换了寄存器值。如果需要交换更多数据,则重新加载移位寄存器并重复该过程。传输可以持续任意数量的时钟周期。完成后,主机停止切换时钟信号。
SPI通信流程如下:
- SPI主机首先先将SS或CS线拉低,以此来告知SPI从机通信开始。
- 主机通过发送SCLK时钟信号,来告知从机即将进行的读写操作。这里的SCLK时钟信号是由SPI的模式来决定是高电平还是低电平有效的,这点在稍后会进行介绍。
- 主机(Master)将要发送的数据写到发送数据缓存区(Memory),缓存区经过移位寄存器(0~7),串行移位寄存器通过MOSI信号线将字节一位一位地移出去传送给从机,同时MISO接口接收到的数据经过移位寄存器一位一位地移到接收缓存区。
- 从机(Slave)也将自己的串行移位寄存器(0~7)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。
CAN
简介
CAN即控制器局域网总线(CAN,Controller Area Network),一种用于实时应用的串行通讯协议总线,它可以使用双绞线来传输信号,是世界上应用最广泛的现场总线之一。CAN协议用于汽车中各种不同元件之间的通信,以此取代昂贵而笨重的配电线束。该协议的健壮性使其用途延伸到其他自动化和工业应用。
特点
CAN通过两条通信线(双绞线)产生的电压差传输数据,一个CAN网络里的所有节点都挂在这两条通信线上,使用差分信号半双工通信。
CAN使用称为CANH/CANL的通信线路执行传输和接收。电位差较小的电信号称为隐性(Recessive)信号,其逻辑值为1。电位差较大的电信号称为显性(Dominant)信号,其逻辑值0。如果通信总线上发生显性和隐性(Recessive)冲突,则显性(Dominant)优先。总线空闲时保持隐性。
CAN总线的物理层逻辑电平,分为高速ISO11898标准(125kbps ~ 1Mbps)和低速ISO11519标准(10kbps ~ 125kbps),我们现在通常使用的CAN2.0,都是使用高速CAN标准,其物理层电平如下:
- CAN_H-CAN_L < 0.5V 时候为隐性的,逻辑信号表现为”逻辑1”。
- CAN_H-CAN_L > 0.9V 时候为显性的,逻辑信号表现为”逻辑0”。
关于CAN通信的电平传输,一个重要概念就是,CAN总线在电平传输上,具有仲裁判断逻辑,优先级为:显性(逻辑0)>隐形(逻辑1)。在理解CAN总线传输的整个过程中,主要就是清楚这一规则在传输时的灵活运用,并定义的各种帧形式。
传输过程
CAN的数据定义了有5种帧类型:
| 帧 | 帧用途 |
|---|---|
| 数据帧 | 用于节点向外发送数据 |
| 远程帧 | 用于向远端节点请求数据 |
| 错误帧 | 用于向远端节点通知校验错误,请求重新发送上一个数据 |
| 过载帧 | 用于通知远端节点,本节点尚未做好准备 |
| 帧间隔 | 用于将数据帧和远程帧与其他的帧分隔开来 |
其中,远程帧也常被称为远程帧。CAN的应用开发者只能使用“数据帧”和“远程帧”,其他的3种帧类型是由CAN的底层固件自动帮我们在特定场景下进行收发,开发者无需担心也无法直接参与控制。
不管是Classic CAN标准帧还是CANFD标准帧,其帧结构都由以下7个段组成:帧起始(SOF)、仲裁段(arbitration field)、控制段(control field)、数据段(data field)、CRC段(CRC field)、ACK段(ACK field)、帧结束(EOF)。
这7个段,每个段里又都有自己的格式细分,有两种格式:标准格式和扩展格式。对于仲裁段和控制段在标准帧与扩展帧里有不同的定义,其他段一致。CAN的应用开发者只使用其中的仲裁段、控制段和数据段。其他部分都由CAN底层固件自动封装。
帧起始由一个显性位(低电平)组成,发送节点发送帧起始,其他节点同步于帧起始;帧结束由7个隐形位(高电平)组成。
帧起始后是仲裁段,仲裁段决定了CAN通信的仲裁机制。只要总线空闲,总线上任何节点都可以发送报文,如果有两个或两个以上的节点开始传送报文,那么就会存在总线访问冲突的可能。但是CAN使用了标识符的逐位仲裁方法可以解决这个问题。帧ID越小,优先级越高。CAN总线控制器在发送数据的同时监控总线电平,如果电平不同,则停止发送并做其他处理。如果该位位于仲裁段,则退出总线竞争;如果位于其他段,则产生错误事件。
标准格式的仲裁段包括10位的帧ID和RTR位,其中RTR位用于指示这包数据是远程帧还是数据帧,数据帧的RTR位为显性电平,远程帧为隐性电平。所以帧格式和帧ID相同的情况下,数据帧优先于远程帧。而扩展格式的仲裁段包括10位的帧ID、SRR位、IDE位、18位的扩展帧ID和RTR位。其中SRR位替代远程帧请求位,为隐形;IDE位用于指示这包数据是标准帧还是扩展帧,标准帧的IDE位为显性电平,扩展帧的IDE位为隐形电平,对于前11位ID相同的标准帧(RTR为显性的远程帧)和扩展帧,标准帧优先级比扩展帧高。。可以看到,在标准格式里,仲裁段没有IDE位,其实这个位在标准格式里是放在控制段的第一位的,这样就正好可以和扩展格式的IDE位对应上进行仲裁了。
仲裁段之后紧跟控制段,控制段共6位,标准帧的控制段由IDE、保留位r0和数据长度代码DLC组成;扩展帧控制段则由保留位r1、r0和DLC组成。
- 保留位(r0、r1):保留位必须全部以显性电平发送。
- 数据长度码(DLC):数据的字节数必须为0~8字节。数据帧的DLC表示的就是当前包数据段所带的字节数,遥控帧的DLC表示的是请求返回的数据长度。
在标准格式里,IDE位放到了控制段的第一位,对应前文仲裁段的内容,可以使标准格式与扩展格式进行仲裁了。
之后是数据段,一个数据帧传输的数据量为0~8个字节。遥控帧的数据段长度固定为0。
后面是CRC段和ACK段,CAN使用CRC校验进行数据检错,CRC校验值存放于CRC段。CRC校验段由15位CRC值和1位CRC界定符构成。ACK段用于当一个接收节点接收的帧起始到CRC段之间的内容没发生错误时,它将在ACK段发送一个显性电平
CAN数据在收发上除了会遵循以上数据格式定义之外,还有一个“位填充”的底层规则(类似通信协议里的“转义符”),这个操作是在CAN的底层固件中自动判断执行的,其目的是为了增强数据正确性,以便识别错误信号。
为防止突发错误而设定,CAN协议中规定,当相同极性的电平持续五位时,则添加一个极性相反的位。填充位的添加和删除是由发送节点和接收节点完成的,CAN总线只负责传输,不会操纵信号。
- 对于发送节点而言:在发送数据帧和遥控帧时,对于SOF~CRC(除去CRC界定符)之间的位流,相同极性的电平如果持续5位,那么在下一个位插入一个与之前5位反型的电平;
- 对于接收节点而言:在接收数据帧和遥控帧时,对于SOF~CRC(除去CRC界定符)之间的位流,相同极性的电平如果持续5位,那么需要删除下一位再接收。如果这个第6个位的电平与前5位相同,将被视为错误并发送位填充错误帧。
参考
[1] 17岁boy想当攻城狮. UART工作原理详解
https://blog.csdn.net/bjbz_cxy/article/details/120020250
[2] 一个人一支队伍. 基础通信协议之IIC详细讲解
https://zhuanlan.zhihu.com/p/556505577
[3] 亿佰特物联网应用. SPI通信协议详解,一篇就够!
https://baijiahao.baidu.com/s?id=1746087964061209214&wfr=spider&for=pc
[4] 不脱发的程序猿. 一文搞懂SPI通信协议
https://zhuanlan.zhihu.com/p/503777954
[5] EXyang. CAN通信讲解
https://zhuanlan.zhihu.com/p/538834760
[6] 百度百科. CAN总线协议
https://baike.baidu.com/item/CAN%E6%80%BB%E7%BA%BF%E5%8D%8F%E8%AE%AE/1789683