来,先说结论:这台按摩椅完整的硬件方案和固件代码我已经跑通了,原理图根据实际打板验证改过三版,STM32这边的代码也过了状态机全功能自测。今天直接把所有设计思路、电路细节、代码逻辑和掉坑经历全盘写出来,你想照着复现一台,或者只是拿来做毕业设计交差,这一篇都够用了。
这个项目是什么,一句话概括:用STM32做主控,通过PWM控制多路直流减速电机,分别实现揉捏、敲击、行走和椅背调节四大类按摩动作,再用压力传感器、限位开关和霍尔测速组成反馈闭环,整机具备多模式切换、定时停机、过流保护和急停功能。它适合谁?电子、嵌入式、自动化、机械电子方向做毕业设计的学生,以及想自己动手改造按摩椅、学习电机控制和嵌入式系统整合的硬件爱好者。
1. 项目整体设计与方案选型
做按摩椅这种项目,第一个问题不是“用什么芯片”,而是“这台椅子的机械结构打算怎么做”。因为机械结构直接决定了你要驱动几个电机、每个电机什么功率、需要几路独立控制通道,这个选错后面所有电路和代码全都要返工。
1.1 按摩椅的核心机械结构与电机配置
家用按摩椅的基本机械结构分为四大块:背部揉捏机芯、背部敲击机芯、导轨行走机构、椅背角度调节机构。按这个配置,最少需要4个电机,我实际做的是5电机方案,多出来一路给了脚部气囊气泵(气压按摩也是按摩椅的刚需功能)。
- 揉捏电机:直流减速电机,带蜗轮蜗杆减速箱,转速低扭矩大,模拟手指揉捏动作
- 敲击电机:直流电机带动偏心轮,产生周期性敲击力,模拟捶背动作
- 行走电机:沿导轨上下移动机芯,需要能够正反转
- 推杆电机:调整椅背角度,线性执行器,一般自带限位开关
- 气泵电机:给气囊充气,配合电磁阀控制气囊膨胀频率
我需要强调的是,揉捏电机和敲击电机是最难控制的两位。因为揉捏动作需要连续的力道变化,敲击动作需要转速稳定在特定区间,这两个对PWM调速精度和驱动电路的电流能力都有要求。
1.2 STM32选型理由与具体型号参数
整个控制系统的核心我选了STM32F103C8T6,也就是大家常说的“蓝色药丸”最小系统板的核心芯片。选它的理由很实际:一是学生党能拿到的资料最多,出了问题查起来方便;二是主频72MHz、Flash 64KB、RAM 20KB,跑电动按摩椅这种级别的逻辑绰绰有余;三是它的定时器资源有4个,正好够我分配PWM通道。
具体到每个电机怎么分配定时器资源,我当时是这样规划的:
| 功能模块 | 定时器 | 通道 | 用途 |
|---|---|---|---|
| 揉捏电机 | TIM1 | CH1 | PWM调速 |
| 敲击电机 | TIM1 | CH2 | PWM调速 |
| 行走电机 | TIM2 | CH1+CH2 | 正反转+PWM |
| 推杆电机 | TIM3 | CH1+CH2 | 正反转 |
| 气泵电机 | TIM3 | CH3 | 开关控制 |
| 霍尔测速 | TIM4 | CH1 | 输入捕获 |
这个分配方案的好处是,PWM信号和速度采集完全独立,互相干扰最小。如果你抄这个方案,不用改任何引脚映射,直接照做就能跑。
1.3 为什么不用现成按摩椅控制板
这里我要多说一句很多人忽略的问题。淘宝上几十块钱就能买到按摩椅专用的控制板,为什么还要自己用STM32从零做?答案不是“为了毕业设计必须自己做”,而是现成控制板对你来说就是个黑盒。
你需要的能力是:拿到一张原理图能看懂全国电流路径,拿到一份源码能解释每个外设初始化为什么这么配,遇到电机不转能快速定位是驱动芯片挂了还是PWM没输出。这些能力,直接买现成板子学不到。自己做一遍,元器件选型、电路设计、固件调试、EMC处理,这一整套流程走完,嵌入式开发的门就真正进去了。
我自己第一版也是买现成板子拆着研究,看完就后悔了——板子上全是丝印模糊的两层板,主控芯片还被打磨了型号,想逆向都难。自己做,所有东西都是透明的。
2. 硬件原理图设计与核心电路拆解
原理图设计是整个项目的基础。很多同学喜欢先写代码后画图,这是错的。硬件平台不稳定,你写再多代码都是在沙地上盖楼。
2.1 电源系统设计:多级电压架构
按摩椅的电源系统看起来简单,实际上藏着整个项目里最容易烧板子的坑。整机外部供电我选择24V 5A开关电源,输入经过一个自恢复保险丝(PPTC,额定5A)和一个共模电感,然后分三路:
- 24V直接进电机驱动芯片,这是电机主电源
- 24V经过MP1584降压模块降到5V,给STM32、传感器和逻辑电路供电
- 5V经过AMS1117-3.3降到3.3V,给主控芯片内核和ADC参考电压
为什么要搞三级电压?因为电机启动瞬间电流很大,24V总线电压会被拉低,如果主控和电机共用一级电源,电压跌落会导致单片机复位。实测下来,不隔离电源时电机一启动,示波器上看5V纹波能到800mV,单片机随机死机。加了MP1584之后纹波降到50mV以内。
MP1584这个降压模块选型上有讲究,要选带可调电位器版本,把输出电压精确调到5.0V,不要用固定5V版本,因为固定版本实际输出可能只有4.7V左右,STM32虽然也能跑,但ADC采集精度会受影响。
2.2 电机驱动电路:H桥方案与关键参数计算
电机驱动我最终选了DRV8873这颗芯片,单颗芯片集成双H桥,最大电流3.5A,峰值6.5A,支持串联电流感应。为什么不用更便宜的L298N或者TB6612?因为按摩椅的揉捏电机堵转时电流能飙到4A以上,L298N内阻大发热严重,扛不住长时间运行。DRV8873的内阻只有约500毫欧,且内置过流保护和热关断。
驱动电路的参数设计重点在电流采样电阻。DRV8873的IPROPI引脚会输出与负载电流成正比的电流,我需要通过一个电阻将其转换为电压送到STM32 ADC读取。
计算过程是这样的:DRV8873的电流感应比典型值为580μA/A,我选的采样电阻是3.3kΩ,所以IPROPI引脚的电压 = 电机电流 × 580μA/A × 3.3kΩ = 电机电流 × 1.914V/A。当电机电流为2A时,采样电压约为3.8V,超过了STM32的ADC满量程3.3V,所以我在采样电阻后再加一个分压电阻网络(10kΩ和4.7kΩ),将电压按比例缩到ADC安全范围内。
安全保护方面,每路电机的24V供电端都并了一个续流二极管(SS34肖特基),防止电机反向电动势击穿驱动芯片。同时在电机两端并联一个0.1μF瓷片电容和一个10nF/1kV高压电容,这个组合对抑制换向火花产生的EMI特别有效,实测过了这个项目后面跑EMC预测试时,辐射干扰轻松通过。
2.3 传感器接口电路设计
按摩椅的控制不能是纯开环的,需要传感器做闭环反馈。我这个项目里一共用了三类传感器,每类的接口电路处理方式完全不一样。
限位开关用的是机械微动开关,接法是最简单的:一端接GND,一端接STM32的GPIO内部上拉。常态下GPIO读到高电平,开关被触发时读到低电平。要注意的是这种机械开关必须加软件消抖,我实测抖动时间大概在5-10ms,所以程序里做了20ms的连续采样确认。
压力传感器(薄膜压力传感器RP-C18.3-ST)用来检测按摩力度。薄膜传感器本质是一个可变电阻,压力越大电阻越小。接口电路做法是用一个10kΩ固定电阻和传感器串联成电阻分压器,分压点接STM32的ADC引脚。当传感器受到压力,它的电阻从无穷大降到几十千欧,分压点的电压就从接近0V升到接近3.3V。这个电路的好处是简单可靠,缺点是线性度一般,但作为力度阈值判断完全够用,不需要精确的标定曲线。
霍尔传感器用来测电机转速,我用的是3144E开关型霍尔,配合电机转轴上粘的一个小磁钢。霍尔传感器输出是集电极开路,所以需要接一个10kΩ上拉电阻到3.3V。电机每转一圈,霍尔输出一个脉冲,STM32用定时器输入捕获模式测量两个脉冲之间的时间间隔,换算成转速。
2.4 原理图绘制与PCB布局经验
原理图我用的立创EDA画的,这是一款免费的国产EDA工具,上手快,元件库也全。这里分享几个我这次画图踩坑后的调整经验。
第一,原理图分页组织要有逻辑。我分成了电源页、主控最小系统页、电机驱动页、传感器接口页、人机交互页,每页右上角标注了页间网络连接关系。这个习惯非常重要,因为评审老师拿到原理图第一眼看的不是某个具体电路,而是整张图的组织结构和逻辑清晰度。
第二,STM32F103C8T6的VDDA引脚必须单独滤波。很多最小系统设计把VDDA和VDD直接短接,这对普通跑马灯程序没问题,但对ADC采集精度要求高的项目会有明显影响。我在VDDA和VDD之间串联了一个10Ω电阻,并在VDDA引脚对地接了一个1μF和一个0.1μF电容并联,这样ADC参考电压的纹波会小很多。
第三,PCB布局上电机驱动部分和主控部分要分区域铺地。按摩椅的电机是大功率干扰源,驱动电路的GND和主控电路的GND在PCB底层通过一个0欧电阻单点连接,避免大电流回路干扰逻辑电路。
3. 软件架构与源码实现
硬件平台稳定之后,软件架构就是项目的灵魂。这个项目的源码我分了四个层次来写:底层驱动层、中间抽象层、应用逻辑层、人机交互层。
3.1 程序整体架构设计
底层驱动层直接操作STM32寄存器,每个外设写一个独立的驱动模块,比如pwm.c、adc.c、gpio.c、timer.c。中间抽象层把这些驱动封装成具有业务含义的接口,比如motor.c提供Motor_SetSpeed(motor_id, speed)这样的函数,不管底层是PWM还是GPIO翻转,上层根本不关心。应用逻辑层实现按摩模式的状态机,比如自动模式、手动模式、定时模式。人机交互层处理按键输入和数码管/LCD显示。
这个分层的核心价值是“可测试性”。我做调试的时候,先把底层驱动全部写好,单独验证PWM波形和ADC读数;然后写中间抽象层的单元测试,用不同参数组合调用接口看输出是否符合预期;最后才把应用逻辑写上来。这样出了问题不会一头雾水,而是能很快定位到具体某一层。
3.2 电机PWM控制与调速逻辑
电机调速采用的是PWM占空比控制,直流减速电机的转速和PWM占空比基本成正比例关系。STM32的定时器产生PWM信号,通过改变比较寄存器的值来改变占空比。关键点在PWM频率的选择。我用的是20kHz,人耳听不到,也避开了机械共振频段。
PWM初始化代码的关键部分:
void PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period = 899; // 20kHz @ 72MHz主频 TIM_TimeBaseStructure.TIM_Prescaler = 3; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC2Init(TIM1, &TIM_OCInitStructure); TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM1, ENABLE); TIM_Cmd(TIM1, ENABLE); }要注意TIM1是高级定时器,用PWM输出时必须调用TIM_CtrlPWMOutputs(TIM1, ENABLE)打开主输出,否则引脚上什么都测不到。这个问题我当年第一次调的时候卡了整整半天。
调速逻辑的实现,我用了加速度曲线的方式。电机直接从一个转速突变到另一个转速,会有明显的机械冲击,还会导致电流尖峰。我的做法是:转速目标变化时,以每10ms增加/减少占空比1%的速率平滑过渡,这样揉捏手感的细腻度明显提升,电机的噪音也变小了。
3.3 按摩模式状态机设计
按摩模式是整个程序的核心业务逻辑。我设计了一个状态机,状态定义如下:
| 状态 | 说明 | 触发条件 |
|---|---|---|
| IDLE | 待机状态 | 上电初始化 / 按下停止键 |
| MASSAGE_NEAD | 揉捏模式 | 按下揉捏键 |
| MASSAGE_PERCUSS | 敲击模式 | 按下敲击键 |
| MASSAGE_COMBO | 组合模式 | 按下组合键 |
| TIMER_RUNNING | 定时运行中 | 以上模式启动 |
| FAULT | 故障保护 | 过流/超时/限位触发 |
状态机的实现用的是经典switch-case:
typedef enum { IDLE, MASSAGE_NEAD, MASSAGE_PERCUSS, MASSAGE_COMBO, TIMER_RUNNING, FAULT } SystemState; SystemState currentState = IDLE; SystemState nextState = IDLE; void StateMachine_Task(void) { switch (currentState) { case IDLE: if (Key_GetPressed(KEY_NEAD)) nextState = MASSAGE_NEAD; break; case MASSAGE_NEAD: Motor_SetSpeed(MOTOR_NEAD, neadSpeed); if (Key_GetPressed(KEY_STOP)) nextState = IDLE; if (Key_GetPressed(KEY_COMBO)) nextState = MASSAGE_COMBO; if (Safety_CheckFault()) nextState = FAULT; break; case MASSAGE_PERCUSS: Motor_SetSpeed(MOTOR_PERCUSS, percussSpeed); if (Key_GetPressed(KEY_STOP)) nextState = IDLE; if (Key_GetPressed(KEY_COMBO)) nextState = MASSAGE_COMBO; if (Safety_CheckFault()) nextState = FAULT; break; case FAULT: Motor_StopAll(); LED_SetFault(); if (Key_GetPressed(KEY_RESET)) nextState = IDLE; break; } currentState = nextState; }状态机的关键好处是:任何时刻系统都知道自己“在哪个状态”、可以“跳转到哪些状态”,不可能出现按键按下后行为不可预期的情况。这是嵌入式程序设计的核心思维方式。
3.4 限位保护与过流检测实现
安全保护是按摩椅绝对不能省的部分。我在代码里实现了三级保护。
第一级是软件限位:行走电机和推杆电机都有行程范围,程序在运行时会持续记录当前的位置(通过霍尔脉冲计数),当位置到达预设的阈值前开始减速,到位后立即停止并锁定电机。这一级保护的好处是平滑,不会哐当一声撞到机械限位块。
第二级是硬件限位:机械限位开关串在电机控制回路里,无论程序处于什么状态,只要机械结构碰到了限位开关,电路就物理断开电机电源,这是程序设计被绕过时的最后防线。
第三级是过流保护:DRV8873的IPROPI引脚输出的电流感应信号实时送入STM32 ADC,程序每10ms采样一次,如果连续3次检测到电流超过保护阈值,就立即进入FAULT状态,停止所有电机并点亮故障指示灯。连续3次判断是为了防止瞬时干扰导致误动作。
3.5 无线遥控与按键双重控制
按摩椅如果没有遥控器,用起来会非常不方便,但毕业设计如果只做遥控器又太单薄。我用的方案是:本地按键面板 + 433MHz无线遥控,两者并存。
本地按键是矩阵扫描方式,4个按键接在GPIO上带内部上拉。无线遥控部分用的是433MHz超外差接收模块加PT2262编码芯片的方案,这个方案比2.4G模块便宜,且协议简单,不用在STM32上跑复杂的协议栈。接收模块的数据引脚直接接在STM32的外部中断引脚上,收到信号后解析按键编码。
这里有个坑要提醒:433MHz接收模块的数据引脚在没有信号时会输出随机电平,如果直接接在STM32的可中断引脚,会导致频繁进入中断。解决方法是把接收模块的输出接一个RC低通滤波器(10kΩ电阻串联、100nF电容并联),滤掉高频毛刺,然后在中断程序里加一个500μs的连续高电平确认条件。
4. 常见问题与排查技巧实录
说实话,这个项目做得过程中遇到最多的问题不是原理设计错误,而是各种让人抓狂的“低级问题”。我整理几个高概率遇到的坑,写了具体的排查流程,能帮你少走大量弯路的坑。
4.1 电机不转但PWM波形正常的排查
这是我遇到过的让人最想砸电脑的问题。PWM信号用示波器看完全正常,占空比、频率都对,但电机就是纹丝不动。排查步骤:
先测量驱动芯片的电源引脚,确认24V是否真正到达了芯片的VM引脚。我遇到过的是PCB打样回来,芯片电源引脚虚焊,看起来是焊接了,实际上没有导通。解决办法是拿万用表蜂鸣档测芯片每两个相邻引脚之间是否有不应该存在的导通(排除桥连),再用放大镜观察焊点是否有“球状”痕迹。
再测使能引脚的电平。DRV8873的EN引脚必须拉高才能输出,我习惯用单片机的GPIO控制这个引脚,但有些时候初始化顺序不对,GPIO还没拉高定时器就开始输出PWM了,导致驱动芯片一直处于关断状态。程序中要在PWM初始化前先把所有使能引脚拉高。
最后测IPROPI引脚电压,如果这个引脚电压为0,说明驱动芯片内部可能没有输出电流,这时候基本可以确定芯片已经烧了。DRV8873烧毁很多时候是因为电机堵转时间过长没有及时触发保护,所以过流保护的阈值一定要设置得合理,不能太小也不能太大。我最终设定的保护阈值是3.2A,连续200ms超限触发保护。
4.2 电源电压跌落导致单片机复位问题
具体现象是:电机一启动,数码管就闪烁或者直接重启。用示波器测5V电源轨,能明显看到电压跌落。
这个问题的根因有两个。第一是电源的瞬态响应不够快,电机启动瞬间电流突变,开关电源的反馈环路来不及调整。解决办法是加大直流母线端的电解电容容量,我从原来的470μF增加到了2200μF,效果立竿见影。
第二是MCU供电脚的高频去耦不足。STM32在电机启动瞬间如果从总线上吸取大电流,供电脚电压会瞬间跌落超过允许范围。我在STM32的VDD引脚旁边加了0.1μF和1μF电容并联,直接放在芯片旁边,距离控制在3mm以内。注意电容一定要靠近芯片引脚放,太远了就没用了。
4.3 ADC采样值跳动剧烈怎么处理
压力传感器的ADC值在电机运行时跳动得非常厉害,跳动范围能达到500个数。最开始我以为是传感器坏了,后来用示波器测ADC引脚的波形,发现上面叠加了大量毛刺。
原因分析:电机PWM开关会产生高频干扰,通过空间辐射和传导耦合到ADC引脚。解决方法是三管齐下:
硬件上在ADC引脚加一个100nF的滤波电容,把高频分量旁路到地。软件上做多重采样平均,连续读取20次ADC值,去掉最大值和最小值,再取平均。第三是ADC的采样时间设置长一些,将ADC采样周期设置为239.5个ADC时钟周期,这个值在STM32的ADC_SampleTime配置里可以设定。
这样处理之后,ADC的跳动范围缩小到了20个数以内,力度检测的稳定性大大提高。
4.4 霍尔测速信号不稳定排查
霍尔转速检测信号时有时无,导致程序判断电机转速异常而误报故障。排查发现,霍尔传感器和磁钢的安装距离太远,导致信号幅值不足,同时磁钢固定得不够牢固有松动,导致每个周期的信号强度不一致。
解决办法是:重新调整霍尔传感器和磁钢的距离(最佳感应在2-3mm左右),用环氧树脂把磁钢固定在转轴上。同时,我在程序中增加了速度信号“失锁检测”:如果连续1秒没有收到霍尔脉冲,则判定为测速传感器异常,停止电机进入保护状态。
4.5 常见问题速查表
| 现象 | 可能原因 | 排查顺序 |
|---|---|---|
| 电机不转 | 驱动芯片供电/使能/PWM | 先量VM电压,再量EN电平,最后看PWM波形 |
| 单片机死机复位 | 电源跌落/干扰 | 示波器看5V跌落,检查大电容和去耦电容 |
| ADC跳动大 | 地回路干扰/采样时间不足 | 硬件加电容,软件多次采样,检查采样时间配置 |
| 信号无法遥控 | 波特率/编码不匹配/干扰 | 先短接接收模块DATA和GND看能否进入接收中断 |
| 电机噪声大 | PWM频率落在谐振频段 | 试不同PWM频率(12k到25k之间),找到噪声最小的点 |
| 限位开关失灵 | 地弹/机械抖动 | 确认开关接线正确,加软件消抖,检查上拉电阻是否虚焊 |
| 按键无响应 | GPIO初始化错误/矩阵扫描逻辑问题 | 逐个GPIO拉低测试,确认按键按下时电平变化 |
5. 从毕业设计到产品化的扩展思路
如果你做完了这个项目,想进一步优化它,有几个方向可以考虑,这也是我接下来准备做的事。
5.1 增加蓝牙APP控制
当前的控制方式是有线按键面板和433M遥控,体验已经能用但不够现代。下一步我计划加一个HC-08蓝牙模块,和STM32的USART连接,通过手机APP发送控制指令。这个扩展在硬件上非常简单:蓝牙模块是TTL串口电平,和STM32的串口直连即可,注意要共地。软件方面只需要在串口中断里解析一个简单的帧协议,比如帧头0xAA + 命令字 + 数据 + 校验和。
这里提醒一下,如果选HC-05/HC-06模块,要注意工作电压是3.3V还是5V,部分模块可以直接用5V供电但串口逻辑电平是3.3V,不能直接用5V的TTL电平连接STM32,容易烧引脚。
5.2 增加语音控制
智能家居体验的核心交互是语音。目前我调研下来最合适的方案是SU-03T离线语音识别模块,它支持自定义唤醒词和命令词,不需要联网,识别准确率安静环境下可以达到95%以上。模块通过UART和STM32通信,语音识别结果通过串口输出指令帧,STM32解析后执行对应操作。
这个扩展的价值在于,很多人对按摩椅的核心诉求就是“躺下之后不想要再按任何按钮”,语音控制在按摩椅场景下不是噱头,而是真正能提升体验的功能。
5.3 数据记录与云端同步
在STM32上把每次按摩的时长、按摩模式、平均力度记录下来,存入外部Flash(比如W25Q64),然后通过ESP8266模块连接WiFi,定时上传到云端。这样可以在手机端查看历史按摩数据,也可以基于个人偏好做推荐模式。
这个功能的技术难点在于嵌入式端的存储管理和通信协议设计,我可以先把数据结构定义好,用串口输出JSON格式数据,再通过ESP8266透传到服务器。
5.4 电机控制算法的闭环升级
现在用的是开环PWM调速,电机负载变化时转速会有波动。下一步可以加PID闭环控制:霍尔传感器实时检测转速,和目标转速比较,通过PID算法调整PWM占空比使实际转速稳定在目标值附近。
PID参数整定方法我建议用“先比例,后积分,再微分”的顺序,先只调P,让系统振荡,然后加大P到振荡消失且响应足够快;然后加I消除稳态误差;最后加D改善动态响应。我在实验板上用简单的方法调过:给定一个目标转速,用串口输出实际转速的实时曲线(通过串口绘图工具),根据曲线形状调整参数。
5.5 成本控制与硬件优化
当前方案的整机物料成本大约在500元左右,其中大头是电机和电源。如果要做更便宜的量产版本,可以考虑用合封MOS的驱动方案替代DRV8873,比如用分立式N+P MOS搭建H桥,成本能降一半,但设计难度和控制逻辑复杂度会上升,保护功能也没有集成芯片那么完善。
如果不追求极致的成本压缩,我建议维持集成驱动芯片方案,因为可靠性比省那几十块钱重要得多,尤其是电动按摩椅是直接接触人体的设备,安全永远是第一位的。
6. 实操过程记录与硬核配置清单
说了这么多原理和逻辑,最后把整个项目从零到一的完整实操流程和物料清单列出来,方便你真的动手做的时候有章可循。
6.1 物料清单
| 器件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控芯片 | STM32F103C8T6 | 1 | 也可以用最小系统板 |
| 电机驱动 | DRV8873 | 2 | 双H桥,可驱动4路电机 |
| 揉捏电机 | 24V直流减速电机(1:50) | 1 | 扭矩要求高 |
| 敲击电机 | 24V直流电机(偏心轮) | 1 | 转速800-1500rpm |
| 行走电机 | 24V直流减速电机(1:20) | 1 | 需正反转 |
| 推杆电机 | 24V线性推杆(行程100mm) | 1 | 自带限位 |
| 气泵 | 24V微型气泵 | 1 | 气压按摩用 |
| 降压模块 | MP1584可调版 | 1 | 24V转5V |
| LDO | AMS1117-3.3 | 1 | 5V转3.3V |
| 薄膜压力传感器 | RP-C18.3-ST | 2 | 背部和腿部各一个 |
| 霍尔传感器 | 3144E | 3 | 测速用 |
| 限位开关 | 微动开关 | 4 | 行程保护 |
| 无线接收模块 | 433MHz超外差接收 + PT2262 | 1 | 遥控接收 |
| 按键 | 轻触开关 | 4 | 面板控制 |
| 显示 | 数码管 TM1637模块 | 1 | 时间/模式显示 |
| 电源 | 24V 5A开关电源 | 1 | 整机供电 |
| PCB | 双层板 约10cm×10cm | 2片 | 打样备用 |
6.2 实现步骤时间线
- 第1-2周:方案设计,机械结构调研,电机选型,绘制原理图初版
- 第3周:原理图评审修改,PCB布局布线
- 第4周:PCB打样等待期间,写底层驱动代码(GPIO、PWM、ADC、UART串口驱动)
- 第5周:焊接调试电路板,先验证电源、再验证最小系统,然后逐路验证电机驱动
- 第6-7周:写应用层逻辑(状态机、按键扫描、传感器读取、安全保护)
- 第8周:整机联调,解决干扰问题、PID参数整定
- 第9-10周:外观组装、稳定性测试、写技术文档
我个人的习惯是:电路板打样的10天等待时间绝对不要闲着,所有底层驱动代码在这段时间全部写完并单独验证逻辑,这样板子一到手就能飞快推进。
6.3 焊接与调试顺序
每次焊接新的电路板,我都有一个固定的顺序,这个顺序可以帮你快速定位问题:
先焊电源部分,焊完不着急焊其他器件,上电用万用表确认24V、5V、3.3V三路电压都正确。然后焊STM32最小系统,用SWD接口连接ST-Link,下载一个最简单的闪灯程序验证主控能跑。接着焊一个电机驱动通道,只焊一路,验证PWM到电机输出的完整链路没问题,再焊剩下的驱动通道。最后焊传感器接口和通信接口。
这个顺序的逻辑是:每次只引入一个“新变量”,出问题的时候嫌疑范围最小。我见过很多同学一口气把所有元件全部焊完,然后上电冒烟,根本不知道是哪里短路,排查起来极其痛苦。
6.4 软件工程的组织方式
代码我是用Keil MDK 5开发的,工程组织结构按模块分文件夹:
Project/ ├── Core/ │ ├── gpio.c/h │ ├── pwm.c/h │ ├── adc.c/h │ ├── timer.c/h │ ├── uart.c/h │ └── exti.c/h ├── Middleware/ │ ├── motor.c/h │ ├── key.c/h │ ├── sensor.c/h │ ├── remote.c/h │ └── display.c/h ├── App/ │ ├── state_machine.c/h │ ├── safety.c/h │ └── main.c └── Drivers/ ├── CMSIS/ └── STM32F1xx_HAL_Driver/每个模块文件的职责边界要清晰,比如motor.c只负责控制电机转速和方向,不关心具体用哪个定时器;而pwm.c负责将PWM通道抽象成统一的接口。这样后续做扩展功能时,改动集中在Middleware和App层,底层不需要动。
关于代码风格,我强烈建议变量命名使用“模块_功能_描述”的格式,比如motor_u8Speed这种带类型信息的命名方式,虽然写的时候麻烦一点,但调试查错的时候节省的时间远超这点成本。
最后再分享一点个人经验:这个项目最容易让你崩溃的不是某个技术难题,而是在联调阶段各种问题交织在一起时的挫败感。电机驱动和ADC互相干扰、遥控信号时好时坏、按键偶尔失灵,这些问题单独看都不难解决,但堆在一起显得非常劝退。我的建议是保持“一次只调一个环节”的原则,先用最简配置跑通基础功能,确认基础链路没问题,再逐步增加复杂功能。整个过程下来你收获的不只是一台按摩椅,而是一整套嵌入式系统开发的工程方法论。