文章目录
一、i2c协议
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
I2C是一个能够支持多个设备的总线,包含一条双向串行数据线SDA,一条串行时钟线SCL。
二、看i2c–eeprom手册找关键
1.设别型号选择
这里我是用的C4的板子,24LC04B,时钟最高是400kHZ
2.描述
EEPROM的存储的大小为2个block,一个block是256*8bit的存储大小
3.总线时序图
4.总线开始和停止
红色为开始标志
绿色为结束标志
5.数据在总线上传输
6.设备地址(控制命令)
设备地址也称为控制命令,是一个字节
操作 | Control Code | Block Select | R/W |
---|---|---|---|
读 | 1010( 对24XX04, 1010是读写操作) | XX(对24XX04,这两位don’t care) | 1 |
写 | 1010( 对24XX04, 1010是读写操作) | XX(对24XX04,这两位don’t care) | 0 |
7.写操作
- 单字节写
- 单字节写先开始位,
- 然后写控制字节,从机接收到发应答信号,
- 然后写数据地址,从机接收到发应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到发应答信号,
- 然后写数据,从机接收到发应答信号,
- 最后是结束位。
- 页写
- 页节写先开始位,
- 然后写控制字节,从机接收到应答信号,
- 然后写数据地址,从机接收到应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到应答信号,
- 然后写数据,从机接收到应答信号,
- 然后继续写数据,直到写完全部的数据,
- 最后是结束位。
8.读操作
- 当前地址读
当前一次读操作或者写操作完毕后,24XX04内部有一个地址计数器,会自增一,所以当前地址读是对下一个地址去读。
- 当前地址读先开始位,
- 然后写控制字节,从机接收到应答信号,然后读数据,无应答信号,
- 最后结束位
- 随机读
- 随机读先开始位,
- 然后写控制字节,从机接收到应答信号,
- 然后dummuy write 虚写,写数据地址,从机接收到应答信号,
- 然后开始位,
- 然后读控制字节,从机接收到应答信号
- 然后读数据
- 最后结束位
- 顺序读
- 顺序读就是随机读的加强版,读很多的数据
三、状态机设计
1.i2c协议接口的状态图
2.eeprom读写的状态图
注意事项
eeprom是高位先发,
四、代码部分
1.i2c_interface.v
// i2c接口模块
module i2c_interface(
input clk,
input rst_n,
// 接口与eeprom 单总线sda
output scl,
input sda_in,
output sda_out,
output sda_out_en,
// 接口与主机
input req,
input [3:0] cmd,
input [7:0] din,
output slave_ack,
output [7:0] dout,
output done
);
// i2c兼容100kHZ-400kHZ,我们这里使用200kHZ
// 我们先拉低sclk,再拉高sclk
parameter SCL_TIME = 250,// 200kHZ 的一个周期
SCL_HALF_TIME = 125,// 200kHZ 的半个周期
SCL_LOW_MID = 65,//
SCL_HIGH_MID = 185;
// 开始读写结束命令
localparam START_CMD = 4'b0001,
WRITE_CMD = 4'b0010,
READ_CMD = 4'b0100,
STOP_CMD = 4'b1000;
localparam IDLE = 7'b000_0001,// 默认状态
START = 7'b000_0010,// 开始位
WRITE = 7'b000_0100,// 写状态
READ = 7'b000_1000,// 读状态
SEDACK = 7'b001_0000,// 发送应答信号状态
RECACK = 7'b010_0000,// 接收应答信号状态
STOP = 7'b100_0000;// 停止位
// 状态机
reg [6:0] state_c;
reg [6:0] state_n;
// 状态转移条件
wire idle2start ;
wire idle2write ;
wire idle2read ;
wire start2write ;
wire start2read ;
wire write2recack;
wire read2sedack ;
wire sedack2stop ;
wire sedack2idle ;
wire recack2stop ;
wire recack2idle ;
wire stop2idle ;
// 串行时钟计数器 何时拉高何时拉低
reg [8:0] cnt_scl;
wire add_cnt_scl;
wire end_cnt_scl;
// bit计数器
reg [3:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;
// 寄存i2c接口与eeprom的数据
reg i2c_scl;
reg i2c_sda_out;
reg i2c_sda_out_en;
// 寄存接收从eeprom发来的数据
reg [7:0] rx_data;
// 寄存主机接收从机发来的应答信号
reg rx_ack;
// 状态机
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always @(*)begin
case (state_c)
IDLE :begin
if(idle2start)begin
state_n = START;
end
else if(idle2write)begin
state_n = WRITE;
end
else if(idle2read)begin
state_n = READ;
end
else begin
state_n = state_c;
end
end
START :begin
if(start2write)begin
state_n = WRITE;
end
else if(start2read)begin
state_n = READ;
end
else begin
state_n = state_c;
end
end
WRITE :begin
if(write2recack)begin
state_n = RECACK;
end
else begin
state_n = state_c;
end
end
READ :begin
if(read2sedack)begin
state_n = SEDACK;
end
else begin
state_n = state_c;
end
end
SEDACK :begin
if(sedack2stop)begin
state_n = STOP;
end
else if(sedack2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
RECACK :begin
if(recack2stop)begin
state_n = STOP;
end
else if(recack2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
STOP :begin
if(stop2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default: state_n = IDLE;
endcase
end
assign idle2start = state_c == IDLE && (req && (cmd & START_CMD));// 有请求就开始
assign idle2write = state_c == IDLE && (req && (cmd & WRITE_CMD)); // 有请求并且继续写
assign idle2read = state_c == IDLE && (req && (cmd & READ_CMD));// 有请求并且继续读
assign start2write = state_c == START && (end_cnt_bit && (cmd & WRITE_CMD));// 1bit开始位
assign start2read = state_c == START && (end_cnt_bit && (cmd & READ_CMD));// 1bit1开始位
assign write2recack = state_c == WRITE && (end_cnt_bit);// 8bit数据写完
assign read2sedack = state_c == READ && (end_cnt_bit);// 8bit数据写完
assign sedack2stop = state_c == SEDACK && (end_cnt_bit && (cmd & STOP_CMD));// 1bit发送应答信号
assign sedack2idle = state_c == SEDACK && (end_cnt_bit && (cmd & STOP_CMD) == 0);// 没有停止命令 就继续
assign recack2stop = state_c == RECACK && (end_cnt_bit && (cmd & STOP_CMD));// 1bit接收应答信号
assign recack2idle = state_c == RECACK && (end_cnt_bit && (cmd & STOP_CMD) == 0);// 没有停止命令 就继续
assign stop2idle = state_c == STOP && (end_cnt_bit);// 1bit接收位
// 串行时钟计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_scl <= 0;
end
else if(add_cnt_scl)begin
if(end_cnt_scl)begin
cnt_scl <= 0;
end
else begin
cnt_scl <= cnt_scl + 1;
end
end
else begin
cnt_scl <= cnt_scl;
end
end
assign add_cnt_scl = (state_c != IDLE);
assign end_cnt_scl = add_cnt_scl && cnt_scl == SCL_TIME - 1;
// bit计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0;
end
else begin
cnt_bit <= cnt_bit + 1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_cnt_bit = end_cnt_scl;
assign end_cnt_bit = add_cnt_bit && cnt_bit == (((state_c == WRITE) || (state_c == READ))?(8 - 1):(1 - 1));
// 串行时钟scl
// 利用计数器形成一个串行时钟
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
i2c_scl <= 1;
end
else if(idle2start || idle2write || idle2read)begin
i2c_scl <= 1'b0;
end
else if(add_cnt_scl && (cnt_scl == SCL_HALF_TIME))begin
i2c_scl <= 1'b1; //
end
else if(end_cnt_scl)begin
i2c_scl <= 1'b0;
end
else if(stop2idle)begin
i2c_scl <= 1'b1;
end
end
// sda数据总线
// sda_in
// 在串行总线为高的中间时刻数据稳定采集eeprom的数据
// 串并转换
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_data <= 0;
end
else if((state_c == READ) && add_cnt_scl && (cnt_scl == SCL_HIGH_MID))begin
rx_data[7-cnt_bit] <= sda_in;
end
end
// sda_in
// 主机采样从机的应答信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_ack <= 1'b1;
end
else if((state_c == RECACK) && add_cnt_scl && (cnt_scl == SCL_HIGH_MID))begin
rx_ack <= sda_in;
end
end
// sda_out
// 在串行总线为低的中间时刻数据改变给eeprom发数据
// 串并转换
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
i2c_sda_out <= 0;
end
else if(state_c == START)begin
if(add_cnt_scl && cnt_scl == SCL_LOW_MID)begin
i2c_sda_out <= 1'b1; // 保证之前不是拉低的状态,先将它拉高,检测开始位
end
else if(add_cnt_scl && cnt_scl == SCL_HIGH_MID)begin
i2c_sda_out <= 1'b0; // 检测开始位
end
end
else if(state_c == STOP)begin
if(add_cnt_scl && cnt_scl == SCL_LOW_MID)begin
i2c_sda_out <= 1'b0; // 保证之前不是拉低的状态,先将它拉高,检测结束位
end
else if(add_cnt_scl && cnt_scl == SCL_HIGH_MID)begin
i2c_sda_out <= 1'b1; // 检测结束位
end
end
else if((state_c == WRITE) && add_cnt_scl && (cnt_scl == SCL_LOW_MID))begin
i2c_sda_out <= din[7-cnt_bit]; // 串并转换数据
end
else if((state_c == SEDACK) && add_cnt_scl && (cnt_scl == SCL_LOW_MID))begin
i2c_sda_out <= (cmd & STOP_CMD)?1'b1:1'b0;// 发送应答信号
end
end
// sda_out_en
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
i2c_sda_out_en <= 0;
end
else if(idle2start || idle2write || start2write || read2sedack || sedack2stop || recack2stop)begin
i2c_sda_out_en <= 1'b1;
end
else if(idle2read || start2read || write2recack || sedack2idle || recack2idle || stop2idle)begin
i2c_sda_out_en <= 1'b0;
end
end
assign scl = i2c_scl;
assign sda_out = i2c_sda_out;
assign sda_out_en = i2c_sda_out_en;
assign dout = rx_data;
// 主机接受从机的应答信号
assign slave_ack = rx_ack;
// 一个字节 8bit结束就done
assign done = sedack2idle || recack2idle || stop2idle;
endmodule
2.master_ctrl.v
module master_ctrl(
input clk,
input rst_n,
// 按键
input [1:0] key_out,
// 接口与主机
output req,
output [3:0] cmd,
output [7:0] dout,
input slave_ack,
input done,
input [7:0] rx_data,
// 数码管
output [23:0] seg_data,
// 串口uart
input [7:0] uart_rx_data,
input uart_rx_data_vld,
output [7:0] uart_tx_data,
output uart_tx_data_vld,
input busy
);
parameter DEVICE_ID = 7'b1010_000,// 设备地址 + block(dont care)
WR_ID = 1'b0,// 控制写
RD_ID = 1'b1;// 控制读
parameter WR_LEN = 16+2,// 写的字节
RD_LEN = 16+3;// 读的字节
// 开始读写结束命令
localparam START_CMD = 4'b0001,
WRITE_CMD = 4'b0010,
READ_CMD = 4'b0100,
STOP_CMD = 4'b1000;
localparam IDLE = 6'b000_001,
WRREQ = 6'b000_010,
WAITWR = 6'b000_100,
RDREQ = 6'b001_000,
WAITRD = 6'b010_000,
DONE = 6'b100_000;
reg [5:0] state_c;
reg [5:0] state_n;
wire idle2wrreq ;
wire idle2rdreq ;
wire wrreq2waitwr;
wire WAITWR2wrreq;
wire waitwr2done ;
wire rdreq2waitrd;
wire waitrd2rdreq;
wire waitrd2done ;
// 字节计数器 一个block最大256
reg [7:0] cnt_byte;
wire add_cnt_byte;
wire end_cnt_byte;
// 读写请求
reg wr_req;
reg rd_req;
// 寄存要输出的req cmd dout
reg tx_req;
reg [3:0] tx_cmd;
reg [7:0] tx_data;
// wrfifo参数
wire wrfifo_rdreq;
wire wrfifo_wrreq;
wire wrfifo_empty;
wire wrfifo_full ;
wire [7:0] wrfifo_qout ;
wire [7:0] wrfifo_usedw;
// rdfifo参数
wire rdfifo_rdreq;
wire rdfifo_wrreq;
wire rdfifo_empty;
wire rdfifo_full ;
wire [7:0] rdfifo_qout ;
wire [7:0] rdfifo_usedw;
// 状态机
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always @(*)begin
case (state_c)
IDLE :begin
if(idle2wrreq)begin
state_n = WRREQ;
end
else if(idle2rdreq)begin
state_n = RDREQ;
end
else begin
state_n = state_c;
end
end
WRREQ :begin
if(wrreq2waitwr)begin
state_n = WAITWR;
end
else begin
state_n = state_c;
end
end
WAITWR :begin
if(WAITWR2wrreq)begin
state_n = WRREQ;
end
else if(waitwr2done)begin
state_n = DONE;
end
else begin
state_n = state_c;
end
end
RDREQ :begin
if(rdreq2waitrd)begin
state_n = WAITRD;
end
else begin
state_n = state_c;
end
end
WAITRD :begin
if(waitrd2rdreq)begin
state_n = RDREQ;
end
else if(waitrd2done)begin
state_n = DONE;
end
else begin
state_n = state_c;
end
end
DONE : begin
if(done2idle)begin
state_n = IDLE;
end
else begin
state_n = state_c;
end
end
default: state_n = IDLE;
endcase
end
assign idle2wrreq = state_c == IDLE && (wr_req);// 写请求
assign idle2rdreq = state_c == IDLE && (rd_req);// 读请求
assign wrreq2waitwr = state_c == WRREQ && (1'b1);// 等一个周期
assign WAITWR2wrreq = state_c == WAITWR && (~slave_ack && done && ~end_cnt_byte);// 写完一个字节从机发送了应答信号并没有写完全部字节
assign waitwr2done = state_c == WAITWR && ((slave_ack || end_cnt_byte) && done);// 写完一个字节从机没有发送应答信号或者写完全部字节
assign rdreq2waitrd = state_c == RDREQ && (1'b1);// 等一个周期
assign waitrd2rdreq = state_c == WAITRD && (~slave_ack && done && ~end_cnt_byte);// 写完一个字节从机发送了应答信号并没有写完全部字节
assign waitrd2done = state_c == WAITRD && ((slave_ack || end_cnt_byte) && done);// 写完一个字节从机没有发送应答信号或者写完全部字节
assign done2idle = state_c == DONE && (1'b1);
// 字节计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_byte <= 0;
end
else if(add_cnt_byte)begin
if(end_cnt_byte)begin
cnt_byte <= 0;
end
else begin
cnt_byte <= cnt_byte + 1;
end
end
else if((state_c == WAITWR) && (slave_ack == 0) && done)begin
cnt_byte <= 0;
end
else begin
cnt_byte <= cnt_byte;
end
end
assign add_cnt_byte = ((state_c == WAITRD || state_c == WAITWR) && done);
assign end_cnt_byte = add_cnt_byte && cnt_byte == ((state_c == WAITWR)?(WR_LEN - 1):(RD_LEN - 1));
// rd_req和wr_req
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_req <= 0;
wr_req <= 0;
end
else if(key_out[0])begin
wr_req <= 1'b1;
end
else if(key_out[1])begin
rd_req <= 1'b1;
end
else begin
rd_req <= 0;
wr_req <= 0;
end
end
// 不同byte发送不同的cmd din
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_req <= 0;
tx_cmd <= 4'b0;
tx_data <= 8'b0;
end
else if(state_c == WRREQ)begin
case(cnt_byte)
0 : begin
tx_req <= 1;
tx_cmd <= {START_CMD | WRITE_CMD};
tx_data <= {DEVICE_ID,WR_ID};
end
1 : begin
tx_req <= 1;
tx_cmd <= WRITE_CMD;
tx_data <= 8'b0001_0001;
end
WR_LEN-1: begin
tx_req <= 1;
tx_cmd <= {STOP_CMD | WRITE_CMD};
tx_data <= wrfifo_qout;
end
default :begin
tx_req <= 1;
tx_cmd <= WRITE_CMD;
tx_data <= wrfifo_qout;
end
endcase
end
else if(state_c == RDREQ)begin
case(cnt_byte)
0 : begin
tx_req <= 1;
tx_cmd <= {START_CMD | WRITE_CMD};
tx_data <= {DEVICE_ID,WR_ID};
end
1 : begin
tx_req <= 1;
tx_cmd <= WRITE_CMD;
tx_data <= 8'b0001_0001;
end
2 : begin
tx_req <= 1;
tx_cmd <= {START_CMD | WRITE_CMD};
tx_data <= {DEVICE_ID,RD_ID};
end
RD_LEN-1 : begin
tx_req <= 1;
tx_cmd <= {STOP_CMD | READ_CMD};
tx_data <= 0;
end
default :begin
tx_req <= 1;
tx_cmd <= READ_CMD;
tx_data <= 0;
end
endcase
end
else begin
tx_req <= 0;
tx_cmd <= tx_cmd;
tx_data <= tx_data;
end
end
// 例化写fifo
wrfifo wrfifo_inst (
.clock ( clk ),
.data ( uart_rx_data ),
.rdreq ( wrfifo_rdreq ),
.wrreq ( wrfifo_wrreq ),
.empty ( wrfifo_empty ),
.full ( wrfifo_full ),
.q ( wrfifo_qout ),
.usedw ( wrfifo_usedw )
);
assign wrfifo_wrreq = ~wrfifo_full && uart_rx_data_vld;
assign wrfifo_rdreq = ~wrfifo_empty && state_c == WAITWR && done && cnt_byte > 2;
// 例化读fifo
rdfifo rdfifo_inst (
.clock ( clk ),
.data ( rx_data ),
.rdreq ( rdfifo_rdreq ),
.wrreq ( rdfifo_wrreq ),
.empty ( rdfifo_empty ),
.full ( rdfifo_full ),
.q ( rdfifo_qout ),
.usedw ( rdfifo_usedw )
);
assign rdfifo_wrreq = ~rdfifo_full && state_c == WAITRD && done && cnt_byte > 3;
assign rdfifo_rdreq = ~rdfifo_empty && ~busy;
assign req = tx_req;
assign cmd = tx_cmd;
assign dout = tx_data;
assign uart_tx_data = rdfifo_qout;
assign uart_tx_data_vld = rdfifo_rdreq;
// 数码管
assign seg_data = {16'h0000,rx_data};
endmodule
3.top.v
module top(
input clk,
input rst_n,
input [1:0] key_in,
output scl,
inout sda,
output [7:0] seg_dig,
output [5:0] seg_sel,
input uart_rx,
output uart_tx
);
wire [1:0] key_out;
wire req;
wire [3:0] cmd;
wire [7:0] tx_data;
wire [7:0] rx_data;
wire done;
wire [23:0] seg_data;
wire slave_ack;
wire [7:0] uart_rx_data;
wire uart_rx_data_vld;
wire [7:0] uart_tx_data;
wire uart_tx_data_vld;
wire busy;
wire sda_in ;
wire sda_out;
wire sda_out_en;
assign sda_in = sda;
assign sda = sda_out_en?sda_out:1'bz;
key_filter u_key_filter(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [2-1:0] */.key_in (key_in ),
/* output reg [2-1:0] */.key_out (key_out)
);
// 串口接收模块
uart_rx u_uart_rx(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input */.baud_sel (0 ),// 波特率的选择
/* input */.din (uart_rx ),// 串口接收模块接收到主机来的1bit的数据
/* output [7:0] */.dout (uart_rx_data ),// 串口接收模块串并转换的数据发送
/* output */.dout_vld (uart_rx_data_vld )
);
uart_tx u_uart_tx(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input */.baud_sel (0),// 波特率的选择
/* input [7:0] */.din (uart_tx_data ),// 串并转换的数据
/* input */.din_vld (uart_tx_data_vld ),// 串并转换的数据有效
/* output */.dout (uart_tx ),// 发送模块发送的1bit数据
/* output */.busy (busy ) // 发送模块忙标志
);
// 数码管驱动
seg_driver u_seg_driver(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [23:0] */.data (seg_data ),
/* output reg [7:0] */.seg_dig (seg_dig),
/* output reg [5:0] */.seg_sel (seg_sel)
);
// 控制模块
master_ctrl u_master_ctrl(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* input [1:0] */.key_out (key_out ),
/* output */.req (req ),
/* output [3:0] */.cmd (cmd ),
/* output [7:0] */.dout (tx_data ),
/* input */.slave_ack(slave_ack),
/* input */.done (done ),
/* input [7:0] */.rx_data (rx_data ),
/* output [23:0] */.seg_data (seg_data),
/* input [7:0] */.uart_rx_data (uart_rx_data ),
/* input */.uart_rx_data_vld (uart_rx_data_vld),
/* output [7:0] */.uart_tx_data (uart_tx_data ),
/* output */.uart_tx_data_vld (uart_tx_data_vld),
/* input */.busy (busy )
);
// i2c接口模块
i2c_interface u_i2c_interface(
/* input */.clk (clk ),
/* input */.rst_n (rst_n ),
/* output */.scl (scl ),
/* input */.sda_in (sda_in ),
/* output */.sda_out (sda_out ),
/* output */.sda_out_en (sda_out_en),
/* input */.req (req ),
/* input [3:0] */.cmd (cmd ),
/* input [7:0] */.din (tx_data ),
/* output */.slave_ack (slave_ack ),
/* output [7:0] */.dout (rx_data ),
/* output */.done (done )
);
endmodule
4.其他模块
串口发送模块
串口接收模块
数码管驱动模块
按键消抖模块
五、仿真验证
只是看看i2c接口的状态有无错误
`timescale 1 ns/1 ns
module i2c_interface_tb();
//时钟复位输入
reg clk ;
reg rst_n ;
//激励输入
reg req ;
reg [3:0] cmd ;
reg [7:0] din ;
//输出
wire [7:0] dout ;
wire done ;
wire scl ;
reg sda_in ;
wire sda_out ;
wire sda_out_en ;
//时钟周期定义
parameter CYCLE = 20;
// 参数定义
parameter WR_ID = 8'b1010_1110,
RD_ID = 8'b1010_1111,
START_CMD = 4'b0001,
WRITE_CMD = 4'b0010,
READ_CMD = 4'b0100,
STOP_CMD = 4'b1000;
//复位时间定义
parameter RST_TIME = 3 ;
//模块例化
i2c_interface u_i2c_interface(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.req (req ),
/*input [3:0] */.cmd (cmd ),
/*input [7:0] */.din (din ),
/*output [7:0] */.dout (dout ),
/*output */.done (done ),
/*output */.scl (scl ),
/*input */.sda_in (sda_in ),
/*output */.sda_out (sda_out ),
/*output */.sda_out_en (sda_out_en)
);
task traffic_gen;
input [7:0] data ;
input [3:0] command ;
begin
#2;
req = 1'b1;
din = data;
cmd = command;
#(CYCLE*1);
// req = 1'b0;
@(negedge done);
#(CYCLE*1);
end
endtask
//产生时钟
initial begin
clk = 1;
forever
#(CYCLE/2)
clk=~clk;
end
//产生复位
initial begin
rst_n = 0;
#(CYCLE*RST_TIME);
rst_n = 1;
end
//激励
initial begin
#1;
req = 0 ;
cmd = 0 ;
din = 0 ;
#(10*CYCLE);
//字节写
traffic_gen(WR_ID,{START_CMD | WRITE_CMD});//发起始位 + 写控制字
traffic_gen(8'ha1,WRITE_CMD); //写字地址
traffic_gen(8'hb2,{WRITE_CMD | STOP_CMD}); //发数据 + 停止位
#(50*CYCLE);
//随机地址读
traffic_gen(WR_ID,{START_CMD | WRITE_CMD});//发起始位 + 写控制字
traffic_gen(8'ha1,WRITE_CMD); //写字地址
traffic_gen(RD_ID,{START_CMD | WRITE_CMD});//发起始位 + 发读控制字
traffic_gen(8'h00,{READ_CMD | STOP_CMD}); //读数据 + 发停止位
#(500*CYCLE);
$stop;
end
endmodule
六、上板验证
单字节读写和页写和随机读都没有问题
七、总结
这个i2c模块也花了我很多的时间去调试,一定要好好看看手册,是高字节MSB还是低字节LSB