在嵌入式系统开发中,IIC总线因其简洁的两线制设计和多设备支持能力,成为传感器与存储器件连接的理想选择。本文将带您深入理解IIC协议核心机制,并手把手指导如何用Verilog实现一个稳健的IIC主控制器,最终完成对LM75温度传感器和AT24C128 EEPROM的读写操作。
IIC(Inter-Integrated Circuit)总线由Philips(现NXP)开发,仅需SCL(时钟线)和SDA(数据线)两根信号线即可实现多主多从通信。其核心特征包括:
在设计FPGA实现的IIC控制器时,需要特别注意几个关键时序参数:
| 时序参数 | 标准模式(100kHz) | 快速模式(400kHz) | 单位 |
|---|---|---|---|
| tSU;STA | 4.7 | 0.6 | μs |
| tHD;STA | 4.0 | 0.6 | μs |
| tSU;DAT | 250 | 100 | ns |
| tHD;DAT | 0 | 0 | ns |
| tSU;STO | 4.0 | 0.6 | μs |
提示:实际设计中应留有时序裕量,建议在标准模式按150kHz设计,快速模式按300kHz设计,以应对PCB走线延迟等不确定因素。
一个完整的IIC控制器本质上是一个精密的状态机,需要处理起始条件、停止条件、数据传输和应答等多个状态。我们采用分层状态机设计,将控制流程分为顶层事务状态机和底层比特状态机。
顶层状态机负责完整的IIC事务控制,典型状态包括:
verilog复制parameter MAIN_IDLE = 'd20; // 空闲状态
parameter MAIN_STAR = 'd21; // 起始条件
parameter MAIN_DEV0 = 'd22; // 发送设备地址(写)
parameter MAIN_ADDR = 'd23; // 发送内存地址
parameter MAIN_REST = 'd24; // 重复起始条件
parameter MAIN_DEV1 = 'd25; // 发送设备地址(读)
parameter MAIN_R8BI = 'd26; // 读取数据字节
parameter MAIN_W8BI = 'd27; // 写入数据字节
parameter MAIN_STOP = 'd28; // 停止条件
底层状态机精确控制每个比特位的传输时序,包括时钟高低电平的持续时间管理:
verilog复制// 写数据状态
parameter W1BY_SCLL = 'd6; // SCL低电平期间准备数据
parameter W1BY_SCLH = 'd7; // SCL高电平期间保持数据
parameter W1AK_SCLL = 'd8; // 等待ACK的SCL低电平
parameter W1AK_SCLH = 'd9; // 采样ACK的SCL高电平
// 读数据状态
parameter R1BY_SCLL = 'd11; // SCL低电平期间释放SDA
parameter R1BY_SCLH = 'd12; // SCL高电平期间采样数据
parameter R1AK_SCLL = 'd13; // 发送ACK的SCL低电平
parameter R1AK_SCLH = 'd14; // 发送ACK的SCL高电平
状态转换的关键在于精确的时序控制,以下是典型的状态转换代码片段:
verilog复制always@(posedge clk or posedge rst) begin
if(rst) begin
cur_state <= MAIN_IDLE;
end else begin
case(cur_state)
MAIN_IDLE: if(req) cur_state <= MAIN_STAR;
MAIN_STAR: if(down_ack) cur_state <= MAIN_DEV0;
MAIN_DEV0: if(down_ack) cur_state <= (err_ack) ? MAIN_STOP : MAIN_ADDR;
// ...其他状态转换逻辑
endcase
end
end
LM75是数字温度传感器,通过IIC接口提供9位至12位精度的温度测量。其关键特性包括:
读取LM75温度值的完整流程如下:
温度数据格式处理示例代码:
verilog复制// 温度值转换
wire signed [15:0] temp_raw = {rdata[15:8], rdata[7:5], 3'b000};
wire real temperature = $itor(temp_raw) * 0.125;
注意:LM75的温度寄存器返回16位数据,但实际有效位为[15:5],需要左移3位后再乘以0.125得到实际温度值。
LM75提供配置寄存器(0x01)用于设置工作模式:
| 位域 | 功能 | 设置值 |
|---|---|---|
| [7:5] | 故障队列 | 00-11(触发次数) |
| [4:3] | 报警极性 | 0-1 |
| [2] | 工作模式 | 0:比较器,1:中断 |
| [1:0] | 关断模式 | 1:关断 |
配置示例:
verilog复制// 设置LM75为比较器模式,故障队列4次
send(0x01, 0b00100000); // 配置寄存器地址0x01,值0x20
AT24C128是128Kbit(16K×8)串行EEPROM,支持标准IIC接口,具有以下特点:
单字节写入时序要点:
Verilog实现关键代码:
verilog复制MAIN_DEV0: begin
per_main_state <= MAIN_DEV0;
wdata_shft <= {DEV_ADDR[6:0], 1'b0}; // 设备地址+写
bit_cnt <= 0;
end
MAIN_ADDR: begin
if(ADDR_NUM == 2) begin
wdata_shft <= (addr_cnt==0) ? addr[15:8] : addr[7:0];
end else begin
wdata_shft <= addr[7:0];
end
end
重要提示:AT24C128完成写入操作需要约5ms的周期时间(tWR),在此期间不应发起新的写操作。
随机地址读取操作流程:
多字节读取的状态机处理:
verilog复制MAIN_R8BI: begin
if(down_ack && (data_cnt >= DATA_NUM+1)) begin
cur_state <= MAIN_STOP;
end else begin
cur_state <= R1BY_SCLL;
end
end
R1BY_SCLH: begin
if((delay_cnt == tHIGH-3) && (~state_change)) begin
rdata[15:0] <= {rdata[14:0], sda_i}; // 移位接收数据
bit_cnt <= bit_cnt + 1;
end
end
在实际硬件调试IIC控制器时,以下几个工具和技巧非常有用:
使用Saleae逻辑分析仪捕获IIC信号时建议设置:
问题1:从设备无ACK响应
可能原因及解决方案:
问题2:数据读取错误
调试步骤:
问题3:EEPROM写入失败
注意事项:
verilog复制// 写入延时处理示例
always @(posedge clk) begin
if (write_done) begin
write_timer <= 250000; // 5ms @50MHz
end else if (write_timer > 0) begin
write_timer <= write_timer - 1;
end
end
wire write_ready = (write_timer == 0);
通过本文的实践,您不仅能够掌握IIC控制器的Verilog实现方法,还能获得FPGA与常用外设交互的宝贵经验。在实际项目中,这种可复用的IIC控制器核可以大大加速后续开发进程。