尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于STM32的智能药盒设计与Proteus仿真:从原理图到实物

基于STM32的智能药盒设计与Proteus仿真:从原理图到实物 我家楼下王大爷七十多岁高血压、糖尿病、冠心病三样占全每天早中晚三顿药一次三四片分别装在不同盒子里。上个月我去看他他正对着四五个药瓶发愣——胰岛素打了降压药忘了降压药补上阿司匹林又多吃了一遍。看着他把药盒翻了个底朝天我跟他说我给你做一个不用脑子记事的盒子吧。这就是这个STM32开源智能药盒项目的由来。项目全套资料都在开源仓库里包含完整的C语言工程代码、硬件原理图、Proteus仿真工程照着资料搭一套出来就能实现定时语音提醒老人吃药、自动出药、子女远程知晓服药状态这些核心功能。适合三类人来折腾一是电子/嵌入式方向的在校学生做课设毕设二是想给家里老人做个实用设备但有点硬件基础的开发者三是准备做养老硬件产品的小团队。我断断续续做了十几版前前后后踩了不少坑。这篇文章把硬件选型、电路原理、核心代码思路、Proteus仿真避坑以及从开发板到能做给家人用的实物的完整路径全摊开来写希望能让你少走两个月弯路。1. 为什么智能药盒要自己用STM32做而不直接买成品很多人问我这个问题。市面上的智能药盒从几十块到上千块都有功能看起来也都写得很齐全。但如果你真正给老人用过就会发现大部分成品药盒卡在三个方面提醒方式不够硬闹铃响过就完了老人听不见或者听见了转头忘出药机构不可靠卡药、掉药、一次掉两粒的情况时有发生二次开发完全封闭想改个服药时间、加一路提醒、接一个短信通知模块只能看着壳子干瞪眼。用STM32自己做一套核心价值在于软硬件全部在自己手里。服药的业务逻辑不复杂但它是个典型的实时控制任务到了设定的服药时间盒盖打开、药粒掉落、蜂鸣器报警、屏幕上显示该吃哪一格药这一连串动作必须在几秒钟内协同完成。用STM32F103C8T6这颗芯片来做刚好卡在性能和成本的平衡点上——它主频72MHzFlash 64KBRAM 20KB跑一个裸机状态机加一个显示刷新绰绰有余芯片单价不到十块钱。另一方面这个项目非常适合拿来系统的练一遍嵌入式开发的完整链路从原理图设计到PCB绘制从Keil工程编写到实机调试从Proteus仿真验证逻辑到实物的结构组装。说句掏心窝子的话把这套流程走通你对整个嵌入式开发流程的掌控感会上一个台阶之后去做任何单片机项目心里都有底。我也见过用一种更笨的办法实现类似功能的方案用Arduino加一个12864液晶屏写死十几个if else判断时间。这东西当课设交上去能过但要谈系统还差得远。问题不在于Arduino本身不够好而在于它隐藏了太多底层细节——定时器怎么配置、中断优先级怎么协调、PWM输出频率和占空比怎么计算、IO口驱动能力够不够驱动舵机这些恰恰是嵌入式开发真正的基本功。用STM32从寄存器层面把这些东西捋一遍和调库调通完全是两种收获。2. 核心架构与硬件选型系统拆解后你会发现没那么复杂整个系统的功能需求听着唬人实际拆开就是几块主控负责大脑计时、逻辑判断、控制外围设备人机交互负责脸面让老人看得懂、按得动提醒与执行负责手脚让该发生的事准时发生状态感知负责反馈让系统知道药有没有被正确拿走2.1 各模块选型对比与最终选择主控芯片STM32F103C8T6选择这颗芯片几乎没有悬念。它在STM32家族里属于入门级但该有的资源一样不少3个USART、2个I2C、2个SPI、4个16位定时器、10个ADC通道、37个GPIO。在智能药盒这个项目里I2C接屏幕、PWM输出接舵机、GPIO接按键和传感器空闲资源还一大半。更关键的是这颗芯片的开源资料多到看不完遇到问题网上一搜基本都有解。显示模块0.96寸 OLEDI2C接口对老人来说屏幕不是越大越好关键是要清晰、低功耗。OLED的显示内容不需要背光黑色像素完全不耗电这对于需要常年插电或电池供电的药盒很重要。I2C接口只需要两根线SCL/SDA省IO口布线也方便。128x64的分辨率在0.96寸屏幕上显示汉字、符号、数字足够了——你可以用它显示当前时间、下一组服药倒计时、药格编号和日期。RTC时钟模块DS1302这里有个重要的设计决策。STM32芯片内部有RTC实时时钟但它依赖VBAT引脚外接电池保持计时且掉电后配置容易丢失。DS1302模块本身自带纽扣电池座断电后时间继续走重新上电不需要重新校准。对于药盒这类必须保证时间准确的设备独立的RTC模块是更稳的选择。DS1302走SPI-like的三线协议STM32用GPIO模拟即可。出药执行机构SG90 9g舵机药盒出药最常见的方案有两种电磁铁推杆和舵机旋转挡板。电磁铁的优点是响应快、动作干脆但发热严重、噪声大、控制逻辑需要额外的MOSFET驱动电路。舵机则开箱即用——PWM信号直接控角度305度范围内转动带动一个扇形挡板把药格出口转到指定位置。SG90扭矩1.8kg/cm带动轻质塑料挡板绰绰有余。提醒机构有源蜂鸣器 高亮LED有源蜂鸣器内部带振荡电路给它一个高电平就响不需要PWM驱动最大的好处是代码极简。提醒时的节奏设计反而更值得花心思——我的建议是响三秒、停一秒、滴滴滴三短一长的循环持续30秒这个节奏对老年人的听觉刺激更强也容易和门铃等其他声音区分。状态感知轻触开关 光电对射传感器药被拿没拿走系统必须知道。最开始我的方案直接在舵机挡板处装一个微型轻触开关舵机转开之后开关弹起说明这个格子空了。后来发现药粒太小、形状又不规则靠挡板位置判断不可靠。最终方案是在每个药格里加一对红外对射管有药遮挡和没药遮挡的光电信号不同检测准确率高得多。这部分电路稍微复杂一点仿真阶段可以先跳过用按键模拟已取药信号。2.2 模块工作原理与接线简表把模块工作原理捋一遍你对接线会更有底。OLED走I2CSCL接PB6、SDA接PB7这是STM32F103的硬件I2C1引脚但实际项目中我推荐用GPIO模拟I2C——原因后面在踩坑部分细说。DS1302三根线SCLK接PA5、IO接PA6、CE接PA7。舵机信号线接PA1TIM2_CH2PG电平5V由外部供电但要注意STM32的5V容忍引脚才能直接接5V信号。蜂鸣器接PB12LED接PB13按键全部接PC13到PC15。我整理了一份完整的引脚分配表方便你对照设计模块接口类型引脚说明OLEDI2CPB6(SCL), PB7(SDA)GPIO模拟I2C稳定可靠DS1302三线SPIPA5(SCLK), PA6(IO), PA7(CE)独立电池保持时间SG90舵机PWMPA1TIM2_CH250Hz方波有源蜂鸣器GPIO输出PB12高电平触发状态LEDGPIO输出PB13提醒时同步闪烁按键1-3GPIO输入PC13, PC14, PC15菜单/确认/返回取药检测GPIO输入PB0光电对射传感器预留串口USART1PA9(PA9-TX), PA10(RX)调试和后续扩展这套分配原则背后的考量是把PWM、I2C等对引脚有特殊要求的资源单独规划普通的GPIO收发让给按键和蜂鸣器。这样即使后面想换MCU或调整功能绝大部分硬件连接不用推翻重来。3. 原理图设计每个电阻电容背后的真实作用很多新手画原理图时喜欢照抄参考设计但抄完不知道为什么要这样接。等你拿到一张PCB遇到做出来的板子不稳定、时钟跑偏、舵机一抖一抖的时候就知道原理图里那些不起眼的电阻电容有多关键。3.1 电源电路给整个系统打好地基STM32F103C8T6工作电压是2.0V到3.6V典型值3.3V。药盒大多数场景用5V的USB电源适配器供电所以需要一个5V转3.3V的稳压电路。我选用AMS1117-3.3这颗LDO线性稳压器成本几毛钱最大输出电流1A对药盒这种十几毫安到几百毫安的负载绰绰有余。原理图里的电容配置要讲究。AMS1117的输入端接10uF电解电容加0.1uF陶瓷电容输出端同样接10uF加0.1uF。输入端的10uF用来缓冲电源适配器的电压波动0.1uF滤除高频噪声输出端的大电容保证负载突变时电压跌落不明显——舵机启动瞬间的电流尖峰可能到几百毫安没有这个电容3.3V电压会被瞬间拉低导致STM32复位重启。这就解释了为什么有些人的板子舵机一转整机就重启十有八九是电源储能不足。3.3V轨旁边还要放一个1uF和0.1uF的组合电容给MCU芯片本身。这个位置要尽量靠近芯片的VDD引脚走线越短越好这是PCB布线的基本功但体现在原理图上就是每个电源引脚都有干净的退耦电容。3.2 晶振电路主时钟与RTC的心跳设计STM32F103支持外部8MHz晶振经过片内PLL倍频到72MHz作为系统主频。晶振电路看似简单就两个电容加一个电阻但参数选错可能导致系统主频偏差甚至无法启动。根据STM32数据手册的说明8MHz晶振的负载电容典型值在16pF到20pF之间。实际计算电容的公式是CL (C1 x C2) / (C1 C2) Cstray。Cstray是PCB上的寄生电容一般取2到5pF。如果目标CL取20pFC1C222pF那么并联后是11pF加上寄生电容约3-5pF总的在14到16pF。这个值在典型范围内是经过大量实践验证的稳妥取值。放一个1M欧姆的并联反馈电阻在晶振两端用于稳定振荡器工作点也是厂商推荐的标准做法。另外别忘了给DS1302也接一个32.768KHz的晶振。这个频率的晶振是实时时钟的标配因为它恰好等于2的15次方分频出1Hz信号每秒走一格最为方便。DS1302内部自带振荡电路外部只需要接晶振和两个6pF负载电容。3.3 SWD下载调试电路调试口是不容忽视的基础设施这个环节很容易被新手忽略觉得不就是一个4P排针吗。实际上SWD接口的设计质量直接影响开发效率。上表已经预留了PA13(SWDIO)和PA14(SWCLK)在原理图上需要做的只是引出这两根线加一个3.3V和GND共4个引脚。但有个细节要注意SWDIO和SWCLK引脚要各接一个10k欧姆上拉电阻到3.3V。虽然STM32内部自带弱上拉但外部上拉能让下载更稳定特别是在使用杜邦线连接下载器时线长、接触不良都会导致芯连不上的报错。这个电阻值取10k是因为它不影响正常调试速率又能提供足够的驱动强度。3.4 舵机驱动电路为什么不能直接接在MCU引脚上这是很多初学者最容易犯的错误也是原理图设计中最关键的一个电路环节。SG90舵机的正常工作电流在200mA左右堵转时峰值电流可能超过700mA。而STM32的GPIO最大输出电流只有25mA绝对最大额定值直接连接会直接把芯片烧毁。正确的做法是MCU的GPIO输出PWM信号到舵机信号线但舵机的电源正负极必须单独接5V和GND并且要加一个至少100uF的电解电容做储能。如果项目里用了高扭矩舵机比如在药盒里使用了MG996R金属齿轮舵机工作电流能到1A以上这时必须外接一个舵机专用电源模块并把舵机电源地和MCU数字地通过磁珠或低阻值电阻做单点连接防止舵机运转时地线噪声干扰MCU工作。我的标准配置是舵机电源线红色直接从5V电源入口取电线径尽量粗信号线橙色串一个100欧姆电阻再接引脚地线棕色走星型接地单独回到电源地端。这个设计不是纸上谈兵。我第一版项目的实测是不带100欧姆串联电阻时舵机一动作OLED屏幕就开始花屏闪烁加了这个电阻和100uF电容后整个系统的电源噪声大幅降低屏幕稳定如初。原理图上的每个器件背后都是真金白银换来的教训。4. 核心代码设计与出药逻辑状态机比你想的更实用代码是整个项目的大脑。这个项目的代码总共约2000行用C语言写的基于标准外设库StdPeriph_Lib没有用HAL库。原因很简单标准库代码执行效率更高、代码体积更小、直接操作寄存器的地方逻辑更透明而且网上STM32F103的资料绝大多数是基于标准库的对新手更友好。4.1 整体程序框架定时器驱动的裸机状态机整个程序采用裸机 主循环模式没有上操作系统。裸机在这个规模的系统中不仅够用反而更可控——你清楚地知道每个时刻CPU在干什么不会出现RTOS调度带来的意外延迟。程序初始化阶段做五件事时钟配置系统主频72MHz、GPIO配置所有引脚的模式和默认电平、定时器配置TIM2产生50Hz的PWM信号驱动舵机TIM3产生1ms的Tick中断作为系统心跳、I2C配置GPIO模拟用于OLED通信、DS1302初始化读取时间到系统缓存)。主循环的逻辑非常简单while(1) { scan_keys(); // 扫描按键 update_display(); // 更新OLED显示 check_alarm(); // 检查是否到达服药时间 handle_alarm(); // 处理服药提醒流程 handle_dispense(); // 控制出药流程 check_med_taken(); // 检测药品是否被取走 }这个循环看似朴素实际用了一个协作式调度的思维。实时性要求最高的动作比如到点响铃并不依赖主循环轮询的速度而是借助TIM3中断里设的标志位主循环每次进来先检查这个标志位如果到了吃药时间就立即进入提醒状态保证不丢事件。4.2 服药提醒的状态机设计从到点响到确认吃下这块是整个软件的核心。我设计了一个四状态状态机把提醒吃药这件事从简单到复杂地拆开S_IDLE待机状态系统正常计时OLED显示当前时间。每次进入该状态检查rtc_time和预设服药时间表一旦匹配跳转到S_ALARM。S_ALARM提醒状态蜂鸣器响、LED闪烁、OLED显示到服药时间。持续30秒如果期间检测到确认按键被按下进入S_DISPENSE如果超时没人反应进入S_LATE。S_DISPENSE出药状态舵机转到对应药格动作完成后等待光电传感器检测到药被拿走的信号。取药成功后蜂鸣器短鸣一声确认记录本次服药时间返回S_IDLE。S_LATE迟到状态提醒发出后长时间无人响应。蜂鸣器以更急促的节奏五短一长持续响同时OLED提示请尽快服药。直到检测到取药信号或人工按下复位键才回到S_IDLE。这个状态机的核心价值在于它把提醒--出药--确认--超时整个闭环用一个统一框架串起来了。每个状态都有明确的进入条件和退出条件代码可读性高后面维护加功能比如加一个家人远程通知的短信模块也只需要在状态转移时插入一个函数调用。4.3 舵机PWM控制占空比是怎么算出来的SG90舵机要求50Hz的控制信号周期20ms对应高电平时间是0.5ms到2.5ms映射到0度到180度。STM32的PWM输出实现方式是TIM2的预分频PSC和自动重装载ARR决定PWM周期捕获比较寄存器CCR决定占空比。我的配置是PSC71ARR19999所以PWM频率 72MHz / (711) / (199991) 50Hz周期正好20ms。角度到CCR的映射公式为// 高电平时间范围 0.5ms ~ 2.5ms // CCR值 高电平时间 / PWM周期 * ARR // 0度对应 CCR 0.5/20 * 19999 ≈ 500 // 90度对应 CCR 1.5/20 * 19999 ≈ 1500 // 180度对应 CCR 2.5/20 * 19999 ≈ 2500 uint16_t angle_to_ccr(uint8_t angle) { return 500 (uint16_t)((uint32_t)angle * 2000 / 180); }这个公式看着简单实际调试中一定要在实机上验证舵机的实际角度。因为不同厂家的SG90同样的PWM高电平时间对应的角度可能差5到10度。我的建议是初始设置角度到90度CCR1500用尺子量一下舵机臂是否真的垂直于机体如果偏差明显微调公式里的500和2500这两个端值。4.4 表格式服药计划让代码适应每个人吃的药都不一样不同老人的服药习惯千差万別有人一天两次有人一天四次有人饭前吃有人饭后吃有人周一三五吃的药和二四六不一样。这些差异不应该通过改代码来解决而应该在数据结构里解决。我实现了一个简单的服药计划表typedef struct { uint8_t hour; // 服药时间小时 uint8_t minute; // 服药时间分钟 uint8_t slot; // 药格编号0-3 uint8_t days_mask; // 星期掩码bit0周一, bit1周二... uint8_t enabled; // 启用标志 } MedSchedule_t; MedSchedule_t g_schedule[] { {7, 30, 0, 0x7F, 1}, // 每天 7:30 吃第1格药 {12, 30, 1, 0x7F, 1}, // 每天12:30 吃第2格药 {18, 30, 2, 0x7F, 1}, // 每天18:30 吃第3格药 {21, 0, 3, 0x5F, 1}, // 周一至周五 21:00 吃第4格药 };check_alarm()函数每次从表头遍历判断当前时间是否匹配某个计划的时和分再拿当前星期去匹配days_mask的对应位。这个结构的好处是调整服药时间、增删药格只需要改这个数组并重新烧录固件不需要动任何业务逻辑代码。甚至可以通过OLED菜单做一个简易的时间修改界面实现不接电脑、直接在药盒上修改服药计划的功能。5. Proteus仿真在买硬件之前先把逻辑跑通仿真这个环节很多人嫌麻烦直接跳过但我强烈建议别省。原因很现实一套硬件样品从买芯片到焊接调试少说三五天遇到问题查起来更费时间而Proteus里搭仿真电路用不了半天绝大部分逻辑问题在仿真阶段就能暴露。先把软件逻辑调通再去做实物整体效率高得多。5.1 仿真工程搭建哪些模块能仿哪些模块要替代Proteus 8.x版本自带STM32F103C8T6的模型可以直接从元件库里选。搭建一个最小系统只需要四样东西STM32芯片、电源VCC/GND、晶振电路在仿真里可以用晶振元件加两个电容、复位电路RC复位。以下模块在Proteus里有原生模型建议直接使用LCD/OLED显示Proteus里有LGM12864和LM016L等液晶模型。OLED在仿真里没有完全一致的模型但可以用LM016L字符屏替代调试显示逻辑或者用LGM12864模拟图形界面的布局。由于I2C OLED是实物上的标准外设仿真阶段先用LM016L把显示内容对不对调通等移植到实机上再换成OLED驱动。按键用Button元件与实物一样接上拉电阻再接GPIO。LED和蜂鸣器直接用元件即可用来验证提醒逻辑。DS1302Proteus里有DS1302模型但仿真上RTC的时间推进通常靠仿真运行时间累积。如果发现时间不走可以换一种思路——用一个定时中断模拟每隔100ms时间加一秒这就是仿真里可以简化时钟源的意义。舵机是仿真的重灾区。Proteus有舵机模型但驱动的PWM信号频率、初始位置都和实物有差异而且仿真里无法真实模拟药粒掉落这种机械动作。我的处理方法是把舵机动作简化成一个状态LED舵机转到哪个药格就点亮对应的LED出药完成的判定用一个按键模拟取药完成信号。5.2 典型仿真调试案例按键消抖的仿真过、实机挂分享一个真实的调试案例。我的第一版代码按键处理没有做消抖debounce仿真里跑起来一切正常——按键按下、状态切换、蜂鸣器响起逻辑完美。结果烧到实物上按下按键偶尔触发两次甚至三次状态切换OLED菜单跳来跳去完全不受控制。原因在Proteus的按键模型是理想的不会产生机械抖动而实物的机械触点从接触到稳定导通通常会抖动5-20ms。解决办法就是经典的软件消抖检测到电平变化后延时20ms再次确认电平是否稳定再进一步用状态机做按下—确认—松开—确认的四次判断彻底消除误触发。这个案例告诉我们仿真通过只是第一步实机调试才是真考验。仿真阶段能发现的是逻辑缺陷硬件上特有的时序问题是仿真覆盖不到的这也是为什么我不建议只做仿真、不做实物——两者结合才是完整的开发流程。5.3 仿真边界与实机差异哪些坑别在仿真阶段踩明确说一下Proteus仿真在这类项目中的局限性方便你规划时间DS1302时间不走或走时不准。这是Proteus仿真的一个常见坑。破解办法是仿真时用一个全局变量模拟时间推进等烧到实机上再换成真正的DS1302读时间代码里做好接口隔离切换只在初始化函数里做。串口打印调试。Proteus的Virtual Terminal元件可以接在USART_TX和USART_RX上代码里的printf调试信息可以原样显示。这个功能极其好用建议从第一天写代码就把串口调试开好。中断优先级问题。仿真环境中中断优先级的表现和硬件有差异。在仿真中测试时注意把TIM3的中断优先级设为比外部中断低养成好的中断设计习惯不要靠仿真通过来推断实机优先级。I2C时序的模拟方式。如果你用硬I2CSTM32的I2C外设在仿真里跑OLED可能一切正常但实机上I2C时序可能因为电气噪声不平滑而出错。所以我前面才建议直接用GPIO模拟I2C在仿真和实机之间保持完全一致的行为。6. 从仿真到实物结构化组装与可靠性调试仿真通过之后面对一堆散件很多人会有从哪下手的茫然感。这里分享一套我自己反复验证过的组装流程。6.1 模块化焊接与逐模块上电测试千万不要焊一块板直接把所有模块接上去通电。风险太大了——如果某个模块本身有故障可能会拉低电源电压影响整个系统排查起来极其痛苦。我的顺序是先焊最小系统MCU 晶振 复位 电源写一个LED闪烁程序验证核心板能工作。加上OLED跑I2C扫描程序确认地址和显示正常。加上DS1302写时间设置和读取程序用串口打印验证时间准确。加上舵机验证PWM输出和角度控制。最后加蜂鸣器、LED、按键等逐步集成到完整功能。每一步都必须验证通过再进入下一步。这个方法虽然慢但它把问题隔离在了最小范围平均而言反而是最快的路径。6.2 电源完整性与抗干扰舵机一抖全板遭殃实机测试中最常见的诡异现象恰恰是原理图阶段就埋下的隐患。舵机启动瞬间电流尖峰会让RTC走时异常、OLED闪烁、甚至MCU重启。原因在于舵机的瞬态电流直接从3.3V稳压器后级抽取导致3.3V电压跌落。我的解决方案有三层你可以根据预算和需求选配第一层舵机供电直接从5V取不给3.3V侧带来冲击。第二层舵机电源引脚和MCU电源引脚之间增加一个100uF电解电容0.1uF陶瓷电容组合吸收瞬态电流。第三层在PCB上做地线分割或者星型接地舵机的地单独回流到电源入口。实测效果加了第二层电容之后原本舵机一动作就屏幕闪烁的现象彻底消失。这套储能电容电源隔离的思路同样适用于后续加电机、电磁铁等其他执行机构。6.3 药格结构设计与出药可靠性硬件电气调通了出药机构的机械可靠性是决定这项目能不能给家人用的关键。我做了几版原型后发现转盘落药比挡板开合可靠得多。转盘式结构是这样的一个圆形的药盒底部有一个出药口药盒内部被分隔成4-6个独立扇形区域。舵机轴穿过药盒中心带动一个底部有缺口的转盘。正常状态下转盘缺口不对准任何药格出口药粒被挡住到了服药时间舵机转到一个指定的角度转盘上的缺口对准期望药格药粒从这个口掉出来。这个结构的优点在于药粒只需要滚动不需要推动卡药概率大幅降低。另外一个容易忽略的可靠性细节是药粒的湿度管理。药粒暴露在空气中久了会有一定程度吸潮粘在一起导致掉不出。我的建议是在药盒盖子内侧加一个硅胶干燥剂包并保证药盒总体密封性只在出药口留一个小通道。6.4 老人使用体验的细节打磨技术参数都调好了最后还是要回到人身上。给老人用的设备顺手比功能多重要得多。几个我反复打磨的细节按键设计老人戴老花镜按键必须大而明显的。我的做法是使用三个独立的大面积按钮分别标注确认、上翻、下翻间距至少15mm按压力度适中按下时有明确的手感反馈clicky感避免用软膜按键。OLED显示尺寸在0.96寸确实偏小但字号尽量调大。我的界面设计是第一行时间大号数字第二行当前状态请吃第1格药第三行下一组时间下次 12:30。背景用反白显示黑字白底保证在强光下也能看清。声音提示有源蜂鸣器频率单调老人很容易听腻或忽略。如果你手头有闲置的语音模块如DFPlayer Mini插一个TF卡可以录制王奶奶该吃降压药了的语音播报。这个升级对老人的接受度提升是非常显著的。7. 开源项目如何从能跑到能维护我踩过的工程化坑项目开源意味着别人会看你的代码、会尝试编译、会提出各种问题。代码质量直接决定这个项目能走多远。这一节讲讲我在整理开源代码时踩过的坑。7.1 代码风格与模块化别把所有逻辑塞进main.c第一版代码我犯了一个新手典型错误所有逻辑全堆在main.c里初始化一两百行主循环又几百行加上各个外设的配置代码穿插其间。代码一长肉眼找bug的效率直线下降。后面我重构成了分层结构APP层存放业务逻辑药盒状态机、服药计划管理。BSP层存放硬件驱动OLED驱动、DS1302驱动、舵机驱动、按键驱动、蜂鸣器驱动。Middle层存放通用模块如软件定时器、队列、防抖逻辑。每一层的代码只调用下层接口不跨层调用。这个分层思想能显著提高可读性和可移植性。如果你做的是课设分层也许不强制但你将来从事嵌入式开发这种分层思维是必须内化的基本功。7.2 硬件引脚定义统一管理避免改动时牵一发动全身引脚宏定义是工程化管理的重灾区。第一版代码引脚分配写死在各个驱动文件的初始化函数里后来想把舵机从PA1改到PA2必须逐个文件挖出来改。重构之后我在bsp_pin.h里集中管理所有引脚映射#define PIN_LED GPIO_Pin_13 #define PORT_LED GPIOC #define PIN_BUZZER GPIO_Pin_12 #define PORT_BUZZER GPIOB #define PIN_SERVO_PWM GPIO_Pin_1 #define PORT_SERVO_PWM GPIOA所有驱动文件include这个头文件通过宏名使用引脚。改硬件时只动这一个文件其余代码不用碰。这个习惯建议从第一个项目就养成。7.3 文档与资料整理的实用建议开源项目除了代码原理图/仿真/说明文档各占一席。整理时按模块分文件夹/Doc说明文档、/Hardware原理图和PCB工程、/Firmware代码工程、/SimulationProteus仿真、/Images实物照片和截图。README.md里写清楚开发环境版本Keil版本、Proteus版本、库函数版本否则别人下载下来编译报错第一反应就是项目有问题。当时我犯过一个低级但杀伤力极大的错误——开源仓库存了一份实验过度的代码里面有一段注释掉的旧逻辑导致别人编译后按键没反应。这种测试垃圾在开源前必须清理干净。8. 后续扩展想法从智能药盒到用药管理终端如果一个版本做完你还想继续升级这几条路是我自己正在探索的也欢迎一起交流GSM/4G远程通知通过SIM800C模块在老人超时未服药时给子女发短信。这个功能在独居老人场景下价值极大。电路上只用串口接模块代码上就是检测到S_LATE状态时通过串口AT指令发一条短信。注意SIM800C峰值电流较大电源设计要单独规划。血压/心率监测整合利用STM32的ADC采集指夹式血氧/心率传感器数据服药提醒时顺带提醒先测血压再吃药。这部分涉及模拟信号处理对PCB布局和抗干扰能力的要求上一个台阶。云端数据同步加一个ESP8266 WiFi模块把每次服药时间和药格用量通过MQTT上报到云端子女手机端实时查看。这是目前很多商业产品的模式自己用STM32做出来硬件成本还不到商业产品的三分之一。如果这个项目帮你完成了课程设计或者你真给家里老人做了一台而且用上了语音提醒模块——记得回来告诉我。看着自己写出来的代码真正在某个老人每天的生活里按时响起那种感觉是自己折腾这么久最值得的时刻。
返回列表