news 2026/5/8 8:32:42

别再死记硬背I2C时序了!用Verilog手搓一个I2C Master控制器(FPGA/数字IC验证适用)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再死记硬背I2C时序了!用Verilog手搓一个I2C Master控制器(FPGA/数字IC验证适用)

用Verilog实现I2C Master控制器的工程实践

在数字电路设计中,I2C总线因其简洁的两线制结构和灵活的多设备连接能力,成为芯片间通信的主流选择之一。但对于许多刚接触RTL设计的工程师来说,从协议理解到实际代码实现之间往往存在一道难以跨越的鸿沟。本文将从一个可综合的Verilog实现角度,带你完整构建一个支持标准模式的I2C Master控制器。

1. 架构设计与接口定义

一个完整的I2C Master控制器需要处理协议时序、状态转换和数据缓冲等多重任务。我们采用模块化设计思想,将其分解为以下几个关键部分:

module i2c_master ( input wire clk, // 系统时钟 (100MHz) input wire rst_n, // 异步复位 input wire en, // 使能信号 input wire [6:0] dev_addr, // 7位从设备地址 input wire rw, // 读写控制 (0:写, 1:读) input wire [7:0] data_tx, // 发送数据 output reg [7:0] data_rx, // 接收数据 output reg busy, // 忙标志 output reg ack_err, // ACK错误标志 inout sda, // 双向数据线 output reg scl // 时钟线 );

时钟分频模块是基础组件,用于产生符合标准模式(100kHz)的SCL时钟。在100MHz系统时钟下,我们需要进行500分频(每个SCL周期包含500个系统时钟):

reg [8:0] clk_div; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin clk_div <= 0; scl <= 1; end else if (en) begin if (clk_div == 499) begin clk_div <= 0; scl <= ~scl; // SCL翻转 end else begin clk_div <= clk_div + 1; end end end

2. 状态机设计与实现

I2C协议的本质是一个严格的状态转换过程。我们采用Moore型状态机,定义以下主要状态:

状态编码状态名称功能描述
4'b0000IDLE空闲状态,SCL和SDA均为高
4'b0001START产生起始条件(SDA下降沿)
4'b0010ADDR发送7位地址+1位读写控制
4'b0011WAIT_ACK等待从设备ACK响应
4'b0100WR_DATA写模式下发送8位数据
4'b0101RD_DATA读模式下接收8位数据
4'b0110SEND_ACK读模式下发送ACK/NACK
4'b0111STOP产生停止条件(SDA上升沿)

状态转移的核心逻辑如下:

always @(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= IDLE; end else begin case (state) IDLE: if (en) state <= START; START: if (scl_fall) state <= ADDR; ADDR: if (bit_cnt == 8 && scl_fall) state <= WAIT_ACK; WAIT_ACK: if (scl_rise) state <= (ack_received) ? (rw ? RD_DATA : WR_DATA) : STOP; WR_DATA: if (bit_cnt == 8 && scl_fall) state <= WAIT_ACK; RD_DATA: if (bit_cnt == 8 && scl_fall) state <= SEND_ACK; SEND_ACK: if (scl_fall) state <= (more_data) ? RD_DATA : STOP; STOP: if (scl_rise) state <= IDLE; endcase end end

注意:scl_fall和scl_rise分别是SCL的下降沿和上升沿检测信号,需要通过边沿检测电路生成。

3. 关键信号生成与三态控制

SDA线的控制是I2C实现的难点之一,需要正确处理主从设备间的控制权切换。我们采用三态门实现双向控制:

reg sda_out; // SDA输出寄存器 reg sda_oe; // SDA输出使能 assign sda = sda_oe ? sda_out : 1'bz; // 三态控制 // SDA输入采样 always @(posedge clk) begin if (!sda_oe) sda_in <= sda; end

起始位和停止位的生成需要严格遵循协议时序:

// 起始位生成 always @(*) begin if (state == START) begin sda_out = (scl == 1) ? 1'b0 : 1'b1; sda_oe = 1; end end // 停止位生成 always @(*) begin if (state == STOP) begin sda_out = (scl == 1) ? 1'b1 : 1'b0; sda_oe = 1; end end

数据移位寄存器负责地址和数据的串行化:

reg [7:0] shift_reg; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin shift_reg <= 0; end else begin case (state) START: shift_reg <= {dev_addr, rw}; WR_DATA: shift_reg <= data_tx; default: if (scl_fall && (state == ADDR || state == WR_DATA)) shift_reg <= {shift_reg[6:0], 1'b0}; endcase end end

4. 调试技巧与实战经验

在实际FPGA实现中,以下几个调试技巧可以节省大量时间:

  1. 仿真波形分析:重点关注以下信号关系

    • SCL时钟的稳定性和占空比(通常保持50%)
    • SDA数据变化必须发生在SCL低电平期间
    • 起始/停止条件是否符合协议定义
  2. 上拉电阻选择:虽然属于硬件设计范畴,但会影响信号质量

    • 标准模式(100kHz)通常使用4.7kΩ上拉
    • 快速模式(400kHz)建议使用2.2kΩ上拉
  3. 跨时钟域处理:当与慢速从设备通信时

    • 添加时钟伸展(clock stretching)检测逻辑
    • 使用同步器处理异步信号
// 时钟伸展检测示例 reg scl_meta, scl_sync; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin {scl_sync, scl_meta} <= 2'b11; end else begin {scl_sync, scl_meta} <= {scl_meta, scl}; end end wire scl_stretched = (scl_sync == 0) && (clk_div > 250);
  1. 错误恢复机制:增强控制器鲁棒性
    • 添加超时计数器防止总线挂死
    • 设计软件可触发的总线复位功能

在最近的一个EEPROM读写项目中,我们发现当连续写入多字节时,从设备偶尔会延长ACK响应时间。通过添加以下超时检测逻辑,有效解决了问题:

reg [7:0] timeout_cnt; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin timeout_cnt <= 0; ack_err <= 0; end else if (state == WAIT_ACK) begin if (timeout_cnt == 255) begin ack_err <= 1; state <= STOP; end else begin timeout_cnt <= timeout_cnt + 1; end end else begin timeout_cnt <= 0; end end

实现一个可靠的I2C Master控制器不仅需要深入理解协议细节,更需要考虑实际工程中的各种边界条件和异常情况。本文提供的Verilog实现已经过Xilinx Artix-7 FPGA平台的实测验证,可直接作为设计参考。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 8:32:35

Linux光标主题深度定制:基于Breeze的魔改与个性化实践

1. 项目概述&#xff1a;打造你的专属Linux光标主题如果你和我一样&#xff0c;是个在Linux桌面上折腾了多年的老用户&#xff0c;那你一定对系统默认的那几套光标主题感到审美疲劳了。无论是经典的“Adwaita”还是KDE的“Breeze”&#xff0c;用久了总觉得少了点个性。今天要聊…

作者头像 李华
网站建设 2026/5/8 8:29:33

深入探讨SwiftUI中的内存泄漏

在SwiftUI开发过程中,内存泄漏是一个常见的问题,特别是在使用闭包和嵌套闭包时。本文将通过一个实际的例子,探讨如何避免这种情况下的内存泄漏,并提供解决方案。 背景介绍 最近,我在使用SwiftUI开发一个应用时,遇到了一个非常奇怪的内存泄漏问题。这个问题主要是由@esc…

作者头像 李华
网站建设 2026/5/8 8:29:32

智能机械爪Sanna-OpenClaw:视觉伺服与强化学习的工程实践

1. 项目概述&#xff1a;当AI遇上机械爪&#xff0c;Sanna-OpenClaw的诞生最近在机器人抓取和自动化领域&#xff0c;一个名为Sanna-OpenClaw的项目引起了我的注意。这个项目听起来就很有意思&#xff0c;它把“Sanna”这个AI前缀和“OpenClaw”这个开源机械爪结合在了一起。简…

作者头像 李华
网站建设 2026/5/8 8:28:29

丝杆升降机进出料不同步该怎么调试?

丝杆升降机进出料不同步&#xff0c;本质是多台升降机的位移或速度产生了累积偏差。调试的核心思路是&#xff1a;先排除机械“别劲”和安装误差&#xff0c;再根据你的驱动方式&#xff08;机械硬连接还是电机独立控制&#xff09;进行针对性校准。你可以按照以下顺序排查和调…

作者头像 李华
网站建设 2026/5/8 8:27:31

冒险岛WZ文件解析终极指南:3步轻松提取游戏资源

冒险岛WZ文件解析终极指南&#xff1a;3步轻松提取游戏资源 【免费下载链接】WzComparerR2 Maplestory online Extractor 项目地址: https://gitcode.com/gh_mirrors/wz/WzComparerR2 想要探索冒险岛游戏背后的秘密资源吗&#xff1f;WzComparerR2这款开源工具让您零基础…

作者头像 李华
网站建设 2026/5/8 8:24:44

推荐一款现代化 Vue 后台框架:Vben Admin

在中后台系统开发中&#xff0c;我们经常会遇到一类重复工作&#xff1a;登录鉴权、权限菜单、动态路由、标签页、主题切换、国际化、接口封装、表格表单封装、工程化配置等。 如果每个项目都从零开始搭建&#xff0c;不仅浪费时间&#xff0c;还容易因为个人实现差异导致项目…

作者头像 李华