FPGA_ISP:IIC控制器的设计

本文详细介绍了FPGA如何通过IIC协议与EEPROM进行通信,涵盖了IIC协议的基本原理、时序分析以及在FPGA中的实现细节。作者通过设计任务——读写EEPROM来阐述IIC协议的应用,强调了FPGA实现IIC协议的优势在于其精确的时序控制和实时性。在程序设计部分,展示了模块化的FPGA代码,包括顶层模块、IIC驱动模块和读写模块,每个模块都详细描述了状态机和时钟控制。最后,作者对IIC协议的字地址发送和页写操作进行了思考和解释。


一、引言

IIC协议最早是我在学习单片机时接触的。那会,IIC协议就是作为器件间进行交互的两线协议,只要把几个功能函数的时序设计出来,IIC模块也就适用了这一类的外设。
但后来接触了FPGA,却一直没想过用输入输出的方式实现IIC,原因是觉得很麻烦,直到后来接触了FPGA控制摄像头,才明白这东西的重要性。
和以往GPIO来模仿IIC时序的方式相比,FPGA直接产生的时序,非常精准,实时性也高,而且具备一定的通用性,在摄像头配置、HDMI的EDID配置和EEPROM等存储设备上,有广泛的应用。


二、协议介绍

IIC协议,Inter Integrated Circuit,即集成电路总线。是由Philips半导体公司(如今是NXP半导体公司)在上世纪80年代初设计的一种“简单”、“双向”的二进制总线标准。多用于主机、从机在数据量不大且传输距离短的场合下通信。
在这里插入图片描述
IIC总线由数据线SDA与时钟线SCL共同构成通信线路,既能发送数据,也能接收数据。在主控与被控间可双向数据传送,数据传输速率标准模式下可达100kbit/s、快速模式下可达400kbit/s、高速模式下可达3.4mbit/s。被控器件均并联与总线上,通过slave addr器件地址识别。上述是我的达芬奇开发板的IIC总线物理拓扑结构。

Notes:
1° 从上述拓扑中,还可以发现,SDA和SCL都是有上拉电阻的,空闲状态下是高电平,当总线上任一器件输出低电平时,总线都会被拉低,表明诸多器件各自的SDA和SCL是“线与”的关系。
2° IIC总线有多主和主从两种工作方式,一般是主从的方式(达芬奇开发板)。在主从工作方式下,主机启动数据的发送(启动信号),并产生时钟信号,数据发送完了后,发出停止信号。


三、协议时序

在这里插入图片描述
上面这图是IIC的整体协议图。在IIC器件开始通信传输数据前,SCL和SDA被上拉而处于高电平(空闲状态)。如果FPGA(主机)想开始传输数据,只需SCL为高时,将SDA给拉低,来产生一个起始信号,从机检测到这个起始信号后,准备接收或发送数据,当接收或发送完成后,主机产生一个停止信号(SCL为高时,SDA从低变高),告诉从机数据传输结束。

在这里插入图片描述
IIC是两线结构,在时钟线下,数据线可以有顺序的传送数据。如上面所示,当SCL为低电平时,SDA允许改变传输的数据位(电平值),但当SCL为高电平后,SDA要求保持稳定,相当于一个时钟周期内传输1bit,经过8个时钟周期后,传输一个字节。第8个时钟周期末时,主机释放SDA以使从机应答,第9个时钟周期时,从机将SDA拉低以应答。如果第9个周期,SCL为高电平时,SDA未被检测到为低电平,则视为非应答,表明此次数据传输失败。第9个时钟周期末,从机释放SDA,以使主机继续传输数据,若主机发送停止信号,表示传输结束。另外,要注意:数据以8bit为单位串行发出,最先发送的是字节的最高位。如下面这图所示。
在这里插入图片描述


四、设计任务

这东西本来是该最开始说的,但规矩是死的…本次的任务:读写板卡上的EEPROM,0~255地址分别写入255到0的数据,写完再读取,IF正确则LED灯亮,否则灯闪烁。


五、一些说明

说明1:板卡上的EEPROM是由AT24C64来提供的,器件地址为1010 + 3位可编程地址,3位可编程地址由器件上的3个管脚A2、A1和A0的硬件连接来决定(分别接VCC或GND来实现),在我的达芬奇开发板上,3个管脚直接接地。

说明2:数据传输时,主机先向总线上发出开始信号,对应开始位S,然后按最高位到低位的方式,发送器件地址,一般为7bit,第8bit是读写控制位R/W,0表示主机对从机进行写操作,1表示主机对从机进行读操作,然后从机响应。在AT24C64中的传输器件地址格式为下图所示。
在这里插入图片描述
说明3:当发送完第一个字节(7bit器件地址和1bit读写控制位),并收到从机正确的应答后,就开始发送字地址(Word Address)。
说明4:主机发送完字地址,从机正确应答后,就把内部的存储单元地址指针指向该单位,IF为写命令,从机就处于接收数据的状态,此时,主机就开始写数据了,包括单次写(字节写)和连续写(页写),区别是发送完一个字节后,发送的是结束信号,还是下一个字节数据。
说明5:AT24C64引脚说明。
在这里插入图片描述


六、程序设计

1、模块架构:顶层e2prom_top、读写模块e2prom_rw、I2C驱动模块i2c_dri和LED灯显示模块led_alarm。
在这里插入图片描述
2、管脚约束。时钟50M,复位低电平有效,时钟线、数据线,LED灯线。

set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN F13 IOSTANDARD LVCMOS33} [get_ports iic_scl]
set_property -dict {PACKAGE_PIN A19 IOSTANDARD LVCMOS33} [get_ports iic_sda]
set_property -dict {PACKAGE_PIN V9 IOSTANDARD LVCMOS15} [get_ports led]

3、e2prom_top层代码。

// top 主要是模块的例化
module e2prom_top(
    input               sys_clk    ,      //系统时钟
    input               sys_rst_n  ,      //系统复位
    //eeprom interface
    output              iic_scl    ,      //eeprom的时钟线scl
    inout               iic_sda    ,      //eeprom的数据线sda
    //user interface
    output              led               //led显示
);

//parameter define
parameter    SLAVE_ADDR = 7'b1010000    ; //器件地址(SLAVE_ADDR)
parameter    BIT_CTRL   = 1'b1          ; //字地址位控制参数(16b/8b)
parameter    CLK_FREQ   = 26'd50_000_000; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter    I2C_FREQ   = 18'd250_000   ; //I2C的SCL时钟频率
parameter    L_TIME     = 17'd125_000   ; //led闪烁时间参数

//wire define
wire           dri_clk   ; //I2C操作时钟
wire           i2c_exec  ; //I2C触发控制
wire   [15:0]  i2c_addr  ; //I2C操作地址
wire   [ 7:0]  i2c_data_w; //I2C写入的数据
wire           i2c_done  ; //I2C操作结束标志
wire           i2c_ack   ; //I2C应答标志 0:应答 1:未应答
wire           i2c_rh_wl ; //I2C读写控制
wire   [ 7:0]  i2c_data_r; //I2C读出的数据
wire           rw_done   ; //E2PROM读写测试完成
wire           rw_result ; //E2PROM读写测试结果 0:失败 1:成功 

//*****************************************************
//**                    main code
//*****************************************************

//e2prom读写测试模块
e2prom_rw u_e2prom_rw(
    .clk         (dri_clk   ),  //时钟信号
    .rst_n       (sys_rst_n ),  //复位信号
    //i2c interface
    .i2c_exec    (i2c_exec  ),  //I2C触发执行信号
    .i2c_rh_wl   (i2c_rh_wl ),  //I2C读写控制信号
    .i2c_addr    (i2c_addr  ),  //I2C器件内地址
    .i2c_data_w  (i2c_data_w),  //I2C要写的数据
    .i2c_data_r  (i2c_data_r),  //I2C读出的数据
    .i2c_done    (i2c_done  ),  //I2C一次操作完成
    .i2c_ack     (i2c_ack   ),  //I2C应答标志 
    //user interface
    .rw_done     (rw_done   ),  //E2PROM读写测试完成
    .rw_result   (rw_result )   //E2PROM读写测试结果 0:失败 1:成功
);

//i2c驱动模块
i2c_dri #(
    .SLAVE_ADDR  (SLAVE_ADDR),  //EEPROM从机地址
    .CLK_FREQ    (CLK_FREQ  ),  //模块输入的时钟频率
    .I2C_FREQ    (I2C_FREQ  )   //IIC_SCL的时钟频率
) u_i2c_dri(
    .clk         (sys_clk   ),  
    .rst_n       (sys_rst_n ),  
    //i2c interface
    .i2c_exec    (i2c_exec  ),  //I2C触发执行信号
    .bit_ctrl    (BIT_CTRL  ),  //器件地址位控制(16b/8b)
    .i2c_rh_wl   (i2c_rh_wl ),  //I2C读写控制信号
    .i2c_addr    (i2c_addr  ),  //I2C器件内地址
    .i2c_data_w  (i2c_data_w),  //I2C要写的数据
    .i2c_data_r  (i2c_data_r),  //I2C读出的数据
    .i2c_done    (i2c_done  ),  //I2C一次操作完成
    .i2c_ack     (i2c_ack   ),  //I2C应答标志
    .scl         (iic_scl   ),  //I2C的SCL时钟信号
    .sda         (iic_sda   ),  //I2C的SDA信号
    //user interface
    .dri_clk     (dri_clk   )   //I2C操作时钟
);

//led指示模块
led_alarm #(.L_TIME(L_TIME  )   //控制led闪烁时间
) u_led_alarm(
    .clk         (dri_clk   ),  
    .rst_n       (sys_rst_n ), 
    
    .rw_done     (rw_done   ),  
    .rw_result   (rw_result ),
    .led         (led 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ATian+

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值