FPGA对EEPROM驱动操控(I2C协议)
本文摘要:本文首要对I2C协议的通讯形式和AT24C16-EEPROM芯片时序操控进行剖析和了解,规划了一个i2c通讯计划。人为按下写操作按键后,FPGA(Altera EP4CE10)对EEPROM指定地址写入字节数据,并接后按下读操作按键,读取该地址上的一个字节数据在数码管低两位显现出来。其间包含了对此计划的Modelsim仿真测验,而且接续完结板级验证。(进程笔记)
关键词:EEPROM、I2C协议、Verilog HDL、FPGA
框图规划:
输入端口包含体系时钟、复位信号、写操作和读操作按键,输出端口包含IIC通讯接口、数码管段选和位选信号。
合计用到了四种子模块,别离是按键消抖、i2c通讯数据处理、i2c通讯操控、2khz时钟分配。按逻辑链接成体,完结本文规划的测验计划。在调试进程中,为便利调查操作反响,别的加入了两个LED灯接口,详细框图如下。
【I2C协议通讯形式】
I2C协议(一般称为IIC协议)是一种串行、同步、半双工通讯协议。I2C协议支撑多主机功用,答应多个具有主控才干的设备在同一总线上竞赛操控权,并经过硬件裁定机制防止抵触。其运用串行数据线(SDA
)和串行时钟线(SCL
)进行通讯,标准形式的传输速率为100 kbit/s(Standard-mode, Sm),快速形式下的I2C传输速率进步至400 kbit/s(Fast-mode,Fm),在高速形式下可达3.4Mbit/s。
发送到SDA
线上的每个字节有必要为8位,且每次传输能够发送的字节数量不受约束,每个字节后有必要跟一个ACK呼应,位首要传输的是数据的最高位MSB,SDA线上的数据有必要在SCL
时钟的高电平周期坚持稳定,数据线的高或低电平状况只要在SCL
时钟是低电平时才干改动。
传输信号类型:(见上图)
- 发动信号(START)(S条件):在
SCL
线处于高电平,SDA
上的数据由高向低转化,则标明发动IIC总线; - 应对信号(ACK):在接纳到了8bit的信息后, 接纳数据一方需求向发送信息的另一方传递默许的低电平脉冲作为信号,标明现已获取了数据;
- 完毕信号(STOP)(P条件):在
SCL
线处于高电平,SDA
上的数据由低向高转化,则标明中止IIC总线。
开端(S)和中止(P)条件一般由主机产生,总线在开端条件后被以为处于忙的状况,在中止条件的某段时间后总线被以为再次处于闲暇状况 。
由于I2C 总线没有中心操控,其操控只能由地址或主机码以及竞赛主机发送的数据决议,总线也没有任何定制的优先权。所以,当产生多主机通讯时,需求一个操控裁定机制来决议通讯优先。
裁定进程中的时钟同步:
在I2C通讯中,一切主机在SCL
线上生成自己的时钟信号以传输数据。数据仅在时钟高电平时有用,因而需求同步时钟以完结逐位裁定。时钟同步经过线与衔接完结,即SCL
线状况由一切设备一起决议。
①见上图中,当SCL线
从高变低时,一切设备开端计数其低电平周期。若某设备时钟先变低,则它会坚持SCL
线在低电平直到其时钟再次变高。若此刻有其他设备仍处于低电平周期,它们的时钟改动不会改动SCL线
状况,直至最长低电平周期的设备完结计数。
②一旦一切设备完结低电平周期,SCL
线开释并变为高电平,随后一切设备同步开端计数高电平周期。
③首要完结高电平周期的设备将再次拉低SCL
线。因而,SCL
时钟的低电平周期由最长低电平周期的设备决议
,而高电平周期由最短高电平周期的设备
决议。(巧记:确保最长低电平周期)
裁定断定优先:
主机只能在总线闲暇时发动传输,两个或多个主机可能在开端条件的最小持续时间内产生一个开端条件,成果在总线上产生一个标准的开端条件(如下框S条件内)。当SCL
线是高电平时,裁定在SDA
线产生,在其他主机坚持低电平状况时,首要拉高电平的主机将断开数据输出级,如下图DATA1失去了通讯的优先权。
裁定能够持续多位,第一个阶段是比较地址位。 假如各主机都测验寻址同一器材,裁定机制会持续到数据位。在裁定进程中不会丢掉信息(裁定区间有用信息共同),丢掉裁定的主机能够产生时钟脉冲直到丢掉裁定的该字节结尾 。
弥补:假如主机也结合了从机功用,而且在寻址阶段丢掉裁定,由于它存在是赢得裁定的主机所寻址的器材。因而,丢掉裁定的主机有必要当即切换到从机形式。
【AT24C16时序剖析】
AT24C16
EEPROM存储芯片的相关信息:存储容量为16Kbit,即2048字节。芯片内部分红128页,每页16字节,读写操作都是以字节为基本单位。AT24C16具有低电压作业、高速串行通讯和硬件写保护等特色。(下图为芯片引脚阐明)
- 地址组成:AT24C16的器材地址包含高4位固定地址(1010)和用户需设置的低3位地址(
A0、A1、A2
); - 地址设置:经过衔接芯片的
A0、A1、A2
这3个引脚到VCC或GND来完结地址的低3位设置。例如这3个引脚均衔接到VCC,则器材地址为1010_111。由于该 3 位只能组合出 8 种状况,因而,一个主机最多能够衔接8个AT24C16存储芯片。
1、EEPROM驱动写时序进行剖析
字节写入时序 (Byte Write):通讯开端,由主机发送一个开端条件。紧接着,主机发送EEPROM的设备地址,挑选方针EEPROM芯片。假如EEPROM支撑字地址寻址,主机将接续发送一个或多个字节的字地址来指定要写入数据的内存方位。然后,主机写入数据字节,最高有用位(MSB)首要发送。 EEPROM在接纳到每个字节后,回来一个应对信号(ACK),应对信号是低电平脉冲,标明已成功接纳字节并预备接纳下一个字节或中止信号。待一切数据字节都发送完毕后,主机发送中止信号(STOP)来完毕现在的通讯。
页写入时序 (Page Write):与字节写入时序比较,页写入时序相似,但数据被分组为多个字节,作为一个接连的数据块发送。 发送开端设备地址后,接续发送一系列的数据字节(DATA (n), DATA (n + 1),......, DATA (n + x))单个字节发送完结,EEPROM都会回来一个应对信号(ACK)。
弥补:一切I2C设备均支撑单字节数据写入操作,但只要部分I2C设备支撑页写操作;且支撑页写操作的设备,一次页写操作写入的字节数不能超过设备单页包含的存储单元数。
2、读时序进行剖析
IIC协议支撑三种EEPROM读时序。首要是指定地址单字节读取方法:操作时序和写时序相似,不同的是,在写入方针地址后,主机的操作方法换为读取。
次序读取时序:主机发送一个开端地址后,EEPROM开端接连发送数据(DATA n, DATA n+1, DATA n+2, ... DATA n+X)。在每个数据字节的结尾,EEPROM相同等候主机的应对信号(ACK)。主机在接纳到每个数据字节后发送ACK信号,直到一切数据都被接纳。当一切数据发送完毕或主机决议中止时,它会发送一个中止信号(STOP),完毕次序读取操作。
随机读取时序:开端条件后,发送设备地址和读写位(R/W=1),接着发送随机地址。EEPROM发送数据,主机接纳后发送ACK。一切数据发送完毕后,发送中止信号。EEPROM在接纳到有用地址后,经过SDA线接连发送数据(DATA n, DATA n+1, ...)。在每个数据字节的结尾,EEPROM等候主机的应对信号(ACK)。主机待接纳到数据字节后,经过发送ACK信号来承认接纳,并奉告EEPROM能够持续发送下一个数据字节。
【时序逻辑规划计划】
1、时钟处理与i2c通讯发动
体系时钟频率为50Mhz,频率很高,这儿首要需求从体系时钟分频供给一个1Mhz的i2c_clk
时钟用于i2c通讯处理。下图中cnt_clk
为一个时钟分频计数器。
以写操作为例,待写触发信号write
产生,拉高写有用信号write_valid
,而且为使i2c_clk
时钟信号要在上升沿检测到其高电平,write
触发要坚持≥2个时钟周期,对应100个体系时钟周期。这儿设定有用计数器cnt_wr
,计数150个体系周期,即300ns,满足要求。计数完结,拉低写有用信号write_valid
完结触发操作,详细时序见下图,当然,写有用时序也是如此。
写有用信号write_valid
触发,在其拉高的下一个i2c_clk
时钟上升沿,触发写使能wr_en
信号,而且,发动cnt_start
计数器,计数值为5000(1Mhz ->1us)≈ 5ms。这是由于AT64C16单次操作距离周期需求坚持5ms闲暇状况。完结计数后,发动i2c_start
开端信号触发,后边便是i2c通讯的详细流程。当完结操作后,i2c_end
完毕信号标志通讯完结,然后拉低写使能wr_en
信号。进程中,设定了写操作的方针地址为16'H00_4D,写入数据为8'H8A。
留意,大部分信号时钟触发源都是i2c_clk
,而不是体系时钟。当i2c_start
开端信号触发,i2c_clk
发动,作为i2c_scl
通讯时钟的触发源。一起,依据下图逻辑,状况机状况由IDLE
转到START1
。开端信号仅占一个i2c_clk
周期。cnt_i2c_clk
是i2c_scl
通讯时钟线的计数器,计数值规模0-3,使得i2c_scl
周期为250khz。
完结后,进入SEND_ADDR
状况,向IIC总线产生器材地址1010011
+操控位0
,标明写入。比特位计数器cnt_bit
用于比特位0-7的计数。在ACK1
状况,等候总线回应。ACK
是从机,即EEPROM,传回来的低电平信号。关于主机来说SDA
线此刻是高阻态,总线的上拉电阻将电位钳位在高电位(sda
为inout类型)。所以在后边,能够看到这儿是处于高阻态的。通讯后边的流程依据状况机的跳转,在时序图体现得显着。在STOP
中止状况,FPGA向EEPROM发送中止信号,一次单字节数据写操作完结,拉低使能信号i2c_clk_en
和we_en
,而且及时触发i2c_end
,随后状况机跳回IDLE
初始状况。
读操控时序处理与写时序处理流程相似,详细见上图,不同在ACK3
状况的跳转,接纳一个周期回应信号后,进入的是开端状况START2
,然后触发第2次开端信号,再次写入器材地址,读取一字节数据。rd_data_reg
作为一个暂存器,存储读取到的字节数据,待完结后,转录到rd_data
。在N_ACK
等候回应一个高电平回应信号,将i2c_scl
由地拉高产生一个中止信号,一起触发i2c_end
。两个操作进程详细状况判别条件,需详细剖析,但都相似。
2、STATE状况机转化逻辑
i2c整个通讯的进程能够分为许多阶段,将它们细分隔:闲暇、开端、器材地址写入、字/字节地址写入、各级呼应、字节数据写入、读取字节数据、中止。主机视角对从设备操作进程,详细可见如下。
设备发动,主机处于闲暇状况IDLE
,待检测到写/读操作信号后,程序转入到第一个开端状况START_1
,体现为SDA
线产生一个由高到低的电平转化信号。接续进入器材地址写入状况SEND_ADDR
,依据硬件图,这儿预先设定器材是1010_011。器材承认后,向主机产生一个低电平回应ACK_1
。寄存器存储地址类型分为字地址和字节地址,别离对应不同的状况切换。完结后ACK_3
呼应后,写操作,进入字节数据写入状况WR_DATA
,写入一个预先数据,并回应ACK_4
进入完毕中止状况STOP
。而ACK_3
呼应后,进入读操作流程,需再次产生开端START_2
,接后写入读取方针地址,等候从机完结数据发送后,进入完毕中止状况STOP
。终究坚持必定周期,再次转化至闲暇状况IDLE
。
依据时序图逻辑,确认状况机状况转化条件:
当时状况 | 方针跳转状况 | 跳转条件 | 操作类型 |
---|---|---|---|
IDLE | START1 | i2c_start拉高 | 读/写 |
START1 | SEND_ADDR | cnt_i2c_clk == 2'd3(一个i2c_clk周期 ) | 读/写 |
SEND_ADDR | ACK1 | (cnt_i2c_clk == 2'd3)&&(!ack) | 读/写 |
ACK1 | SEND_BH | (cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3) | 读/写 |
SEND_BH | ACK2 | (cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3) | 读/写 |
ACK2 | SEND_BL | (cnt_i2c_clk == 2'd3)&&(!ack) | 读/写 |
SEND_BL | ACK3 | (cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3) | 读/写 |
ACK3 | WR_DATA | (cnt_i2c_clk == 2'd3)&&(!ack) | 读/写 |
WR_DATA | ACK4 | (cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3) | 写 |
ACK4 | STOP | (cnt_i2c_clk == 2'd3)&&(!ack) | 写 |
ACK3 | START2 | rd_en拉高 | 读 |
START2 | SEND_RA | cnt_i2c_clk == 2'd3 | 读 |
SEND_RA | ACK5 | (cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3) | 读 |
ACK5 | RD_DATA | (cnt_i2c_clk == 2'd3)&&(!ack) | 读 |
RD_DATA | N_ACK | (cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3) | 读 |
N_ACK | STOP | cnt_i2c_clk == 2'd3 | 读 |
STOP | IDLE | (cnt_bit == 3'd3)&&(cnt_i2c_clk == 2'd3) | 读/写 |
对上面的表格能够总结,ACK1
- ACK5
条件都是相同的,仅仅跳转方针不同,单字节读/写完毕后状况跳转判别条件相同,两个开端状况跳转判别条件也是相同的 ,N_ACK
和STOP
的跳转留意差异。跳起色Verilog HDL详细程序如下:
//dispose state condition
always @(posedge i2c_clk or negedge sys_rst)begin
if(!sys_rst) state <= IDLE;
else case(state)
IDLE: if(i2c_start) state <= START1;else state<= state;
START1: if(cnt_i2c_clk == 2'd3) state <= SEND_ADDR;
else state <= state;
SEND_ADDR:
if((cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3))
state <= ACK1;
else state <= state;
ACK1: if((cnt_i2c_clk == 2'd3)&&(!ack))begin
if(addr_num)state <= SEND_BH;
else state<= SEND_BL;
end else state <= state;
SEND_BH:if((cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3))
state <= ACK2;
else state<= state;
ACK2: if((cnt_i2c_clk == 2'd3)&&(!ack))
state <= SEND_BL;
else state <= state;
SEND_BL:if((cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3))
state <= ACK3;
else state <= state;
ACK3: if((cnt_i2c_clk == 2'd3)&&(!ack))begin
if(wr_en) state <= WR_DATA;
else if(rd_en) state <= START2;
end
else state <= state;
WR_DATA:if((cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3))
state <= ACK4;
else state <= state;
ACK4: if((cnt_i2c_clk == 2'd3)&&(!ack))
state <= STOP;
else state <= state;
START2: if(cnt_i2c_clk == 2'd3) state <= SEND_RA;
else state <= state;
SEND_RA:if((cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3))
state <= ACK5;
else state <= state;
ACK5: if((cnt_i2c_clk == 2'd3)&&(!ack))
state <= RD_DATA;
else state <= state;
RD_DATA:if((cnt_bit == 3'd7)&&(cnt_i2c_clk == 2'd3))
state <= N_ACK;
else state <= state;
N_ACK: if(cnt_i2c_clk == 2'd3)
state <= STOP;
else state <= state;
STOP: if((cnt_bit == 3'd3)&&(cnt_i2c_clk == 2'd3))
state <= IDLE;
else state <= state;
endcase
end
STATE状况机在整个程序进程中十分重要,i2c_scl
时钟电平缓i2c_sda
输出电平状况,也要依据其状况的不同做出详细剖析。
//dispose i2c_scl sequence
always@(*)begin
case(state)
IDLE: i2c_scl <= 1'b1;
START1:
if(cnt_i2c_clk == 2'd3) i2c_scl <= 1'b0;
else i2c_scl <= 1'b1;
SEND_ADDR,ACK1,SEND_BH,ACK2,SEND_BL,
ACK3,WR_DATA,ACK4,START2,SEND_RA,ACK5,RD_DATA,N_ACK:
if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2)) i2c_scl <= 1'b1;
else i2c_scl <= 1'b0;
STOP:
if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0)) i2c_scl <= 1'b0;
else i2c_scl <= 1'b1;
default: i2c_scl <= 1'b1;
endcase
end
//dispose i2c_sda_reg & rd_data_reg sequence
always @(*)begin
case(state)
IDLE: begin
i2c_sda_reg <= 1'b1;
rd_data_reg <= 8'd0;
end
START1: if(cnt_i2c_clk == 2'd0) i2c_sda_reg <= 1'b1;
else i2c_sda_reg <= 1'b0;
SEND_ADDR: if(cnt_bit <= 3'd6) i2c_sda_reg<= DEVICE_ADDR[6-cnt_bit];
else i2c_sda_reg<= 1'b0;
ACK1: i2c_sda_reg<= 1'b1;
SEND_BH: i2c_sda_reg<= byte_addr[15-cnt_bit];
ACK2: i2c_sda_reg<= 1'b1;
SEND_BL: i2c_sda_reg<= byte_addr[7-cnt_bit];
ACK3: i2c_sda_reg<= 1'b1;
WR_DATA: i2c_sda_reg<= wr_data[7-cnt_bit];
ACK4: i2c_sda_reg<= 1'b1;
START2: if(cnt_i2c_clk <= 2'd1)i2c_sda_reg <= 1'b1;
else i2c_sda_reg <= 1'b0;
SEND_RA: if(cnt_bit <= 3'd6)i2c_sda_reg<= DEVICE_ADDR[6-cnt_bit];
else i2c_sda_reg<= 1'b1;
ACK5: i2c_sda_reg<= 1'b1;
RD_DATA: if(cnt_i2c_clk == 2'd2) rd_data_reg[7-cnt_bit] <= sda_in;
else rd_data_reg <= rd_data_reg;
N_ACK: i2c_sda_reg<= 1'b1;
STOP: if((cnt_bit==3'd0)&&(cnt_i2c_clk<2'd3))i2c_sda_reg <= 1'b0;
else i2c_sda_reg<= 1'b1;
default: begin
i2c_sda_reg<= 1'b1;
rd_data_reg <= rd_data_reg;
end
endcase
end
其他信号在此不再罗列,它们的判别条件相对状况机来说处理起来比较简单,逻辑也很明晰。
【阶段仿真验证】
下面看下程序的仿真成果,检查四个:读/写操作发动时间、写操作全体时序、读操作全体时序、读/写操作完毕时序。
操作发动:write
信号产生,write_valid
拉高,并在下一i2c_clk
上升沿拉高wr_en
使能信号,等候cnt_wr
完结计数150后,放低write_valid
。cnt_start
待wr_en
拉高后,开端计数。
写操作(这儿器材地址设定1010_011):cnt_start
计数到4900处,建议i2c_start
触发,state
值变为4'h01,标明IDLE
进入START1
状况,顺次完结后续的状况切换。STOP
状况下,待cnt_bit
坚持到3‘h3,完毕中止状况回来IDLE
。wr_en
拉低,i2c_end
拉高一个周期后放下。从下面的仿真图能够看到,整个写操作的时序体现正常。
读操作及读操作完毕仿真图如下:
【终究上机验证】
Quartus II生成框图如下,两个按键做为输入触发,输出包含IIC通讯总线SDA和SCL,数码管段选SEG_SEL和位选SEG_LED,以及外部挂载的两个LED。程序烧录后,设备低二位数码管显现00
,LED均处于平息状况,按下key_wr后,led_wr点亮,标明写操作发动;接续,按下key_rd后,led_rd点亮,标明读操作发动,而且数码管显现读取数据8A
。终究得到的现象与预期共同。
文献参阅:
[1] I2C总线标准 (https://sumcu.suda.edu.cn/_upload/article/files/74/e5/d4eb93de45808d71ad8aad542ede/a3cb5873-aaf4-4af0-9e5f-521793fbba46.pdf);
[2] 根据I2C协议的EEPROM驱动操控(https://doc.embedfire.com/fpga/altera/ep4ce10_mini/zh/latest/fpga/I2C.html);
[3] 王荣华. 可装备的IIC协议操控器IP核的规划[D]. 黑龙江:哈尔滨理工大学,2011.( DOI:10.7666/d.y2012472);
本篇文章中运用的Verilog程序模块,若有需见网页左栏Gitee库房链接:https://gitee.com/silly-big-head/little-mouse-funnyhouse/tree/FPGA-Verilog/