FPGA实战:用Verilog手搓一个IIC控制器,驱动AT24C128和LM75(附完整源码)

张开发
2026/6/9 15:27:26 15 分钟阅读
FPGA实战:用Verilog手搓一个IIC控制器,驱动AT24C128和LM75(附完整源码)
FPGA实战从零构建IIC控制器驱动AT24C128与LM75在嵌入式系统开发中IIC总线因其简洁的两线制设计和多设备支持特性成为连接各类传感器的首选方案。本文将带您深入理解IIC协议底层机制并完整实现一个可复用的Verilog IIC控制器模块重点解决AT24C128 EEPROM存储芯片和LM75温度传感器的实际驱动问题。1. IIC协议核心机制解析IIC总线由串行时钟线(SCL)和串行数据线(SDA)组成采用主从架构。理解以下关键时序是自主实现控制器的前提基本时序单元对比表时序类型SCL状态SDA变化时机典型持续时间START高电平下降沿600nsSTOP高电平上升沿1300ns数据位低电平允许变化根据时钟速率ACK周期第9时钟从机响应完整时钟周期实际调试中发现从设备对时序的要求往往比协议规范更严格。例如在驱动AT24C128时两次写操作之间必须保证至少5ms的间隔否则会导致写入失败。提示使用示波器抓取总线波形时建议将触发条件设置为SDA下降沿这样能清晰捕捉START条件和数据起始位2. 状态机设计与实现策略2.1 分层状态机架构我们将控制器分为主状态机和底层时序机两个层级// 主状态机状态定义 parameter MAIN_IDLE 0; // 空闲状态 parameter MAIN_STAR 1; // 启动传输 parameter MAIN_DEV0 2; // 发送设备地址(写) parameter MAIN_ADDR 3; // 发送内存地址 parameter MAIN_REST 4; // 重复启动 parameter MAIN_DEV1 5; // 发送设备地址(读) parameter MAIN_R8BI 6; // 读取数据字节 parameter MAIN_W8BI 7; // 写入数据字节 parameter MAIN_STOP 8; // 停止传输2.2 关键状态转换逻辑写操作典型流程发送START条件发送设备地址 写标志位发送内存地址(1-2字节)发送数据字节(可连续多个)发送STOP条件读操作需要特别注意重复启动机制先按写操作流程发送设备地址和内存地址发送重复START条件发送设备地址 读标志位读取数据字节发送STOP条件always (posedge clk) begin case(cur_state) MAIN_IDLE: if(req) next_state MAIN_STAR; MAIN_STAR: if(start_done) next_state MAIN_DEV0; MAIN_DEV0: if(addr_ack) next_state wr ? MAIN_ADDR : MAIN_REST; // ...其他状态转换 endcase end3. 设备特定驱动实现3.1 AT24C128 EEPROM驱动要点AT24C128采用7位设备地址0b1010xxx其中xxx由硬件引脚决定。其特殊要求包括地址字节2字节最大支持128Kbit页写入限制每页64字节跨页需要分多次写入写入周期典型5ms必须等待完成典型写入序列START0xA0 (设备地址 写)地址高字节地址低字节数据字节1...数据字节NSTOP3.2 LM75温度传感器驱动LM75作为温度传感器具有以下特点固定设备地址0b1001xxx温度寄存器地址0x0016位温度值实际有效位为11位bit[15:5]温度分辨率0.125°C读取温度值的典型流程// 伪代码示例 iic_start(); iic_write_byte(0x90); // 设备地址 写 iic_write_byte(0x00); // 温度寄存器地址 iic_start(); // 重复启动 iic_write_byte(0x91); // 设备地址 读 temp_high iic_read_byte(ACK); temp_low iic_read_byte(NOACK); iic_stop();4. 调试技巧与实战经验4.1 仿真波形分析在ModelSim中设置合理的时序约束后重点关注以下信号SCL/SDA的相位关系START/STOP条件的建立时间ACK位的响应时机数据位的建立保持时间常见问题排查表现象可能原因解决方案无ACK响应设备地址错误核对设备手册地址数据错误时序不满足调整时钟速率写入失败未满足保持时间增加两次操作间隔4.2 硬件调试要点上拉电阻选择通常4.7kΩ长线缆需减小阻值信号完整性避免过长的飞线必要时使用屏蔽线电源干扰确保传感器供电稳定可增加去耦电容注意实际测试中发现某些国产替代芯片的时序要求比原厂更严格建议在设计阶段预留20%的时序余量5. 完整模块设计与优化5.1 接口定义module iic_controller ( input clk, // 系统时钟(建议10MHz) input rst, // 异步复位 // 用户接口 input [6:0] dev_addr, // 设备地址 input [15:0] mem_addr, // 内存地址 input [7:0] wdata, // 写入数据 output [15:0] rdata, // 读取数据 input wr, // 写使能 input req, // 请求启动 output reg ack, // 操作完成确认 // 物理接口 output scl_out, output scl_oen, // 输出使能(0:输出) input scl_in, output sda_out, output sda_oen, input sda_in );5.2 时钟生成策略为支持不同速率的设备我们实现可配置的时钟分频// 时钟分频参数计算 parameter CLK_FREQ 50_000_000; // 50MHz系统时钟 parameter IIC_FREQ 100_000; // 100kHz IIC时钟 localparam DIVIDER CLK_FREQ / (IIC_FREQ * 2); reg [7:0] clk_cnt; always (posedge clk) begin if(clk_cnt DIVIDER-1) begin clk_cnt 0; scl_tick 1; end else begin clk_cnt clk_cnt 1; scl_tick 0; end end5.3 仲裁与错误处理完善的控制器应包含以下保护机制总线忙检测超时处理多主机仲裁支持ACK错误重试// 超时计数器示例 reg [15:0] timeout; always (posedge clk) begin if(state ! next_state) timeout 0; else if(timeout 16hFFFF) timeout timeout 1; end wire bus_timeout (timeout TIMEOUT_LIMIT);在多次项目实践中这个自主实现的IIC控制器表现出比商用IP核更好的灵活性特别是在需要非标准时钟速率或特殊时序要求的场景下。调试时建议先使用低速模式(如10kHz)确保基本功能正常再逐步提高时钟频率。

更多文章