news 2026/1/9 9:46:57

Vivado使用项目实战:实现I2C温度传感器读取

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vivado使用项目实战:实现I2C温度传感器读取

从零开始用FPGA读取温度传感器:Vivado实战全记录

你有没有过这样的经历?明明代码写得一丝不苟,时序也反复推敲,可I2C总线上就是“静如止水”——SDA纹丝不动,SCL也没波形。或者更糟,读回来的数据全是0xFF,像在跟你开个无声的玩笑。

别急,这正是我们今天要一起攻克的实战项目:在Xilinx FPGA上,用Vivado从头搭建一个I2C主控制器,读取TMP102这类常见温度传感器的数据。这不是理论课,而是一次真实开发流程的完整复现——从创建工程、写状态机、加约束、仿真验证,到板级调试、抓波形、改bug,全程手把手。


为什么选I2C?因为它“小但难搞”

I2C看似简单:两根线,SCL和SDA。但它对时序要求严苛,又是开漏结构,还得处理三态控制和应答机制。对于初学者来说,它就像一块“试金石”——能通,说明你真懂了FPGA的时序和协同设计;不通,那问题往往藏得很深。

更重要的是,I2C是嵌入式系统中最常见的低速外设接口之一。掌握它,意味着你能轻松对接EEPROM、RTC、触摸屏、环境传感器……这些在工业控制、物联网节点中无处不在的模块。

而Vivado,作为Xilinx主流开发工具,它的价值不仅在于综合与实现,更在于那一套完整的硬件协同开发闭环:仿真、ILA在线调试、约束管理、IP集成。这次我们就用最“硬核”的方式,把这套流程走一遍。


I2C协议的本质:一场精确的“电平舞蹈”

在动手前,先搞清楚I2C到底在做什么。

两根线,四种动作

  • SCL:时钟线,由主机(这里是FPGA)完全控制。
  • SDA:数据线,双向,所有设备共用。

通信靠的是四个关键信号跳变:

动作SCL状态SDA变化
起始(Start)高 → 保持高高 → 低
停止(Stop)高 → 保持高低 → 高
数据写入低 → 保持低可变
数据采样上升沿后必须稳定

记住一句话:SDA只能在SCL为低时改变,在SCL为高时必须保持稳定,否则可能误触发起始/停止条件。

一次温度读取的真实流程

以TMP102为例,我们要读它的温度寄存器(地址0x00),典型操作如下:

  1. Start
  2. 发送Slave_Write_Address(比如0b10010000
  3. 等待ACK
  4. 发送寄存器地址0x00
  5. 等待ACK
  6. ReStart
  7. 发送Slave_Read_Address0b10010001
  8. 接收第一个字节
  9. 回复ACK
  10. 接收第二个字节
  11. 回复NACK(表示不再接收)
  12. Stop

整个过程涉及两次地址传输、一次寄存器选择、两次数据接收,中间夹着多个ACK/NACK判断。稍有延迟或提前,从机就可能“罢工”。


FPGA怎么“演”好I2C主机?状态机是核心

要在FPGA里实现这个流程,最可靠的方法是有限状态机(FSM) + 精确计数

状态机怎么分?按通信阶段切

我把整个流程拆成13个状态,清晰对应每一步操作:

typedef enum logic [3:0] { IDLE, START, ADDR_WR, ACK1, REG_SET, RESTART, ADDR_RD, ACK2, READ_BYTE1, ACK3, READ_BYTE2, NACK, STOP, DONE } i2c_state_t;

每个状态干一件事,比如:

  • START:拉高SCL和SDA → 拉低SDA
  • ADDR_WR:逐位发送从机写地址(7位地址 + 0)
  • ACK1:释放SDA,等待从机拉低(ACK)
  • READ_BYTE1:在SCL上升沿采样SDA,移位保存

状态转移靠内部计数器推进。例如,发送8位地址需要8个SCL周期,我们就用一个bit_cnt从0计到7。

时钟怎么分?别让SCL太快

假设FPGA主频是100MHz,目标SCL = 100kHz,那么每个SCL周期是10μs,高低各5μs。

100MHz下,一个时钟周期是10ns,所以每半个SCL周期需要计数:

5μs / 10ns = 500

也就是说,SCL高/低电平各维持500个系统时钟。用一个clk_div计数器即可实现。

⚠️ 注意:实际设计中建议留些裕量,比如设为550,避免因布线延迟导致时序紧张。


Vivado实战:从创建工程到下载运行

第一步:建工程,选对芯片

打开Vivado,新建RTL工程:

  • Project name:i2c_temp_reader
  • Device: 根据你的开发板选择,比如Digilent Nexys A7-100T →xc7a100tcsg324-1
  • 不勾选“Sources in external location”

添加两个文件:

  • i2c_temp_sensor_top.v:顶层模块
  • i2c_master_ctrl.v:I2C主控核心

第二步:引脚约束(XDC)不能错

I2C信号必须接到支持inout的IO上。以PMOD JA为例:

set_property PACKAGE_PIN J17 [get_ports {scl_io}] ;# PMOD JA1 set_property PACKAGE_PIN K16 [get_ports {sda_io}] ;# PMOD JA2 set_property IOSTANDARD LVCMOS33 [get_ports {scl_io sda_io}] set_property CONFIG_PACKAGE_PIN_PULL none [get_ports {scl_io sda_io}] ;# 关闭内部弱上拉,外接电阻更稳 # 复位按键 set_property PACKAGE_PIN C12 [get_ports {btn_rst}] set_property IOSTANDARD LVCMOS33 [get_ports btn_rst]

🔧 实践提示:虽然FPGA IO有弱上拉,但I2C总线最好外接4.7kΩ上拉电阻到3.3V。我吃过亏——没接电阻,通信成功率不到30%。

第三步:三态控制是关键

FPGA的SDA和SCL都是inout端口,必须通过oe(output enable)控制方向:

// 在顶层连接 wire scl_o, sda_o; wire scl_i, sda_i; wire scl_oe, sda_oe; assign scl_io = scl_oe ? scl_o : 1'bz; assign sda_io = sda_oe ? sda_o : 1'bz; assign scl_i = scl_io; assign sda_i = sda_io;
  • oe=1,输出由o决定
  • oe=0,呈高阻态,允许从机驱动

在状态机中,只有主机发送数据或生成Start/Stop时才使能输出;接收ACK时需释放SDA,让从机拉低。


仿真验证:别等上板才发现逻辑错了

写Testbench,模拟一次完整读取:

initial begin clk = 0; forever #5 clk = ~clk; // 100MHz end initial begin rst_n = 0; start_req = 0; slave_addr = 7'b1001000; reg_addr = 8'h00; #100 rst_n = 1; #100 start_req = 1; #20 start_req = 0; wait(done); $display("✅ 温度读取完成,值为: %h %h", temp_data[15:8], temp_data[7:0]); #100 $finish; end

用Vivado Simulator跑一下,重点看:

  • Start条件是否正确生成(SDA下降早于SCL)
  • 地址和寄存器是否匹配
  • ACK是否在第9个SCL周期被采样
  • 两个字节是否完整接收

如果仿真都过不去,上板只会更糟。


板级调试:ILA是你最好的朋友

仿真通过了,烧录到板子,结果还是没数据?

别慌,插入ILA核,实时抓信号!

在Block Design中添加ILA IP,监控这些信号:

  • state:当前状态机状态
  • scl_o,sda_o:实际输出
  • scl_oe,sda_oe:输出使能
  • ack_err:是否有ACK错误

重新综合、实现、生成比特流,下载后打开Hardware Manager,点击“Debug Probes”,就能看到实时波形。

常见坑点与解决方法

问题现象可能原因解决方案
SCL无波形时钟未启用或分频错误检查MMCM配置,确认计数器是否启动
读回0xFFSDA未释放,总线被锁死检查三态控制,确保ACK阶段oe=0
ACK失败地址错误或从机未响应用逻辑分析仪确认地址是否0x90(写)和0x91(读)
通信偶尔成功上拉电阻过大或分布电容大改用1.8kΩ~3.3kΩ电阻,降低SCL频率至50kHz

有一次我折腾了半天,最后发现是TMP102的ADDR引脚接地不良,导致地址变成了0x4C而不是默认的0x48。焊好之后,立马通了。


系统扩展:让温度看得见

光读出来还不够,我们得让它“说话”。

可以在系统中加入:

  • UART Lite模块:将温度值格式化为字符串,发送到PC串口助手
  • LED指示灯:用4个LED显示粗略温度区间(如<25°C蓝,>30°C红)
  • OLED显示:通过SPI驱动SSD1306,本地显示温度曲线

甚至可以加个MicroBlaze软核,跑FreeRTOS,定时采集并上传云端。


写在最后:为什么这个项目值得做?

因为它是从理论到落地的完整闭环

你不仅写了Verilog,还用了Vivado的约束、仿真、ILA调试、比特流生成——这些都是工程师日常工作的缩影。你学会了:

  • 如何用状态机建模复杂时序
  • 如何处理inout端口和三态控制
  • 如何通过ILA定位硬件bug
  • 如何与真实传感器“对话”

下次当你面对SPI、UART、甚至是自定义协议时,你会更有底气。

如果你也在用Vivado做FPGA开发,不妨试试这个项目。哪怕只为了亲眼看到那一行“Temperature: 26.5°C”从串口蹦出来,也值得。

毕竟,硬件的魅力就在于——你写的每一行代码,最终都会变成实实在在的电信号,在芯片间流动

欢迎在评论区分享你的I2C踩坑经历,或者你用FPGA连过的最奇怪的传感器是什么?

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

WindowResizer:重新定义你的窗口管理效率

你是否曾因窗口尺寸不匹配而频繁切换&#xff1f;是否在多任务处理时感到界面布局混乱&#xff1f;这正是WindowResizer智能窗口尺寸调整工具要解决的核心问题。作为专业的智能窗口管理解决方案&#xff0c;它通过精准控制窗口尺寸、优化多显示器布局&#xff0c;为你的工作流带…

作者头像 李华
网站建设 2026/1/1 15:33:59

从零搭建企业级前端架构:D2Admin微前端改造终极指南

从零搭建企业级前端架构&#xff1a;D2Admin微前端改造终极指南 【免费下载链接】d2-admin 项目地址: https://gitcode.com/gh_mirrors/d2a/d2-admin 引言&#xff1a;企业级前端架构的演进之路 随着前端技术的快速发展&#xff0c;企业级应用面临着前所未有的挑战&am…

作者头像 李华
网站建设 2025/12/25 0:06:35

5分钟掌握gTTS:Python文本转语音的终极指南

5分钟掌握gTTS&#xff1a;Python文本转语音的终极指南 【免费下载链接】gTTS Python library and CLI tool to interface with Google Translates text-to-speech API 项目地址: https://gitcode.com/gh_mirrors/gt/gTTS 想要为你的Python项目添加语音功能吗&#xff1…

作者头像 李华
网站建设 2026/1/1 14:06:15

Unity资源逆向工程完全指南:AssetStudio从入门到精通

Unity资源逆向工程完全指南&#xff1a;AssetStudio从入门到精通 【免费下载链接】AssetStudio 项目地址: https://gitcode.com/gh_mirrors/asse/AssetStudio AssetStudio作为一款专业的Unity资源逆向分析工具&#xff0c;为游戏开发者、逆向工程师和资源爱好者提供了强…

作者头像 李华
网站建设 2025/12/27 13:42:14

5分钟掌握在线JSON对比工具:快速定位数据差异的终极指南

5分钟掌握在线JSON对比工具&#xff1a;快速定位数据差异的终极指南 【免费下载链接】online-json-diff 项目地址: https://gitcode.com/gh_mirrors/on/online-json-diff 在API开发、数据迁移和配置管理的日常工作中&#xff0c;JSON对比分析是每个开发者都绕不开的关键…

作者头像 李华
网站建设 2025/12/25 3:53:24

28、数据时间分析与文本可视化指南

数据时间分析与文本可视化指南 1. 时间数据分析基础 在数据分析中,对数据进行时间维度的分析至关重要,它能帮助我们洞察业务的发展趋势。通过一些DAX公式,我们可以实现对不同时间周期数据的分析。 1.1 计算前一年平均销售价格 以下公式用于计算所选时间段在前一年的平均…

作者头像 李华