突然发现好久没写文章了,今天就写一篇关于i2c的通用控制模块。
i2c协议保护起始,数据传输,ACK或NACK,和传输终止信号。以下是对应的时序图:
在SCL为高的情况下,SDA由高跳到低,这是起始信号,之后在时钟为低电平时更新数据,在高电平时数据保持稳定,每一次传输8bit数据之后是ACK信号,在受到ACK信号后可以选择结束通信或者继续传输数据,这是基本的i2c协议。
而eeprom的i2c有些许不一样,所以在设计时应考虑加入一些冗余以兼容eeprom的读写。
eeprom的读操作如下图:
在向SDA写入设备地址和设备内的存贮地址时,待ACK信号后不是继续传输地址,而是发出起始信号,在接受读操作时需要回复NACK信号(ACK信号是接受方在SCL为高电平时写入低电平数据,NACK,则相反),之后便是终止信号。
基于上述内容,设计如下状态机:
这里WAIT_READ和WAIT_WRITE是为满足时序,实现传输8bit后给外部模块相应的信号,WAIT状态将接受外部的信号以确定下一步的动作是继续传输数据还是终止传输,内部保留超时机制,确保状态机不会被卡死,并且存在跳到START的可能,主要是为了兼容eeprom的读操作而特意设计的,W_ACK,R_ACK是为区分上一步的操作是都还是写,WRITE和READ是读写状态,在需要回复ACK信号时,如果回复NACK信号会跳转到STOP状态以结束通信,整个状态跳转机流程基本介绍完毕,以下是对应的实现代码:
module i2c_ctrl(clk,rst_n,din,w_next,r_next,t_done,ena,restart,i2c_scl,i2c_sdl,done,tc,dout,sdl_ena);
input clk;
input rst_n;
input [7:0]din;
input w_next;
input r_next;
input t_done;
input ena;
input restart;
inout i2c_sdl;
output i2c_scl;
output reg done;
output reg tc;
output reg [7:0]dout;
reg [10:0]state;
reg [1:0]scl_cnt;
reg [7:0]wait_cnt;
reg [3:0]bit_cnt;
reg [7:0]d_in_tmp;
reg sdl_data;
output reg sdl_