news 2026/6/10 22:18:07

ego1开发板大作业vivado实践指南:温度传感器数据采集系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ego1开发板大作业vivado实践指南:温度传感器数据采集系统

从零构建温度采集系统:Ego1开发板实战全解析

最近带学生做FPGA大作业,发现很多人卡在“温度传感器数据采集”这个项目上。其实这看似复杂的系统,拆解开来不过就是信号怎么来、数据怎么传、结果怎么用三个问题。

今天我就以Xilinx Ego1开发板为平台,带你完整走一遍基于Vivado的温度采集系统设计流程——不讲空话,只说实战中真正踩过的坑和验证有效的解法。


为什么选TMP102?不是所有传感器都适合FPGA新手

项目第一步永远是选型。面对热敏电阻、DS18B20、LM35、TMP102一堆选项,我为什么推荐TMP102?

因为对FPGA初学者来说,数字接口 > 模拟接口,标准协议 > 私有协议

  • 热敏电阻需要外接ADC + 校准曲线拟合,直接劝退;
  • DS18B20虽然也是数字输出,但用的是单总线(1-Wire),时序极其严格,一个周期差几纳秒就丢包;
  • TMP102走I²C,两根线、有规范、速率稳定,哪怕你代码写得糙一点,只要主循环够快,基本都能通。

更重要的是:它不需要外部元件。你在面包板上插个芯片,接两个4.7kΩ上拉电阻到3.3V,SCL/SDA连到FPGA引脚,就能通信。这对调试太友好了。

它的关键参数你也得记住:

参数数值
地址(默认)0x48
分辨率12位(0.0625°C/LSB)
测温范围-55°C ~ +128°C
接口速率支持100kHz / 400kHz

这些数字会直接影响你的状态机设计和数据处理逻辑。


FPGA如何“假装”是一个I²C主机?手把手教你写软核驱动

Artix-7没有硬核I²C控制器,所以我们必须“模拟”出整个协议过程——这就是所谓的Bit-Banging

别被术语吓到,其实就是让FPGA按顺序控制SCL和SDA的电平变化,模仿出起始条件、地址传输、应答检测等动作。

关键挑战:时序精度

I²C协议要求:
- 起始条件:SCL高时,SDA由高变低;
- 数据稳定期:SCL为高时,SDA不能变;
- 数据采样点:SCL下降沿写入,上升沿读取。

如果你用50MHz系统时钟(周期20ns),要生成100kHz的SCL(周期10μs),那每个SCL高低电平至少要维持50个时钟周期。

所以第一步:先做一个分频器

localparam CLK_DIV = 50_000_000 / (4 * 100_000) - 1; // ≈124 reg [7:0] clk_cnt; reg i2c_clk; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin clk_cnt <= 0; i2c_clk <= 0; end else if (clk_cnt >= CLK_DIV) begin clk_cnt <= 0; i2c_clk <= ~i2c_clk; end else begin clk_cnt <= clk_cnt + 1; end end

这里我们把SCL分成四段,每段约2.5μs,这样可以在中间点进行数据切换和采样,避开边沿抖动区。

💡 小技巧:实际设计中建议使用三段式状态机,并在SCL低电平时更新SDA,在SCL高电平时采样SDA,避免违反“数据必须在SCL高期间保持稳定”的规定。

状态机才是核心:一步步发指令读数据

下面是简化版的状态流转图:

IDLE → START → 发送设备地址+写 → 等ACK → 发寄存器地址 → 等ACK → RESTART → 发设备地址+读 → 等ACK → 连续读2字节 → NACK → STOP

对应Verilog中的枚举类型:

typedef enum logic [3:0] { IDLE, START, ADDR_WR, WAIT_ACK1, REG_PTR, WAIT_ACK2, RESTART, ADDR_RD, WAIT_ACK3, READ_MSB, READ_LSB, SEND_NACK, STOP, DONE } i2c_state_t;

重点在于:每一个状态只做一件事,比如“发送一位地址”或“等待ACK”,而不是在一个状态里塞进多个操作。

双向IO怎么处理?inout端口的经典玩法

SDA是双向引脚,作为主机时既要能输出命令,又要能接收从机返回的ACK/NACK。

解决方法是引入三态控制:

inout sda; reg sda_out; reg sda_en; // 1=输出模式,0=输入模式(高阻) assign sda = sda_en ? sda_out : 1'bz;

当你要发送数据时,sda_en=1sda_out赋值;当你想读ACK时,设sda_en=0,然后读sda引脚的真实电平。

⚠️ 坑点提醒:一定要确保在释放SDA前,其他设备没有同时驱动!否则可能造成短路。


数据拿到之后怎么办?别忘了符号扩展!

TMP102返回的是16位数据,格式如下:

bit[15:12]: 温度整数部分(补码) bit[11:8]: 小数部分(0.0625°C步长) bit[7:0]: 保留(读出来可能是随机值)

例如收到16'h8080,你以为是正数?错!最高位是1,说明是负温度。

正确解析方式:

wire signed [15:0] raw = {rx_data[15], rx_data[15], rx_data[15], rx_data[15], rx_data[15:4]}; // 扩展成完整补码 wire [15:0] abs_val = (raw[15]) ? (~raw + 1) : raw; real temp_c = $itor(abs_val) * 0.0625; if (raw[15]) temp_c = -temp_c;

当然,你也可以在纯逻辑中用组合逻辑判断符号位并取反加一,但要注意延迟匹配。


更高级玩法:MicroBlaze + AXI 让开发效率翻倍

如果你已经掌握了纯逻辑设计,下一步可以尝试加入MicroBlaze 软核处理器,把复杂的数据处理交给C语言来做。

为什么这么做?

想想看:你现在要用数码管显示温度,还要支持小数点闪烁、负号显示、单位标注……这些逻辑用Verilog写起来又臭又长。

但如果有一个CPU呢?

你可以:
- 写一段C代码格式化字符串;
- 通过UART打印到电脑;
- 或者调用LCD驱动库直接显示;
- 甚至加个按键实现“切换摄氏/华氏”。

这一切只需要在Vivado里添加一个MicroBlaze IP核,再配一个AXI GPIO或自定义AXI Lite外设即可。

如何连接你的I²C模块?

最简单的办法:把你原来的temp_data输出接到一个只读寄存器上,然后把这个寄存器挂到AXI总线上。

在SDK中就可以这样访问:

uint16_t raw = Xil_In16(XPAR_TEMP_SENSOR_0_BASEADDR); float temp = (int16_t)(raw >> 4) * 0.0625f; xil_printf("Temp: %.2f°C\n", temp);

是不是瞬间清爽了?

而且一旦出了问题,你可以打printf调试,不像纯逻辑只能靠ILA抓波形。

✅ 实战建议:前期先用纯逻辑验证I²C通信是否正常;后期迁移到MicroBlaze提升交互能力。


实际搭建时最容易翻车的几个点

别以为仿真过了就能跑通,现场调试才是真考验。

1. 上拉电阻没接 or 阻值不对

I²C是开漏输出,必须外加上拉电阻!常见的错误包括:
- 忘记接;
- 接成10kΩ导致上升沿太慢(超过300ns);
- 只给SCL加上拉,SDA没接。

标准做法:SCL和SDA各接一个4.7kΩ3.3V电源

2. 引脚约束写错电压标准

Ego1开发板使用LVCMOS33电平。如果你在XDC文件里写成了LVCMOS25,轻则通信不稳定,重则烧IO。

正确的约束示例:

set_property PACKAGE_PIN "P14" [get_ports i2c_scl] set_property IOSTANDARD LVCMOS33 [get_ports i2c_scl] set_property PACKAGE_PIN "P15" [get_ports i2c_sda] set_property IOSTANDARD LVCMOS33 [get_ports i2c_sda]

3. 时钟分频算错,导致速率超标

有人直接拿50MHz除以100k得到500,然后计数到500翻转SCL——这是错的!

因为你每个状态至少占一个时钟周期,而SCL一个完整周期需要两次翻转(高→低→高),所以实际频率是clk_freq / (2 * count)

更安全的做法是:宁可慢一点,也不要超限。跑50kHz也能读数据,但跑500kHz可能直接失败。

4. 多次读取之间没有延时

TMP102内部转换需要时间(典型35ms)。如果你连续发起读操作,很可能拿到旧数据。

解决方案:
- 在状态机里加一个“delay”状态,持续若干毫秒;
- 或者查手册启用“one-shot”模式,每次触发才采样。


如何快速定位问题?ILA是你最好的朋友

Integrated Logic Analyzer(ILA)是Vivado内置的在线逻辑分析仪,能把FPGA内部信号实时抓出来看。

建议你至少监控这几组信号:
-scl,sda:观察实际波形是否符合I²C协议;
-curr_state:确认状态机有没有卡住;
-rx_data,done:检查数据是否正确接收。

设置触发条件也很关键:
- 触发条件设为start == 1
- 捕获深度设为1024点以上;
- 采样时钟选比I²C快至少4倍的时钟。

你会发现,很多时候你以为SDA拉低了,实际上因为竞争条件根本没生效。


最后一点思考:这个项目到底教会了我们什么?

完成一次温度采集,远不止“读个数”那么简单。

它让你亲手实践了:
-硬件描述语言的真实用途:不是写代码,而是“描述电路行为”;
-跨时钟域的基本意识:高速逻辑如何与低速外设握手;
-软硬件分工的设计思维:哪些该用逻辑实现,哪些交给软件更高效;
-从仿真到实物的鸿沟跨越:理论再完美,不上电就不知道哪里漏了上拉。

而这,正是现代嵌入式系统工程师的核心能力。


未来你可以在这个基础上继续拓展:
- 加一个PWM风扇,做成闭环温控;
- 接Pmod WIFI,把温度上传云端;
- 用BRAM缓存历史数据,画趋势图;
- 甚至部署轻量级神经网络做异常检测。

但所有这一切,都要从你第一次成功读出那个0x0080开始。

所以,现在就打开Vivado,新建工程吧。
遇到问题别怕,评论区见。

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

从零实现Keil正确配置toolkit路径

如何一劳永逸解决 Keil 的c9511e编译器路径错误&#xff1f;——深入剖析 ARM 工具链配置的本质你有没有在打开一个旧项目、换了一台新电脑&#xff0c;或者刚装完 Keil 后&#xff0c;点击“编译”按钮却只看到这样一行红字&#xff1a;error: c9511e: unable to determine th…

作者头像 李华
网站建设 2026/6/10 14:33:32

基于STM32CubeMX的PLC开发完整指南

从零构建软PLC&#xff1a;基于STM32CubeMX的工业控制开发实战 你有没有遇到过这样的场景&#xff1f;客户要一个小型自动化控制器&#xff0c;功能不复杂&#xff0c;但商用PLC太贵、体积太大、还不能定制逻辑。这时候&#xff0c;如果能用一颗STM32芯片自己“造”一个PLC&…

作者头像 李华
网站建设 2026/6/9 22:26:47

嵌入式环境下堆溢出导致crash的系统学习

堆溢出为何让嵌入式系统“猝死”&#xff1f;一次 HardFault 背后的真相你有没有遇到过这样的场景&#xff1a;设备在实验室跑得好好的&#xff0c;一到现场却隔三差五重启&#xff1b;调试器抓到的调用栈停在free()里&#xff0c;但代码里明明没写错&#xff1b;翻遍逻辑也找不…

作者头像 李华
网站建设 2026/6/10 19:46:42

强化学习算法

摘要&#xff1a;强化学习算法是一类通过环境交互优化决策的机器学习方法&#xff0c;分为基于模型和无模型两种类型。基于模型算法&#xff08;如动态规划、蒙特卡洛树搜索&#xff09;先构建环境模型进行预测&#xff0c;具有较高样本效率但计算复杂&#xff1b;无模型算法&a…

作者头像 李华
网站建设 2026/6/4 5:56:17

STM32CubeMX打不开:端口或服务占用的深度讲解

STM32CubeMX打不开&#xff1f;别急&#xff0c;可能是这个端口被“劫持”了&#xff01;你有没有遇到过这样的场景&#xff1a;刚打开电脑&#xff0c;兴致勃勃准备配置一个STM32项目&#xff0c;双击STM32CubeMX图标——结果……没反应&#xff1f;或者闪一下就没了&#xff…

作者头像 李华
网站建设 2026/6/9 21:46:07

51单片机点亮一个led灯的抗干扰操作指南

从点亮一颗LED开始&#xff1a;51单片机抗干扰设计的工程实战课你有没有遇到过这种情况——代码写得没错&#xff0c;电路也照着原理图连了&#xff0c;可LED就是不听话&#xff1a;时亮时不亮、微亮、闪烁频率乱跳&#xff0c;甚至单片机莫名其妙复位&#xff1f;别急&#xf…

作者头像 李华