目录
- 1.24c04芯片手册解读
- 2.纯verilog的i2c驱动
- 3.24c04读写状态机设计
- 4.上板调试验证
- 5.福利:工程源码获取
1.24c04芯片手册解读
24c04芯片手册很简单,原理图设计页很简单,这里只说代码设计需要注意的点:
1、写操作延时周期大于等于2ms,如下:
这个其实也很好理解,这毕竟是写一次数据就可以保存200年的东西,你写操作难道不应该多花点时间让期间“固化”完毕吗?
具体在代码层面如何体现呢?
那就是写完一次数据后要求等待至少2ms后才能发起下一次写操作,本设计采用延时4ms的稳妥方案;
2、器件地址,如下:
常规操作,3个地址引脚的电平决定器件地址,我的板子原理图如下:
所以器件地址就是7’b1010000;也就是0xa0;
3、穿行时钟,如下:
100k到400k,用100k最保险,宜小不宜大;本设计也是用的100k;
2.纯verilog的i2c驱动
详细代码不在这里给出,因为粘贴出来会很长,影响阅读体验,私我可得源码慢慢欣赏;
下面给出i2c驱动的顶层接口说明:
rst //高电平复位
clk //输入系统时钟
clk_div_cnt //i2c_scl分配系数:clk_div_cnt=clk/(5*i2c_scl)-1
scl_pad_i // SCL-line input
scl_pad_o // SCL-line output (always 1'b0)
scl_padoen_o // SCL-line output enable (active low)
sda_pad_i // SDA-line input
sda_pad_o // SDA-line output (always 1'b0)
sda_padoen_o // SDA-line output enable (active low)
i2c_addr_2byte // 地址位宽选择:1-->16bit ? 0-->8bit
i2c_read_req // 发起读操作,高电平有效
i2c_read_req_ack // 读操作完成反馈高脉冲信号
i2c_write_req // 发写读操作,高电平有效
i2c_write_req_ack // 写操作完成反馈高脉冲信号
i2c_slave_dev_addr// I2c device address
i2c_slave_reg_addr// I2c register address
i2c_write_data // I2c write register data
i2c_read_data // I2c read register data
error // 读写错误反馈信号
3.24c04读写状态机设计
设计很简单,分为一下几步:
1:上电等待250ms,让24c04准备好;
2:发起一次写操作,并给出写地址和写数据;
3:完成写操作后,写地址和写数据累加,并循环写255次;
4:写完255个数据后,发起读操作,并给出读地址;
5:完成读操作后,读地址累加,并循环读255次;
6:读完255个数据后,再从第一步开始循环;
状态机部分代码如下:
always @(posedge clk) begin
if(~rstn) begin
ST <='d0;
i2c_read_req <='d0;
i2c_write_req <='d0;
i2c_slave_reg_addr<='d0;
w_slave_reg_addr <='d0;
r_slave_reg_addr <='d0;
i2c_write_data <='d0;
write_data_cnt <='d0;
read_data_cnt <='d0;
r_i2c_write_data <='d0;
e2p_cnt <='d0;
end
else begin
case(ST)
'd0: begin
if(e2p_cnt==E2P_DELAY) begin //上电后延时250ms,让24c04准备好
ST<='d1;
e2p_cnt<='d0;
end
else e2p_cnt<=e2p_cnt+'d1;
end
'd1: begin
if(i2c_write_req_ack) begin //写操作完成,进入等待写周期和地址、数据累加
i2c_write_req<='d0;
ST<='d2;
end
else begin
i2c_write_req<='d1; //发起写操作
i2c_write_data<=r_i2c_write_data; //给出写数据
i2c_slave_reg_addr<=w_slave_reg_addr; //给出写地址
end
end
'd2: begin
if(write_data_cnt=='d255) begin //写满255个数据后进入读数据状态
write_data_cnt <='d0;
i2c_write_data <='d0;
r_i2c_write_data <='d0;
w_slave_reg_addr <='d0;
i2c_slave_reg_addr<='d0;
ST<='d4;
end
else begin
if(wr_delay==WR_CYCEL) begin //延时4ms写周期,写数据和地址累加
write_data_cnt <=write_data_cnt+'d1;
r_i2c_write_data<=r_i2c_write_data+'d1;
w_slave_reg_addr<=w_slave_reg_addr+'d1;
ST<='d3;
end
end
end
'd3: ST<='d1; //打一排再循环写,保证写数据和地址在发起写操作前已经更新
'd4: begin
if(i2c_read_req_ack) begin
i2c_read_req<='d0;
ST<='d5;
end
else begin
i2c_read_req<='d1; //发起读操作
i2c_slave_reg_addr<=r_slave_reg_addr; //给出读地址
end
end
'd5: begin
if(read_data_cnt=='d255) begin //读完255个数据后重新开始状态机
read_data_cnt <='d0;
r_slave_reg_addr<='d0;
i2c_slave_reg_addr<='d0;
ST<='d0;
end
else begin
read_data_cnt <=read_data_cnt+'d1;
r_slave_reg_addr <=r_slave_reg_addr+'d1; //读地址累加
ST<='d6;
end
end
'd6: ST<='d4;
default: ST<='d0;
endcase
end
end
4.上板调试验证
开发板:Xilinx Artix7-35T开发板;
开发环境:vivado2019.1;
ila抓取波形如下:
写操作:
随机抓取写地址为100的波形,此时写地址为100,写数据也应该为100;抓取结果如下:
再抓读地址为100时的波形,此时读数据也应该是100才对,抓取结果如下:
可以看到,当读反馈信号i2c_read_req_ack为高时,地址为100,读数据也为100;
我们的读写完全正确。。。
5.福利:工程源码获取
福利:工程代码的获取
代码太大,无法邮箱发送,以某度网盘链接方式发送,
资料如下:获取方式:私。