FPGA基础协议二:I2C读写E2PROM
一、I2C与E²PROM
I2C常用念法:I²C(读作"I-squared-C" ),还有可选的拼写方式是I2C(读作I-two-C)以及IIC(读作I-I-
C),在中国则多以**“I方C”**称之。
1. I2C
I2C:(Inter-Integrated Circuit)即集成电路总线,是一种两线式串行总线,由PHILIPS公司开发,用于连接微控制器及其外围设备。多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。
-
I2C总线由数据线SDA和时钟线SCL构成双向可收发的通信线路
-
是一种半双工通信协议。
-
总线上的主设备与从设备之间以字节(8bit)为单位进行双向的数据传输。(升级版UART?)
1.1 物理层
-
它是一个支持多设备的总线(支持多主机多从机)。
-
I2C总线只使用两条总线线路,一条双向串行数据线(SDA) 一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
-
每个连接到I2C总线的设备都有一个独立的地址,主机可以利用设备独立地址访问不同设备。
-
I2C总线通过上拉电阻接到电源。当I2C设备空闲时,设备会输出高阻态,当所有设备都空闲,都输出高阻态时,由上拉电阻把I2C总线拉成高电平。
-
I2C总线具有仲裁机制1。
-
具有三种传输模式:
**标准模式:**传输速率为100kbit/s
**快速模式:**传输速率为400kbit/s
**高速模式:**传输速率为3.4Mbit/s(不支持E2PROM)
老师说他们一般写200kbit/s
1.2 协议层
-
I2C协议空闲状态:SCL与SDA均保持高电平,并且此时无I2C设备工作。
-
I2C协议的起始信号 :SCL保持高电平的同时SDA拉低。
-
I2C协议的数据传输状态:在SCL低电平时更新SDA的数据信息。
SCL高电平时改变SDA会误发送起始/停止信号。
-
应答信号:发送端每发送一个字节,就必须在第9个SCL脉冲期间释放SDA,由接收端反馈一个应答信
号。
-
ACK:有效应答,表示成功接收端接收了该字节,低电平表示
-
NACK:非应答,表示接受失败、传输结束,高电平表示。
-
-
I2C协议的停止信号:SCL高电平时拉高SDA。
把起始位看作一个时钟周期,8位数据位8个时钟周期,应答位一个时钟周期,约为10个时钟周期。所以这里可以与UART协议进行比对,在不算校验位且每次传输1字节的uart数据帧也是10bit,冥冥之中感到了某种联系。
I2C到此为止,感觉像是一个强化以后的UART协议
2. E2PROM
EEPROM,或写作E2PROM,全称电可擦除可编程只读存储器 (英语:Electrically-Erasable Programmable Read-Only Memory),是一种可以通过电子方式多次复写的半导体存储设备。相比EPROM,EEPROM不需要用紫外线照射,也不需取下,就可以用特定的电压,来抹除芯片上的信息,以便写入新的数据。
我的C4开发板使用的是型号为24LC04B的EEPROM存储芯片。24LC04B的存储容量为512Bytes/4Kbits,其内部有两个Block,每个Block中有256个字节(一个字节为8bit)。其读写操作都是以字节(8bit)为基本单位。 24LC04B EEPROM 存储芯片的器件地址包括:
- 厂商设置的高4位1010,这里表设备代码。
- 用户需自主设置的低3位 x、x、B0 来选择块地址。
- 字节存储地址,一共8bit。
在IIC主从设备通讯时,主机在发送了起始信号后,接着会向从机发送控制命令。控制命令长度为1个字节,它的高7位为上文讲解的 IIC设备的器件地址,最低位为读写控制位。EEPROM储存芯片控制命令格式示意图,具体见下图。
因为只有两个块,所以只设置B0即可,前面两位不会对结果造成影响。
- 读写控制位为 0 时,表示主机(FPGA)要对从机(EEPROM)进行数据
写入
操作:{7’b1010xxx,1’b0} - 读写控制位为 1 时,表示主机(FPGA)要对从机(EEPROM)进行数据
读出
操作:{7’b1010xxx,1’b1}
2.1 写操作
字节写(BYTE WRITE)操作:一次写入1个字节。在数据信号线SDA上,发起始位(START)->写写控制字(Control Byte)->接收ACK->写字节地址(Word Address)->接收ACK->写数据(Data) ->接收ACK->发停止位(STOP)。
页写(PAGE WRITE)操作:一次写入16个字节(每字节为8bit)数据。在数据信号线SDA上,发起始位(START)->写写控制字(Control Byte)->接收ACK->写字节地址(Word Address)->接收ACK->写数据(Data(n)) ->接收ACK->写数据(Data(n+1))->接收ACK->(一共发送16字节(Byte)数据,中间数据省略)->写数据(Data(n+15))->接收ACK->发停止位(STOP)。
2.2 读操作
当前地址读(Current Address READ)操作:在数据信号线SDA上,发起始位(START)->写读控制字(Control Byte)->接收ACK->接收读数据(Data)->发No ACK->发停止位(STOP)。
随机地址读(RANDOM READ)操作:在数据信号线SDA上,发起始位(START)->写写控制字(Control Byte)->写读地址(Word Address)->接收ACK->再发起始位(START)->写读控制字(Control Byte)->接收读数据(Data)->发No ACK->发停止位(STOP)。
顺序地址读(SEQUENTIAL READ)操作:在数据信号线SDA上,发起始位(START)->写写控制字(Control Byte)->写读地址(Word Address)->接收ACK->再发起始位(START)->写读控制字(Control Byte)->接收读数据(Data(n)) ->接收ACK->接收读数据(Data(n+1)) ->接收ACK->…->接收读数据(Data(n+x)) ->发No ACK->发停止位(STOP)。
我更愿意把它叫做 页读,对应页写都是一次性读取多个字节,只不过它可以从指定地址一直连续读完整个内存。
然后要注意的是No Ack,这代表着是由I2C向E2PROM发送应答位,高电平表示?
二、逻辑设计
1. 实验需求
-
使用按键,模拟读写请求信号
-
收到读写请求信号时,FPGA通过I2C协议向E2PROM芯片写入单字节数据或从E2PROM芯片读出单字节数据;
-
使用串口来接收需要写入的数据和显示被读出的数据
2. 设计思路
- 一提到按键就要想到按键消抖模块。
- 需要一个读写控制模块与I2C接口模块:
- 读写控制模块负责指挥I2C接口模块发送读写命令、地址、数据,以及使用两个fifo分别寄存uart写入数据和E2PROM读出数据。
- 2C接口模块负责将这些字节并串转换为I2C协议允许的格式再传输E2PROM中,并将接收到的信息翻译再反馈给FPGA(串并转换)。
- 串口收发模块以及收发分别对应的2个fifo。
2.1 模块:
- key_debounce模块:按键消抖模块,没什么好说的。
- uart模块:分为收发两个模块,分别配备写与读两个fifo,用以缓存等待写和发的数据。
- e2prom_rwctrl模块:E2PROM读写控制模块,用来向E2PROM存储器发送读写控制命令,以及缓存写入E2PROM的数据和从E2PROM读取到的数据。
- i2c_interface模块:i2c接口模块,用来把接收到的指令并串转换为I2C协议的格式然后传输到E2PROM存储器中,或将E2PROM传输回来的信息进行串并转换传回到fifo中。
- i2c_ctrl模块:SDA总线控制模块,负责SDA这根inout双向数据总线的输入输出控制。
2.2 工作步骤:
写:
- 由上位机串行发送 x Bytes的数据到串口接收模块
uart_rx
中; uart_rx
模块将接收到的串行信号进行串并转换然后发送到e2prom_rwctrl
模块中;e2prom_rwctrl
模块,每接收到一次rx_vld
(数据接收有效信号)后,开启wrfifo
的写请求,写入一个字节,直到接收完为止;wrfifo
缓存完成后,e2prom_rwctrl
模块向i2c_interface
模块发送写请求和需要写入的数据,每次发送1个字节的数据,在收到done
信号后再次发送1个字节,知道发送完3个字节为止;i2c_interface
负责将接收到的写控制字
、写地址
、wr_data[7:0]
进行一个符合I2C协议的并串转换,并在每转换传输完成1个字节后,向e2prom_rwctrl
模块发送done信号,提醒它传输下一个字节。
读:
- 按下key,经过按键消抖模块后输出稳定的key信号到
e2prom_rwctrl
模块中; e2prom_rwctrl
模块接收到key信号后向i2c_interface
模块发送写控制字
、读地址
、读控制字
;i2c_interface
模块接收到读请求后发送写控制字
、读地址
、读控制字
,接着在一个应答信号后将接收到的SDA信号进行一个依据I2C协议的串并转换发送到e2prom_rwctrl
模块中;e2prom_rwctrl
模块接收到的所有rd_data[07:00]
缓存到读fifo中,然后以先进先出的原则依次发送给uart_tx
模块;uart_tx
模块将接收到的每一个rd_data[07:00]
进行并串转换,然后发送到上位机中,在发送途中拉高busy
信号,表示tx线已被占用:等我发完这个字节你这个fifo再传下一个进来。
以上便是本次项目全部代码的真谛,🤯顿悟这个流程后再写代码就不难了。
3. 程序框图
4. 状态机
根据主状态机产生时钟的原则,i2c_interface模块为主状态机,e2prom读写控制模块为从状态机。