从AT24C02 EEPROM读写实战,反推Verilog I2C控制器的设计思路与调试技巧

张开发
2026/4/11 18:09:32 15 分钟阅读

分享文章

从AT24C02 EEPROM读写实战,反推Verilog I2C控制器的设计思路与调试技巧
从AT24C02 EEPROM读写实战反推Verilog I2C控制器设计精髓在嵌入式系统开发中I2C总线因其简洁的两线制设计和多主从架构成为连接低速外设的首选方案。但看似简单的协议背后隐藏着许多硬件工程师必须直面的设计挑战。本文将以AT24C02 EEPROM为实际应用目标逆向推导I2C控制器的设计思路分享从芯片手册到可靠代码的完整实现路径。1. AT24C02时序分析与需求转化1.1 关键时序参数解码AT24C02数据手册中的时序图是设计控制器的圣经。在快速模式400kHz下几个关键参数需要特别注意t_{HD;STA}起始条件保持时间最小600ns意味着START信号后SCL第一个下降沿的延迟t_{SU;STA}起始条件建立时间最小600ns确保起始条件被可靠识别t_{SU;DAT}数据建立时间最小100ns数据必须在SCL上升沿前稳定t_{HD;DAT}数据保持时间最小0ns理论上允许数据与时钟边沿同时变化// 100MHz时钟下的时序参数转换示例 parameter t_HD_STA 60; // 600ns/10ns(100MHz周期) parameter t_SU_STA 60; parameter t_SU_DAT 10;1.2 读写时序的状态分解通过分析AT24C02的单字节写和随机读时序可以提取出共有的状态序列公共前导状态起始位START发送7位从地址写命令W_SLAVE_ADDR等待应答1ACK1发送8位字节地址W_BYTE_ADDR等待应答2ACK2写操作特有状态发送8位数据W_DATA等待应答3W_ACK3停止位STOP读操作特有状态重复起始位START2发送7位从地址读命令R_SLAVE_ADDR等待应答3R_ACK3接收8位数据R_DATA发送无应答N_ACK停止位STOP提示状态划分时建议将ACK等待单独作为状态而不是合并到数据发送/接收状态中这样时序控制更清晰。2. 时钟生成与精确控制2.1 400kHz SCL时钟生成在100MHz系统时钟下生成精确的400kHz SCL信号需要250分频100MHz/400kHz。但简单分频无法满足I2C协议对占空比的要求// 400kHz时钟生成核心代码 reg [7:0] clk_div_cnt; reg scl_out; always (posedge clk or negedge rst_n) begin if (!rst_n) begin clk_div_cnt 8d0; scl_out 1b1; // 空闲高电平 end else if (work_en) begin if (clk_div_cnt 124) begin // 125分频(0-124) clk_div_cnt 8d0; scl_out ~scl_out; end else begin clk_div_cnt clk_div_cnt 1; end end else begin scl_out 1b1; // 非工作状态保持高电平 end end2.2 关键时序点的定义为精确控制数据采样和变化时刻需要定义几个关键时间点信号名称描述对应SCL相位scl_half_1SCL高电平中点数据采样点高电平期间scl_half_0SCL低电平中点数据变化点低电平期间scl_ack_jumpACK状态提前跳转点低电平期间assign scl_half_1 (clk_div_cnt 62) scl_out; // 高电平中点 assign scl_half_0 (clk_div_cnt 62) !scl_out; // 低电平中点 assign scl_ack_jump (clk_div_cnt 57) !scl_out; // 提前5个周期3. 双向SDA信号处理技巧3.1 inout端口的三态控制SDA线的双向特性是I2C控制器设计的核心难点之一。Verilog中需要精心设计输出使能控制reg sda_oe; // 输出使能 reg sda_out; // 输出数据 wire sda_in; // 输入数据 assign sda sda_oe ? (sda_out ? 1bz : 1b0) : 1bz; assign sda_in sda;3.2 信号切换的时序考量状态转换时特别是从输入接收ACK转为输出发送数据时需要提前切换方向ACK接收结束前提前5个时钟周期切换为输出模式起始位生成在SCL高电平时拉低SDA停止位生成在SCL高电平时释放SDA变高注意方向切换过早会影响从设备应答过晚会导致第一位数据丢失需要精确计算。4. 状态机设计与优化4.1 主状态机架构基于AT24C02的时序要求设计包含13个状态的状态机localparam IDLE 4d0, START 4d1, W_SLAVE_ADDR 4d2, ACK1 4d3, W_BYTE_ADDR 4d4, ACK2 4d5, STOP 4d6, W_DATA 4d7, W_ACK3 4d8, START2 4d9, R_SLAVE_ADDR 4d10, R_ACK3 4d11, R_DATA 4d12, N_ACK 4d13;4.2 状态转移的关键逻辑状态转移主要发生在特定时钟点并考虑异常情况always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; end else begin case(state) ACK2: begin if (scl_ack_jump) begin if (ack_buf) begin // NACK state IDLE; end else begin state rw_ctrl ? START2 : W_DATA; end end end // 其他状态转移... endcase end end5. 实战调试与波形分析5.1 Vivado ILA调试技巧使用ILA抓取波形时建议设置以下触发条件写操作在START状态触发读操作在START2状态触发异常情况在ACK状态检测到NACK时触发典型信号捕获组应包括SCL、SDA信号当前状态(state)位计数器(cnt_bit)输出使能(sda_oe)5.2 常见问题诊断应答位毛刺现象ACK位出现短暂高脉冲原因从设备释放SDA后主设备采样时刻不准确解决调整scl_half_1的位置确保在ACK窗口中央采样第一位数据丢失现象传输的数据左移一位原因状态切换与数据输出不同步解决在scl_ack_jump提前切换输出状态停止位识别失败现象从设备未正确结束本次传输原因停止位建立时间不足解决确保STOP状态保持足够时钟周期// 停止位生成示例 STOP: begin sda_oe 1b1; if (scl_half_1) begin sda_out 1b1; // 先确保SDA为高 if (scl_out) begin // SCL为高时产生停止条件 work_done 1b1; state IDLE; end end end6. 性能优化与扩展思考6.1 时序余量计算在400kHz快速模式下100MHz时钟每个周期10ns关键时序余量时序参数要求最小值实际实现余量t_{HD;STA}600ns620ns20nst_{SU;DAT}100ns150ns50nst_{HIGH}600ns1250ns650nst_{LOW}1300ns1250ns-50ns注意t_{LOW}余量为负时需要降低时钟频率或优化分频设计6.2 多字节读写扩展在单字节读写基础上可扩展支持页写模式连续写入一页AT24C02为8字节顺序读模式连续读取多个地址当前地址读省略地址发送阶段状态机需要新增状态localparam W_PAGE_DATA 4d14, W_PAGE_ACK 4d15, R_SEQ_DATA 4d16, R_SEQ_ACK 4d17;6.3 时钟拉伸处理某些从设备可能需要时钟拉伸SCL保持低电平。改进设计检测SCL被拉低的时间暂停内部时钟计数等待SCL被释放后继续// 时钟拉伸检测 wire scl_stretched (scl 1b0) (scl_out 1b1); always (posedge clk) begin if (scl_stretched) begin clk_div_cnt clk_div_cnt; // 暂停计数 end end在多次实际项目验证中这种基于应用需求逆向设计的方法比直接实现协议规范更高效。特别是在调试阶段对照AT24C02的时序图逐状态检查波形可以快速定位问题根源。一个实用的建议是在状态机设计中为每个状态添加独特的调试输出这样在ILA中可以直接通过状态编码判断执行流程。

更多文章