FPGA实战:构建IIC主控模块驱动AT24C04存储器的完整指南
1. 项目背景与硬件准备
在嵌入式系统中,非易失性存储器的读写操作是基础但关键的功能。AT24C04作为一款经典的IIC接口EEPROM,因其体积小、功耗低、接口简单等特点,被广泛应用于各类设备中。而FPGA的灵活性和并行处理能力,使其成为实现定制化IIC主控的理想选择。
硬件连接要点:
- FPGA开发板:推荐使用Xilinx Artix-7或Intel Cyclone IV系列入门级开发板
- AT24C04芯片:工作电压需与FPGA I/O电压匹配(通常3.3V)
- 上拉电阻:SDA和SCL线需接4.7kΩ上拉电阻至VCC
- 电平转换:若FPGA与EEPROM电压不匹配,需添加电平转换电路
注意:实际布线时,SCL和SDA走线应尽量短,避免平行走线以减少串扰。对于高频应用,建议使用双绞线。
2. IIC协议深度解析
IIC总线协议虽然简单,但要实现稳定可靠的通信,需要深入理解其时序特性。与常见的UART、SPI不同,IIC是真正的多主多从总线,这带来了独特的仲裁机制和时钟同步特性。
关键时序参数对比:
| 参数 | 标准模式(100kHz) | 快速模式(400kHz) | 高速模式(3.4MHz) |
|---|---|---|---|
| t_HD;STA | 4.0μs | 0.6μs | 0.26μs |
| t_LOW | 4.7μs | 1.3μs | 0.5μs |
| t_HIGH | 4.0μs | 0.6μs | 0.26μs |
| t_SU;STA | 4.7μs | 0.6μs | 0.26μs |
| t_HD;DAT | 250ns | 100ns | 0ns |
| t_SU;DAT | 100ns | 100ns | 100ns |
AT24C04特有要点:
- 7位设备地址:1010[A2][A1][R/W],其中A2,A1由硬件引脚决定
- 页写模式:每页16字节,跨页写入会导致地址回卷
- 写周期时间:典型值5ms,最大10ms
3. Verilog实现方案设计
3.1 顶层模块架构
我们的IIC主控设计采用分层状态机架构,主要包含以下功能模块:
module iic_master ( input clk, // 系统时钟(50MHz) input reset_n, // 异步复位 input start, // 传输启动信号 input [1:0] mode, // 00:写字节 01:读字节 10:写页 11:读序列 input [7:0] dev_addr, // 设备地址+读写位 input [7:0] mem_addr, // 存储器地址 input [7:0] data_in, // 写入数据 output [7:0] data_out, // 读取数据 output busy, // 忙标志 inout sda, // 双向数据线 output scl // 时钟线 );3.2 时钟分频模块
为满足不同速度需求,设计可配置的时钟分频器:
// 时钟分频计算 localparam DIVIDER = (SYS_CLK*1000)/(2*IIC_CLK); // 系统时钟50MHz,目标IIC时钟400kHz reg [7:0] clk_cnt; always @(posedge clk or negedge reset_n) begin if(!reset_n) begin clk_cnt <= 0; scl <= 1'b1; end else if(clk_cnt == DIVIDER-1) begin clk_cnt <= 0; scl <= ~scl; end else begin clk_cnt <= clk_cnt + 1; end end3.3 主状态机设计
状态机采用One-Hot编码,包含以下主要状态:
- IDLE:空闲状态,等待启动命令
- START:产生起始条件
- SEND_ADDR:发送设备地址+读写位
- SEND_MEM_ADDR:发送存储器地址
- WRITE_DATA:写入数据状态
- READ_DATA:读取数据状态
- ACK_CHECK:应答检测
- STOP:产生停止条件
状态转移图关键路径:
- 写操作:IDLE→START→SEND_ADDR→ACK_CHECK→SEND_MEM_ADDR→ACK_CHECK→WRITE_DATA→ACK_CHECK→STOP
- 读操作:IDLE→START→SEND_ADDR→ACK_CHECK→SEND_MEM_ADDR→ACK_CHECK→START→SEND_ADDR(读)→ACK_CHECK→READ_DATA→NACK→STOP
4. 关键实现技巧
4.1 双向SDA处理
Verilog中实现双向端口需要特别注意三态控制:
reg sda_out; reg sda_oe; // 输出使能 assign sda = sda_oe ? sda_out : 1'bz; // 数据方向控制示例 always @(*) begin case(state) IDLE: begin sda_oe = 1'b0; sda_out = 1'b1; end START: begin sda_oe = 1'b1; sda_out = 1'b0; end // 其他状态... endcase end4.2 精确时序控制
每个IIC相位都需要精确控制,我们采用四相位法:
// 时钟相位生成 wire phase0 = (clk_cnt == 0); wire phase90 = (clk_cnt == DIVIDER/4); wire phase180 = (clk_cnt == DIVIDER/2); wire phase270 = (clk_cnt == 3*DIVIDER/4); // 数据采样与变化点 always @(posedge clk) begin if(phase180 && (state == READ_DATA)) begin data_shift <= {data_shift[6:0], sda}; end if(phase270) begin bit_cnt <= bit_cnt + 1; end end4.3 页写优化
AT24C04支持16字节页写,合理利用可显著提高写入效率:
// 页写控制逻辑 reg [3:0] page_cnt; always @(posedge clk) begin if(state == ACK_CHECK && next_state == WRITE_DATA) begin if(page_cnt == 4'hF) begin page_cnt <= 0; end else begin page_cnt <= page_cnt + 1; end end end5. 仿真与测试方案
5.1 ModelSim仿真环境搭建
创建完整的测试平台需要以下组件:
- 被测IIC主控模块
- AT24C04行为模型
- 测试激励生成器
- 结果检查器
典型测试用例:
- 单字节写入后读取验证
- 连续页写测试
- 随机地址读写测试
- 时钟拉伸测试
- 总线竞争测试
5.2 自动化测试脚本
使用Tcl脚本实现自动化仿真:
# 仿真脚本示例 vlib work vlog iic_master.v at24c04_model.v tb_iic.v vsim -voptargs=+acc work.tb_iic # 添加波形 add wave -position insertpoint sim:/tb_iic/* # 运行测试 run -all5.3 上板实测技巧
实际硬件调试中,这些工具非常有用:
- 逻辑分析仪:捕获IIC总线实际波形
- 嵌入式ILA:利用FPGA内置逻辑分析仪
- 串口调试:实时输出调试信息
常见问题排查:
- 无应答:检查设备地址、上拉电阻、电源
- 数据错误:检查时序参数、信号完整性
- 写操作失败:确保遵守写周期时间
6. 性能优化进阶
6.1 时钟拉伸支持
为兼容某些特殊从设备,需添加时钟拉伸检测:
// 时钟拉伸检测 reg scl_stretched; always @(negedge scl) begin if(scl_oe) begin scl_stretched <= 1'b0; end else begin scl_stretched <= 1'b1; end end6.2 多主仲裁实现
完整的多主支持需要总线仲裁逻辑:
// 仲裁检测 reg arbitration_lost; always @(needge scl) begin if(sda_oe && !sda && (sda_in != sda_out)) begin arbitration_lost <= 1'b1; end end6.3 DMA接口设计
为提高吞吐量,可添加AXI Stream接口:
// AXI Stream接口示例 input axis_tvalid; output axis_tready; input [7:0] axis_tdata; always @(posedge clk) begin if(state == WRITE_DATA && axis_tvalid && axis_tready) begin tx_data <= axis_tdata; end end7. 实际项目经验分享
在最近的一个工业传感器项目中,我们使用该IIC控制器实现了对24个AT24C04的集中管理。通过以下优化,读写速度提升了3倍:
- 批量操作:将多个单次操作合并为页操作
- 流水线设计:重叠地址发送和数据传输
- 时钟自适应:根据从设备响应动态调整速度
遇到的坑与解决方案:
问题1:高温环境下偶发读写错误
- 原因:上拉电阻值过大导致上升时间不足
- 解决:将4.7kΩ改为2.2kΩ,并添加小电容滤波
问题2:长距离传输不稳定
- 原因:总线电容过大导致信号畸变
- 解决:改用IIC缓冲器并降低时钟速度至100kHz
对于需要更高可靠性的应用,建议添加CRC校验和自动重试机制。在FPGA资源允许的情况下,可以实现双缓冲设计,使得当前页写入的同时准备下一页数据。