当前位置:首页 > 后端开发 > 正文内容

FPGA对EEPROM驱动操控(I2C协议)

邻居的猫1个月前 (12-09)后端开发1176

本文摘要:本文首要对I2C协议的通讯形式和AT24C16-EEPROM芯片时序操控进行剖析和了解,规划了一个i2c通讯计划。人为按下写操作按键后,FPGA(Altera EP4CE10)对EEPROM指定地址写入字节数据,并接后按下读操作按键,读取该地址上的一个字节数据在数码管低两位显现出来。其间包含了对此计划的Modelsim仿真测验,而且接续完结板级验证。(进程笔记)

关键词:EEPROM、I2C协议、Verilog HDL、FPGA

框图规划:

输入端口包含体系时钟、复位信号、写操作和读操作按键,输出端口包含IIC通讯接口、数码管段选和位选信号。

image

合计用到了四种子模块,别离是按键消抖、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时钟是低电平时才干改动。

image

传输信号类型:(见上图)

  • 发动信号(START)(S条件):在SCL线处于高电平,SDA上的数据由高向低转化,则标明发动IIC总线;
  • 应对信号(ACK):在接纳到了8bit的信息后, 接纳数据一方需求向发送信息的另一方传递默许的低电平脉冲作为信号,标明现已获取了数据;
  • 完毕信号(STOP)(P条件):在SCL线处于高电平,SDA上的数据由低向高转化,则标明中止IIC总线。

开端(S)和中止(P)条件一般由主机产生,总线在开端条件后被以为处于忙的状况,在中止条件的某段时间后总线被以为再次处于闲暇状况 。

由于I2C 总线没有中心操控,其操控只能由地址或主机码以及竞赛主机发送的数据决议,总线也没有任何定制的优先权。所以,当产生多主机通讯时,需求一个操控裁定机制来决议通讯优先。

裁定进程中的时钟同步:

在I2C通讯中,一切主机在SCL线上生成自己的时钟信号以传输数据。数据仅在时钟高电平时有用,因而需求同步时钟以完结逐位裁定。时钟同步经过线与衔接完结,即SCL线状况由一切设备一起决议。

image

①见上图中,当SCL线从高变低时,一切设备开端计数其低电平周期。若某设备时钟先变低,则它会坚持SCL线在低电平直到其时钟再次变高。若此刻有其他设备仍处于低电平周期,它们的时钟改动不会改动SCL线状况,直至最长低电平周期的设备完结计数。

②一旦一切设备完结低电平周期,SCL线开释并变为高电平,随后一切设备同步开端计数高电平周期。

③首要完结高电平周期的设备将再次拉低SCL线。因而,SCL时钟的低电平周期由最长低电平周期的设备决议,而高电平周期由最短高电平周期的设备决议。(巧记:确保最长低电平周期)

裁定断定优先:

主机只能在总线闲暇时发动传输,两个或多个主机可能在开端条件的最小持续时间内产生一个开端条件,成果在总线上产生一个标准的开端条件(如下框S条件内)。当SCL线是高电平时,裁定在SDA线产生,在其他主机坚持低电平状况时,首要拉高电平的主机将断开数据输出级,如下图DATA1失去了通讯的优先权。

image

裁定能够持续多位,第一个阶段是比较地址位。 假如各主机都测验寻址同一器材,裁定机制会持续到数据位。在裁定进程中不会丢掉信息(裁定区间有用信息共同),丢掉裁定的主机能够产生时钟脉冲直到丢掉裁定的该字节结尾 。

弥补:假如主机也结合了从机功用,而且在寻址阶段丢掉裁定,由于它存在是赢得裁定的主机所寻址的器材。因而,丢掉裁定的主机有必要当即切换到从机形式。

【AT24C16时序剖析】

AT24C16 EEPROM存储芯片的相关信息:存储容量为16Kbit,即2048字节。芯片内部分红128页,每页16字节,读写操作都是以字节为基本单位。AT24C16具有低电压作业、高速串行通讯和硬件写保护等特色。(下图为芯片引脚阐明)

image

  • 地址组成:AT24C16的器材地址包含高4位固定地址(1010)和用户需设置的低3位地址(A0、A1、A2);
  • 地址设置:经过衔接芯片的A0、A1、A2这3个引脚到VCC或GND来完结地址的低3位设置。例如这3个引脚均衔接到VCC,则器材地址为1010_111。由于该 3 位只能组合出 8 种状况,因而,一个主机最多能够衔接8个AT24C16存储芯片。

1、EEPROM驱动写时序进行剖析

image

字节写入时序 (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读时序。首要是指定地址单字节读取方法:操作时序和写时序相似,不同的是,在写入方针地址后,主机的操作方法换为读取。

image

次序读取时序:主机发送一个开端地址后,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为一个时钟分频计数器。

image

以写操作为例,待写触发信号write产生,拉高写有用信号write_valid,而且为使i2c_clk时钟信号要在上升沿检测到其高电平,write触发要坚持≥2个时钟周期,对应100个体系时钟周期。这儿设定有用计数器cnt_wr,计数150个体系周期,即300ns,满足要求。计数完结,拉低写有用信号write_valid完结触发操作,详细时序见下图,当然,写有用时序也是如此。

image

写有用信号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。

image

留意,大部分信号时钟触发源都是i2c_clk,而不是体系时钟。当i2c_start开端信号触发,i2c_clk发动,作为i2c_scl通讯时钟的触发源。一起,依据下图逻辑,状况机状况由IDLE转到START1。开端信号仅占一个i2c_clk周期。cnt_i2c_clki2c_scl通讯时钟线的计数器,计数值规模0-3,使得i2c_scl周期为250khz。

image

完结后,进入SEND_ADDR状况,向IIC总线产生器材地址1010011+操控位0,标明写入。比特位计数器cnt_bit用于比特位0-7的计数。在ACK1状况,等候总线回应。ACK是从机,即EEPROM,传回来的低电平信号。关于主机来说SDA线此刻是高阻态,总线的上拉电阻将电位钳位在高电位(sda为inout类型)。所以在后边,能够看到这儿是处于高阻态的。通讯后边的流程依据状况机的跳转,在时序图体现得显着。在STOP中止状况,FPGA向EEPROM发送中止信号,一次单字节数据写操作完结,拉低使能信号i2c_clk_enwe_en,而且及时触发i2c_end,随后状况机跳回IDLE初始状况。

image

读操控时序处理与写时序处理流程相似,详细见上图,不同在ACK3状况的跳转,接纳一个周期回应信号后,进入的是开端状况START2,然后触发第2次开端信号,再次写入器材地址,读取一字节数据。rd_data_reg作为一个暂存器,存储读取到的字节数据,待完结后,转录到rd_data。在N_ACK等候回应一个高电平回应信号,将i2c_scl由地拉高产生一个中止信号,一起触发i2c_end。两个操作进程详细状况判别条件,需详细剖析,但都相似。

2、STATE状况机转化逻辑

i2c整个通讯的进程能够分为许多阶段,将它们细分隔:闲暇、开端、器材地址写入、字/字节地址写入、各级呼应、字节数据写入、读取字节数据、中止。主机视角对从设备操作进程,详细可见如下。

image

设备发动,主机处于闲暇状况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_ACKSTOP的跳转留意差异。跳起色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_validcnt_startwr_en拉高后,开端计数。

image

写操作(这儿器材地址设定1010_011):cnt_start计数到4900处,建议i2c_start触发,state值变为4'h01,标明IDLE进入START1状况,顺次完结后续的状况切换。STOP状况下,待cnt_bit坚持到3‘h3,完毕中止状况回来IDLEwr_en拉低,i2c_end拉高一个周期后放下。从下面的仿真图能够看到,整个写操作的时序体现正常。

image

读操作及读操作完毕仿真图如下:

image

image

【终究上机验证】

image

Quartus II生成框图如下,两个按键做为输入触发,输出包含IIC通讯总线SDA和SCL,数码管段选SEG_SEL和位选SEG_LED,以及外部挂载的两个LED。程序烧录后,设备低二位数码管显现00,LED均处于平息状况,按下key_wr后,led_wr点亮,标明写操作发动;接续,按下key_rd后,led_rd点亮,标明读操作发动,而且数码管显现读取数据8A。终究得到的现象与预期共同。

image

文献参阅:

[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/

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=256

标签: FPGA-Verilog类
分享给朋友:

“FPGA对EEPROM驱动操控(I2C协议)” 的相关文章

go-live,什么是Go-Live?

go-live,什么是Go-Live?

“Go live”通常有几种不同的含义,具体取n2. 活动或项目启动:在某些情况下,“go live”也用于描述一个活动或项目的正式启动。例如,一个公司可能会宣布某个新服务或产品“go live”,意味着该服务或产品开始正式提供。3. 实时互动或直播:在娱乐或活动策划领域,“go live”可以指...

php显示图片, 图片路径处理

php显示图片, 图片路径处理

在PHP中显示图片可以通过多种方式实现,下面我将介绍几种常见的方法:```html``` 2. 使用PHP读取图片并输出如果你想在PHP脚本中动态生成图片或者从数据库中读取图片并显示,你可以使用PHP的文件处理函数来读取图片文件的内容,然后输出它。这里有一个简单的例子:```php// 检查文件是否...

python机器学习,从基础到实践

python机器学习,从基础到实践

当然可以,机器学习是Python编程中的一个重要领域,它涉及到使用算法从数据中学习,以便做出预测或决策。Python有许多流行的库和框架,如scikitlearn、TensorFlow和PyTorch,可以用于机器学习。如果你对机器学习感兴趣,我可以帮助你学习基础知识,包括数据预处理、特征工程、模型...

python在线编辑器,便捷编程的云端伴侣

python在线编辑器,便捷编程的云端伴侣

我无法直接提供一个完整的在线编辑器界面,但我可以提供一个简单的代码示例,用于创建一个基本的文本编辑器界面。这个示例将使用Python的Tkinter库来创建一个简单的图形用户界面(GUI),用户可以在其中输入和编辑文本。请注意,这个示例非常基础,它只提供了一个文本框供用户输入文本,并没有保存或加载文...

python随机数,功能与应用

python随机数,功能与应用

生成的随机浮点数是 0.626538175575775,它在0到1之间。如果您有其他关于随机数的生成或使用的问题,请随时告诉我!Python随机数生成器:功能与应用在编程中,随机数生成是一个常见的需求,Python 提供了多种方式来生成随机数。本文将介绍 Python 中随机数生成的基本概念、常用库...

php判断字符串中是否包含某个字符, 使用 `strpos()` 函数

在PHP中,你可以使用 `strpos` 函数来判断一个字符串中是否包含另一个字符或字符串。这个函数会返回字符串在另一个字符串中第一次出现的位置,如果没有找到,则返回 `false`。下面是一个示例代码,演示如何使用 `strpos` 函数:```phpif !== falseqwe2 {...