二、常用通信协议,摸索探究:
5.温度传感器实时采集温度
温度传感器、重力传感器等等这些传感器模块在生活中有非常广泛的应用,只是可能大家平时没有留心去观察。举个现实中的例子,比如我们经常用的平板电脑或者电子书阅读器都有“自动旋转”的功能,这里面其实就是用了重力传感器,硬件上重力传感器去检测当前用户使用设备的姿势,并传入SOC芯片一个信号,软件上再根据该信号去改变屏幕的旋转角度。同样的温度传感器也使用广泛,因为现在很多的嵌入式产品都带有温度显示或者温度检测的功能,这里毫无疑问都会用到温度传感器去实时采集当前设备的温度,其中典型的就有:LM75和DS18B20,它们是实际应用最多的两种温度传感器。
如图1所示,豌豆开发板上配有一颗NXP原装的LM75温度传感器,同时LM75也是一款支持IIC通信的外设,在这个例程中通过读取LM75的当前采样温度,我们也实际动手用verilog实现IIC的底层时序逻辑,IIC通信协议在实际工作中很常用,相信大家在学习实践过这个例程后一定可以触类旁通,举一反三地去完成后续例程的相关设计。
图1 豌豆开发板Artix7上LM75电路
IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备,是典型的半双工通信方式,IIC总线有两根双向的信号线一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步,如图2所示是IIC总线的硬件拓扑结构,在一般情况下硬件上对于SDA和SCL都会连接4.7K或10K上拉电阻。
图2 IIC总线的硬件拓扑结构
对于IIC总线协议,虽然属于低速总线,但其较UART和SPI相对复杂些,笔者上学读书的时候在学习这部分内容,看了很多相关的教程,对此说得也都模糊不清,随着工作的积累,在工作以后实践才慢慢搞清楚IIC总线的通信原理和底层逻辑,在这里笔者把对IIC总线协议的总结和理解详细地分享给大家。
IIC因为其总线硬件结构简单,成本较低,在各个领域得到了广泛的应用。IIC 标准速率为 100kbit/s,快速模式为 400kbit/s,是一种多主机总线,连接在IIC总线上的器件分为主机和从机,主机有权发起和结束一次通信,而从机只能被主机呼叫;当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能来防止错误产生;每个连接到IIC总线上的器件都有一个唯一的地址(7bit),且每个器件都可以作为主机也可以作为从机,但同一时刻只能有一个主机,总线上的器件增加和删除不影响其他器件正常工作;IIC总线在通信时总线上发送数据的器件为发送器,而接收数据的器件为接收器,如下图3所示是IIC总线的通信示意图。
图3 IIC总线通信示意图
如图4所示是IIC总线从机寻址示意图,IIC总线上数据传输是广义的,既包括地址又包括数据。主机在发送起始信号后必须先发送一个字节的数据,该数据的高7位为从机地址,最低位表示后续字节的传送方向,其中0表示主机发送数据,1表示主机接收数据;总线上所有的从机接收到该字节数据后都将这7位地址与自己的地址进行比较,如果相同,则认为自己被主机寻址,然后再根据第8位将自己定为发送器或接收器。
图4 IIC总线从机寻址示意图
如图5所示非常形象地说明了IIC总线的起始信号S和终止信号P的电平变化,SCL为高电平时,SDA由高变低表示起始信号,SCL为高电平时,SDA由低变高表示停止信号,起始信号和停止信号都是由主机发出,起始信号产生后总线处于占用状态,停止信号产生后总线处于空闲状态。
图5 IIC总线起始信号S和终止信号P示意图
如图6所示是IIC总线主从机应答示意图,IIC总线通信时每个字节为8位长度,数据传送时,先传送最高位,后传送低位,发送器发送完一个字节数据后接收器必须发送1位应答位来回应发送器即一帧共有9位。
图6 IIC总线主从机应答示意图
如图7所示是 IIC总线数据保持和建立时间示意图,IIC总线在进行数据传送时,时钟线SCL为低电平期间发送器向数据线上发送一位数据,在此期间数据线上的信号允许发生变化,时钟线SCL为高电平期间接收器从数据线上读取一位数据,在此期间数据线上的信号不允许发生变化,必须保持稳定。
图7 IIC总线数据保持和建立时间示意图
结合上面的具体说明,我们可以对IIC总线通信过程和寻址方式归纳如下:
1.主机发送起始信号启用总线;
2.主机发送一个字节数据指明从机地址和后续字节的传送方向;
3.被寻址的从机发送应答信号回应主机;
4.发送器发送一个字节数据;
5.接收器发送应答信号回应发送器;
… … (循环步骤4、5)
n.通信完成后主机发送停止信号释放总线
可能有的同学看完上面的说明和介绍后,还是对IIC总线协议有些不太理解,那么下面笔者就画图进行更加细节上的说明,如图8、9、10所示是典型的IIC总线通信时序图,其中阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送,A表示应答,A非表示非应答,S表示起始信号,P表示终止信号,图片非常直观明了,对照上面的归纳总结很容易理解,所以在这里不过多赘述了,如果还存在疑问,那么请大家一起来学习实践完这个例程后就会自然而然地理解了。
图8 IIC总线主机向从机发送数据示意图
图9 IIC总线从机向主机发送数据示意图
图10 IIC总线主机先向从机发送数据,然后从机再向主机发送数据示意图
在熟悉了IIC总线通信的底层逻辑后,我们回过头来再阅读NXP的LM75芯片手册,把对于FPGA设计上的有效信息提取出来,这时候结合实例,回顾前面介绍的IIC总线协议知识就更容易加深理解了。
如图11所示是LM75的地址定义说明,正如前面所说,IIC总线上每个设备都有唯一的标识地址,这里手册写得很清楚对于LM75的7位地址,高4位固定是4 'b 1001,而对于低3位则有A2、A1、A0决定,如果在PCB板上连接到GND是逻辑0,如果连接到VCC是逻辑1,在原理图中我们把LM75的三个引脚都接GND了,所以LM75的7位地址是7 'b 1001000,然后大家需要注意在IIC通信过程中主机发送地址时,前7位是从机的物理地址,最后1位是0表示主机发送数据,1表示主机接收数据,在这个例程中我们需要对LM75读取采集温度数据,所以主机发送地址的应该是8'b10010001。
图11 LM75的地址定义说明
如图12所示是LM75中对MSB和LSB数据的定义,手册里标明了温度传感器的有效最低分辨率是0.125摄氏度,MSB的8位和LSB的前3位共同组成了一个11位的有效采集数据,而这个数据即是当前温度值的补码,所谓补码即正数的补码是它本身,负整数的补码是符号位不变,其余位按位取反加一!
图12 LM75中对MSB和LSB数据的定义
进一步地,如图13所示是LM75的温度采集数据解析示意图,举例说明11位的采集数据应该怎么计算,再具体折合成当前的温度值,感兴趣的同学可以把表中的例子动手算一算,其实说到底当前的温度值即上述11位的有效采集数据取原码值再乘以0.125即可。
图13 LM75的温度采集数据解析示意图
如图14所示是LM75读温度采集值的IIC时序逻辑图,LM75也有几种不同类型的IIC读写逻辑,感兴趣的同学可以仔细深入研究下芯片手册,当然下面这种用法是LM75最普遍的读取方式,介绍到这里大家不妨可以回过头看一看这和前面的图2-27的IIC总线通信时序图恰好完全吻合,即IIC总线主机先向从机发送数据,然后从机再向主机发送数据。
图14 LM75读温度采集值的IIC时序逻辑图
在详细说明IIC总线协议的通信细节,以及提取LM75温度传感器的有效信息后,下面就到了具体编码的环节,在这个例程中我们实现如下功能:每次用户按下豌豆开发板按键,即会触发一次LM75采集当前温度的动作,FPGA再把LM75采集到的温度数据从BCD码转换成ASCII码,最后通过RS232打印到上位机串口助手上显示。
这里需要两个核心模块即i2c_lm75和lm75bcd_ascii,前者是LM75的IIC读取控制模块,后者是将LM75读取后的MSB和LSB组成的16位数据转换成对应温度的ASCII码模块,如表1和2所示是两个模块的信号列表,下面简要说明两个模块的核心设计内容。
信号列表 | ||
信号名 | I/O | 位宽 |
clk | I | 1 |
rst_n | I | 1 |
lm75_en | I | 1 |
i2c_sda | I/O | 1 |
i2c_scl | O | 1 |
lm75_dout | O | 16 |
lm75_dout_vld | O | 1 |
表1 i2c_lm75模块信号列表
信号列表 | ||
信号名 | I/O | 位宽 |
clk | I | 1 |
rst_n | I | 1 |
din | I | 16 |
din_vld | I | 1 |
lm75_fifo_rdy | I | 1 |
lm75_ascii_dout | O | 8 |
lm75_ascii_dout_vld | O | 1 |
表2 lm75bcd_ascii模块信号列表
对于i2c_lm75模块,需要在这里介绍FPGA中对三态门的设计,典型地如IIC,MDIO等等接口,对于FPGA来说是inout信号,即是输出信号同时也是输入信号,一般性的设计方法即是用dir信号区分,dir信号为高对于FPGA表示输出,dir信号为低对于FPGA表示输入。如下图15所示是FPGA控制IIC总线的三态门示意图,通过i2c_sda_dir信号控制i2c_sda_out和i2c_sda_in信号作为FPGA的输出和输入,再连接到外围器件的i2c_sda上,这样设计也简化了代码的逻辑,提高了代码的可读性。
图15 FPGA控制IIC总线的三态门示意图
如图16所示是LM75读取温度采集数据的状态机,模块中选取SCL是250Khz时钟,大家回过头对照前面的说明,就可以非常清晰地理解整个模块的状态机设计思想了,在这里不再赘述了,LM75的IIC读取控制模块的整体代码设计如图17所示,搞清楚逻辑后,就非常容易理解。
图16 LM75读温度采集数据的状态机
图17 IIC读取LM75采样温度模块的代码设计
在lm75bcd_ascii模块中,我们需要把从LM75读到的MSB+LSB组成的16位数据转化成ASCII码,通过前面阅读LM75数据手册,大家了解到这16位数据,只有前11位是有效的,其中最高位0代表温度为正值,1代表温度为负值,同时如果是负值则需要把当前温度值取补码再做计算,最后把10位的采集数据值乘0.125摄氏度,即为当前采样温度值,考虑到是乘0.125即除8的关系,所以在模块设计中笔者需要对已经取补码后的10位的lm75_value值再做处理:前7位直接作为整数位,这样就完成了右移3位即除8的效果,后3位作为小数位将其转化成0、125、250、375、500、675、750、875,然后整数位百位、十位、个位逐位取整并转换成ASCII 码,同样的小数位也第一位、第二位、第三位逐位取整并转换成ASCII码,温度正负号、小数点前三位、小数点、小数点后三位、回车行组成72位9个ASCII码写入FIFO中,下游的串口发送模块再把FIFO中的数据打印到上位机的串口调试助手上,如图18所示是该模块的代码设计,供大家参考。
图18 BCD码转ASCII码模块的代码设计
如图19所示是温度传感器实时采集温度顶层文件的例化,大家把各个模块之间的相关信号例化到一起即可,这里key_scan和uart_transfer模块在前面的例程中已经详细说明,忘记的同学可以回过头复习下,笔者也在工程里加入了ILA方便大家上板观察信号波形,如图20所示是该例程的上板现象,按下豌豆开发板按键,即可触发一次LM75采集温度,再转换成ASCII码打印到串口助手上显示。
图19 温度传感器实时采集温度顶层文件的例化
图20 串口调试助手打印温度采集值