assign w_cunt_flag = state == ADDR_MATCH | state == ADD_DATA | state == READ_DATA | state == ACK_HOLD | state == ADD_ACK_HOLD;//设备地址检测时需要计数,子地址写入时也需要检测
assign w_shift_flag = state == ADDR_MATCH | state == ADD_DATA;
// --- SCL和SDA值保存 ---
always @(posedge I_clk or negedge I_reset_n) begin
if (!I_reset_n) begin
r_adv_scl <= 'b0;
r_adv_sda <= 'b0;
end
r_adv_scl <= I_adv_scl; // 保存上一个时钟周期的SCL值
r_adv_sda <= IO_adv_sda; // 保存上一个时钟周期的SDA值
rr_adv_scl <= r_adv_scl; // 保存上一个时钟周期的SCL值
rr_adv_sda <= r_adv_sda; // 保存上一个时钟周期的SDA值
end
// --- 主状态机 ---
always @(posedge I_clk or negedge I_reset_n) begin
if (!I_reset_n) begin
state <= IDLE; // 复位状态下进入空闲状态
end else begin
case (state)
IDLE: begin
if (r_start_begin && w_scl_ns) begin // 检测到起始条件
state <= ADDR_MATCH; // 进入地址匹配状态
end else state <= IDLE; // 保持空闲状态
end
ADDR_MATCH: begin
if (bit_cnt == 8) begin
if (shift_reg[7:1] == I2C_ADDR) begin // 检查设备地址是否匹配
state <= ACK_HOLD; // ACK从应答状态
end else begin
state <= IDLE; // 地址不匹配,回到空闲状态
end
end else state <= ADDR_MATCH; // 未循环采样完成,重复进入地址匹配状态
end
ACK_HOLD: begin
if (bit_cnt == 4'd9 &&r_sda_cunt == I2C_CLK_DVI && shift_reg[0] == 0 ) begin //释放SDA线,ACK发送完毕
state <= ADD_DATA;
end else if (bit_cnt == 4'd9 && r_sda_cunt == I2C_CLK_DVI && shift_reg[0] == 1) begin
state <= READ_DATA;
end else begin
state <= ACK_HOLD;
end
end
ADD_DATA: begin // 写子地址(通常是0x00)
if (w_scl_ps && bit_cnt == 8) begin // 子地址接收完成
state <= ADD_ACK_HOLD; // 读取完子地址,进入子地址ACK响应
end else state <= ADD_DATA; // 未循环采样完成,重复进入地址匹配状态
end
ADD_ACK_HOLD: begin // 子地址应答ACK
if (bit_cnt == 4'd9 && r_sda_cunt == I2C_CLK_DVI) begin //释放SDA线,ACK发送完毕
state <= IDLE;
end else begin
state <= ADD_ACK_HOLD;
end
end
READ_DATA: begin
if (bit_cnt == 8 && r_sda_cunt == I2C_CLK_DVI) begin //记录8个SCL上升沿
state <= WAIT_ACK; // 进入ACK/NACK状态
end else state <= READ_DATA;
end
WAIT_ACK: begin
if (~r_ack_charge_flag && w_scl_ns) begin // 在SCL正跳变时检查ACK/NACK
state <= READ_DATA; // 继续读取数据
end else if (r_ack_charge_flag && w_scl_ns) begin // 主设备发送NACK,结束传输
state <= IDLE; // 回到空闲状态
end else state <= WAIT_ACK;
end
default: state <= IDLE; // 默认情况下回到空闲状态
endcase
end
end
//开始标志位检测
always @(posedge I_clk or negedge I_reset_n) begin
if (!I_reset_n) begin
r_start_begin <= 'b0;
end else if (start_cond) begin //检测到开始信号
r_start_begin <= 'b1; //拉高代表检测开始信号标志,等待SCL下降沿
end else if (w_scl_ns) //检测到scl下降沿,拉低r_start_begin
r_start_begin <= 'b0;
else r_start_begin <= r_start_begin;
end
//计数器,高电平计数,记8位有效数据
always @(posedge I_clk or negedge I_reset_n) begin
if (!I_reset_n) begin
bit_cnt <= 'b0; // 初始化位计数器为0
end else if (w_cunt_flag && w_scl_ps && bit_cnt < 9) begin
bit_cnt <= bit_cnt + 1; // 增加位计数器
end else if (state == IDLE | state == WAIT_ACK) begin
bit_cnt <= 'b0; // 位计数器复位
end else if (w_scl_ps && bit_cnt == 9) begin
bit_cnt <= 'b1; // 增加位计数器
end else begin
bit_cnt <= bit_cnt; // 位计数器清零
end
end
//位移寄存器,将接收到的数据并转串
always @(posedge I_clk or negedge I_reset_n) begin
if (!I_reset_n) begin
shift_reg <= 'b0; // 清空移位寄存器
end else if (w_shift_flag && w_scl_ps && bit_cnt <= 8) begin