字节传输的具体实现流程如图 4-6 所示。
图 4-6 字节传输控制模块流程图
字节传输控制模块控制以字节为单位的数据传输。它根据命令寄存器的设置将数据传输寄存器中的内容传输到外部节点,将外部节点的数据接收到数据接收寄存器中。
实现代码如下:
- `include "timescale.v"
- `include "i2c_master_defines.v"
- //模块
- module i2c_master_byte_ctrl (
- clk, rst, nReset, ena, clk_cnt, start, stop, read, write, ack_in, din,
- cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen );
- // 输入、输出
- input clk; // 主时钟
- input rst; // 同步 RESET,高有效
- input nReset; // 异步 RESET,低有效
- input ena; // 模块使能信号
- input [15:0] clk_cnt; // 4 倍 SCL 信号
- // 控制信号输入
- input start;
- input stop;
- input read;
- input write;
- input ack_in;
- input [7:0] din;
- // 状态信号输出
- output cmd_ack;
- reg cmd_ack;
- output ack_out;
- reg ack_out;
- output i2c_busy;
- output i2c_al;
- output [7:0] dout;
- // I²C 信号
- input scl_i;
- output scl_o;
- output scl_oen;
- input sda_i;
- output sda_o;
- output sda_oen;
- // 变量申明
- // 状态机
- parameter [4:0] ST_IDLE = 5'b0_0000;
- parameter [4:0] ST_START = 5'b0_0001;
- parameter [4:0] ST_READ = 5'b0_0010;
- parameter [4:0] ST_WRITE = 5'b0_0100;
- parameter [4:0] ST_ACK = 5'b0_1000;
- parameter [4:0] ST_STOP = 5'b1_0000;
- // 位控制模块的信号
- reg [3:0] core_cmd;
- reg core_txd;
- wire core_ack, core_rxd;
- // 移位寄存器信号
- reg [7:0] sr; //8 位移位寄存器
- reg shift, ld;
- // 状态机信号
- wire go;
- reg [2:0] dcnt;
- wire cnt_done;
- // 模块主体
- // 连接位控制模块
- i2c_master_bit_ctrl bit_controller (
- .clk ( clk ),
- .rst ( rst ),
- .nReset ( nReset ),
- .ena ( ena ),
- .clk_cnt ( clk_cnt ),
- .cmd ( core_cmd ),
- .cmd_ack ( core_ack ),
- .busy ( i2c_busy ),
- .al ( i2c_al ),
- .din ( core_txd ),
- .dout ( core_rxd ),
- .scl_i ( scl_i ),
- .scl_o ( scl_o ),
- .scl_oen ( scl_oen ),
- .sda_i ( sda_i ),
- .sda_o ( sda_o ),
- .sda_oen ( sda_oen )
- );
- // 产生 GO 信号,当读/写/停止/应答时发生
- assign go = (read | write | stop) & ~cmd_ack;
- // 分配输出到移位寄存器
- assign dout = sr;
- // 产生移位寄存器
- always @(posedge clk or negedge nReset)
- if (!nReset)
- sr <= #1 8'h0;
- else if (rst)
- sr <= #1 8'h0;
- else if (ld)
- sr <= #1 din;
- else if (shift)
- sr <= #1 {sr[6:0], core_rxd};
- // 产生计数器
- always @(posedge clk or negedge nReset)
- if (!nReset)
- dcnt <= #1 3'h0;
- else if (rst)
- dcnt <= #1 3'h0;
- else if (ld)
- dcnt <= #1 3'h7;
- else if (shift)
- dcnt <= #1 dcnt - 3'h1;
- assign cnt_done = ~(|dcnt);
- // 状态机
- reg [4:0] c_state;
- always @(posedge clk or negedge nReset)
- if (!nReset)
- begin
- core_cmd <= #1 `I2C_CMD_NOP;
- core_txd <= #1 1'b0;
- shift <= #1 1'b0;
- ld <= #1 1'b0;
- cmd_ack <= #1 1'b0;
- c_state <= #1 ST_IDLE;
- ack_out <= #1 1'b0;
- end
- else if (rst | i2c_al)
- begin
- core_cmd <= #1 `I2C_CMD_NOP;
- core_txd <= #1 1'b0;
- shift <= #1 1'b0;
- ld <= #1 1'b0;
- cmd_ack <= #1 1'b0;
- c_state <= #1 ST_IDLE;
- ack_out <= #1 1'b0;
- end
- else
- begin
- // 初始化所有信号
- core_txd <= #1 sr[7];
- shift <= #1 1'b0;
- ld <= #1 1'b0;
- cmd_ack <= #1 1'b0;
- case (c_state)
- //IDLE 状态
- ST_IDLE:
- if (go)
- begin
- if (start)
- begin
- c_state <= #1 ST_START;
- core_cmd <= #1 `I2C_CMD_START;
- end
- else if (read)
- begin
- c_state <= #1 ST_READ;
- core_cmd <= #1 `I2C_CMD_READ;
- end
- else if (write)
- begin
- c_state <= #1 ST_WRITE;
- core_cmd <= #1 `I2C_CMD_WRITE;
- end
- else // 缺省的是 stop 状态
- begin
- c_state <= #1 ST_STOP;
- core_cmd <= #1 `I2C_CMD_STOP;
- // 产生应答信号
- cmd_ack <= #1 1'b1;
- end
- ld <= #1 1'b1;
- end
- //开始状态
- ST_START:
- if (core_ack)
- begin
- if (read)
- begin
- c_state <= #1 ST_READ;
- core_cmd <= #1 `I2C_CMD_READ;
- end
- else
- begin
- c_state <= #1 ST_WRITE;
- core_cmd <= #1 `I2C_CMD_WRITE;
- end
- ld <= #1 1'b1;
- end
- //写数据状态
- ST_WRITE:
- if (core_ack)
- if (cnt_done)
- begin
- c_state <= #1 ST_ACK;
- core_cmd <= #1 `I2C_CMD_READ;
- end
- else
- begin
- c_state <= #1 ST_WRITE; // 保持在原来状态
- core_cmd <= #1 `I2C_CMD_WRITE; // 写下一位数据
- shift <= #1 1'b1;
- end
- //读信号状态
- ST_READ:
- if (core_ack)
- begin
- if (cnt_done)
- begin
- c_state <= #1 ST_ACK;
- core_cmd <= #1 `I2C_CMD_WRITE;
- end
- else
- begin
- c_state <= #1 ST_READ; // 保留在原来状态
- core_cmd <= #1 `I2C_CMD_READ; // 读下一位数据
- end
- shift <= #1 1'b1;
- core_txd <= #1 ack_in;
- end
- //应答数据状态
- ST_ACK:
- if (core_ack)
- begin
- if (stop)
- begin
- c_state <= #1 ST_STOP;
- core_cmd <= #1 `I2C_CMD_STOP;
- end
- else
- begin
- c_state <= #1 ST_IDLE;
- core_cmd <= #1 `I2C_CMD_NOP;
- end
- // 把应答信号输出连接到位控制模块
- ack_out <= #1 core_rxd;
- // 产生应答信号
- cmd_ack <= #1 1'b1;
- core_txd <= #1 1'b1;
- end
- else
- core_txd <= #1 ack_in;
- //停止状态
- ST_STOP:
- if (core_ack)
- begin
- c_state <= #1 ST_IDLE;
- core_cmd <= #1 `I2C_CMD_NOP;
- end
- endcase
- end
- endmodule