一、引言
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

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

被折叠的 条评论
为什么被折叠?



