本人FPGA小白,对FPGA比较感兴趣,前段时间跟某位同学讨论I2C总线通讯协议,我以前写过关于串口和SPI的通讯协议,还没有接触过I2C总线通讯协议。这次就抱着试试看心态,去了解了下I2C总线通讯协议。
结果就是,I2C通讯的复杂程度远超串口和SPI,我查找了一些关于I2C总线通讯的资料内容,比较不开心的是,各类资料对于I2C总线通讯协议的描述不尽相同,我选了其中的两篇我能接受的资料作为这次博客内容的基础,一篇是对I2C总线通讯的基本描述,一篇是其中关于协议通讯中ACK过程的描述,在接下来的内容中,会用到两篇资料中的内容,以下是两篇资料的链接:
2.对I2C总线时序的一点理解以及ACK和NACK(NAK)
I2C总线通讯协议概述:
1.信号线描述
通讯协议为串行通讯协议,总共用到两根双向信号线,为SDA与SCL,其中SDA为数据线,SCL为时钟线;总线上通过上拉电阻接正电源,当总线空闲时,两根信号线均为高电平,连接到总线上的任一设备输出低电平时都会使总线拉低,表面两根信号线均为线“与”逻辑。
SCL为时钟线,为OD门,当上升沿时将数据输入到EEPROM中,下降沿时驱动EEPROM输出数据。
SDA也为OD门,输出与其他OD门或者OC门构成线与逻辑。
2.主从设备区分
协议区分主设备与从设备,每个从设备拥有对应于自己的7位地址码,前4位为器件类型,由厂家决定,而后三位则由用户自己定义。同一时间,主设备只能与一个从设备通讯,从设备挂载在总线上的数量由地址码位数以及总线最大电容400pf限制。
主设备在通讯中主要承担提供SCL时钟,控制信息读写流向,决定通讯的开始与结束的任务。
从设备则是提供和接收信息并于主机进行交互。
3.协议的简要内容
3.1 开始和结束信号
协议有自己的通讯帧,每帧都有开始信号与结束信号
开始信号:当SCL为高电平期间,将SDA信号从高电平拉低,则构成开始信号,此时总线将由空闲状态转为被占用状态,各从机将准备好从主机接收数据。
结束信号:当SCL为高电平期间,将SDA信号从低电平拉高,则构成结束信号,此时总线将被释放,从占用状态变为空闲状态,通讯结束。
在通讯过程中,SCL为高电平时,SDA均不允许发生变化,否则将会被视作开始或者结束信号,导致通讯出错。
3.2 ACK状态
ACK状态是I2C区别串口和SPI的一个很大的地方,当发送方发送8位数据后,在第9个SCL时钟上升沿之前需要释放总线(在我写的程序中,我选择在第9个时钟的下降沿),从接收方接收一个来自SDA的信号,该信号在第9个SCL时钟的高电平期间应该保持不变,对于接收方而言,若成功接收这8位数据,在总线释放期间,将SDA线拉低,表示ACK;若无法接收这8位数据,在总线释放期间,需要将SDA线拉高,表示NACK,通知发送方结束本次发送。
如果接收方是主设备,则在接收到从设备发来的最后一位数据后,在结束信号前,需要发送一个NACK状态,以通知从设备发送方释放总线,使主设备可以发送结束信号结束本次通讯。
3.3 I2C总线读写部分
I2C总线的读写过程有相同的部分也有各自区别,对于单个8位数据读写过程而言,从主机角度看,写过程需要写入3个8位数据,分别为从设备地址与写标志(7位从地址,写标志),从设备子寄存器地址(有的设备可能不需要),写入数据;而读过程需要写3个8位数据,读一个8位数据,分别为写从设备地址与写标志,写从设备子寄存器地址,写从设备地址与读标志,读出子寄存器数据。
3.3.1主设备向从设备写
如前述,主设备发送开始信号,接下来发送7位从设备地址和写标志信号(低电平),与之匹配的从机继续通讯过程,之后主机向匹配到的从机发送要写入的子寄存器地址,随后传送写入的数据,最后发送结束信号结束本次通讯。
更加具体的过程可以参看我给的链接一中的资料,这里直接上图:
3.3.2 主设备从从设备读
读过程第一次接触的时候感觉非常的怪异,就我个人的习惯看,我觉得应该只需要改变写过程中的读写标志信号和最后一个字节的信号流向就可以改变读写过程,可事实却不是这样,读过程远比写过程复杂的多。
读过程大致过程如下:
1)发送开始信号
2)发送7位从机地址和写标志信号,接收ACK信号
3)发送8位从机子寄存器地址,接收ACK信号
4)重启开始信号
5)发送7位从机地址和读标志信号,接收ACK信号
6)读出SDA上传过来的从机子寄存器数据,发送NACK信号
7)发送结束信号
4.主机模块FPGA实现与仿真
I2C通讯协议可以有很多种实现方法,可以对硬件的I2C电路控制编程实现,也可以用模拟GPIO的时序方法实现,在这里我用的就是后者,毕竟FPGA直接写接口就是模拟GPIO时序的方式。
软件版本是ISE14.7,仿真工具用的Moesim_10.1c,主要内容包括:
I2C_Master.v-----------I2C通信协议的通用主机读写模块
SI5338_Init.v------------通过I2C通信的方式初始化时钟芯片SI5338,完成将输入25MHz时钟倍频为50MHz的任务
I2C_Master_tb.v-------I2C_Master.v的testbench