一文讲透SPI总线和IIC总线通信1

电子说

1.3w人已加入

描述

上一章所讲的UART通信属于异步通信,比如计算机发送数据给单片机,计算机只负责把数据通过TXD发送出来,接收数据是单片机自己的事情。而IIC和SPI都属于同步通信,收发双方需要一条时钟线来控制收发双方的通信节奏。

从应用上来讲,UART通信多用于板间通信,比如单片机和计算机,这个设备和另外一个设备之间的通信。而IIC和SPI多用于板内通信,比如使用IIC进行单片机和EEPROM的通信,比如使用SPI进行单片机和FLASH之间的通信。

16.1 IIC总线协议

在硬件上,IIC总线是由时钟总线SCL和数据总线SDA两条线构成,其中SCL为时钟线,SDA为数据线,如图6-1所示。总线上可以同时连接多个器件,所有器件的SCL都连到一起,所有SDA都连到一起。

uart

6-1 IIC总线时序图

起始信号:IIC通信的起始信号的定义是SCL为高电平期间,SDA由高电平变化到低电平产生的一个下降沿,表示一次通信过程的开始,如图6-2中的Start部分所示。

数据传输:IIC通信是高位在前,低位在后。IIC通信要求当SCL在低电平的时候,SDA允许变化,也就是说,发送方必须先保持SCL是低电平,才可以改变数据线SDA,输出要发送的当前数据的一个位;而当SCL在高电平的时候,SDA绝对不可以变化,因为这个时候,接收方要来读取当前SDA的电平信号是0还是1,因此要保证SDA的稳定,如图6-1中的每一位数据的变化,都是在SCL的低电平位置。8位数据位后边跟着的是一位应答位,应答位我们后边还要具体介绍。

停止信号:IIC通信停止信号的定义是SCL为高电平期间,SDA由低电平向高电平变化产生一个上升沿,表示一次通信过程的结束,如图6-1中的Stop部分所示。

26.2 IIC寻址模式

上一节介绍的是IIC每一位信号的时序流程,而IIC通信在字节级的传输中,也有固定的时序要求。IIC通信的起始信号(Start)后,首先要发送一个从机的地址,这个地址一共有7位,紧跟着的第8位是数据方向位(R/W),“0”表示接下来要发送数据(写),“1”表示接下来是请求数据(读)。

Kingst-32F1板子上的EEPROM器件型号是24C02,在24C02的数据手册3.6节中可查到,24C02的7位地址中,其中高4位是固定的0b1010,而低3位的地址取决于具体电路的设计,由芯片上的A2、A1、A0这3个引脚的实际电平决定。IIC总线器件是开漏引脚,因此外部要添加上拉电阻,保证总线空闲时为高电平。来看一下24C02的电路图,如图6-2所示。

uart

从图6-2可以看出来,A2、A1、A0都是接的GND,也就是说都是0,因此24C02的7位地址实际上是二进制的0b1010000,也就是0x50。

IIC通信分为标准模式100kbit/s、快速模式400kbit/s和高速模式3.4Mbit/s。因为所有的IIC器件都支持标准模式,但却未必支持另外两种速度,所以作为通用的IIC程序我们选择100k这个速率来实现,也就是说实际程序产生的时序必须小于等于100k的时序参数,有特殊速度需求的器件再针对性写高速通信程序。

IIC引脚属于开漏并联结构,并且STM32的GPIO端口引脚设置为开漏输出时,可以直接从输入数据寄存器获取I/O电平状态,因此将IIC引脚配置为开漏输出模式。由于IIC总线空闲时默认为高,初始化时还需要设置引脚输出高电平,不过设置引脚输出高电平并不是在初始化之后,而应该放在初始化之前。这是因为STM32在上电复位时I/O口为高阻状态,复位结束后,GPIO端口引脚默认为浮空输入,由于上拉电阻的存在,IIC引脚被拉高;当程序执行到IIC初始化时又被配置为开漏输出模式,由于GPIO端口输出数据寄存器初始值默认全为0,初始化后I/O口输出低电平,如果初始化之后再设置引脚输出高电平,势必会在I/O口上产生一个低电平的毛刺。如果在IIC引脚初始化之前先设置输出数据寄存器相应位为高,初始化IIC引脚后,I/O口会直接输出高电平,避免毛刺信号。

以下是IIC总线的驱动程序:

uart

uart

uart

uart

26.3 初识EEPROM

在实际的应用中,保存在单片机RAM中的数据,掉电后就丢失了,保存在单片机内部FLASH中的数据,又不能随意改变。但是在某些场合,我们需要记录下某些数据,而它们还需要时常改变或更新,并且掉电之后数据还不能丢失,比如我们的家用电表度数,电视机里边的频道记忆,一般都是使用EEPROM来保存数据,特点就是掉电后不丢失。Kingst-32F1板子上使用的这个器件是24C02,是一个容量大小是2Kbits,也就是256个字节的EEPROM。一般情况下,EEPROM拥有30万到100万次的擦除寿命,也就是它可以反复写入30-100万次,而读取次数是无限的。

24C02是一个基于IIC通信协议的器件,因此从现在开始,IIC和EEPROM就要合体了。但要分清楚,IIC是一个通信协议,它拥有严密的通信时序逻辑要求,而EEPROM是掉电后数据不丢失的一种存储器件的统称,24C02就属于EEPROM,只不过24C02采样了IIC协议的接口与单片机相连而已,二者并没有必然的联系,EEPROM可以用其它接口,IIC也可以用在其它很多器件上。

46.4 EEPROM单字节读写操作时序

STM32F103系列单片机本身自带硬件IIC模块,可以类似USART通信那样,通过配置实现数据的收发。本书对IIC协议的介绍针对的是绝大多数的应用场合,实际上IIC的配置过程比较复杂,比如要充分考虑冲突和仲裁等处理方式,但是那些处理方式在绝大多数场合用不到。STM32F103系列自带的IIC协议模块设计的过于复杂,对于实际应用来讲实用性不强,因此实际应用IIC时,还是用IO口直接模拟协议。本书原则就是实际开发采用何种用法,就重点介绍何种用法,下面用实例来实现一下EEPROM读写的基本流程。

1、EEPROM写数据流程

第一步,首先是IIC的起始信号,接着跟上首字节,也就是前边讲的IIC的器件地址,并且在读写方向上选择“写”操作。

第二步,发送数据的存储地址。24C02一共256个字节的存储空间,地址从0x00~0xFF,想把数据存储在哪个位置此刻写的就是哪个地址。

第三步,发送要存储的数据,注意在写数据的过程中,EEPROM每个字节会回应一个“应答位0”,来通知用户写EEPROM数据成功,如果没有回应答位,说明写入不成功。单字节写时序如图6-3所示。

uart

图6-3 IIC Byte Write时序图

2、EEPROM读数据流程

第一步,首先是IIC的起始信号,接着跟上首字节,也就是前边讲的IIC的器件地址,并且在读写方向上选择“写”操作。这个地方可能有读者会诧异,明明是读数据为何方向也要选“写”呢?刚才说过了,24C02一共有256个地址,选择写操作是为了把所要读的数据的存储地址先写进去,告诉EEPROM将要读取哪个地址的数据。这就如同打电话,先拨总机号码(EEPROM器件地址),而后还要继续拨分机号码(数据地址),而拨分机号码这个动作,主机仍然是发送方,方向依然是“写”。

第二步,发送要读取的数据的地址,注意是地址而非存在EEPROM中的数据,通知EEPROM要哪个分机的信息。

第三步,重新发送IIC起始信号和器件地址,并且在方向位选择“读”操作。

这三步当中,每一个字节实际上都是在“写”,所以每一个字节EEPROM都会回应一个“应答位0”。

第四步,读取从器件发回的数据,读一个字节,如果还想继续读下一个字节,就发送一个“应答位ACK(0)”,如果不想读了,告诉EEPROM,不想要数据了,别再发数据了,那就发送一个“非应答位NAK(1)”。

每读一个字节,地址会自动加1,那如果想继续往下读,给EEPROM一个ACK(0)低电平,那再继续给SCL完整的时序,EEPROM会继续往外送数据。如果不想读了,要告诉EEPROM不要数据了,直接发送一个NAK(1)高电平。24C02读数据时序如图6-4所示。

uart

图6-4 IIC Read时序图

利用EEPROM单字节读写功能设计了一个记录开发板复位次数的小程序。由于EEPROM中的数据很容易被擦除或者改写,为了保证记录数据的准确性,需要对读出的数据进行校验。这里向大家介绍一种简单又实用的校验方法:将复位次数保存在EEPROM地址0x00中,并对复位次数按位取反后保存在地址0x01中。开发板每次复位后先去读取地址0x00和0x01中的数据,对其进行异或运算,如果运算结果为0xFF,表明数据正确,将地址0x00中的数据加1后,重新写入到EEPROM中,并通过数码管显示读出的数据;否则记录的数据被改写,从0开始重新记录复位次数。由于板载数码管仅能显示两位数,记录的最大次数为99,超过99重新开始记录。具体代码如下:

uart

uart

uart

56.5 EEPROM多字节读写操作时序

读取EEPROM很简单,EEPROM根据时序直接把数据送出来,但是写EEPROM却没有这么简单了。给EEPROM发送数据后,先保存在EEPROM的缓存,EEPROM必须要把缓存中的数据搬移到“非易失”的区域,才能达到掉电不丢失的效果。而往非易失区域写需要一定的时间,每种器件不完全一样,ATMEL公司的24C02的这个写入时间最长不超过5ms。在往非易失区域写的过程,EEPROM是不会再响应用户访问的,不仅接收不到用户的数据,即使用I^2^C标准的寻址模式去寻址,EEPROM都不会应答,就如同这个总线上没有这个器件一样。数据写入非易失区域完毕后,EEPROM再次恢复正常,可以正常读写了。

在向EEPROM连续写入多个字节的数据时,如果每写一个字节都要等待几ms的话,整体上的写入效率就太低了。因此EEPROM的厂商就想了一个办法,把EEPROM分页管理。24C01、24C02这两个型号是8个字节一个页,24C04、24C08、24C16是16个字节一页。Kingst-32F1开发板上用的型号是24C02,一共是256个字节,8个字节一页,那么就一共有32页。

分配好页之后,如果在同一个页内连续写入几个字节后,最后再发送停止位的时序。EEPROM检测到这个停止位后,会一次性把这一页的数据写到非易失区域,就不需要写一个字节检测一次了,并且页写入的时间也不会超过5ms。如果写入的数据跨页了,写完一页之后要发送一个停止位,然后等待并且检测EEPROM的空闲模式,一直等到把上一页数据完全写到非易失区域后,再进行下一页的写入,这样就可以在很大程度上提高数据的写入效率,页写时序如图6-5所示。

uart

图6-5 EEPROM页写时序

本节利用EEPROM多字节读写功能设计了一个通过串口发送指令控制EEPROM读写数据的例程。该例程只需要下载一次程序,就能通过串口调试助手实现对EEPROM任意地址的读写操作,避免重复编程控制EEPROM读写数据的方式,具有很高是实用意义。与第五章串口实用例程类似,通过串口调试助手发送控制指令,单片机检测到指令后做出相应动作。

EEPROM读数据指令格式:“e2read 地址 字节长度”,其中地址范围为0~255,e2read、地址、字节长度之间由空格隔开,比如从地址1开始读取5字节数据:e2read 1 5。单片机收到指令后执行多字节读操作,通过串口助手返回读出的数据。

EEPROM写数据指令格式:“e2write 地址 数据”,地址范围为0~255,e2write、地址、数据之间同样由空格隔开,比如从地址1开始写入hello:e2write 1 hello。单片机收到指令后执行多字节写操作,写入成功后通过串口助手返回“e2write done.”

如果发送指令格式错误,返回“bad parameter.”,如果发送指令错误,将返回发送的指令。

uart

uart

uart

uart

uart

uart

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分