1. IIC总线协议与FPGA实现概述
IIC(Inter-Integrated Circuit)作为一种简单高效的双向二线制同步串行总线,在嵌入式系统中广泛应用。FPGA因其并行处理能力和可编程特性,特别适合实现定制化的IIC控制器。与通用MCU的硬件IIC外设不同,FPGA方案可以提供更灵活的时序控制和多主机支持。
我在多个工业传感器项目中验证过,用FPGA实现IIC主设备的核心优势在于:
- 可精确控制SCL时钟频率(从100kHz到3.4MHz)
- 支持同一总线挂载多个从设备(通过可编程地址识别)
- 实现硬件级的状态机控制,不占用处理器资源
2. IIC协议状态机设计
2.1 标准IIC时序分解
典型IIC传输包含以下几个关键阶段:
- 起始条件(START):SCL高电平时SDA从高到低跳变
- 地址传输:7位从机地址+1位读写方向
- 数据帧传输:每字节后跟随ACK/NACK
- 停止条件(STOP):SCL高电平时SDA从低到高跳变
在Verilog中,我用独立的状态机处理每个阶段:
verilog复制parameter [3:0]
IDLE = 4'd0,
START = 4'd1,
ADDR = 4'd2,
DATA = 4'd3,
STOP = 4'd4;
2.2 时钟同步机制
FPGA需要同时作为时钟源(主模式)和时钟跟随者(从模式)。我的解决方案是:
- 主模式下使用可配置的分频器生成SCL
- 从模式下检测SCL边沿触发数据采样
- 插入半个时钟周期的延时保证建立保持时间
关键经验:SCL低电平期间改变SDA,高电平期间保持稳定。实测发现保持时间至少需要50ns才能兼容多数从设备。
3. Verilog实现细节
3.1 顶层模块设计
verilog复制module i2c_master (
input wire clk,
input wire rst,
inout wire sda,
output wire scl,
// 用户接口
input wire [6:0] dev_addr,
input wire [7:0] reg_addr,
input wire [7:0] data_wr,
output reg [7:0] data_rd,
input wire start,
output reg busy
);
3.2 关键子模块实现
- 时钟分频器:根据系统时钟生成IIC标准速率
verilog复制always @(posedge clk) begin
if (clk_div_cnt == DIVIDER-1) begin
clk_div_cnt <= 0;
scl_clk <= ~scl_clk;
end else begin
clk_div_cnt <= clk_div_cnt + 1;
end
end
- SDA三态控制:精确管理输出使能
verilog复制assign sda = (sda_oe) ? sda_out : 1'bz;
- ACK检测电路:在第九个时钟周期采样SDA
verilog复制always @(negedge scl) begin
if (bit_cnt == 3'd8) begin
ack <= sda;
end
end
4. 仿真验证流程
4.1 Testbench构建要点
verilog复制initial begin
// 初始化
sda = 1'bz;
start = 0;
// 发送测试序列
#100 start = 1;
dev_addr = 7'h50;
reg_addr = 8'h00;
data_wr = 8'hA5;
// 等待传输完成
@(negedge busy);
#1000 $finish;
end
4.2 关键检查点
- 起始条件时序:SCL高期间SDA下降沿
- 地址相位:7位地址+1位R/W
- 数据有效性:SCL高电平期间数据稳定
- 停止条件:SCL高期间SDA上升沿
使用ModelSim的波形测量工具验证时序参数:
- tSU;STA(起始条件建立时间)> 600ns
- tHD;DAT(数据保持时间)> 300ns
5. 下板调试实战
5.1 硬件连接注意事项
| 信号线 | FPGA管脚 | 上拉电阻 | 备注 |
|---|---|---|---|
| SCL | J12 | 4.7kΩ | 靠近连接器 |
| SDA | K15 | 4.7kΩ | 远离电源线 |
血泪教训:曾因忘记上拉电阻导致总线始终为低电平,调试2小时才发现问题。
5.2 逻辑分析仪抓包技巧
- 设置触发条件为SDA下降沿+SCL高电平(起始条件)
- 采用异步采样模式(至少4倍过采样)
- 解码器设置为I2C协议,显示地址和数据值
实测发现当总线电容>400pF时,需要降低传输速率至100kHz以下。
6. 典型问题排查指南
6.1 无ACK响应
- 检查从设备地址是否正确(示波器观察发送的地址字节)
- 测量SDA上拉电压(应大于VIL最大值)
- 确认从设备电源正常
6.2 数据校验错误
- 检查SCL/SDA信号完整性(过冲/振铃)
- 调整FPGA输出的驱动强度
- 在高速模式下增加时钟延迟
6.3 总线死锁处理
- 发送9个SCL脉冲强制释放总线
- 实现看门狗定时器自动复位
- 添加总线空闲检测逻辑
我在实际项目中总结的调试顺序:先验证物理层(信号质量),再检查协议层(时序参数),最后确认应用层(数据内容)。
7. 性能优化方向
对于需要高速传输的场景:
- 使用流水线技术预取下一个数据
- 实现DMA接口减少CPU干预
- 添加时钟拉伸(clock stretching)支持
在Xilinx Artix-7上实测结果:
- 标准模式(100kHz)下功耗增加2.3mW
- 快速模式(400kHz)传输速率可达427kbps
- 时钟抖动小于周期的5%