公司动态

STM32手环DIY:ADXL345计步+MAX30102心率+DS18B20体温全流程

📅 2026/9/1 5:44:49
STM32手环DIY:ADXL345计步+MAX30102心率+DS18B20体温全流程
简介基于STM32F103C8T6的智能手环主控工程完整实现了ADXL345计步、心率与体温监测、LCD1602动态显示等核心功能面向嵌入式初学者、课程设计与毕业设计人群可作为传感器驱动与低功耗外设集成的实用参考。资源包共92个文件包含38个C源码、37个头文件另有Keil工程文件、原理图、PCB PDF、设计说明文档和烧录hex文件压缩包大小454KB。其中代码采用STM32F10x标准外设库将传感器驱动与显示逻辑分层模块边界清晰便于移植和二次扩展。设计文档与原理图配套能辅助理解硬件连接和软件流程。目前已有62人学习下载适合需要完整工程范例快速上手的开发者。 如果你也想用STM32做一款可以戴在手腕上的智能手环大概率会在选主控这一步纠结半天。市面上的手环方案几乎清一色是蓝牙SoC先把协议栈、芯片手册、烧录工具折腾明白就得花掉好几天最后连传感器都没读起来。这次我换了一个思路主控用STM32F103C8T6最小系统板姿态传感器用ADXL345测量加速度并完成计步心率模块用MAX30102体温用DS18B20三路数据实时刷到LCD1602上。整套源码和原理图都是验证过的可以直接套进你的工程。特别适合毕业设计快速出一版完整功能、电赛前练手或者纯粹想拆解手环计步和心率原理的人。1. 方案选型为什么用C8T6加三颗外设就能做手环1.1 主控选C8T6而不选蓝牙SoC的真实原因很多人一听“手环”就觉得必须上nRF52832或国产BLE芯片其实不是。手环的核心功能——计步、心率、体温、显示——没有一项强依赖蓝牙蓝牙只是数据上传的通道。如果你只想把传感器的逻辑跑通或者毕业设计只需要一个“能戴在手上、能看到数据的设备”用STM32F103C8T6是最省时间的路。C8T6这颗料现在十几块钱一块64KB Flash、20KB SRAM、72MHz主频整套驱动加计步算法编译出来大概30KB出头Flash还剩一半多SRAM用在传感器缓冲上也绰绰有余。CubeMX里勾几下就能生成工程标准库、HAL库资料铺天盖地遇到问题搜索出来全是答案。相比之下蓝牙SoC的IDE、协议栈、广播配置、OTA那一套新手自己啃至少要一周。等蓝牙方案调通了我这个项目早就跑起来并写完这篇总结了。1.2 传感器组合ADXL345、MAX30102、DS18B20传感器选型是这个项目的关键我用的是三颗数字接口传感器全部避开ADC采集和运放放大原因很简单数字接口直接用I2C或单总线读寄存器不会受模拟电路布线干扰手揉板也能稳定跑。功能型号接口关键参数地址/注意点计步ADXL345I2C/SPI三轴16位量程±2g到±16g内部32级FIFO7位地址0x53还有0x1D可选心率MAX30102I2C红光红外双LED反射式测量7位地址0x57需要紧贴皮肤体温DS18B20单总线9~12位精度-55℃到125℃数据线必须接4.7k上拉到3.3VADXL345做计步是手环项目里的成熟方案它自带FIFO可以缓存加速度数据主控不用频繁打断去读静态功耗也低。MAX30102是现在各种穿戴模块里最常用的脉搏传感器放手指上读波形很清晰贴在手腕上也能测就是佩戴松紧会影响幅值。体温这里用DS18B20而不是MLX90614红外测温主要原因是便宜、数字接口简单虽然接触式测温响应慢一点但作为健康监测demo完全够用。1.3 LCD1602显示内容少但调试价值高显示方案我没有选OLED而是用了LCD1602这个决定很多人不理解。OLED显示确实细腻但在这个工程里我需要的是一个能直接看出“传感器有没有数据”的调试窗口LCD1602的16列2行布局正好第一行显示心率和体温第二行显示步数一目了然。更关键的是LCD1602的HD44780驱动时序非常经典学会它之后再去驱动字符屏、段码屏甚至自己画LCD驱动都会顺手很多。如果你手里只有OLED也可以参考这篇的思路把显示驱动层换成SSD1306即可上层逻辑不需要动。2. 原理图设计最小系统与引脚分配哪些雷不能踩2.1 直接买最小系统板还是自己画最小系统我给这个项目画了两版原理图第一版是基于现成C8T6最小系统板的外设扩展图第二版是完整的独立最小系统。如果你是快速验证直接用最小系统板加杜邦线就能跑如果是要做作品交作业建议自己把最小系统画进PCB里。独立最小系统需要注意四个部分8MHz晶振配两个20pF负载电容复位引脚NRST接10k上拉和100nF电容到地BOOT0接10k下拉保证从Flash启动电源用AMS1117-3.3把5V转成3.3V。每个电源引脚旁边放一个100nF去耦电容这是很多自制板子SWD下载失败的根源。我遇到过不少人在洞洞板上焊接后程序能烧录但跑不起来十有八九是复位电容选得太大复位时间过长导致上电后一直停留在复位状态。一般NRST对地电容100nF就够了别手一抖换成10uF。2.2 引脚分配一条I2C总线上挂三个设备这个项目的引脚分配很有讲究我把ADXL345和MAX30102都挂在I2C1上SCL接PB6SDA接PB7两根线各接一个4.7k上拉到3.3V。为什么挂在同一条总线上因为两个传感器地址不冲突省引脚而且软件I2C可以统一用一个读写函数。外设引脚说明I2C1 SCLPB6ADXL345 MAX30102 共用4.7k上拉I2C1 SDAPB7ADXL345 MAX30102 共用4.7k上拉DS18B20PA4单总线4.7k上拉到3.3VLCD1602 I2C同上I2C1PCF8574转接板地址0x27LCD1602并口备选PA0~PA6RS/RW/E/D4~D74线模式SWD下载PA13/PA14SWDIO/SWCLK下程序用这里有个容易被忽略的点LCD1602如果用的是PCF8574转接板它的I2C地址默认是0x27和传感器不冲突可以放心挂在同一条总线上。但如果你还挂了AT24C02之类的外部存储地址是0xA07位0x50和0x27错开仍然没问题。总之一条I2C总线上到底能挂多少设备取决于地址不冲突以及总线上拉电阻的强度挂三四个外设很正常。2.3 电源、下载与复位电路细节手环的供电方式我建议两种USB供电给AMS1117转3.3V或者3.7V锂电池给低压差LDO。如果用电池LDO要选静态电流小的型号比如XC6206P332MR空载电流只有1uA级别比AMS1117更适合电池设备。下载接口只用SWD四根线3.3V、SWDIO、SWCLK、GND不需要接复位引脚。C8T6内部自带bootloader如果不用ST-Link也可以用串口1的ISP下载PA9、PA10接USB转TTLBOOT0拉高进串口下载模式。不过日常调试用ST-Link方便得多还能在线看变量。原理图里还加了一颗LED指示灯接PC13这是最小系统板上自带的。很多人下载完程序后看到这颗LED在闪担心是不是板子坏了。其实只要程序里写了PC13翻转的代码它就会按节奏闪这是正常现象。CPU跑没跑起来看LED状态比猜强得多。3. 传感器驱动软件I2C为什么更稳寄存器配置怎么记3.1 软件I2C与硬件I2C的取舍工程里我全部用软件模拟I2C没用F103的硬件I2C外设。原因有三点第一F103的硬件I2C在标准库时代有Busy标志卡死的案例虽然HAL库修复了很多但还是有不少人栽过坑第二软件I2C的引脚可以任意指定方便PCB布线第三两个传感器加LCD转接板共用I2C总线软件模拟时时序完全可控排查问题少一半。软件I2C的核心就是SCL和SDA两个GPIO的翻转加上起始信号、停止信号、应答判断。写一个I2C_WriteReg函数先发设备地址写位再发寄存器地址最后发数据。读的时候多发一个设备地址读位在最后一个字节返回NACK。整套代码不到100行但稳定性完全够用。实测在400kHz频率下连续读ADXL345和MAX30102没有出现一次数据错乱。3.2 ADXL345三行配置就能进测量模式ADXL345的配置非常友好初始化只操作三个寄存器关闭待机模式进入测量模式要写POWER_CTL寄存器地址0x2D为0x08设置量程写DATA_FORMAT0x31为0x0B这是±16g、13位分辨率、右对齐设置输出速率写BW_RATE0x2C为0x0A对应100Hz输出。读加速度数据时从地址0x32连续读6个字节前两个是X轴低高字节接下来是Y轴最后是Z轴。每个轴合成一个16位有符号数乘以量程比例就能还原成g值。这里我提供一个检测I2C是否连通的技巧读ADXL345的DEVID寄存器地址0x00如果返回0xE5说明传感器活着、地址正确、线路没问题这是调试的第一步。3.3 MAX30102和DS18B20的驱动流程MAX30102初始化比ADXL345繁琐一点先读ID寄存器0xFE确认等于0x15再配FIFO配置寄存器0x08为0x4FFIFO几乎不清空中断在几乎满时触发模式配置寄存器0x09设0x03进入心率模式LED脉冲幅度寄存器0x0C和0x0D设为默认值。之后主循环里读FIFO数据寄存器0x04到0x07就能拿到红光和红外的ADC原始值。DS18B20是单总线协议时序要求比I2C严格。整个读取流程分四步拉低总线480us以上产生复位脉冲释放总线等70us检测器件存在脉冲发跳过ROM命令0xCC发转换命令0x44等750ms转换完成再发一次复位、跳过ROM、读暂存器0xBE最后连续读两个字节合成16位温度值。这里有坑DS18B20的时序是微秒级的用HAL_Delay这种毫秒级延时函数根本不行必须用基于SysTick的微秒延时或者干脆用for循环做近似延时。4. 计步算法不是算个加速度大小就能数步4.1 为什么固定阈值法计步不靠谱网上很多教程教你看合加速度的大小超过某个数值就算一步比如大于1.3g计一步。但实际佩戴测试会发现坐公交颠一下、手敲键盘、甩胳膊都会触发误计。原因在于手部运动不是只有走路这一种模式固定阈值无法区分真正的步态和随机抖动。我最初用简单阈值法测试正常走路50步计数结果87步多出来的是抬手和抖手腕的动作。后来改掉了算法计步误差基本控制在正负3步以内。所以如果要做穿戴计步不能只算合加速度大小必须加滤波和步态识别。4.2 滤波、动态阈值、步态时间窗三步算法我用的计步算法分成三层核心代码并不复杂。第一层是滑动平均滤波。每收到一个加速度样本就计算最近10个样本的合加速度平均值。这一步能把高频抖动、传感器噪声压掉大半避免把毛刺当成步峰。我在源码里用了一个环形缓冲区每来一个新样本就覆盖最旧的平均值的计算量很小。第二层是动态阈值检测。人体走路时加速度波形会周期性起伏静止时约1g迈步时先超过1g再低于1g。固定阈值最大问题是不同佩戴松紧、不同走路习惯导致波形幅值不同。动态阈值的做法是每400ms统计一次窗口内的最大值和最小值取它们的中值作为当前阈值。当前加速度从低于阈值穿越到高于阈值并且保持峰值超过120ms才记一次候选步。第三层是步态时间窗口过滤。正常成人步行频率大约每分钟100到130步对应每步间隔450到600ms。我把合法间隔放宽到300到1500ms。如果两次候选步间隔小于300ms判定为抖动手臂产生的毛刺丢弃大于1500ms判定为走路停顿也丢弃。只有连续出现3次以上合法间隔才正式计入步数。核心主循环大概长这样float acc_mag sqrtf(x*x y*y z*z); buf_push(filter_buf, acc_mag); float mag_avg buf_mean(filter_buf); if (mag_avg dyn_threshold) { if (peak_time 0) peak_time current_ticks; if (current_ticks - peak_time 100) peak_active 1; } else { if (peak_active) { interval current_ticks - last_step_time; if (interval 300 interval 1500) { step_count; last_step_time current_ticks; } peak_active 0; peak_time 0; } dyn_threshold (win_max win_min) / 2; }这里current_ticks由1ms定时器溢出提供win_max和win_min是最近400ms窗口内的加速度极值。整套逻辑跑在100Hz的采样频率下C8T6的72MHz主频处理起来毫无压力。4.3 参数标定拿串口波形盯着调算法写好后最花时间的是调参数。我的方法是把合加速度值和计步状态通过串口发出来用VOFA上位机画波形边走边看。正常迈步时合加速度波形会形成一个一个清晰的峰峰之间的间隔很均匀。这时候把动态阈值的窗口长度设400ms峰值持续时间设120ms计步结果最稳定。如果你手边没有上位机还有一个笨办法来回走50步看计数结果。偏多了说明阈值太低或者窗口太紧偏少了说明阈值太高或者峰值持续时间太长。多试几组找到一个误差在5步以内的参数组合。4.4 计步数据的断电保护步数是存储在RAM变量里的断电就清零。如果希望手环重启后步数还在最简单的方案不是外挂EEPROM而是用STM32内部Flash的最后一个扇区把步数数值写到固定地址每次计步变化时擦写一次。注意Flash擦写寿命大概一万次频繁计步会很快耗尽所以不能每走一步就写一次正确做法是每累计10步写一次或者每分钟写一次。5. LCD1602显示初始化时序里全是坑5.1 I2C转接板和并口直连二选一LCD1602有两种接线方式。一种是用PCF8574转接板只占I2C总线的SDA和SCL两根线适合手环这种空间紧凑的场景。另一种是直接用GPIO并口驱动RS、RW、E、D4到D7一共6根线接线多但时序直观方便学习HD44780协议。我的原理图里做了两套接口用宏定义切换。用PCF8574时需要注意数据位映射转接板的P0接RSP1接RWP2接EP3接背光P4到P7接LCD的数据D4到D7。写数据时要分两次发送高四位和低四位在高四位发送后把E引脚拉高再拉低产生一个下降沿让LCD锁存数据。如果你发现屏幕只亮背光但不出字先检查背光位是不是被置高了P3要输出高电平背光才亮。5.2 初始化必须按HD44780手册的时序来LCD1602初始化写错一个延时屏幕上就是一片黑块。正确的初始化流程是上电后等40ms以上发送功能设置命令0x30三次第一次等4.1ms以上第二次等100us以上第三次可以短延时然后切换成4线模式发送0x20接着设置显示参数0x284线、2行、5x8点阵再发显示开关0x0C开显示不显示光标最后清屏0x01。很多人初始化后出现乱码原因通常是上电后第一个延时不加或者把三条0x30命令当成一条写了。LCD1602上电需要内部自检必须从头等够时间。我实测用PCF8574转接板初始化时每个命令之间加5ms延时最保险虽然慢一点但不容易翻车。5.3 显示布局与刷新策略我把显示分成两行第一行显示心率和体温第二行显示步数和状态。格式固定为HR: 072bpm T:36.5C STEP:01234 NORMAL心率是整数体温保留一位小数步数用四位补零显示。刷新时不需要每帧清屏只在数值变化时才重写对应位置否则屏幕会闪。我测试过用I2C转接板刷新整个屏幕大概要几毫秒主程序每500ms刷新一次就够不会影响计步的实时性。心率值不是一直能测出来的刚开机时MAX30102还没检测到脉搏波形所以界面上要设计一个状态位。检测到稳定的心率波形前显示“--”心率算法连续计算5秒后才把结果刷上去这个细节让整个显示看起来专业很多。6. 调试实录这四类问题占了90%的排障时间6.1 I2C扫描不到设备先查上拉电阻和地址我在项目调试中用STM32写了一个简单的I2C地址扫描函数循环向0x01到0x7F发送地址读位看哪个地址有ACK。第一次扫描时ADXL345的0x53和MAX30102的0x57都能扫到但LCD转接板死活不出来。最后发现是转接板模块上的地址跳线默认A0A1A2全是高实际地址不是0x27而是0x3F把扫描程序里的目标地址改成0x3F立刻通了。所以遇到扫描不到设备先别怀疑代码看看模块引脚跳线再确认SDA和SCL上有没有上拉电阻。F103内部上拉太弱不能替代外部4.7k。6.2 DS18B20读回85℃的真相DS18B20有个经典现象刚上电读温度返回85℃而不是环境温度。这不是传感器坏了而是它的上电复位值就是85℃。如果反复读到85℃说明转换命令根本没执行成功或者复位时序有问题。这类问题的根源通常是三条上拉电阻没接、杜邦线太长导致寄生电容过大、延时函数不够精确。我排查时发现是第三种。当时用了一个自己写的延时函数没校准标称500us实际只延时了200usDS18B20复位不成功。后来用逻辑分析仪看时序把微秒延时校准到±10us误差范围内温度恢复正常。6.3 LCD1602乱码和黑块的排查顺序屏幕上全是方块先调对比度。LCD1602的V0引脚需要接一个电位器到GND阻值1k到10k之间调节到字符清晰为止。很多模块出厂时对比度调得太极端直接上电显示黑块。排除了对比度还乱码再看初始化函数里的延时。用PCF8574转接板时顺序发送高位和低位之间必须加至少1ms延时。最后检查接线并口模式下RS和RW接反是乱码的高发原因。6.4 ST-Link下载不运行和PC13闪烁疑问如果用ST-Link下载后程序没有自动运行先检查BOOT0是否拉低。BOOT0悬空时内部也可能是低但最可靠的方式是接一个10k下拉电阻。还有一个容易忽略的点最小系统板的复位电容如果取得太大ST-Link下载完成后CPU会一直停在复位状态表现为程序不运行。解决方法是把复位电容降到100nF以内或者下载后手动按一下复位按键。那些担心PC13指示灯闪烁是不是板子坏了的同学可以放心C8T6最小系统板板载LED接在PC13上程序里写了这个引脚的翻转逻辑下载后闪烁说明代码在里面正常跑。如果想让LED彻底熄灭把对应的GPIO配置成推挽输出并输出低电平即可。7. 重新审视几个关键决定显示、I2C和测温方案7.1 LCD1602确实过时但它最适合这个阶段的验证整套工程做下来我对LCD1602的评价是显示内容少、视觉效果一般但调试价值极高。它的字符型驱动逻辑非常直观每一步都在告诉你“数据到底有没有送进去”。相比之下OLED虽然美观但引脚接口和显存操作多一层新手一开始就被那部分内容带偏容易忽略传感器本身的问题。如果你后续要做产品原型再把显示层往OLED或TFT上平移驱动分离后改动不大。7.2 软件I2C的“笨办法”反而带来了最大灵活性用软件I2C时有同行提醒我F103硬件I2C其实也没那么难用HAL库已经稳定了。但在这次多设备共用总线的场景下软件模拟的调试速度确实快很多。出问题时拿示波器点一下GPIO就能看到波形不需要翻I2C外设的状态寄存器。而且后面想换引脚、换板子软件I2C的代码基本不用改只改引脚定义。对于教学和验证项目我强烈建议用软件I2C先把功能跑通再研究硬件I2C的高效性。7.3 体温方案DS18B20够用换代方向在MLX90614DS18B20作为体温采集最大的问题是响应慢和接触误差。因为它是接触式测温传感器探头需要和皮肤保持良好接触手环佩戴松一点读数就会偏低。实际测试中DS18B20在手腕上稳定读数需要佩戴2到3分钟误差大约在0.5到1℃之间这是物理接触条件决定的。如果你想把体温做成红外非接触式可以换MLX90614它通过I2C直接输出目标温度和环境温度不用接触皮肤响应快很多。代价是价格高好几倍而且对准角度对读数影响大。这个项目里我保留DS18B20主要是控制成本也让整个传感器链路清一色数字接口信号链简单。做完这套工程后我最大的体会是手环的难点不在硬件选型而在数据处理。ADXL345的驱动半小时能写完但让计步数在真实走路时不飘调算法占了两天时间。所以在抄源码和原理图的同时建议你重点把计步那一段的滤波和步态识别逻辑吃透。这个思路一旦建立起来后面再做温湿度记录、运动检测都是一层窗户纸的事。本文还有配套的精品资源点击获取