十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于STM32的电阻与移动物体实时监测系统设计

基于STM32的电阻与移动物体实时监测系统设计 简介来自东南大学自动化学院的一套嵌入式设计完整资料围绕电阻与移动物体实时监测系统覆盖课程实验与综合大作业。代码、报告与目录结构一一对应方便自动化、电子信息、计算机等专业学生用作课程设计、毕业设计或入门进阶。压缩包共115个文件整体约8.69兆字节以 C 语言和 C 源代码cpp、c、h为主另有 Python 脚本、PDF 实验报告、Qt 工程文件、Makefile、UI 界面与运行截图可对照查看实际效果内容涉及 UART 串口通信、链表、TCP 客户端与服务端、文件输入输出等从基础外设操作延伸到网络通信。源代码目录按实验序号组织Python 代码目录集中网络实验与最后大作业的本地代码便于按模块检索学习。实验代码均在提交前测试通过报告展示了设计思路与排错过程评审平均分达九十六分目前已有164人浏览学习整体可作为嵌入式课程项目从原理到实现的系统样例。1. 电阻和移动物体实时监测系统先定一个能过验收的目标在东南大学自动化学院的嵌入式设计类课程里把“电阻监测”和“移动物体监测”放进同一套系统是很典型的一类综合题。两块内容单独看都不复杂电阻测量讲的是分压加 ADC移动物体检测讲的是传感器加中断真正拉开差距的是把两个任务挤进同一颗 MCU、同一个电源、同一个定时周期之后信号串扰、滤波顺序和响应时序会变成一层一层的坑。本文以“电阻和移动物体实时监测系统”为标题给出一条我常用的复现路径主控用 STM32F103C8T6电阻端做“量程自动切换 内部 VREFINT 校准”移动端用“主动红外传感器 定时器去抖”固件按事件驱动组织最后把源代码工程和实验报告打包成可验收的 PDF。适合正在赶课程设计、又希望在演示现场不翻车的人。2. 实时监测系统的信号链电阻量程划分与移动物体传感器选型2.1 先把“实时”拆成两个指标“实时监测”这四个字必须拆开看。电阻值是一条周期性数据流刷新率做到 20ms 到 50ms 一次已经足够因为电阻是缓变量移动物体则是突发的离散事件从物体进入检测区到系统完成响应必须控制在几十毫秒级别否则红外已经扫过去了蜂鸣器才响验收时很难看。所以设计的第一步不是选主控而是把系统拆成两条独立的信号链。电阻通道的信号流是“分压网络 → ADC 采样 → 数字滤波 → 显示”瓶颈在参考电压精度和量程覆盖率移动物体通道的信号流是“传感器电平 → 去抖 → 事件标志 → 声光动作”瓶颈在误触发抑制和响应延迟。两条链路共用同一颗芯片但电源和地不能共得很随意。尤其是移动物体传感器模块上电瞬间电流变化比较明显会把 ADC 通道的地电位拉偏造成电阻读数出现固定偏移。我会在硬件上给传感器模块单独走线串联一个 10Ω 磁珠再并 100μF 电容效果立竿见影。2.2 电阻通道单挡分压不够三段量程才稳电阻测量的基础电路是参考电阻R_ref与被测电阻Rx串联把分压结果送入 ADC。关系式只有两个Vout Vcc * Rx / (R_ref Rx)反解得到Rx R_ref * Vout / (Vcc - Vout)许多课程设计只用一个参考电阻比如 10kΩ然后用公式硬算整个阻值范围。表面上看 100Ω 到 100kΩ 都能读出数但误差分布非常不均匀。当Rx远小于R_ref时Vout趋近 0ADC 的量化误差被放大成很大的阻值误差当Rx远大于R_ref时Vout又趋近满量程同样丢失分辨率。只有Rx与R_ref在同一数量级时公式才接近最优工作点。工程上的常用做法是分段量程。我一般把测量区间切三档参考电阻分别取 100Ω、1kΩ、10kΩ用 CD4052 模拟开关配合两个 GPIO 做选通。下表是每个档位的设计参数档位参考电阻 R_ref推荐测量区间12bit ADC 中心分辨率典型被测对象X1100Ω10Ω1kΩ约 0.1Ω康铜丝、继电器线圈X101kΩ100Ω10kΩ约 1Ω常规直插/贴片电阻X10010kΩ1kΩ100kΩ约 10Ω光敏电阻、电位器、湿敏电阻自动量程的判断条件也很直观采样后如果Vout低于Vcc的 5%说明被测电阻太小应该升档把 R_ref 调大如果高于 95%则降档。档位切换后需要让 ADC 重新建立稳定通常等待 1ms 再读取代码实现见第 4 章。参考电阻本身要用 1% 精度、温漂 100PPM 以内的金属膜电阻普通色环电阻误差太大在校准时会把整个系统的偏差直接拉高。2.3 移动物体通道主动红外比热释电更可控移动物体检测在课程项目里最常见的两个选项是 HC-SR501 热释电红外传感器和 E18-D80NK 主动红外漫反射开关。HC-SR501 检测的是人体辐射的红外变化只对人体这种带体温并且还在运动的目标响应上电还需要约一分钟预热一旦监测对象是纸箱、机械滑块或者任何常温物体它就直接失效。E18-D80NK 是主动红外自己发射红外光并接收反射能量物体进入设定距离后输出电平变化响应速度更快距离电位器可以在 3cm 到 80cm 之间调节。以“移动物体”这种泛化描述为题目本意我更倾向于 E18-D80NK再把 HC-SR501 作为一个可选的第二通道保留。这样实验报告里可以多写一组对比数据说明主动红外对静止物体、深色物体和人体运动的不同表现章节厚度一下就出来了。接线方面E18-D80NK 电源是 5V输出端通常是低电平有效也就是检测到物体时拉低。不同厂家的模块极性存在差异调试第一步务必用万用表量一下空闲电平把这个值记录下来免得程序里把有效电平写反在验收现场反复找问题。3. 采样策略与去抖逻辑让电阻读数不乱跳让移动事件不误报3.1 ADC 直接读数为什么不能用于实时监测12bit ADC 的满量程是 4095在 3.3V 基准下 1 个 LSB 约等于 0.8mV看起来足够精细。但把它代入分压公式后微小的量化误差会被放大成电阻误差尤其是在量程边沿。再加上主板上开关电源带来的纹波单次采样结果会明显抖动。直接读一次就刷新 OLED显示出来的电阻值最后一位会跳得没法看。常用手段是滑动窗口中值滤波。每档量程连续读取 8 个样本先去掉最大值和最小值再对剩余 6 个样本取平均。这个组合比纯均值滤波更能抵抗突发干扰同时保留了均值滤波对随机噪声的抑制能力。采样之间的间隔取 1ms由定时器调度不要在主循环里连读否则传感器上电瞬间的磁场变化会污染整批数据。3.2 用 VREFINT 校准实际 Vcc替代“认为就是 3.3V”分压公式里的 Vcc 不是完美 3.3V。板载 LDO 输出电压通常在 3.30V 上下浮动USB 供电时波动更明显代入公式Rx R_ref * vout / (Vcc - vout)后Vcc 的偏差会直接进入结果并且这个误差是系统性的滤波无法消除。STM32F103 内置了内部参考电压 VREFINT典型值 1.2V出厂校准值烧录在地址0x1FFFF7BA。通过读取 VREFINT 通道的 ADC 计数值可以反推出当前真实的 Vdda。关键代码段如下/* 读取内部 VREFINT 通道返回 12bit ADC 原始值 */ uint16_t read_vrefint(void) { ADC_ChannelConfTypeDef cfg {0}; cfg.Channel ADC_CHANNEL_VREFINT; cfg.Rank ADC_REGULAR_RANK_1; cfg.SamplingTime ADC_SAMPLETIME_239CYCLES_5; /* 内部通道阻抗高采样时间拉长 */ HAL_ADC_ConfigChannel(hadc1, cfg); HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); HAL_ADC_Stop(hadc1); return (uint16_t)HAL_ADC_GetValue(hadc1); } /* 结合 VREFINT 反算 Vdda再计算电阻值 */ float calc_resistance(uint16_t adc_value, uint32_t r_ref_ohm) { float vdda 3.3f * 4095.0f / read_vrefint(); float vout (float)adc_value * vdda / 4095.0f; if (vout vdda * 0.05f || vout vdda * 0.95f) { return -1.0f; /* 返回 -1 表示当前量程不合适 */ } return (float)r_ref_ohm * vout / (vdda - vout); }这段代码里有两个参数值得单独说明。ADC_SAMPLETIME_239CYCLES_5选择较长的采样时间是因为 VREFINT 通道内部源阻抗偏高采样时间太短会导致转换结果偏小。read_vrefint()每计算一次电阻就调用一次会临时改变 ADC 的通道配置所以它只能在需要校准时调用校准完成后主通道必须重新配置回 ADC_CHANNEL_0。如果你的项目对实时性要求更高可以改成每 500ms 校准一次 Vdda而不是每帧都校准。3.3 移动物体去抖定时器抖动不能进中断服务函数移动物体传感器的电平变化很干净但外部环境会产生毛刺比如日光灯频闪、人或物体快速掠过时信号只持续几毫秒。直接在外部中断里驱动蜂鸣器或继电器是最差的处理方式因为中断服务函数一旦阻塞系统里其它任务就会被耽误ADC 数据流也跟着断。我在工程里始终用定时器去抖。把定时器配成 1ms 中断在中断回调里轮询传感器引脚有效电平维持 5ms 才置位事件标志/* 在 TIM2 的 1ms 中断回调中调用 */ void motion_debounce_poll(void) { if (HAL_GPIO_ReadPin(MOVE_GPIO_PORT, MOVE_PIN) GPIO_RESET) { /* 检测到物体且为低电平有效 */ if (run_cnt 20) run_cnt; if (run_cnt 5) { motion_event 1; /* 连续 5ms 确认过滤瞬时毛刺 */ event_tick HAL_GetTick(); /* 记录首次触发时刻 */ } } else { run_cnt 0; /* 信号消失立即清零 */ } }去抖参数5ms不是随手填的。若设成 1ms日光灯或电机火花引起的毛刺很容易穿过滤波器设成 50ms实验室里快速移动的小物体会被漏检。对于常见的可调距离红外传感器5ms 到 20ms 之间是比较可靠的范围。最后在中断回调里只写变量不写 OLED、不操作蜂鸣器、不打日志这些全部放到主循环处理保证中断占用时间在微秒级别。4. 源代码结构量程自动切换、主循环调度与串口日志4.1 工程文件划分与初始化顺序一套能连续运行不被看门狗咬死的固件文件划分必须清晰。规模适中的课程设计工程我建议按功能拆成四个模块文件职责对外接口adc.c电阻量程切换、ADC 采样、VREFINT 校准adc_auto_range_read()motion.c移动传感器引脚轮询与去抖motion_debounce_poll()display.cOLED 显示与蜂鸣器控制oled_show()、buzzer_beep()uart.c串口调试日志输出uart_send()初始化顺序固定为时钟 → TIM2 定时器 → GPIO → ADC → I2C OLED → 串口 → 传感器。传感器模块的电源由 GPIO 控制在初始化最后阶段再上电避免它上电瞬间的电流冲击影响 ADC 首次校准的准确性。4.2 电阻量程自动切换的实现自动量程切换不能无脑切换。切换条件用电压比例判断而不是只看电阻计算结果是否落在某一个区间。代码如下#define RES_TOO_LOW (-1.0f) /* 分压值太低需要把 R_ref 调大 */ #define RES_TOO_HIGH (-2.0f) /* 分压值接近满量程需要调小 R_ref */ static const uint32_t range_ohm_table[3] { 100, 1000, 10000 }; static uint8_t range 1; /* 初始档位 1kΩ */ void adc_auto_range_read(void) { uint32_t sum 0, raw; for (int i 0; i 8; i) { HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); sum HAL_ADC_GetValue(hadc1); } raw sum 3; /* 8 次采样求平均 */ float rx calc_resistance((uint16_t)raw, range_ohm_table[range]); if (rx RES_TOO_LOW range 2) { range; /* 升档R_ref 从 1k 切到 10k */ HAL_Delay(1); adc_auto_range_read(); } else if (rx RES_TOO_HIGH range 0) { range--; /* 降档R_ref 从 1k 切回 100 */ HAL_Delay(1); adc_auto_range_read(); } else { current_rx rx; /* 当前量程合适结果可用 */ } }两个参数值得在实验报告里讨论。采样次数取 8是因为 8 次求平均的运算可以退化成右移 3 位MCU 开销极小如果取 16 次抗噪效果更好但 ADC 采集耗时翻倍对 20Hz 的刷新率仍有富余你可以按需调节。HAL_Delay(1)放在量程切换后是为了等模拟开关和外部 RC 网络稳定直接连续读会读到前一个档位的残余电压。4.3 主循环事件驱动而不是顺序轮询主循环采用事件驱动。电阻值刷新是一个定时事件移动物体响应是一个标记事件两者互不阻塞static uint32_t last_alert_tick 0; while (1) { /* 周期性任务30ms 刷新一次电阻显示频率过高 OLED 刷不过来 */ if (HAL_GetTick() - last_display_tick 30) { last_display_tick HAL_GetTick(); adc_auto_range_read(); oled_show(0, Rx%.2f, current_rx); } /* 突发任务移动事件响应 */ if (motion_event) { motion_event 0; if (HAL_GetTick() - last_alert_tick 500) { last_alert_tick HAL_GetTick(); buzzer_beep(200); oled_show(1, MOVE!); uart_send([MOTION] detected\n); } } }这段调度的关键点在于“标志位只管事件爆发不管后续重复触发”。last_alert_tick保证同一个持续存在的物体不会让蜂鸣器每 5 毫秒响一次而是 500ms 内最多响应一次而电阻刷新周期设为 30ms是因为 OLED 通过 I2C 刷屏本身有耗时刷新太密反而会把主线调度拖慢。串口日志uart_send放在判定通过之后属于可观测事件便于实验报告里统计响应时间和误报次数。5. 标定验证与实验报告整理把源代码、数据和 PDF 打包成可验收交付5.1 用 1% 精度电阻完成全量程标定程序跑通之后最重要一环是标定。准备一组 1% 精度金属膜电阻按 100Ω、330Ω、1kΩ、3.3kΩ、10kΩ、33kΩ、100kΩ 覆盖三个量程的交叉区域。测量时把系统读数与万用表读数逐一记录算出相对误差。正常情况下每个量程中心区域的误差应该在 ±3% 以内如果偏差具有一致方向比如全部偏大 3%基本可以确定是 Vdda 校准没有生效优先回头检查 VREFINT 通道是否在每次电阻采样前都被重新配置。5.2 验收现场最常踩的三个坑第一个坑是传感器模块的“空闲电平没确认就写代码”。很多模块上电后有一段自检时间输出电平会先跳变一次程序如果把这个跳变当作移动物体就会造成“上电即报警”。所以固件里加一段 500ms 的“启动静默期”期间屏蔽所有移动事件。第二个坑是 ADC 通道切换后没有恢复。read_vrefint()会在计算电阻时占用 ADC 通道如果下一次读取外部通道前忘了重配置读到的永远是 VREFINT 数据电阻显示变成一条恒定值。排查方法很简单串口把原始 ADC 值打出来看它是否随被测电阻变化。第三个坑是报告里没有量化数据。实验报告用表格记录每个标准电阻的测量值、误差和量程状态再用串口记录一次移动物体从触发到蜂鸣的延迟这部分数据比任何文字描述都更有说服力。如果测量结果系统性偏大报告里还可以补一段说明解释 VREFINT 校准原理和参考电阻温漂对精度的影响这属于非常出彩的加分内容。5.3 报告与源代码打包的收尾顺序最后提交时把源代码工程、串口日志截图、标定数据表和 PDF 报告放在同一个压缩包里。报告正文包含四块内容系统框图与硬件选型说明、传感器标定数据表、关键代码段注释、以及响应时间/误报率的测试记录。标定数据表里单独留一行把“系统显示值”和“万用表实测值”的偏差写到 0.1Ω 分辨率这个细节很容易被评审注意到。写报告时把“上电静默期”和“VREFINT 校准”这两点作为设计亮点展开比贴十行流水账截图更有说服力。本文还有配套的精品资源点击获取
返回列表