行业资讯

Verilog实现IIC主控制器:参数化设计支持多字节地址与突发读写

发布时间:2026/6/29 15:24:39
Verilog实现IIC主控制器:参数化设计支持多字节地址与突发读写 1. IIC协议与FPGA应用场景IICInter-Integrated Circuit作为Philips现NXP推出的两线制串行通信协议在嵌入式领域已有30多年历史。你可能在树莓派上用过它读取温湿度传感器数据或者在Arduino项目里配置过OLED屏幕。但用FPGA实现IIC主控制器完全是另一种体验——就像从开自动挡汽车突然变成手动挡赛车手所有时序细节都需要你精确掌控。在FPGA项目中IIC主控制器最常见的应用场景包括多传感器管理系统比如同时读取16路ADC芯片ADS1115的环境监测系统非易失性存储阵列使用24LC256系列EEPROM构建分布式配置存储器RTC时钟同步为整个系统提供精确时间基准IO扩展器控制通过PCA9538等芯片实现GPIO扩展传统MCU方案受限于固定硬件IIC控制器遇到多字节地址设备如16位地址的EEPROM或需要突发传输时往往要依赖软件模拟效率低下。而FPGA实现的参数化IIC控制器可以硬件级适配不同地址宽度8/16/24位动态调整传输速率标准/快速/高速模式支持连续读写时的自动地址递增实现真正的并行多设备管理2. 参数化设计核心思路2.1 模块接口定义先看这个支持参数化的模块声明module iic_master #( parameter SYS_CLK 50_000_000, // 输入时钟频率 parameter IIC_CLK 100_000, // IIC目标时钟频率 parameter ADDR_LEN 2, // 地址字节数(1/2/3) parameter DATA_LEN 1 // 单次传输数据字节数 )( input wire clk, input wire rst_n, // 用户接口 input wire start, input wire rw, // 0:写 1:读 input wire [6:0] dev_addr, input wire [23:0] reg_addr, // 自动根据ADDR_LEN截取 input wire [7:0] data_in, output reg [7:0] data_out, output reg done, // 物理接口 inout sda, output reg scl );关键参数说明ADDR_LEN动态配置地址长度1字节时使用reg_addr[7:0]2字节时用reg_addr[15:0]DATA_LEN突发传输长度实现自动地址递增时每次传输后内部地址寄存器1IIC_CLK通过系统时钟分频实现精确的SCL时序控制2.2 状态机设计精髓IIC协议本质上是典型的状态机应用场景。我们的设计采用三段式状态机结构localparam [3:0] IDLE 4d0, START 4d1, SEND_ADDR 4d2, ACK1 4d3, SEND_REG 4d4, ACK2 4d5, WR_DATA 4d6, RD_DATA 4d7, STOP 4d8; always (posedge clk or negedge rst_n) begin if(!rst_n) begin state IDLE; // 其他寄存器复位 end else begin case(state) IDLE: if(start) state START; START: if(scl_edge) state SEND_ADDR; // 其他状态转移... endcase end end状态机设计技巧每个状态保持至少一个完整的SCL周期在SCL低电平期间改变SDA信号建立时间满足使用边沿检测信号scl_edge协调状态转移对ACK/NACK处理增加超时保护3. 多字节地址实现细节3.1 地址自动装配机制对于24LC512这类16位地址的EEPROM需要发送两个地址字节。我们的参数化设计通过ADDR_LEN参数自动处理// 地址字节选择逻辑 wire [7:0] addr_byte; assign addr_byte (addr_cnt 0) ? reg_addr[15:8] : (addr_cnt 1) ? reg_addr[7:0] : 8h00; // 地址计数器控制 always (posedge clk) begin if(state SEND_ADDR bit_cnt 7 scl_neg) addr_cnt addr_cnt 1; else if(state IDLE) addr_cnt 0; end3.2 突发读写实现突发传输时DATA_LEN1会启用自动地址递增模式。以写操作为例// 数据写入流程 always (posedge clk) begin if(state WR_DATA bit_cnt 7 scl_neg) begin if(data_cnt DATA_LEN-1) begin data_cnt data_cnt 1; reg_addr_int reg_addr_int 1; // 地址自增 end end else if(state IDLE) begin data_cnt 0; reg_addr_int reg_addr; // 初始地址加载 end end关键点内部维护reg_addr_int实现地址递增每个数据字节传输完成后更新地址最后字节传输后不发送地址递增4. 时序精确控制技术4.1 SCL时钟生成精确的时钟控制是IIC协议的关键。我们采用计数器分频方案// SCL时钟分频计算 localparam DIVIDER SYS_CLK / (IIC_CLK * 2); reg [15:0] scl_cnt; wire scl_pos (scl_cnt DIVIDER-1); wire scl_neg (scl_cnt DIVIDER*2-1); always (posedge clk) begin if(state IDLE) begin scl_cnt 0; scl 1b1; end else begin if(scl_pos) scl 1b0; else if(scl_neg) scl 1b1; if(scl_cnt DIVIDER*2-1) scl_cnt 0; else scl_cnt scl_cnt 1; end end4.2 建立/保持时间保障根据IIC规范数据建立时间(tSU:DAT) 100ns(标准模式)数据保持时间(tHD:DAT) 0ns我们的实现方案// SDA数据变化时刻控制 always (posedge clk) begin if(scl_neg !scl) begin // SCL低电平中点改变SDA sda_out next_bit; end end // 输入采样时刻 always (posedge clk) begin if(scl_pos scl) begin // SCL高电平中点采样SDA sda_in sda; end end5. 实测优化经验分享在实际项目中使用这个IIC控制器时有几个容易踩的坑上拉电阻选择标准模式(100kHz)建议4.7kΩ快速模式(400kHz)建议2.2kΩ线缆较长时需要减小阻值跨时钟域处理// 用户接口到IIC时钟域的同步 reg [1:0] start_sync; always (posedge clk) start_sync {start_sync[0], user_start}; wire start_rise (start_sync 2b01);异常恢复机制增加看门狗计数器检测总线挂死超时后主动发送STOP条件复位总线状态机增加ERROR状态进行恢复多主竞争处理// 总线仲裁检测 wire arbitration_lost (sda_out 1b1) (sda_in 1b0); always (posedge clk) begin if(arbitration_lost) state IDLE; // 立即释放总线 end这个设计在Xilinx Artix-7平台上实测可以稳定驱动24系列EEPROM16位地址ADXL345加速度计8位地址MCP4725 DAC支持连续写多设备混合连接场景完整代码已通过仿真验证包含完整的Testbench测试用例支持随机化测试和时序检查。在实际部署时建议根据具体设备特性调整以下参数tHD:STA起始条件保持时间tSU:STO停止条件建立时间tBUF总线空闲时间