1、I2C 通讯协议
I2C 通讯协议(Inter-Integrated Circuit)是由Philips公司开发的一种简单、双向二线制同步串行总线,只需要两根线即可在连接于总线上的器件之间传送信息。
1.1 I2C物理层
-
它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。
-
一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
-
每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
-
总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
-
多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
-
具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。
-
连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。
1.2 i2c协议层
1:空闲状态
2:起始状态
3:读写数据状态
注意I2C通讯设备的通讯模式是主从通讯模式,通讯双方有主从之分,通信时一般一次传输一个字节,并且是在SCL高电平读写,低电平数据变换,并在传输完成一个字节后从机会发送一个应答信号提示从机已经接收完成。
4:停止状态
2、i2c器件读写
2.1 i2c设备器件地址与存储地址
每个I2C设备在出厂前都被设置了器件地址,用户不可自主更改;器件地址一般位宽为7位,有的I2C设备的器件地址设置了全部位宽,例如OV7725、OV5640摄像头;有的I2C设备的器件地址设置了部分位宽,例如EEPROM存储芯片,它的器件地址只设置了高4位,剩下的低3位由 用户在设计硬件时自主设置。
在I2C主从设备通讯时,主机在发送了起始信号后,接着会向从机发送控制命令。控制命令长度为1个字节,它的高7位为上文讲解的I2C设备的器件地址,最低位为读写控制位。读写控制位为0时,表示主机要对从机进行数据写入操作;读写控制位为1时,表示主机要对从机进行数据读出操作。
通常情况下,主机在与从机建立通讯时,并不是直接向想要通讯的从机发送控制命令(器件地址 + 读/写控制位)以建立通讯,而是主机会将控制命令直接发送到串行数据线SDA上,与主机硬件相连的从机设备都会接收到主机发送的控制命令。所有从机设备在接收到主机发送的控制命令后会与自身器件地址做对比;若两者地址相同, 该从机设备会回应一个应答信号告知主机设备,主机设备接收到应答信号后,主从设备建立通讯连接,两者可进行数据通讯。
2.2 I2C读/写操作
1、写(以2字节地址单字节写为例)
-
主机产生并发送起始信号到从机,将控制命令写入从机设备,读写控制位设置为低电平,表示对从机进行数据写操作,控制命令的写入高位在前低位在后;
-
从机接收到控制指令后,回传应答信号,主机接收到应答信号后开始存储地址的写入。若为2字节地址,顺序执行操作;若为单字节地址跳转到步骤(5);
-
先向从机写入高8位地址,且高位在前低位在后;
-
待接收到从机回传的应答信号,再写入低8位地址,且高位在前低位在后
-
地址写入完成,主机接收到从机回传的应答信号后,开始单字节数据的写入;
-
单字节数据写入完成,主机接收到应答信号后,向从机发送停止信号,单字节数据写入完成。
2、读(以2字节地址随机读为例)
-
主机产生并发送起始信号到从机,将控制命令写入从机设备,读写控制位设置为低电平,表示对从机进行数据写操作,控制命令的写入高位在前低位在后;
-
从机接收到控制指令后,回传应答信号,主机接收到应答信号后开始存储地址的写入。若为2字节地址,顺序执行操作;若为单字节地址跳转到步骤(5);
-
先向从机写入高8位地址,且高位在前低位在后;
-
待接收到从机回传的应答信号,再写入低8位地址,且高位在前低位在后.
-
地址写入完成,主机接收到从机回传的应答信号后,主机再次向从机发送一个起始信号;
-
主机向从机发送控制命令,读写控制位设置为高电平,表示对从机进行数据读操作;
-
主机接收到从机回传的应答信号后,开始接收从机传回的单字节数据;
-
数据接收完成后,主机产生一个时钟的高电平无应答信号;
-
主机向从机发送停止信号,单字节读操作完成。
3、读写fpga中eeprom
整体说明:首先,实验目标要使用I2C通讯协议,那么工程中要包含一个I2C驱动控制模块;其次,使用按键控制数据读/写,并要求将读出数据显示到数码管上,我们可以调用按键消抖模块和数码管动态显示模块;
再次,我们需要设计一个数据收发模块控制数据的收发;
最后,需要顶层模块将各子功能模块例化起来,连接个 功能模块对应信号。
模块名称 |
功能描述 |
---|---|
key_filter |
按键消抖模块,将物理按键传入的读/写触发信号作消抖处理 |
I2eeprom_rw_data |
数据收发模块,生成eeprom待写入数据,暂存eeprom读出数据 |
I2eeprom_ctrl |
I2C驱动模块,按照I2C协议对I2C设备进行数据读写操作 |
seg_595_dynamic |
数码管动态显示模块,显示读出eeprom的数据 |
TOP_I2c_eeprom |
顶层模块,实例化各子功能模块,连接各模块对应信号 |
3.1 I2C控制模块
各个管脚的名称作用
信号 |
位宽 |
类型 |
功能描述 |
---|---|---|---|
sys_clk |
1Bit |
Input |
系统时钟50MHz |
sys_rst_n |
1Bit |
Input |
复位信号,低有效 |
wr_en |
1Bit |
Input |
写使能信号 |
rd_en |
1Bit |
Input |
读使能信号 |
i2c_start |
1Bit |
Input |
单字节数据读/写开始信号 |
number |
1Bit |
Input |
数据存储地址字节数标志信号 |
byte_addr |
16Bit |
Input |
数据存储地址 |
wr_data |
8Bit |
Input |
待写入EEPROM字节数据 |
i2c_clk |
1Bit |
Output |
工作时钟 |
i2c_end |
1Bit |
Output |
单字节数据读/写结束信号 |
rd_data |
8Bit |
Output |
自EEPROM中读出的单字节数据 |
sck |
1Bit |
Output |
I2C串行时钟信号SCL |
sda |
1Bit |
Output |
I2C串行数据信号SDA |
I2C控制模块状态图
状态机中共包含16个状态,将单字节写操作和随机读操作相结合,可以实现I2C设备单字节写操作和随机读操作的状态跳转。
系统上电后,状态机处于IDLE(初始状态),接收到有效的单字节数据读/写开始信号i2c_start后,状态机跳转到START_1(起始状态);FPGA向EEPROM存储芯片发送起始信号;随后状态机跳转到SEND_D_ADDR(发送器件地址状态),在此状态下向EEPROM存储芯片写入控制指令,控制指令 高7位为器件地址,最低位为读写控制字,写入“0”,表示执行写操作;控制指令写入完毕后,状态机跳转到ACK_1(应答状态)。
在ACK_1(应答状态)状态下,要根据存储地址字节数进行不同状态的跳转。当FPGA接收到EEPROM回传的应答信号且存储地址字节为2字节,状态机跳转到SEND_B_ADDR_H(发送高字节地址状态),将存储地址的高8位写入EEPROM,写入完成后,状态机跳转到ACK_2(应答状态);FPGA接收到应 答信号后,状态机跳转到SEND_B_ADDR_L(发送低字节地址状态);当FPGA接收到EEPROM回传的应答信号且存储地址字节为单字节,状态机状态机直接跳转到SEND_B_ADDR_L(发送低字节地址状态);在此状态低8位存储地址或单字节存储地址写入完成后,状态机跳转到ACK_3(应答状态)。
在ACK_3(应答状态)状态下,要根据读/写使能信号做不同的状态跳转。当FPGA接收到应答信号且写使能信号有效,状态机跳转到WR_DATA(写数据状态);在写数据状态,向EEPROM写入单字节数据后,状态机跳转到ACK_4(应答状态);待FPGA接收到有效应答信号后,状态机跳转到STOP(停止状态) ;当FPGA接收到应答信号且读使能信号有效,状态机跳转到START_2(起始状态);再次向EEPROM写入起始信号,状态跳转到SEND_RD_ADDR(发送读控制状态);再次向EEPROM写入控制字节,高7位器件地址不变,读写控制位写入“1”,表示进行读操作,控制字节写入完毕后,状态机跳转到ACK_ 5(应答状态);待FPGA接收到有效应答信号后,状态机跳转到RD_DATA(读数据状态);在RD_DATA(读数据状态)状态,EEPROM向FPGA发送存储地址对应存储单元下的单字节数据,待数据读取完成户,状态机跳转到N_ACK(无应答状态),在此状态下向EEPROM写入一个时钟的高电平,表示数据读 取完成,随后状态机跳转到STOP(停止状态)。
在STOP(停止状态)状态,FPGA向EEPROM发送停止信号,一次单字节数据读/写操作完成,随后状态机跳回IDLE(初始状态),等待下一次单字节数据读/写开始信号i2c_start。
使用状态机实现I2C驱动模块功能是模块的大体思路,结合前面讲解的I2C通讯协议的相关知识和相关设计方法,开始模块波形图的绘制。I2C驱动模块波形图太大只截图了变量,如下
注意:本实验对EEPROM读写操作的串行时钟scl的频率为250KHz,且只在数据读写操作时时钟信号才有效,其他时刻scl始终保持高电平。若直接使用系统时钟生成串行时钟scl,计数器要设置较大的位宽,较为麻烦,我们这里先将系统时钟分频为频率较小的时钟,在使用新分频的时钟来生成串行时钟scl。
单字节写入波形图
单字节读取波形图
第一部分:输入信号说明
本模块的输入信号有8路,其中6路信号与随机读操作有关。系统时钟信号sys_clk和复位信号sys_rst_n不必多说,这是模块正常工作必不可少的;读使能信号rd_en、 单字节数据读/写开始信号i2c_start,只有在两信号同时有效时,模块才会执行随机读操作,若rd_en有效时,i2c_start 信号n次有效输入,可以实现n个字节的连续读操作;addr_num信号为存储地址字节数标志信号,赋值为0时,表示I2C设备存储地址为单字节,赋值为1时,表示2C设备存储地址为2字节,本实验使用的EEPROM存储芯片的存储地址位2字节,此信号恒为高电平;信号byte_addr为存储地址。
第二部分:状态机相关信号波形的设计与实现
状态机状态跳转的各约束条件,读者可回顾单字节写操作部分介绍。声明状态变量state,结合各约束信号,单字节写操作状态机跳转流程如下:
系统上电后,状态机处于IDLE(初始状态),接收到有效的单字节数据读/写开始信号i2c_start后,状态机跳转到START_1(起始状态),同时使能信号cnt_i2c_clk_en拉高、计数器cnt_i2c_clk、cnt_bit开始计数,开始数据读写操作;
在START_1(起始状态)状态保持一个串行时钟周期,期间FPGA向EEPROM存储芯片发送起始信号,一个时钟周期过后,计数器cnt_ i2c_clk完成一个周期计数,计数器cnt_ i2c_clk计数到最大值3,状态机跳转到SEND_D_ADDR(发送器件地址状态);
计数器cnt_i2c_clk、cnt_bit同时归0,重新计数,计数器cnt_i2c_clk每计完一个周期,cnt_bit自加1,当计数器cnt_i2c_clk完成8个计数周期后,cnt_bit计数到7,实现8个比特计数,器件FPGA按照时序向EEPROM存储芯片写入控制指令,控制指令高7位为器件地 址,最低位为读写控制字,写入“0”,表示执行写操作。当计数器cnt_ i2c_clk计数到最大值3、cnt_bit计数到7,两计数器同时归0,状态机跳转到转到ACK_1(应答状态);
在ACK_1(应答状态)状态下,计数器cnt_i2c_clk、cnt_bit重新计数,当计数器cnt_ i2c_clk计数到最大值3,且应答信号ack为有效的低电平,状态机跳转到SEND_B_ADDR_H(发送高字节地址状态),两计数器清0;
此状态下,FPGA将存储地址的高8位按时序写入EEPROM,当计数器cnt_ i2c_clk计数到3、cnt_bit计数到7,状态机跳转到ACK_2(应答状态), 两计数器清0;
ACK_2状态下,当计数器cnt_ i2c_clk计数到3,且应答信号ack为有效的低电平,状态机跳转到SEND_B_ADDR_L(发送低字节地址状态) ,两计数器清0;
在此状态下,低8位存储地址按时序写入EEPROM,计数器cnt_ i2c_clk计数到3、cnt_bit计数到7,状态机跳转到ACK_3(应答状态);
在ACK_3(应答状态)状态下,当cnt_ i2c_clk计数3、应答信号ack有效,且读使能信号rd_en有效,状态机跳转到START_2(起始状态);
在START_2(起始状态)状态保持一个串行时钟周期,期间FPGA再次向EEPROM存储芯片发送起始信号,一个时钟周期过后,计数器cnt_ i2c_clk完成一个周期计数,计数器cnt_ i2c_clk计数到3,状态机跳转到SEND_RD_ADDR(发送读控制状态);
在此状态下,按时序向EEPROM写入控制指令,控制指令高7位为器件地址,最低位为读写控制字,写入“1”,表示执行读操作。当计数器cnt_ i2c_clk计数到3、cnt_bit计数到7,两计数器同时归0,状态机跳转到ACK_5(应答状态);
在ACK_5(应答状态)状态下,当cnt_ i2c_clk计数3、应答信号ack有效,状态机跳转到RD_DATA(读数据状态);读数据状态下,主机读取从机发送的单字节数据,当计数器cnt_ i2c_clk计数到3、cnt_bit计数到7,数据读取完成,计数器清0,状态机跳转到N_ACK(非应答状态);在非应答状态下,向EEPROM写入一个时钟的高电平,当cnt_ i2c_clk计数3,状态机跳转到STOP(停止状态)。
在STOP(停止状态)状态,FPGA向EEPROM发送停止信号,一次随机数据读操作完成,随后状态机跳回IDLE(初始状态),等待下一次单字节数据读/写开始信号i2c_start。
第三部分:输出串行时钟sck、串行数据信号sda、读出数据rd_data及相关信号的波形设计与实现
串口数据sda端口作为一个双向端口,在随机读操作中,主机只在除应答状态、读数据状态之外的其他状态拥有它的控制权,在应答状态下主机接收由从机通过sda传入的应答信号,在读数据状态下主机接收由从机传入的单字节数据。声明使能信号sda_en,只在除应答状态、读数据状态之外的其他状态赋值为有效的高电平,sda_en有效时,主机拥有对sda的控制权。
声明i2c_sda_reg作为输出sda信号的数据缓存;声明i2c_sda_reg作为EEPROM读出数据缓存。
i2c_sda_reg在使能信号sda_en无效时始终保持高电平,在使能sda_en有效时,在状态机对应状态下,以计数器cnt_ i2c_clk、cnt_bit为约束条件,对应写入起始信号、控制指令、存储地址、写入数据、停止信号;在状态机处于读数据状态时,变量rd_data_reg由输入信号sda_in赋值,暂存EEPROM读取数据。
当sda_en有效时,将i2c_sda_reg赋值给i2c_sda;当sda_en无效时,i2c_sda保持高阻态。主机放弃对sda端口的控制;在状态机处于读数据状态时,变量rd_data_reg暂存EEPROM读取数据,读数据状态结束后,将暂存数据赋值给输出信号rd_data。
对于输出的串行时钟i2c_clk,由I2C通讯协议可知,I2C设备只在串行时钟为高电平时进行数据采集,在串行时钟低电平时实现串行数据更新。我们使用计数器cnt_ i2c_clk、cnt_bit以及状态变量state为约束条件,结合I2C通讯协议,生成满足时序要求的输出串行时钟i2c_clk。
数据收发模块
数据收发模块的主要功能是:为I2C驱动模块提供读/写数据存储地址、待写入数据以及作为EEPROM读出数据缓存,待数据读取完成后将读出数据发送给数码管显示模块进行数据显示。数据收发模块框图及模块输入输出端口简介,具体见图 47‑35、表格 47‑3。
图 47‑35 数据收发模块框图
表格 47‑3 I2C数据收发模块输入输出信号简介
信号 |
位宽 |
类型 |
功能描述 |
---|---|---|---|
sys_clk |
1Bit |
Input |
系统时钟50MHz |
i2c_clk |
1Bit |
Input |
模块工作时钟,频率1MHz |
sys_rst_n |
1Bit |
Input |
复位信号,低有效 |
write |
1Bit |
Input |
写触发信号 |
read |
1Bit |
Input |
读触发信号 |
i2c_end |
1Bit |
Input |
单字节数据读/写结束信号 |
rd_data |
8Bit |
Input |
EEPROM读出数据 |
wr_en |
1Bit |
Output |
写使能信号 |
rd_en |
1Bit |
Output |
读使能信号 |
i2c_start |
1Bit |
Output |
单字节数据读/写开始信号 |
byte_addr |
16Bit |
Output |
读/写数据存储地址 |
wr_data |
8Bit |
Output |
待写入EEPROM数据 |
fifo_rd_data |
8Bit |
Output |
数码管待显示数据 |
由图表可知,I2C驱动模块包括13路输入输出信号,其中输入信号7路、输出信号6路。
输入信号中,有2路时钟信号和1路复位信号,sys_clk为系统时钟信号,在数据收发模块中用于采集读/写触发信号read和write,2路触发信号均由外部按键输出,经消抖处理后传入本模块,消抖模块使用的时钟信号为与sys_clk相同的系统时钟,所以读/写触发信号的采集要使用系统时钟;i2c_clk为模 块工作时钟,由I2C驱动模块生成并传入,是存储地址、读/写数据以及使能信号的同步时钟,因为I2C模块的工作时钟为i2c_clk时钟信号,两模块工作时钟相同,不会出现时钟不同引起时序问题;复位信号sys_rst_n,低电平有效,不必多说;i2c_end为单字节数据读/写接数信号,由I2C驱动模块产生并 传入,告知数据生成模块单字节数据读/写操作完成。若连续读/写多字节数据,此信号可作为存储地址、写数据的更新标志;rd_data为I2C驱动模块传入的数据信号,表示由EEPROM读出的字节数据。
输出信号中, rd_en、wr_en分别为读写使能信号,生成后传入I2C驱动模块,作为I2C驱动模块读/写操作的判断标志;i2c_start是单字节数据读/写开始信号,作为I2C驱动模块单字节读/写操作开始的标志信号;byte_addr为读写数据存储地址;wr_data为待写入EEPROM的字节数据 ;fifo_rd_data为自EEPROM读出的字节数据,要发送到数码换显示模块在数码管显示出来。
注:数据收发模块内部实例化一个FIFO,将读出EEPROM的字节数据做暂存,待所有数据读取完成后,开始向数码管发送数据。例如本实验向EEPROM连续写入10个字节数据,随后将写入数据读出并在数码管显示,数据收发模块只有接收到读出的10个字节数据后,才会开始向数码管显示模块发送数据。
第一部分:输出写使能信号wr_en及其相关信号波形的设计与实现
外部按键传入的写触发信号经消抖处理后传入本模块,该信号只保持一个有效时钟,且同步时钟为系统时钟sys_clk,模块工作时钟i2c_clk很难采集到该触发信号。我们需要延长该写使能触发信号的有效时间,使模块工作时钟i2c_clk可以采集到该触发信号。
声明计数器cnt_wr和写有效信号wr_valid两信号的同步时钟均为系统时钟sys_clk,当外部传入有效的写触发信号write,写有效信号wr_valid拉高,计数器cnt_wr来时计数,计数器计数到设定值(200)后归0,写有效信号拉低。计数器cnt_wr的计数设定值可自主设定,只要能使wr_ valid信号保持一个工作时钟周期高电平即可。
第二部分:输出存储地址byte_addr、写数据wr_data信号波形的设计与实现
既然是对EEPROM中兴写数据操作,存储地址和写数据必不可少,在本从实验中,向EEPROM中10个连续存储存储单元写入10字节数据。对输出存储地址byte_addr,赋值初始存储地址,当i2c_end信号有效时,地址加1,待10字节数据均写入完毕,再次赋值初始从从地址;对于写数据wr_data处理方 式相同,先赋值写数据初值,当i2c_end信号有效时,写数据加1 ,待10字节数据均写入完毕,在此赋值写数据初值。两输出信号波形如下。
第三部分:接收读数据rd_data信号波形的设计与实现
一次读操作连续读出10字节数据,先将读取的10字节数据暂存到内部例化的FIFO中,以传回的i2c_end结束信号为写使能,在i2c_clk时钟同步下将读出数据写入FIFO中。同时我们将FIFO的数据计数器引出,方便后续数据发送阶段的操作。相关信号波形图如下 。
第四部分:发送接收到的接收读数据rd_data信号波形的设计与实现
等到读取的10字节均写入FIFO中,FIFO数据计数器data_num显示为10,表示FIFO中存有10字节读出数据。此时拉高FIFO读有效信号fifo_rd_valid,只有信号fifo_rd_valid为有效高电平,对FIFO的读操作才有效;fifo_rd_valid有效时,计数器cnt_wai t开始循环计数,声明此计数器的目的是计数字节数据读出时间间隔,间隔越长,每字节数据在数码管显示时间越长,方面现象观察;当计数器cnt_wait计数到最大值时,归0重新计数,FIFO读使能信号信号fifo_rd_en拉高一个时钟周期,自FIFO读出一个字节数据,由fifo_rd_data将数据传出给数 码管显示模块,读出字节计数器rd_data_num加1;等到10字节数据均读取并传出后,fifo_rd_valid信号拉低,数据发送操作完成。相关信号波形如下。
I2C驱动模块、数据收发模块的相关内容介绍完毕,对于其他消抖模块、数码管显示模块等子功能模块的介绍不再赘述。
顶层模块内部实例化实验工程的个子功能模块,连接个模块对应信号;对外接收外部传入的时钟、复位信号以及读/写操作触发信号,发送串行时钟scl和串行数据sda给EEPROM存储芯片,发送片选和段选给数码管。顶层模块框图如下。
完整内容去:18. 基于i2c协议的adda实验 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档 (embedfire.com)
标签:i2c,模块,状态机,verilog,信号,I2C,数据,EEPROM From: https://www.cnblogs.com/xuhuiyuan/p/17218425.html