资讯动态

基于STM32的智能头盔系统设计:从环境感知到摔倒报警

发布时间:2026/9/1 22:15:44 来源:尧图企业网站定制
简介本资源是一套基于STM32F103平台的物联网智能头盔完整嵌入式开发方案面向嵌入式初学者、电子设计竞赛学生及物联网应用开发者解决环境感知、远程交互与多模态反馈集成等典型智能可穿戴设备开发问题。压缩包共263个文件含47个C源文件如stm32f10x_adc.c、gizwits_protocol.c、49个头文件、47个编译中间文件.o、46个依赖文件.crf以及Keil工程uvprojx、固件镜像hex/axf、原理图pcbdoc、用户手册pdf和演示视频mp4等全面覆盖硬件驱动、传感器融合、机智云协议对接与APP联动全流程总大小43.45MB。已有293人学习下载。读者可直接导入Keil工程编译烧录获得支持自动/手动双模式运行的实物系统包括温湿度联动风扇、光照自适应LED补光、超声波近距声光报警并通过OLED本地显示与手机APP双端实时监控全部传感数据。 做智能头盔这个项目说实话一开始不是奔着“智能”去的纯粹是骑电动车冬天戴头盔太冷想加个加热功能。结果做着做着发现头盔这个载体能玩的东西太多了——测速、转向提示、环境监测、摔倒报警全都塞进去之后一个基于STM32的智能头盔系统就这么成型了。这篇文章我把整个项目的设计思路、硬件选型、软件实现和调试过程完整记录下来包括踩过的坑希望能给正在做类似项目的朋友一些参考。先交代一下项目背景整个系统以STM32F103C8T6为主控集成OLED显示、温湿度采集、有害气体检测、GPS定位、摔倒检测、转向灯提示和手把遥控等功能。日常骑行时头盔能显示实时速度、里程和环境数据检测到摔倒会自动通过无线模块发送求救信息。硬件成本控制在200元以内软件全部基于STM32标准库和HAL库编写适合有一定单片机基础、想做一个完整嵌入式项目的开发者复现。1. 项目整体方案与架构设计1.1 核心需求拆解头盔到底需要什么功能做项目第一步不是画电路板而是把需求彻底想清楚。我当时列了一个很朴素的问题清单骑车时最需要什么、最怕什么、什么功能值得用电池和单片机去换。最终的答案集中在四类安全类摔倒检测与报警、转向灯提示、夜间灯光警示环境感知类温度湿度、空气质量CO/可燃气体、海拔/气压骑行数据类速度、里程、GPS轨迹、剩余电量交互类OLED信息显示、按键操作、蓝牙/无线数据上报很多朋友看到这堆功能会本能地想“全都要”但嵌入式项目最忌讳的就是功能堆砌。每个功能都在消耗引脚、功耗、代码空间和调试时间。我最后做了个筛选保留摔倒检测和空气质量这两项“真正能救命的”砍掉了语音播报和摄像头因为后者对算力和功耗的要求远超一块F103能承受的范围。1.2 主控选型与开发环境搭建主控选了STM32F103C8T6也就是大家熟知的“蓝丸”核心板。选它的理由很实在便宜十几块钱、资料多、引脚够用37个IO、主频72MHz跑这些任务绰绰有余。如果你手头有F103RCT6或者F407项目也能平移只是引脚映射要对应改。开发环境这块我经历过Keil MDK和VSCodeEIDE两种方案。Keil上手快但代码编辑体验确实一般VSCode配EIDE插件后语法提示、代码跳转舒服很多但工程配置需要多花点时间。我的建议是如果刚开始学用Keil如果已经能熟练建工程了换VSCode。这里有个实用技巧在Keil里开启MicroLIB可以解决printf重定向到串口时的浮点支持问题。在main.c里加这几行#include stdio.h int fputc(int ch, FILE *f) { while ((USART1-SR USART_SR_TXE) 0) {} USART1-DR ch; return ch; }然后勾选Options for Target里的Use MicroLIBprintf就能直接通过串口打印调试信息看变量、看状态非常方便。1.3 系统架构设计前后台还是RTOS这个项目的任务不算多传感器采集、GPS解析、显示刷新、按键扫描、摔倒检测。如果在裸机上写就是一个超级循环里轮流跑各个模块逻辑简单但实时性差——GPS解析卡一下摔倒检测就延迟了。我最终采用了 FreeRTOS 任务分层的方案。倒不是说前后台跑不动而是FreeRTOS的队列和信号量机制让模块间通信变得很清晰。比如 GPS 解析任务算好速度后通过队列发给显示任务摔倒检测任务检测到异常直接发事件给报警任务。任务划分如下Task_Display刷新OLED优先级中周期200msTask_Sensor读取温湿度、气压、空气质量周期500msTask_GPS解析GPS数据周期1sTask_KeyScan扫描按键周期20msTask_FallDetect加速度计数据滑动窗口分析周期50msTask_Report通过无线模块发送状态周期1s这样切分的好处是单个任务卡住不会拖垮整个系统。比如Task_GPS如果没搜到星超时返回就行其他任务照常运行。实际测试中系统整体运行很稳定。2. 核心器件选型与硬件电路设计2.1 传感器选型每颗芯片都有它存在的理由传感器选型是整个项目里最需要权衡的部分。既要考虑精度又要考虑功耗、体积和驱动难度。最终选型和理由如下传感器型号接口测量内容选型理由六轴惯性MPU6050I2C加速度、角速度摔倒检测核心资料多驱动成熟温湿度SHT30I2C温度、湿度精度高±0.3°C比DHT11可靠太多气压/海拔BMP280I2C气压、海拔登山/爬坡场景有用体积小空气质量MQ-7ADCCO浓度地下车库等场景CO风险高选它针对性强GPSATGM336HUART经纬度、速度国产模块功耗低秒定快OLEDSSD1306 0.96寸I2C显示128x64分辨率足够驱动简单MQ-7是模拟量输出需要接ADC。它的加热丝需要周期性通断驱动时我通过STM32的TIM定时器控制加热引脚保持5V加热1.5s、1.4V加热90s的循环这个占空比直接影响传感器的灵敏度。如果你的板子上没做加热控制电路直接用模块的默认电路也行但功耗会高不少。2.2 电源方案电池、升压与低功耗头盔系统的电源设计是个容易被低估的坑。传感器有5V的MQ-7、3.3V的其余大部分、GPS模块也要3.3VOLED要3.3V主控3.3V。如果直接用锂电池3.7V供电MQ-7的5V会遇到麻烦。我的方案是锂电池 →SX1308升压到5V→AMS1117-3.3稳压到3.3V。SX1308是一颗很常见的升压芯片效率能到90%以上静态电流在微安级。5V出来后一路直接给MQ-7另一路进AMS1117转3.3V给主控和外设。这里有个设计细节AMS1117在压差只有0.7V时效率很低但胜在便宜、稳定。如果你追求续航可以用ME6211这类LDO压差只有几十毫伏静态电流更低。我实测两颗芯片在同样负载下ME6211的发热明显小一圈。电源设计的另一个重点是滤波。STM32的ADC对电源纹波很敏感MQ-7的模拟输出更是。我在5V输出端加了10uF0.1uF的组合电容3.3V输出端加了47uF钽电容。调试时用示波器看纹波从80mV降到了15mV左右ADC采样的跳动明显减小。2.3 PCB布局与结构安装头盔不是开发板硬件设计完原理图接下来就是PCB和结构安装。PCB布局上要注意的是功率电路升压、加热丝和模拟电路ADC采集、I2C传感器分开布中间用地隔离。MQ-7尽量远离天线和OLED因为它的加热电流变化会引入电磁干扰。安装到头盔上时要考虑重心和舒适度。主控和电池放在头盔后脑勺位置显示屏和按键固定在下巴处GPS模块放在头盔顶部外侧这样信号最好。MPU6050必须固定在头盔壳体上而不是电路板上否则减震泡棉会影响摔倒检测的准确性——这个细节我一开始忽略了后来发现检测不灵敏排查好久才发现是安装问题。所有走线都要预留松动余量并打胶固定因为头盔在骑行中会持续震动排线根部断裂是最高发的故障点。3. 软件实现与核心代码逻辑3.1 传感器读取ADC多通道DMA采样与数据处理MQ-7输出的CO浓度是模拟电压STM32需要采样。这个项目里我同时还接了电池电压检测和另一路传感器备用采样所以一共有三路模拟信号需要采集。如果裸写ADC_GetValue()一路一路轮询主循环会被采样阻塞很久。正确的做法是ADC多通道扫描模式 DMA循环采样。配置一次DMA会自动把所有通道的数据搬进数组CPU零负担。static uint16_t adc_buf[3]; void ADC_DMA_Init(void) { GPIO_InitTypeDef gpio {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); gpio.Pin GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4; gpio.Mode GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, gpio); DMA_HandleTypeDef hdma {0}; hdma.Init.Direction DMA_PERIPH_TO_MEMORY; hdma.Init.PeriphInc DMA_PINC_DISABLE; hdma.Init.MemInc DMA_MINC_ENABLE; hdma.Init.PeriphDataAlignment DMA_PDATAALIGN_HALFWORD; hdma.Init.MemDataAlignment DMA_MDATAALIGN_HALFWORD; hdma.Init.Mode DMA_CIRCULAR; hdma.Init.Priority DMA_PRIORITY_HIGH; HAL_DMA_Init(hdma); __HAL_LINKDMA(hadc1, DMA_Handle, hdma); ADC_ChannelConfTypeDef sConfig {0}; sConfig.Channel ADC_CHANNEL_0; sConfig.Rank 1; sConfig.SamplingTime ADC_SAMPLINGTIME_55CYCLES_5; HAL_ADC_ConfigChannel(hadc1, sConfig); sConfig.Channel ADC_CHANNEL_1; sConfig.Rank 2; HAL_ADC_ConfigChannel(hadc1, sConfig); sConfig.Channel ADC_CHANNEL_4; sConfig.Rank 3; HAL_ADC_ConfigChannel(hadc1, sConfig); HAL_ADC_Start_DMA(hadc1, (uint32_t *)adc_buf, 3); }采样完成后数据还要做软件滤波。我用了滑动平均缓存最近5次的数据取平均。注意 MQ-7 在刚上电的前几分钟输出会很不稳定这段时间应该丢弃采样数据等传感器预热完成再显示否则屏幕上会出现“CO浓度爆表”这种吓人的假数据。3.2 摔倒检测算法不只是阈值判断摔倒检测是最有技术含量的部分。最初的方案是简单的加速度阈值判断|加速度| 2g就触发。实测下来误报率很高——急刹车、过减速带、甚至头盔不小心从桌上掉下来都会触发。后来我看了几篇跌倒检测的论文把方案改成了加速度幅值 姿态角变化 静止时间确认三步判断冲击检测加速度合矢量模值超过 2.5g判定为剧烈冲击姿态变化检测冲击发生后静止时俯仰角/横滚角变化超过 45°静止确认检测冲击发生后 1.5s~3s 内加速度模值稳定在接近 1g 的区间说明人已经倒地不动了三步全部满足才确认为摔倒触发报警。这个逻辑实现起来大概几十行代码但误报率大幅下降。用MPU6050的数据计算合加速度的代码大致如下float get_accel_magnitude(void) { float x, y, z; mpu6050_read_accel(x, y, z); return sqrt(x*x y*y z*z); }姿态角我用的不是完整的四元数Mahony解算而是简化版利用重力加速度在三个轴的分量通过atan2计算俯仰角和横滚角。这个方案在静态和低速场景下够用避免了引入 DMP 库带来的Flash占用。需要注意的是这个简化方案在急转弯时姿态角不可信但摔倒检测本身就要求静止后判断两者并不冲突。3.3 GPS解析与显示驱动ATGM336H输出的NMEA格式数据里$GPRMC包含了速度、时间、经纬度是最常用的语句。解析时要注意速度字段的单位是“节”knots要转成 km/h 需要乘以 1.852。格式化输出到OLED时格式控制符要写对否则会出现速度显示成乱码的情况。float speed_knots atof(tokens[7]); float speed_kmh speed_knots * 1.852; sprintf(display_buf, Speed:%.1fkm/h, speed_kmh);OLED驱动用的是SSD1306的I2C模式这个网上驱动代码很多不多赘述。但有一个优化很关键显示刷新走局部刷新而非全屏清空。SSD1306没有局部刷新硬件支持但软件上你可以维护一个帧缓冲数组只把有变化的区域通过set_pos 写数据的方式更新。否则200ms刷新一次全屏I2C会被占满其他传感器读取都会受影响。3.4 按键与调参交互菜单系统设计头盔上有两个按键一个“功能键”一个“设置键”。显示界面分为骑行主界面、环境界面、设置界面三页。菜单系统的实现我建议用状态机而不是一堆散乱的 if 嵌套。状态枚举如下typedef enum { UI_PAGE_MODE 0, UI_PAGE_SPEED, UI_PAGE_ENV, UI_PAGE_SETTING, } UI_Page_t;按键触发切换设置页面里短按加减参数、长按确认。这个状态机写好后后续扩展新页面非常方便只要加一个枚举值和一个渲染函数即可。这里要特别提一个按键消抖的问题。头盔在骑行中震动很大按键抖动远高于桌面实验。我用的是“定时器轮询消抖”每10ms扫描一次按键状态连续3次采样电平一致才确认状态变化。单纯用delay消抖会阻塞任务调度不建议。4. 调试过程与常见问题排查4.1 经典问题串口打印卡死、ADC数值跳变整个调试周期里最让我头疼的是串口打印卡死。现象是程序运行十几秒后printf突然不再输出。排查了很久最终定位到fputc函数的死等待问题——当串口接收中断和DMA同时抢占了发送完成标志时忙等待会卡死在while ((USART1-SR USART_SR_TXE)0)。解决办法有两个在fputc里加超时避免无限等待打印时不关闭中断改用带超时的轮询int fputc(int ch, FILE *f) { uint32_t timeout 0xFFFF; while ((USART1-SR USART_SR_TXE) 0 timeout-- 0) {} USART1-DR ch; return ch; }ADC数值跳变的问题排查后发现是采样时间太短。STM32的ADC采样时间设置过短会导致内部采样电容没充满结果浮空电平被采进来。我改成了55.5周期配合DMA循环模式数值就稳了。4.2 硬件排查工具清单与经验调试这个项目用到最值的工具是一台几十块钱的USB逻辑分析仪。I2C和UART协议都靠它抓波形排查比用示波器方便太多。排查经验可以总结成三句话传感器数据一直为0先用逻辑分析仪测SCL/SDA波形看ACK是否正常GPS不定位先看串口有没有$GPRMC语句再检查天线是否朝天电池掉电快先量静态电流而不是动态电流睡眠时所有LED都要断4.3 常见问题速查表现象大概率原因排查/解决办法OLED白屏I2C地址不对SSD1306地址默认0x3C检查模块是0x3C还是0x3DGPS搜不到星天线朝向不对天线必须朝上远离金属和FPC排线MPU6050数据全是0没有退出睡眠模式初始化时写PWR_MGMT_1寄存器清除SLEEP位按键按下无响应抖动GPIO模式错误用内部上拉输入并做20ms消抖MQ-7读数乱跳未预热首次上电预热3分钟再采集电池电压偏低LDO压差太大电池3.6V时LDO输出可能掉到3.1V换低压差LDO程序跑飞/死机堆栈溢出FreeRTOS任务栈调大用uxTaskGetStackHighWaterMark检测4.4 功耗实测与续航优化我实测了系统各状态的电流情况状态电流全功能运行OLED亮、GPS刷新、传感器开启85mA关OLED、GPS进入背光模式45mAStop模式 RTC唤醒1.2mA如果用1200mAh锂电池全功能运行大概能撑12小时左右日常通勤够用。如果想进一步延长续航可以加一个霍尔开关头盔摘下后自动进入低功耗模式只留加速度计唤醒——这个功能我后续迭代已经加上了原理就是在MPU6050上配置运动中断引脚唤醒STM32。5. 项目可以怎么继续往下做这个项目完成后可扩展的方向很多。我当时列了一个“下一代头盔”的功能清单4G Cat.1模块如Air724UG替代蓝牙实现远程报警和轨迹上报不用依赖手机屏幕升级为1.3寸或者2寸的彩屏LVGL界面会更加直观可以显示地图简化信息增加UWB精准测距模块检测后方来车距离距离过近时提醒空气检测扩展为PM2.5传感器颗粒物检测针对雾霾天的需求更精准语音助手芯片通过语音完成导航和设置操作减少骑行中的手动操作从技术角度看这些方向都在STM32的能力范围边缘正好能继续推动自己去学新的协议、新的硬件和新的中间件。最后分享一个我个人的体会做嵌入式项目硬件和软件永远是相互成就的。前期选型时多花一天思考电源和传感器布局后期调试会节省一周。别急着焊板子先把这些底层问题想透彻。这个项目麻雀虽小五脏俱全做完之后我对ADC、DMA、FreeRTOS任务调度和功耗设计的理解比刷十遍教程都深。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价