I2C驱动模块代码编写
module i2c_ctrl
#(
parameter SYS_CLK_FREQ = 'd50_000_000,
parameter SCL_FREQ = 'd250_000,
parameter DEVICE_ADDR = 7'd1010_011
)
//系统时钟 50mhz和 i2c_scl 250khz
(
input wire sys_clk,
input wire sys_rst_n,
input wire i2c_start,
input wire wr_en,
input wire [15:0] byte_addr,
input wire [7:0] wr_data,
input wire rd_en,
input wire addr_num,
output wire i2c_scl,
output wire i2c_sda,
output wire [7:0] rd_data,
output wire i2c_end,
output reg i2c_clk
);
parameter CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >>3//将这个数值缩写四倍 获取 i2c_clk,右移三位,得到 i2c_clk的计数值,/6
parameter IDLE = 4'd00
START = 4'd01
SEND_D_A = 4'd02
ACK_1 = 4'd03
SEND_B_H = 4'd04
ACK_2 = 4'd05
SEND_B_L = 4'd06
ACK_3 = 4'd07
WR_DATA = 4'd08
ACK_4 = 4'd09
START_2 = 4'd10
SEND_R_A = 4'd11 //发送读地址
ACK_5 = 4'd12
RD_DATA = 4'd13
N_ACK = 4'd14
STOP = 4'd15
reg [7:0] cnt_clk;
reg [3:0] state;
reg [1:0] cnt_i2c_clk;
reg cnt_i2c_clk_en;
reg [3:0]cnt_bit;
reg sda_out;
wire sda_en;
reg ack;
wire sda_in;
// cnt_clk的生成
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 8'b0;
else if(cnt_clk == CNT_CLK_MAX - 1)
cnt_clk <= 8'd0;
else
cnt_clk <= cnt_clk + 1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
i2c_clk <= 1'b1;
else if(cnt_clk == CNT_CLK_MAX - 1)
i2c_clk <= ~i2c_clk;
// i2c_clk为工作时钟 状态转移
always@(posedge i2c_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE:
if(i2c_start == 1'b1)
state<= START;
else
state <= state;
START:
if(cnt_i2c_clk == 2'd3)
state <= SEND_D_A;
else
state <= state;
SEND_D_A:
if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
state <= ACK_1;
else
state <= state;
ACK_1:
if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
begin
if(addr_num == 1'b1)
state <= SEND_B_H;
else
state <= SEND_B_L;
end
state <= state;
SEND_B_H:
if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
state <= ACK_2;
else
state <= state;
ACK_2:
if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
state <= SEND_B_L;
else
state <= state;
SEND_B_L:
if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
state <= ACK_3;
else
state <= state;
ACK_3:
if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
begin
if(wr_en == 1'b1)
state <= WR_DATA;
else if(rd_en == 1'b1)
state <= START_2;
else
state <= state;
end
else
state<=state;
WR_DATA:
if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
state <= ACK_4
else
state <= state;
ACK_4:
if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
state <= STOP;
else
state <= state;
START2:
if(cnt_i2c_clk == 2'd3)
state <= SEND_R_A;
else
state <= state;
SEND_R_A:
if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
state <= ACK_5
else
state <= state;
ACK_5:
if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
state <= RD_DATA;
else
state <= state;
RD_DATA:
if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
state <= N_ACK;
else
state <= state;
N_ACK:
if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
state <= STOP;
else
state <= state;
STOP:
if(cnt_bit == 3'd3 && cnt_i2c_clk == 2'd3)
state <= IDLE;
else
state <= state;
default:
state <= IDLE;
endcase
// cnt_bit 的赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_i2c_clk <= 2'd0;
else if(cnt_i2c_clk_en == 1'b1)
cnt_i2c_clk <= cnt_i2c_clk+ 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_i2c_clk_en <= 1'b0;
else if(cnt_bit == 3'd3 && cnt_i2c_clk == 2'd3&& state ==STOP)
cnt_i2c_clk_en <= 1'b0;
else if(i2c_start == 1'b1)
cnt_i2c_clk_en <= 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_bit <= 3'd0;
else if(state == IDLE || state ==START || state ==ack_1 || state ==ACK_2 ||state ==ACK_3||state ==ACK_4 ||state == ACK_5 ||state==START_2 || state ==N_ACK)
cnt_bit <= 3'd0;
else if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
cnt_bit <= 3'd0;
else if(cnt_i2c_clk ==2'd3 && state != IDLE)
cnt_bit <= cnt_bit + 1'b1;
// 完成组合逻辑赋值 , 状态输出
always@(*)
case(state)
IDLE:
sda_out <= 1’b1;
START:
if(cnt_i2c_clk == 2'd0)
sda_out <=1'b1;
else
sda_out <= 1'b0;
SEND_D_A:
if(cnt_bit <= 3'd6)
sda_out <= DEVICE_ADDR[6-cnt_bit)
.....
......
// sda_en 的赋值
assign sda_en = (state ==RD_DATA || state == ACK_1 || state == ACK_2|| state == ACK_3||state == ACK_4||state == ACK_5) ?1'b0: 1'b1;
// 组合逻辑 ACK的赋值
always@(*)
case(state)
ACK_1,ACK_2,ACK_3,ACK_4,ACK_5;
if(cnt_i2c_clk == 2'd0)
ack <= sda_in;
else
ack <= ack;
default:
ack <= 1'b1;
endcase
assign sda_en = i2c_sda;
always@(*)
case(state)
IDLE:
rd_data_reg <= 8'd0;
RD_DATA:
rd_data_reg[7-cnt_bit] <= sda_in;
default:
rd_data_reg <= rd_data_reg;
endcase
........
//赋值
endmodule
i2c_rw_data:
跨时钟域处理:
sys_clk i2c_clk:
工作时钟为 i2c_clk为1MHZ,此处我们采用低频信号去采集高频信号,出现采集不正确的情况,做跨时钟域处理。需要将读写使能信号 在系统时钟下 同步到 工作时钟i2c_clk信号下。 经过处理后可以正确采集到read和write信号。
跨时钟域处理:
多bit数据:用fifo,数据缓存,写入和读出采用不同时钟。
同频不同相数据: 采用打拍的方法。
我们这里时高频到低频的跨时钟域处理:
经过按键消抖write和read只有一个时钟周期的有效信号,我们可以延长数据有效周期,write_vaild,高电平持续时间大于2个时钟周期的 工作时钟i2c_clk。 也就是说我们这里高电平有效时间 大于100个时钟周期sys_clk, 我们这里取200个时钟周期, 所以我们只需要去采集到 write_vaild信号即可, 而读取信号同理,延长有效时钟周期即可。
i2c_rw_data模块的编写不做叙述了。