news 2026/7/1 8:54:35

FPGA实战:用Verilog手搓一个支持多字节地址的IIC主控制器(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FPGA实战:用Verilog手搓一个支持多字节地址的IIC主控制器(附完整代码)

FPGA实战:用Verilog手搓一个支持多字节地址的IIC主控制器(附完整代码)

在FPGA开发中,IIC(Inter-Integrated Circuit)总线因其简单的两线制(SCL时钟线和SDA数据线)和灵活的多设备连接能力,成为连接低速外设(如EEPROM、传感器、RTC等)的首选方案。然而,商业IP核往往价格昂贵或灵活性不足,而开源实现又难以满足多字节地址访问等高级需求。本文将带你从零开始,用Verilog实现一个参数化、可配置的IIC主控制器,支持1/2字节地址和多字节数据读写,并提供完整的Testbench验证方案。

1. 为什么需要自研IIC控制器?

商业IP核通常存在三个痛点:

  • 灵活性差:难以适配特殊时序要求的设备
  • 扩展性弱:多数只支持单字节地址访问
  • 成本高:优质IP核授权费用可能占项目预算的20%以上

自研方案的优势体现在:

  • 完全可控的时序调整:可精确匹配从设备时序要求
  • 参数化设计:通过宏定义即可切换单/双字节地址模式
  • 零成本复用:一次开发可在多个项目中重复使用

提示:当项目中需要连接超过3个IIC设备时,自研控制器的成本优势会显著体现。

2. IIC协议核心时序解析

2.1 基础通信时序

IIC通信由以下几个关键时序组成:

信号类型时序特征实现要点
STARTSDA在SCL高电平时拉低需严格满足tHD;STA时间参数
STOPSDA在SCL高电平时拉高需满足tSU;STO最小脉冲宽度
ACK第9个时钟周期SDA被从机拉低需在SCL上升沿前检测SDA状态
DATASDA在SCL低电平时变化,高电平稳态建立/保持时间必须满足规格书

2.2 多字节地址访问时序

双字节地址写操作典型流程:

  1. 主设备发送START条件
  2. 发送从设备地址(写模式)
  3. 发送高字节寄存器地址
  4. 发送低字节寄存器地址
  5. 发送数据字节
  6. 主设备发送STOP条件
// 双字节地址写操作状态跳转示例 localparam [3:0] SEND_ADDR_H = 4'd1, SEND_ADDR_L = 4'd2, SEND_DATA = 4'd3;

3. Verilog实现详解

3.1 模块接口设计

module iic_master #( parameter CLK_FREQ = 50_000_000, // 系统时钟频率(Hz) parameter IIC_FREQ = 100_000, // IIC时钟频率(Hz) parameter ADDR_WIDTH = 16, // 地址总线宽度(8/16) parameter DATA_WIDTH = 8 // 数据总线宽度 )( input clk, // 系统时钟 input rst_n, // 异步复位 // 用户接口 input [6:0] dev_addr, // 从设备地址 input [ADDR_WIDTH-1:0] reg_addr, // 寄存器地址 input [DATA_WIDTH-1:0] wr_data, // 写数据 output [DATA_WIDTH-1:0] rd_data, // 读数据 input wr_en, // 写使能 input rd_en, // 读使能 output reg done, // 操作完成标志 // IIC物理接口 output scl, // 时钟线 inout sda // 数据线 );

3.2 状态机设计

采用三段式状态机实现协议控制:

// 状态编码(独热码) localparam [7:0] IDLE = 8'b00000001, START = 8'b00000010, SEND_ADDR = 8'b00000100, SEND_DATA = 8'b00001000, RECV_DATA = 8'b00010000, STOP = 8'b00100000, WAIT_ACK = 8'b01000000; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= IDLE; end else begin case(state) IDLE: if(wr_en || rd_en) state <= START; START: if(scl_gen) state <= SEND_ADDR; // ...其他状态转移 endcase end end

3.3 关键时序实现

START条件生成:

// START信号生成逻辑 always @(posedge clk) begin if(state == START) begin if(scl_high) sda_out <= 1'b0; // SCL高时拉低SDA end end

数据移位发送:

// 数据移位发送过程 always @(posedge clk) begin if(state == SEND_ADDR || state == SEND_DATA) begin if(scl_low) begin shift_reg <= {shift_reg[6:0], 1'b0}; // 左移 sda_out <= shift_reg[7]; // 输出MSB end end end

4. 测试验证方案

4.1 Testbench设计要点

// 模拟EEPROM从设备行为 task eeprom_response; input [7:0] addr; begin // 检查设备地址 if(shift_in[7:1] == DEV_ADDR) begin // 发送ACK force sda = 0; #(IIC_PERIOD/2); release sda; end end endtask

4.2 上板调试技巧

  1. 信号完整性检查

    • 使用示波器确认SCL频率是否符合预期
    • 检查START/STOP条件的上升/下降时间
  2. 常见问题排查

    • 无ACK响应:检查从设备地址是否正确
    • 数据错误:确认时序参数是否满足从设备要求
    • 总线锁死:确保每次操作都有完整的STOP条件

注意:调试时建议先在低速模式(如10kHz)下验证功能,再逐步提高时钟频率。

5. 高级功能扩展

5.1 多主机仲裁支持

通过监测总线状态实现冲突检测:

// 总线冲突检测逻辑 always @(negedge sda) begin if(scl == 1'b1 && sda_out == 1'b1) begin $display("Bus collision detected!"); state <= IDLE; end end

5.2 时钟拉伸处理

应对从设备时钟拉伸需求:

// 时钟拉伸检测 always @(negedge scl) begin if(sda == 0) begin scl_en <= 0; // 暂停时钟 @(posedge sda); // 等待从设备释放 scl_en <= 1; end end

完整实现代码

`timescale 1ns/1ps module iic_master #( // ...参数定义同上 )( // ...端口定义同上 ); // 时钟生成 reg [15:0] clk_cnt; reg scl_en; wire scl_high = (clk_cnt == (DIV_CNT >> 1)); wire scl_low = (clk_cnt == DIV_CNT); always @(posedge clk) begin if(!rst_n) clk_cnt <= 0; else if(scl_en) begin if(clk_cnt == DIV_CNT) clk_cnt <= 0; else clk_cnt <= clk_cnt + 1; end end assign scl = (scl_en && clk_cnt <= (DIV_CNT >> 1)) ? 1'b1 : 1'b0; // 状态机实现 // ...完整状态机代码 // 数据移位寄存器 reg [7:0] shift_reg; always @(posedge clk) begin if(state == IDLE) begin if(wr_en) shift_reg <= {dev_addr, 1'b0}; // 写地址 else if(rd_en) shift_reg <= {dev_addr, 1'b1}; // 读地址 end end // SDA三态控制 reg sda_out; reg sda_oen; // 输出使能 assign sda = sda_oen ? sda_out : 1'bz; // ...其他实现细节 endmodule

在实际项目中验证该控制器时,发现对某些特殊传感器需要调整SCL低电平持续时间,这时只需修改时钟分频参数即可快速适配。这种灵活性正是自研方案的最大价值所在。

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

初学者必看:收藏这份大模型学习指南,轻松入门AI世界!

本文介绍了大语言模型&#xff08;LLM&#xff09;的基本概念、工作原理及其局限性&#xff0c;如参数固定和知识时效性。文章还深入讲解了Token的概念及其在大模型中的应用&#xff0c;以及如何通过RAG技术架构模式结合向量数据库来增强大模型的回答能力&#xff0c;解决幻觉问…

作者头像 李华
网站建设 2026/7/1 8:47:03

LeetCode 1:两数之和(Two Sum)

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出和为目标值 target 的那两个整数&#xff0c;并返回它们的数组下标。你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。你可以按任意顺序返回答案。示例示例 1&…

作者头像 李华
网站建设 2026/7/1 8:39:44

Java 核心语法完整总结博客

一、前言Java 作为面向对象、跨平台的静态强类型编程语言&#xff0c;所有程序运行都基于一套固定核心语法。本文从基础数据类型、流程控制、面向对象、数组集合、异常、常用关键字六大模块梳理 Java 核心语法&#xff0c;覆盖入门到开发必备基础&#xff0c;适合新手系统复习、…

作者头像 李华
网站建设 2026/7/1 8:36:45

3步极速下载:百度网盘直链解析工具让你的下载速度飙升5倍!

3步极速下载&#xff1a;百度网盘直链解析工具让你的下载速度飙升5倍&#xff01; 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 还在为百度网盘的龟速下载而烦恼吗&#xff…

作者头像 李华