FPGA IIC的状态机实现

这篇具有很好参考价值的文章主要介绍了FPGA IIC的状态机实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

IIC协议的简单介绍

1.IIC通讯设备的链接图

FPGA IIC的状态机实现,FPGA,fpga开发
注:一个IIC总线可以挂载多个设备,一个IIC总线有两条线,一个是数据线,一个是时钟线。主机通过访问不同的从机地址来进行不同设备之间的通信。细节请自己百度,这里不做过多介绍。

2.IIC协议的时序

2.1整体时序图
FPGA IIC的状态机实现,FPGA,fpga开发
注:图片纯手画,有些丑,不喜勿喷。
由图中可以看出,整体的时序图由A,B,C,D分割。下面我将详细介绍这四部分。
A:表示空闲状态,此时SCL和SDA都为高电平。
B:表示开始状态,当SCL为高电平时,SDA出现下降沿之后,表示进入了开始状态,数据将要发送或者接受。
C:表示数据读写状态,其中的一段时序波形如下图所示:
FPGA IIC的状态机实现,FPGA,fpga开发
D:表示结束状态,当SCL为高电平时,SDA由低电平变为高电平,表示结束。

3.IIC的读写操作以及状态机的实现

3.1 写操作时序

IIC单字节写操作时序
FPGA IIC的状态机实现,FPGA,fpga开发

IIC双字节写操作时序
FPGA IIC的状态机实现,FPGA,fpga开发

注:这个只介绍2字节写操作,因为掌握了2字节的读写操作,一个字节的读写操作也是轻而易举的。这里不在做过多的解释。

3.1.1状态机的实现

FPGA IIC的状态机实现,FPGA,fpga开发FPGA IIC的状态机实现,FPGA,fpga开发图片太长我分两部分,由上面的写操作的时序,我抽象出不同的状态,状态1,空闲状态,状态2,开始状态,状态3,发送器件地址状态(send_device_addr),状态4,发送存储地址高位(send_storage_addr_H),状态5,发送存储地址高位(send_storage_addr_L),
状态6,写数据(wr_data),状态7,停止状态(stop)。

3.2读操作时序

FPGA IIC的状态机实现,FPGA,fpga开发

3.2.1状态机的实现

FPGA IIC的状态机实现,FPGA,fpga开发FPGA IIC的状态机实现,FPGA,fpga开发FPGA IIC的状态机实现,FPGA,fpga开发
图片太长我分三部分,由上面的读操作的时序,我抽象出不同的状态,状态1,空闲状态,状态2,开始状态,状态3,发送器件地址状态(send_device_addr),状态4,发送存储地址高位(send_storage_addr_H),
状态5,发送存储地址高位(send_storage_addr_L),
状态6,开始二状态(start_2),状态7,读数据状态(RD-DATA),状态8,停止状态(STOP).
由于二者之间有相同的部分,故我在一个状态转移图中表示所有状态的转移,整体的状态转移图如下图所示:
FPGA IIC的状态机实现,FPGA,fpga开发下面开始介绍如何实现这些状态机,在第一节中我们知道,状态跳转最重要的知道跳转的条件是什么,同时也要对系统的输入输出有清晰的认识。再此之前我先将状态跳转的程序附上。

/***************************组合逻辑表示状态转移*********************************/
always @(*) begin
     next_state = IDLE;
     case(current_state)
        IDLE:
            if(i2c_start == 1'b1)
               next_state = START;
            else 
               next_state = IDLE;
        START:
            if(cnt_driver_clk == 2'd3)
                next_state = SEND_DEVICE_ADDR;
            else 
                next_state = START;
        SEND_DEVICE_ADDR:
                if(addr_num == 1'b1) begin 
                        if((cnt_driver_clk == 2'd3)&&(cnt_bit == 4'd8)&&( sda_in == 1'b0 ))
                            next_state = SEND_STORAGE_ADDR_H;
                        else 
                            next_state = SEND_DEVICE_ADDR;
                    end
                else begin
                        if((cnt_driver_clk == 2'd3)&&(cnt_bit == 4'd8)&&( sda_in == 1'b0 ))
                            next_state = SEND_STORAGE_ADDR_L;
                        else 
                            next_state = SEND_DEVICE_ADDR;
                end
        SEND_STORAGE_ADDR_H:
            if((cnt_driver_clk == 2'd3)&&(cnt_bit == 4'd8)&&( sda_in == 1'b0 ))
                next_state = SEND_STORAGE_ADDR_L;
            else 
                next_state = SEND_STORAGE_ADDR_H;
        SEND_STORAGE_ADDR_L:
            if((cnt_driver_clk == 2'd3)&&(cnt_bit == 4'd8)&&( sda_in == 1'b0 )&&(i2c_rh_wl == 1'b0))
                next_state = WR_DATA;
            else if((cnt_driver_clk == 2'd3)&&(cnt_bit == 4'd8)&&( sda_in == 1'b0 )&&(i2c_rh_wl == 1'b1))
                next_state = START_2;
            else 
                next_state = SEND_STORAGE_ADDR_L;
        WR_DATA:
            if((cnt_driver_clk == 2'd3)&&(cnt_bit == 4'd8)&&( sda_in == 1'b0 ))
                next_state = STOP;
            else 
                next_state = WR_DATA;
        STOP:
            if((cnt_driver_clk == 2'd3)&&(cnt_bit == 4'd3))
                next_state = IDLE ;
            else 
                next_state = STOP ;
        START_2:
            if(i2c_rh_wl == 1'b1) begin 
                if(cnt_driver_clk == 2'd3)
                    next_state = SEND_DEVICE_ADDR_RD ;
                else 
                    next_state =  START_2;
            end
            else 
                next_state = START_2 ;
        SEND_DEVICE_ADDR_RD:
            if((cnt_driver_clk == 2'd3)&&(cnt_bit == 4'd8)&&( sda_in == 1'b0 ))
                next_state = RD_DATA ;
            else 
                next_state =  SEND_DEVICE_ADDR_RD;
            
        RD_DATA:
            if((cnt_driver_clk == 2'd3)&&(cnt_bit == 4'd8))
                next_state = STOP ;
            else 
                next_state = RD_DATA ;
        default:next_state = IDLE;
      endcase 
end
/*****************************************************************************
模块说明:状态机中的时序逻辑表示输出
输出参数:i2c_scl
*****************************************************************************/
always @(posedge driver_clk or negedge sys_rst) begin
    if(!sys_rst )
        i2c_scl <= 1'b1;
    else if((current_state ==START )||(current_state == SEND_DEVICE_ADDR)
            ||(current_state == SEND_STORAGE_ADDR_H)||(current_state == SEND_STORAGE_ADDR_L)
                ||(current_state == WR_DATA)||(current_state == START_2)||(current_state == SEND_DEVICE_ADDR_RD)
                    ||(current_state == RD_DATA))
            begin 
                if(cnt_driver_clk == 2'd2 )
                    i2c_scl <= 1'b0;
                else if(cnt_driver_clk == 2'd0)
                    i2c_scl <= 1'b1;
                else 
                    i2c_scl <= i2c_scl;
            end
        else 
            i2c_scl <= 1'b1;
end

解释:在IDLE状态下,当出现一个有效的下降沿时,也就是开始信号(i2c_start),状态跳转到START状态。当经过一个iic的时钟周期之后(cnt_driver_clk == 2‘d3),表示START状态持续了一个时钟周期,此时跳转到SEND_DEVICE_ADDR状态,在这里我参考的野火和原子的教程,对存储地址的字节数进行了判断,当addr_num=1时,表示是两个字节,当(cnt_driver_clk=3,cnt_bit=8,sda_in=0时),表示器件地址写入完成,此时进入下一个状态SEND_STORAGE_ADDR_H。相反 addr_num = 0时为一个字节的存储器地址,此时直接跳到SEND_STORAGE_ADDR_L状态。在SEND_STORAGE_ADDR_H状态时,当写入8个字节的高八位存储器地址时,跳入SEND_STORAGE_ADDR_L状态,在SEND_STORAGE_ADDR_L状态下,在写入的八位低存储器地址,此时若i2c_rh_wl= 0则进入写数据状态WR_DATA,若此时i2c_rh_wl = 1则进入读状态START_2。在WR_DATA状态的完成时,进入STOP状态,在时钟的上升沿的同时数据拉高,状态结束。在START_2状态下,再次经过一个IIc时钟周期,进入SEND_DEVICE_ADDR_RD状态,在这个状态下,当发送完第八个字节,切iic从机的相应为0时,进入读数据状态。无论是在WR_DATA还是RD_DATA状态他们的状态转移条件都是,写或者读满8个字节,之后进行状态的跳转。
下面附上不同状态的输入输出程序:

/*****************************************************************************
模块说明:状态机中的组合逻辑表示输出
输出参数:sda_out
*****************************************************************************/
always @(*) begin 
    if(! sys_rst ) begin 
        sda_out  = 1'b1 ;
        out_flag = 1'b1 ;
    end 
    else  begin
        case(current_state)
            IDLE:begin
                	sda_out  = 1'b1 ;
                	out_flag = 1'b1 ;
                end
            START:
                if(cnt_driver_clk <= 2'b0)
                    sda_out  = 1'b1 ;
                else 
                    sda_out  = 1'b0 ;
            SEND_DEVICE_ADDR:
                if(cnt_bit <= 4'd6 )
                    sda_out = DIVICE_ADDR[6-cnt_bit];
                else if(cnt_bit == 3'd7)
                    sda_out = 1'b0;      //写数据
                else  begin//if(cnt_bit == 3'd8)
                    	out_flag = 1'b0;
                    	sda_out  = 1'b1;
                    end
            SEND_STORAGE_ADDR_H: begin
                out_flag = 1'b1 ;
                if(cnt_bit <= 3'd7)
                    sda_out = byte_addr[15-cnt_bit];
                else  begin//if(cnt_bit == 3'd8)
                    	out_flag = 1'b0;
                    	sda_out  = 1'b1;
                    end
                end
            SEND_STORAGE_ADDR_L: begin
                out_flag = 1'b1 ;
                if(cnt_bit <= 3'd7)
                    sda_out = byte_addr[7-cnt_bit];
                else  begin//if(cnt_bit == 3'd8)
                    	out_flag = 1'b0;
                    	sda_out  = 1'b1;
                     end
                end 
            WR_DATA: begin 
                out_flag = 1'b1 ;
                if(cnt_bit <= 3'd7)
                    sda_out = wr_data[7-cnt_bit];
                else begin //if(cnt_bit == 3'd8)
                    	sda_out = 1'b1;
                    	out_flag = 1'b0;
                    end
                end
            STOP: begin
                out_flag = 1'b1 ;
                if((cnt_bit == 3'd0)&&(cnt_driver_clk == 2'b0))
                    sda_out = 1'b0;
                else if((cnt_bit == 3'd3)&&(cnt_bit == 3'd3))
                    sda_out = 1'b1;
                else 
                    sda_out = sda_out ;
               end
            START_2:begin 
                out_flag = 1'b1 ;
                if(cnt_driver_clk < 2'd2)
                    sda_out  = 1'b1 ;
                else 
                    sda_out  = 1'b0 ;
                end
            SEND_DEVICE_ADDR_RD:
                if(cnt_bit <= 3'd6)
                    sda_out = DIVICE_ADDR[6-cnt_bit];
                else if(cnt_bit == 3'd7)
                    sda_out = 1'b1;
                else begin //if(cnt_bit == 3'd8)
                    	sda_out  = 1'b1;
                    	out_flag = 1'b0; 
                    end
            RD_DATA: 
                if(cnt_bit <= 3'd7)
                    rd_data_reg[7-cnt_bit] = sda_in ;
                else begin  //if(cnt_bit == 3'd8)
                    	out_flag = 1'b1;
                    	sda_out  = 1'b1;    //非应答信号
                    end
            default:;
        endcase
    end
end

再次之前我先将一部分波形图附上:
1.写操作的波形图
FPGA IIC的状态机实现,FPGA,fpga开发FPGA IIC的状态机实现,FPGA,fpga开发
2.读操作的波形图
FPGA IIC的状态机实现,FPGA,fpga开发FPGA IIC的状态机实现,FPGA,fpga开发FPGA IIC的状态机实现,FPGA,fpga开发

cnt_driver_clk 先解释一下,这个变量的意思:本实验输出的IIC频率为250k,fpga的时钟输入时钟为50mhz,先进行分频将1mhz时钟driver_clk,这时采用一个cnt_driver_clk 计数器,计数到3的时候就说明iic经过一个时钟周期。cnt_bit,字节计数器。
大家这里可以参考波形图,理解代码,这里要写的太多了,如果有需要建议私信,我们好友联系。
最后一个知识点:数据io可以是双向的,这个可以将io口设计为三态模式的。

/************************三态门的使用**************************************/

assign  i2c_sda = out_flag ? sda_out : 1'bz ;     //当out_flag输出为高时,表示输出   
assign  sda_in  = i2c_sda                   ;

这段代码的解释就是,当out_flag为高电平时,i2c_sda = sda_out,当为低电平时,i2c_sda为高阻态。out_flag和sda_out的赋值,详细请看上面的代码,里面详细写了不同状态下,sda_out和out_flag是如何赋值的。当out_flag为低电平时,i2c_sda为输入作用,又sda_in输入。sda_in的赋值也在上面的代码中有详细的介绍。
IIC的介绍就到这里,如果需要工程源码,私信即可,欢迎大家交流沟通。
过段时间更新以太网的状态机实现,至此状态机将不再做更新。文章来源地址https://www.toymoban.com/news/detail-786794.html

到了这里,关于FPGA IIC的状态机实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【【IIC模块Verilog实现---用IIC协议从FPGA端读取E2PROM】】

    下面是 design 设计 下面是testbench 下面是注意事项 因为时钟的不同 我们先设计出本次时钟所需要的dri_clk 在配置完dri_clk 之后 我们需要做的是对整个I2C结构 进行状态机的 书写 建议 写成经典的三段状态机的形式 同步时序描述状态转移 组合逻辑判断状态转移条件 时序电路描述

    2024年02月03日
    浏览(45)
  • 通过eeprom验证FPGA实现的单字节/页读写IIC接口时序

      前文设计基于FPGA的IIC接口模块,本文将使用eeprom来验证该模块的设计。为了便于查看读写波形,采用两个按键来控制对eeprom数据的读写,当按键0按下后,FPGA向eeprom的前64个存储地址写入地址对应的数据,当按键1按下后,FPGA从eeprom的前64个存储地址读取数据。   该ee

    2024年02月21日
    浏览(37)
  • FPGA开发基础之三段式状态机

    状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,程序的运行其本质也是状态机,根据输入完成输出,得到新的状态。 在平时硬件电路的设计中经常需要用到状态机,例如CPU的取指、译码、执行,这个流程可以使用状态机来控制

    2024年02月04日
    浏览(34)
  • 【接口协议】FPGA实现IIC协议对EEPROM进行数据存储与读取(AT24C64)

    使用vivado实现IIC协议对EEPROM进行数据存储与读取。本文是基于正点原子的“达芬奇”开发板资料进行学习的笔记,对部分地方进行了修改,并进行了详细的讲解。 IIC(Inter-Integrated Circuit),即集成电路总线,是一种同步半双工串行总线,用于连接微控制器及外围设备,是用于数据

    2024年02月04日
    浏览(64)
  • FPGA以状态机实现串口通信

    UART通信只有两根信号线,一-根是发送数据端口线叫tx ,一 根是接收数据端口线叫rx ,对于上位机来说它的tx要和对于FPGA来说的rx连接,同样上位机的rx要和FPGA的tx连接,如果是两个tx或者两个rx连接那数据就不能正常被发送出去和接收到。UART可以实现全双工,即可以同时进行

    2024年02月03日
    浏览(32)
  • 【FPGA学习】状态机实现UART通信

      在之前的文章中【FPGA学习】实例一、Cyclone IV串口通信(RS232)我们已经能够采用波形图的方法,实现9600bps的Uart通信。近期笔者在整理了状态机和计数器组合的设计方法以后,对状态机的设计又有了新的感悟和体会,所以又把经典的RS232协议拉出来当状态机的例子练手了哈哈

    2023年04月11日
    浏览(36)
  • 【FPGA】基于状态机实现自动售货机模拟

    此自动售货机模拟基于EP4CE6F17C8开发板实现,用按键,led灯,数码管表示各个输入输出 功能: 此自动售货机可以选择A(三元)与B(五元)商品且选择商品数量,一次可以投1块与5块钱。当投币数量满足商品价格时则能出货,且退款额外支付,在任意阶段皆可以取消支付并退

    2024年02月16日
    浏览(32)
  • FPGA:三大协议(IIC、UART、SPI)之IIC

    摘要:1、本文讲述IIC的物理层面的结构(使用iic工作的物理层面的连接);2、本文讲解协议层面的通信交流格式(IIC时序);3、提供一个主机和从机的一个verilog代码;4、本文的主从机指的是:板子一号作为主机,发送数据给作为从机的板子二号;注意:在实际应用中,一

    2024年02月06日
    浏览(34)
  • FPGA在校学习记录系列---实验4不同状态的LED+开发板(Verilog HDL)

    此系列记录FPGA在学校的学习过程。 FPGA系列 需要用到的软硬件: 软件:Quartus II 15.0 (64-bit) 硬件: 5CEBA4F23C7芯片 链接: FPGA在校学习记录系列—新建一个FPGA工程编写程序并仿真(Verilog HDL) 创建的工程名字为:LED (这次不用仿真,直接用开发板验证) 编译文件 按键资源:

    2024年04月09日
    浏览(37)
  • FPGA串行通信(UART,IIC,SPI)

    此篇为学习正点原子FPGA课程总结 串行/并行通信 串行通信即收发双方通过单根线进行数据传输,发送方有并转串逻辑,接收方有串转并逻辑。优点是占用IO少,成本低,缺点是速率低。 并行通信一次用多根数据线传输。优点是速度快,缺点是占用IO多,成本高。 单工/半双工

    2024年02月04日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包