用 jscope 打造实时温度监控系统:从传感器到波形的完整校准实践
你有没有遇到过这样的场景?
手头一个温控电路正在调试,NTC热敏电阻接好了,ADC采样也跑通了,串口打印出来的温度值看着“差不多”,但就是不敢确定——这个升温曲线到底是不是真实的?有没有延迟?滤波是不是太狠导致响应滞后?
传统的printf输出只能看数字,而高端示波器又贵又笨重,还无法直接读取物理意义明确的温度单位。这时候,jscope就成了嵌入式工程师的秘密武器。
它不像传统仪器那样需要探针和触发设置,而是直接把 MCU 内部的数据“搬”到 PC 上,以类示波器的方式实时绘制出来。更关键的是——它是免费的、轻量的、可深度定制的。
本文不讲空泛概念,也不堆砌术语,而是带你走完一个完整的工程闭环:从 NTC 信号采集开始,经过 ADC 转换、软件滤波、数据格式化,最终在 jscope 中实现精准的温度波形显示与校准。目标只有一个:让你看到的波形,就是真实世界的温度变化。
为什么选择 jscope 做温度监控?
先说清楚一个问题:我们为什么要用 jscope,而不是直接用逻辑分析仪抓 UART 或者自己写个 Python 绘图脚本?
答案是——效率 + 精度 + 实时性三者的平衡。
- 逻辑分析仪能抓波形,但它看到的是原始电压跳变,不会自动翻译成“25.3°C”;
- Python + matplotlib可视化灵活,但有延迟,不适合观察瞬态过程;
- 而jscope则介于两者之间:它通过简单的二进制协议接收数据,立刻绘制成带时间轴的波形图,支持多通道、可调垂直刻度、还能设触发点。
更重要的是,它由 ADI 官方维护,稳定可靠,集成在 CrossCore Studio 中也可独立运行,Windows 和 Linux 都支持。对于使用 STM32、ADuC 等 Cortex-M 系列 MCU 的项目来说,几乎是零成本接入。
📌 核心价值一句话总结:让嵌入式系统内部的状态变化,像示波器一样被“看见”。
温度信号是怎么变成波形的?一条链路拆解
要让 jscope 正确显示温度波形,我们必须理清整个数据流路径。这不是单一工具的问题,而是一个端到端系统的协同工作。
物理温度 → NTC阻值变化 → 分压电压 → ADC采样 → 数字滤波 → 温度计算 → 单位转换 → 串口发送 → jscope解析 → 波形显示任何一个环节出错,最终波形就会失真。下面我们逐段打通。
第一步:选对传感器和前端电路
最常见的低成本方案是10kΩ NTC + 上拉电阻构成分压网络,接到 MCU 的 ADC 输入引脚。
假设:
- NTC 标称值:10kΩ @ 25°C
- B 值:3950K
- 固定上拉电阻:10kΩ
- ADC 参考电压 Vref = 3.3V(务必稳定!)
随着温度上升,NTC 阻值下降,分压点电压升高。例如:
| 温度 (°C) | NTC 阻值 (kΩ) | 分压输出 (V) |
|---|---|---|
| 0 | ~30 | ~0.8 |
| 25 | 10 | 1.65 |
| 50 | ~4.5 | ~2.3 |
| 100 | ~1.1 | ~2.9 |
这个非线性关系需要用算法还原。常用方法有两种:
- 查表法 + 插值:适合资源紧张的单片机;
- Steinhart-Hart 公式简化版(Beta 模型):精度高,计算量适中。
我们推荐后者,代码简洁且全温区误差可控在 ±1°C 以内。
float calculate_temperature(uint16_t adc_val) { float vout = (adc_val * 3.3f) / 4095.0f; float r_ntc = 10.0f * vout / (3.3f - vout); // R_fixed = 10kΩ float log_r = logf(r_ntc); float inv_t = (1.0f / 298.15f) + (1.0f / 3950.0f) * log_r; return (1.0f / inv_t) - 273.15f; // 转为 °C }⚠️ 注意事项:确保
logf()来自 math.h 并启用浮点运算支持。若禁用浮点,可用定点查表替代。
ADC 采样怎么做到稳定不跳数?
很多初学者发现:“我明明测的是室温,怎么 ADC 值老是在动?” 这其实是噪声、采样时机不准、电源波动共同作用的结果。
解决办法不是“加 delay”,而是建立一套可靠的采样机制。
推荐配置(以 STM32 HAL 库为例)
// 使用定时器 TRGO 触发 ADC,保证周期精确 TIM3->PSC = 83; // 84MHz APB1 → 1MHz 计数频率 TIM3->ARR = 9999; // 10ms 周期(即 100Hz 采样率) TIM3->CR2 |= TIM_CR2_MMS_1; // MMS[2:0] = 010 → Update Event as TRGO // ADC 配置 hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES; // 足够充电时间这样做的好处是:
- 不依赖 CPU 主动轮询或中断延时,避免抖动;
- 支持 DMA 自动搬运,进一步提升一致性;
- 采样率完全由硬件控制,便于后续与 jscope 同步。
加一层软件滤波,效果立竿见影
即使硬件同步了,ADC 值仍有 ±2~3 LSB 的波动。我们可以用简单的滑动平均滤波抑制随机噪声。
#define FILTER_WINDOW 8 uint16_t filter_buffer[FILTER_WINDOW]; int filter_index = 0; uint16_t apply_filter(uint16_t raw) { filter_buffer[filter_index] = raw; filter_index = (filter_index + 1) % FILTER_WINDOW; uint32_t sum = 0; for (int i = 0; i < FILTER_WINDOW; i++) { sum += filter_buffer[i]; } return sum / FILTER_WINDOW; }✅ 实践建议:8 次均值足够应对大多数场景;若动态响应要求高,可改用一阶 IIR 滤波(
y = α*x + (1−α)*y_prev)。
数据怎么传给 jscope?格式必须严丝合缝
这是最容易被忽视的关键点:jscope 不关心你是谁,只认数据格式。
它默认期待一种特定结构的数据流:
- 每次发送一个样本帧(sample frame)
- 每帧包含N 个通道的数据
- 每个数据为16 位有符号整数(little-endian)
比如你有两个温度点要监控,每帧就发 4 个字节:
[ch1_low][ch1_high][ch2_low][ch2_high]而每个数值代表什么物理量?靠的是客户端的手动校准。
所以强烈建议统一使用毫摄氏度(m°C)作为传输单位。例如:
| 实际温度 | 发送值(m°C) | 16位十六进制 |
|---|---|---|
| 25.000°C | 25000 | 0x1E20 |
| -10.5°C | -10500 | 0xD8F4 |
这样做的优势非常明显:
- 精度达到 0.001°C,远超传感器本身;
- 支持有负温,无需额外标志位;
- 与 jscope 的 Volts/Div 映射关系清晰。
发送代码也很简单:
int16_t temp_raw_mC = (int16_t)(temperature_C * 1000.0f); uint8_t tx_buf[2]; tx_buf[0] = (uint8_t)(temp_raw_mC & 0xFF); // LSB tx_buf[1] = (uint8_t)((temp_raw_mC >> 8) & 0xFF); // MSB HAL_UART_Transmit(&huart2, tx_buf, 2, 10);🔔 提醒:波特率至少设为 115200,否则容易丢帧。如果是多通道,更要提高速率或降低采样率。
jscope 客户端怎么设置才能“看得准”?
打开 jscope 后,第一眼可能会懵:黑屏、乱码、波形飞天遁地……别急,关键在四个参数的匹配。
必须正确配置的四大参数
| 参数名 | 设置依据 | 示例值 |
|---|---|---|
| Sampling Rate | 必须等于 MCU 实际发送频率 | 10 Hz |
| Number of Channels | 几个传感器就填几 | 1 |
| Data Format | 选择 Signed 16-bit | ✔️ |
| Comm Port | 选择正确的 COM 口和波特率 | COM5, 115200 |
一旦配错 Sampling Rate,时间轴就错了。比如你每 100ms 发一次数据(即 10Hz),但 jscope 设成 1kHz,那屏幕上 1 秒会挤进去 1000 个点,实际只有 10 个有效数据,其余都是重复或乱码。
垂直刻度怎么换算成温度?
这是波形校准的核心。
jscope 默认单位是 “Volts”,但我们传的是 m°C。怎么办?
有个技巧:人为定义缩放比例。例如:
设定:1V = 1,000,000 m°C (即 1000°C/V)
那么:
- 25.000°C = 25,000 m°C → 对应 0.025V
- ±50°C 范围 → ±0.05V → 设置 Vertical Scale = 0.01V/div(共 10 格)
操作步骤如下:
1. 打开 jscope → Options → Preferences
2. 设置 Channel 1 的 Units 为 “°C”
3. 设置 Gain = 1e-6 (表示 1 count = 1e-6 V)
4. Offset = 0(除非有系统偏移需补偿)
此时,当 MCU 发送 25000,jscope 就会在 Y 轴显示 0.025V,再结合单位映射,直接标为 “25°C”。
💡 小技巧:首次连接时勾选 Auto-Scale,让波形自动居中,之后再手动微调 Gain 达到最佳分辨率。
常见坑点与调试秘籍
别以为配置完就能一帆风顺。以下是我们在实际项目中踩过的坑:
❌ 问题1:波形刚开始正常,几秒后断掉
原因:UART 缓冲区溢出,MCU 发得太快,PC 来不及收。
解决方案:
- 降低采样率至 5~10Hz(温度变化慢,够用了);
- 使用 RTOS 或调度器控制发送节奏;
- 添加简易握手:上电后等待 jscope 发一个‘S’才开始发数据。
❌ 问题2:波形上下抖动严重,像锯齿
原因:ADC 噪声未滤除干净,或参考电压不稳定。
排查思路:
- 示波器测 Vref 是否平稳;
- 增加滤波窗口大小(如从 4 次改为 8 次);
- 在 PCB 上给 ADC 电源加 10μF + 100nF 滤波电容。
❌ 问题3:温度突变时响应迟钝
原因:滤波太强,或者采样率太低。
优化方向:
- 改用一阶 IIR 滤波,保留更多高频成分;
- 提高采样率至 20~50Hz,观察阶跃响应;
- 在风扇启停等大扰动测试中验证动态性能。
更进一步:不只是看温度
掌握了这套方法论后,你会发现 jscope 的潜力远不止于温度监控。
只要能把物理量转化为周期性输出的 16 位整数,都可以接入:
- 电流检测(mV → mA)
- 湿度采集(%RH × 100)
- 振动幅度(来自加速度计 RMS)
- PWM 占空比变化趋势
- 电池电压衰减曲线
甚至可以组合多个通道,同时观察“温度 vs 电流 vs 风扇转速”的联动关系,快速定位热设计瓶颈。
未来如果结合 FreeRTOS Trace 工具,还能做到事件标记 + 波形回溯,真正实现“所见即所得”的调试体验。
写在最后:让嵌入式系统“会说话”
一个好的工程师,不仅要能让系统跑起来,更要让它“说出来”发生了什么。
jscope 就是那个翻译官——它把沉默的寄存器、隐藏的变量、抽象的数值,转化成你能直观理解的趋势图。
下次当你面对一个温控异常的板子时,不妨试试这个组合拳:
1. 用 NTC + ADC 采集真实信号;
2. 用定时器 + 滤波保证数据质量;
3. 用标准化格式发送 m°C 数据;
4. 在 jscope 中完成单位映射与波形校准。
你会发现,原来那些模糊的“感觉差不多”,变成了清晰可见的上升斜率、稳定时间和超调量。
这才是真正的工程底气。
如果你也在做类似的传感器监控项目,欢迎留言交流实战经验。要不要下一期我们聊聊如何用 jscope 监控电机电流波形?