开源智能药盒/老人用药管理系统的完整实现笔记
家里有老人的朋友应该都经历过这种场景:药盒上写了早中晚三次用药,但老人要么忘记吃,要么重复吃,要么时间完全错乱。尤其是一些需要长期服药的慢性病老人,一顿漏服可能看不出什么,时间长了问题就大了。我最初做这个STM32智能药盒项目,就是想解决“按时吃药”这件看似简单、实际上特别容易出问题的小事。
这套方案我命名为“老人用药管理系统”,主控用的是STM32F103C8T6,配套完成了硬件原理图、驱动代码和Proteus仿真工程,全部开源。整个系统的核心价值在于:它不只是一个定时器加一个舵机的玩具,而是把“提醒—确认—记录—告警”这条完整的用药闭环做通了。老人到点听到蜂鸣器提示,指示灯闪烁,按一下确认键表示“我吃了”;如果超过设定时间没人按键,系统会通过串口向家人终端发送未服药告警。整个过程不需要老人有额外的学习成本,也不需要子女时刻盯着。
这篇文章我会把整个项目的设计思路、硬件选型、电路原理、代码结构、仿真调试以及我实际踩过的坑全部拆开讲清楚。不管你是准备拿这个题目做毕业设计,还是真的想给家里老人做一个能用的药盒,都可以直接参考这套方案。项目里用到的代码、原理图和仿真文件我做了统一打包,文末会说明文件结构和怎么用。
1. 项目整体设计思路与方案选型
1.1 功能需求到底该怎么拆
做嵌入式项目最容易犯的毛病就是一上来先翻数据手册或者找芯片,根本不知道自己要做什么。我习惯的做法是先列需求,越具体越好,然后再倒推硬件和软件。
这个智能药盒我拆出了六个核心功能:
- 实时时钟显示,支持断电走时,掉电不丢时间
- 三组用药时间设定,对应早中晚三次用药,可独立开关
- 到点触发蜂鸣器提醒,同时LED闪烁,提示患者“该吃药了”
- 按键确认用药,按下后消除提醒并在LCD上记录本次服药
- 超时未确认则通过串口/GSM模块向监护端发送告警信息
- 预留温度检测功能,实时显示环境温度(这是仿真的彩蛋,也是实际硬件可以扩展的)
这六个功能看起来不多,但每个都对应一个独立的硬件模块。比如实时时钟需要独立的RTC芯片,提醒需要蜂鸣器和LED,确认需要按键,告警需要串口或通信模块。需求一拆完,硬件选型就水到渠成了。
1.2 主控芯片选择:为什么是STM32F103C8T6
STM32F103C8T6这颗芯片在开源项目里的地位,差不多相当于手机界的“百元机皇”。72MHz主频、64KB Flash、20KB SRAM,集成了USART、I2C、SPI、ADC、定时器等常用外设,价格在几块钱到十几块钱之间,开发资料多得铺天盖地,还能在Proteus里完美仿真,这几个条件叠加起来,做智能药盒这种外设不算多的项目简直绰绰有余。
可能有人会问,用51单片机不是更简单?我承认纯功能实现用STC89C52也能跑,但差距就在“可扩展性”和“开发效率”上。这个项目里我用了多个定时器、RTC通信、串口中断、PWM舵机控制,51单片机做这些虽然能行,但代码复杂度和调试成本都会明显上升。STM32丰富的库函数和CubeMX图形化配置能省掉大量底层初始化时间,后面如果想加联网模块、触摸屏、语音播报,性能也不会成为瓶颈。
1.3 外围模块搭配与选型思路
主控定了,接下来就是逐块选型:
| 功能模块 | 选型方案 | 选型理由 |
|---|---|---|
| 实时时钟 | DS1302 | 功耗低,走时误差可接受(日误差约1秒),价格便宜,Proteus自带模型 |
| 显示模块 | LCD1602 | 字符型液晶,16列2行,能显示时间和状态,调试方便,仿真友好 |
| 提醒模块 | 有源蜂鸣器 + 双色LED | 蜂鸣器声音大,老人能听见;LED用于视觉辅助提示 |
| 输入模块 | 独立按键 x4 | 结构简单,去抖可靠,一个菜单键三个操作键,符合老人使用习惯 |
| 执行模块 | SG90舵机 | 模拟自动出药机构,在仿真中用PWM波形验证控制逻辑 |
| 温度传感器 | DS18B20 | 单总线数字传感器,一根线就能读温度,扩展方便 |
| 通信模块 | USART1串口 / 预留SIM800C | 仿真中用虚拟终端模拟GSM通信,实物可以接SIM800C发短信 |
我特别想强调一下DS1302这个选型。DS3231精度更高,但在Proteus里没有现成模型,而且价格是DS1302的十倍不止。对于这个项目来说,DS1302已经够用,而且它在Proteus里仿真效果很好,晶振和电池电路也简单。如果你做实物又实在在意精度,后面可以换成DS3231,代码只需要改底层读写函数,业务逻辑完全不用动。
1.4 系统整体架构:数据怎么流转
从数据流的角度看,这个系统的逻辑其实很清晰。DS1302负责产生时间基准,系统不断读取当前时间并刷新到LCD上;用户通过按键设定三组用药时间,存入STM32内部Flash的结构体变量中;主循环每次读取时间后,与用药计划表中的时间进行匹配,匹配成功则触发提醒状态机;提醒状态机驱动蜂鸣器和LED,等待用户按键确认;如果超时未确认,则通过串口把告警信息发送到监护端。
这个架构是典型的“事件驱动+状态机”模式,没有跑RTOS,用裸机主循环加定时器中断就够了。为什么不上FreeRTOS?因为这个项目任务数量少、实时性要求不高,裸机代码反而更直观,学习门槛也低,调试更容易。真上了RTOS反而要给每个任务分配栈空间、处理信号量,对新手不友好,没必要。
2. 硬件部分:原理图设计与关键电路解析
2.1 STM32最小系统:这部分不能省
最小系统是STM32能跑起来的底线条件,包括电源电路、复位电路、时钟电路和BOOT启动配置。看原理图时你会发现,STM32F103C8T6其实只需要一个8MHz晶振加两个20pF负载电容,复位引脚接一个10kΩ上拉电阻和一个100nF对地电容,这里电容的作用是滤除复位引脚的毛刺干扰,保证上电时复位信号稳定。
电源部分我用了AMS1117-3.3稳压芯片,输入5V输出3.3V,输入输出各加一个10μF和100nF电容,做高低频滤波配合。有些低成本方案会省略稳压直接给3.3V,但如果你要接舵机、GSM模块这种功耗较大的外设,还是老老实实用稳压芯片,否则电压跌落会导致系统莫名其妙复位。这里还有一个细节,BOOT0和BOOT1引脚各接一个10kΩ下拉电阻到地,保证芯片从Flash启动,不会在调试时意外进入ISP模式。
2.2 DS1302时钟电路:32.768kHz晶振的匹配
DS1302外围电路非常简单,一个32.768kHz晶振加两个6pF负载电容,再接一颗3V纽扣电池做断电备份。晶振频率必须精确匹配,32.768kHz是32768Hz,正好是2的15次方,秒计数器就是靠这个频率分频得到的,频率偏了就会走时不准。
我在设计时特别注意了晶振两端的电容容值,有些参考设计用12.5pF甚至15pF,导致振荡电路起振困难或者频率偏移明显。实际测试下来,6pF是比较稳的匹配值。另外DS1302的VCC1接主电源,VCC2接备份电池,主电源掉电后自动切换到电池供电,这个切换是芯片内部完成的。仿真时这个电路可以简化,但做实物建议完整保留。
2.3 LCD1602接口:4线模式还是8线模式
LCD1602是5V逻辑器件,STM32是3.3V,电平匹配问题必须处理。我在这版设计中直接用4线模式,只用D4~D7四个数据引脚加RS、RW、EN三个控制脚,一共7个GPIO。相比8线模式省了4个引脚,而且数据传输操作只需要两个周期,速度也够用。
电平转换方面,我做实物时在数据线上串联了1kΩ限流电阻再接到STM32,这样能防止5V高电平倒灌进3.3V引脚。Proteus仿真不用纠结电平问题,因为仿真器不模拟绝对电压危害,但做实际PCB时必须加上这个保护电阻。LCD的对比度调节电位器我选了10kΩ,接在V0引脚上,调到中间位置显示最清晰。RW引脚因为只写不读,直接接地处理,既能省一个GPIO,也让代码少一个控制引脚要管理。
2.4 舵机与蜂鸣器驱动:抵消电流冲击
舵机SG90的堵转电流能到几百毫安,蜂鸣器也有几十毫安,这两个负载如果直接从STM32的GPIO口取电,芯片绝对扛不住,必须加驱动电路。舵机的信号线直接接STM32的TIM引脚输出PWM,电源和地单独从5V供电走;蜂鸣器我用一个NPN三极管(S8050)做开关管,GPIO通过1kΩ电阻驱动三极管基极,集电极接蜂鸣器负极,蜂鸣器正极接5V,发射极接地。GPIO输出高电平时三极管导通,蜂鸣器响;输出低电平时截止,蜂鸣器停。
这样设计的关键原因是STM32的GPIO最大输出电流约25mA,根本驱动不了蜂鸣器,而三极管只需很小的基极电流就能控制大的集电极电流。至于舵机的5V电源,我从USB输入的5V直接取,在电源入口加上一个大容量电解电容(470μF缓冲电流冲击),防止舵机堵转时把5V电压拉垮。别小看这个电容,没有它的项目我实测过,舵机一转系统就重启。
2.5 DS18B20与串口电路:一个上拉,一个反相
DS18B20是单总线设备,数据线是开漏输出,必须在数据线上接一个4.7kΩ上拉电阻到3.3V,否则通信不稳定。这是我做这个模块时踩过的坑,一开始没加上拉电阻,温度读取总是偶发失败,加了之后问题立刻消失。
串口部分用CH340G做USB转TTL,连接STM32的PA9(USART1_TX)和PA10(USART1_RX),注意交叉连接——板子的TX接模块的RX,板子的RX接模块的TX。CH340G的TXD和RXD到STM32的连接是相反的对应关系。关于SIM800C GSM模块,我在原理图上预留了接口,实物阶段可以直接把它的UART接到STM32的USART2上,板子就能通过短信发送告警信息了。仿真阶段我用Proteus的Virtual Terminal虚拟终端模拟了这个环节的效果。
3. 软件部分:代码架构与核心功能实现
3.1 工程文件的组织方式
这个项目的代码我用了标准的分层结构,好处是模块清晰、移植方便,找Bug时不用在几千行代码里大海捞针。工程目录按功能分了几个文件夹:
User/ 主函数 main.c、stm32f10x_it.c 中断服务 Hardware/ DS1302.c、LCD1602.c、DS18B20.c、Key.c、Buzzer.c、Servo.c System/ 延时函数、串口打印、GPIO初始化 App/ 用药逻辑状态机 medicine.c、菜单交互 menu.c这种组织方式是我做多个项目之后固定的习惯。每个外设驱动文件只负责最底层的“怎么操作这个芯片”,比如DS1302.c里的函数就是读写寄存器、设置时间、读取时间;真正的业务决策放在App层的medicine.c里,比如“当前时间到了设定的吃药时间,提醒状态机应该进入响铃状态”。这样分层,硬件和业务不扯皮,后期改任何一层都不影响另一层。
3.2 DS1302驱动:读时序的顺序敏感
DS1302的驱动代码其实不复杂,就是一个双向串行通信协议。它有个特点值得新手注意:RST引脚拉高后启动通信,每个字节先发命令字节(最高位固定为1,最低位是读写控制位),命令字节的第0位决定接下来是读还是写。写一个字节是低位在前,读一个字节也是低位在前,数据在时钟上升沿被采样。
我贴一段核心读取函数方便理解:
// 读取DS1302某寄存器地址 uint8_t DS1302_ReadByte(uint8_t addr) { uint8_t data = 0; uint8_t i; GPIO_WriteBit(DS1302_RST_PORT, DS1302_RST_PIN, Bit_SET); // 先发送命令字节(读命令) DS1302_WriteByte(addr | 0x01); // 再读数据字节 for(i = 0; i < 8; i++) { data >>= 1; if(GPIO_ReadInputDataBit(DS1302_IO_PORT, DS1302_IO_PIN)) data |= 0x80; GPIO_WriteBit(DS1302_SCLK_PORT, DS1302_SCLK_PIN, Bit_SET); Delay_us(2); GPIO_WriteBit(DS1302_SCLK_PORT, DS1302_SCLK_PIN, Bit_RESET); Delay_us(2); } GPIO_WriteBit(DS1302_RST_PORT, DS1302_RST_PIN, Bit_RESET); return data; }注意这里的顺序:读数据时是每个SCLK上升沿从数据线采样一位,先读到的是最低位(bit0),所以我的移位方式是把已有数据右移、新数据放到最高位,8次之后data就恢复成正常的字节序了。这个顺序如果搞反了,读出来的时间就是乱的,而且还不容易发现错在哪,我调试时就吃过这个亏。
3.3 按键扫描与消抖:不要用delay
按键消抖是嵌入式入门必踩的坑,按键按下去的瞬间,机械触点会经历一个约5~10ms的抖动期,电平在高低之间快速跳变,如果不消抖,一次按压会被识别成多次。常见的做法有两种:一是延时后再读一次,二是状态机扫描。
我强烈建议用状态机扫描,不要用delay消抖。原因很简单,main函数里还有一个大循环要处理LCD刷新、时间匹配、蜂鸣器状态切换,如果一个按键用了20ms的delay,系统整体响应就会卡顿。我用的消抖方案是定时器中断配合循环扫描:每10ms进一次中断,读取四个按键状态,把状态值存入一个环形缓冲,当连续两次采样值相同且持续超过30ms才认为是有效按键事件。这样既消抖又不阻塞主循环。
3.4 用药状态机的关键设计:从“到点”到“确认”的完整链路
整个项目最有价值的逻辑就是用药状态机,它处理的是用户的“用药流程”:
- 空闲状态(IDLE):系统正常运行,LCD显示时间,无提醒。
- 铃响状态(RING):到了设定吃药时间,蜂鸣器每隔500ms响一次,LED闪烁,LCD显示“TAKE MEDICINE”。
- 确认状态(CONFIRMED):用户按下确认键,蜂鸣器停止,LCD显示“TAKEN”,这次用药完成。
- 超时告警状态(ALARM):从进入RING开始计时,超过10分钟(可配置)无确认,串口发送告警消息,同时蜂鸣器声音模式改变,进入更急促的提醒节奏。
用状态机而不是用多标志位堆积逻辑,最直接的好处是每个状态处理函数只需要关注“当前状态要干什么”和“什么事件会触发状态切换”,不会出现在几十个if嵌套中迷失的情况。我实测下来,之前用标志位写这个逻辑,反复出现倒计时清零后忘记恢复设置位的问题,改成状态机之后整个流程一目了然。
状态切换的代码骨架大致是:
void Medicine_StateMachine(void) { switch(med_state) { case MED_IDLE: if(Time_Match(med_plan[match_index].hour, med_plan[match_index].min)) { med_state = MED_RING; ring_timer = 0; } break; case MED_RING: Buzzer_Control(ring_timer % 20 == 0); if(Key_ConfirmPressed()) { Led_Green(); med_state = MED_CONFIRMED; Save_MedRecord(match_index); } if(ring_timer > ALARM_TIMEOUT) med_state = MED_ALARM; break; case MED_CONFIRMED: // 确认后维持1分钟显示“TAKEN”,之后回到IDLE break; case MED_ALARM: Buzzer_Control(ring_timer % 5 == 0); Uart_SendString("ALARM: NOT TAKEN!\r\n"); if(Key_ConfirmPressed()) med_state = MED_CONFIRMED; break; } }3.5 温度采集:DS18B20的ROM匹配问题
DS18B20占比不大,但我还是加了,主要用来实时显示环境温度并写入给药盒的“存放环境检查”功能。单总线的操作时序比较严格,初始化时主机拉低至少480μs然后释放,等待传感器拉低60~240μs响应;写“0”就是拉低60~120μs,写“1”就是拉低1~15μs然后释放。
因为一个总线上只挂了一个DS18B20,我直接用了跳过ROM(Skip ROM)命令,不读取64位序列号,省去匹配过程。如果以后要挂多个传感器,再把读ROM的代码补上。温度转换完成后,读回来的是16位二进制补码,正数温度直接乘以0.0625就得到摄氏温度,负数需要先取反加一再乘。还有一点,DS18B20的上电默认精度是12位,转换时间约750ms,所以读温度的程序不能太频繁,我放在了主循环每2秒采一次。
3.6 LCD菜单交互:三级菜单怎么设计
老人操作系统的交互逻辑必须简单直接,我设计了两级菜单结构:一级菜单是待机界面,显示时间;二级菜单是设置界面。按下“设置”键进入时间设置,此时小时位闪烁,按“加”/“减”调整,再按“确认”进入分钟设置,最后确认退出;再按“设置”可以切换到用药时间1/2/3的设置,流程和设置当前时间一样。
做这个菜单要注意一个细节:参数的修改范围要限制,小时0~23,分钟0~59,用户按“加”到上限时应该回绕到0。另外光标闪烁的实现是靠定时器中断里对某个全局变量的取反标志位来完成,LCD显示函数里检测到光标所在位置,根据闪烁标志决定显示数字还是显示空格。
3.7 串口打印与调试:一个出色的调试助手
我习惯在所有外设初始化完成后,通过串口打印一个启动横幅,包含固件版本号、RTC初始时间等关键信息。这样串口一开,就能立刻判断系统是否正常启动、RTC有没有正确读到时间。开发阶段这个习惯能省不少事。
除了启动信息,串口还承担了告警消息的输出。实际硬件接GSM模块时,可以把告警发送函数替换为GSM的AT指令调用,向指定手机号发送短信;仿真阶段,把串口接到虚拟终端,效果一样能演示出来。
4. Proteus仿真环境搭建与联合调试
4.1 仿真工程搭建的完整步骤
Proteus仿真在这个项目里的定位是:没有硬件板子时也能完整跑通功能逻辑,尤其适合学生交课程设计或者先模拟再打板。搭建步骤如下:
- 新建工程,选择STM32F103C8T6芯片,在图纸上放置;
- 从元件库搜索并放置DS1302、LCD1602、DS18B20、BUTTON、BUZZER、LED-RED、LED-GREEN、RESISTOR、CAPACITOR等元件;
- 放置了一个Virtual Terminal虚拟终端,连接到USART1对应的PA9(TX)和PA10(RX)引脚;
- 用导线按原理图连线,注意芯片电源引脚VDD和VSS要接3.3V和地,仿真默认不会自动供电;
- 双击STM32芯片,在Program File中加载keil编译生成的hex文件;
- 设置时钟频率为8MHz,启动仿真。
Proteus里一个重要的配置项是芯片的“Clock Frequency”要和实际代码里配置的时钟源一致。我用的是外部8MHz晶振,仿真里也设置成8MHz,时序才真实。
4.2 虚拟终端模拟GSM通信
因为Proteus没有现成的SIM800C模型,仿真里我用虚拟终端来模拟串口通信。虚拟终端接在PA9、PA10上,当系统检测到超时未服药时,会在串口输出类似“AT+CMGS=15900000000”这样的AT指令序列,接着发送“Attention! Grandfather didn't take medicine at 08:00!”这样一条消息。
虚拟终端自带时间戳显示,我可以看到每次告警消息的发出时刻,用来验证超时判断逻辑是否正确。同时虚拟终端还能回显收到的数据,调试阶段也可以通过它直接给单片机发数据,变相模拟按键输入。
4.3 仿真中遇到的两个典型问题
仿真不是万能的,我遇到的两个问题挺有代表性。第一个是LCD1602不显示,检查半天发现是Proteus模型对EN引脚的脉冲宽度要求很严格,代码里如果两个写命令之间的延时太短,液晶控制器就跟不上。解决方案是每个LCD命令操作之后加至少40μs延时,显示数据时延时可以短些。
第二个问题是代码在真实硬件上正常,在Proteus里却跑飞。后来定位到原因是我的延时函数用了SysTick定时器,而Proteus仿真里SysTick的时钟基准跟真实硬件存在差异。解决办法是延时函数不要依赖硬件定时器,用简单的循环次数估算实现,或者依赖一个固定的系统节拍。我在最终开源版本里的Delay函数采用了一个三次循环的软件延时,在仿真和实物上都验证过。
5. 实战中踩过的坑与排查技巧
5.1 STM32烧录失败排查:从驱动到设置的排查顺序
很多人第一次用ST-Link烧录时会遇到“No STM32 Target Found”的报错。这个错误信息把很多新手卡在原地,我总结了一条排查顺序:先看ST-Link的驱动是否装好,设备管理器里有没有出现异常的USB设备;再检查接线,SWDIO接PA13、SWCLK接PA14,另外GND必须共地;然后确认目标板供电正常,3.3V指示灯亮不亮;最后调整ST-Link的通信速率,目标板有长线或者杂散电容时,把频率从4MHz降到1MHz往往就能连上。
还有一个经常被忽略的原因,芯片的读保护(RDP)开启后会阻止调试访问,需要先用ST-Link Utility执行全芯片擦除。这个不是功能性问题,但确实会让人抓狂半天。我在开源文档里专门加了一节“第一次烧录前必看”,把这几个步骤都写清楚了。
5.2 DS1302走时不准或不走:晶振和电容的锅
DS1302不走或者时间走得忽快忽慢,绝大多数情况出在晶振电路上。市面上很多便宜的32.768kHz晶振精度本身不高,偏差可能在20ppm以上,日误差会到一两秒。此外晶振两端的负载电容容值要跟晶振规格匹配,匹配不当会导致起振失败。如果确认电路没问题但还是不走,用示波器量一下晶振引脚是否有波形,没波形说明没起振。
我实际测试过,6pF负载电容在这个项目里表现最稳定,换过12.5pF的电容,DS1302直接不走了。软件层面还要注意,DS1302写保护寄存器(0x8E)必须先写0x00再操作时间寄存器,写完一定要把写保护关闭(写成0x80),否则时间设置不生效。这个顺序很多资料上没写清楚,我是通过看时序才想明白的。
5.3 舵机抖动或不转:PWM频率的对与错
SG90舵机的控制信号是50Hz的PWM,也就是周期20ms,高电平时间1ms对应0度,1.5ms对应90度,2ms对应180度。我在代码里用了TIM2的CH1输出PWM,设置预分频器使计数频率达到1MHz,自动重载值设为20000(即20ms),比较值设为1500(即1.5ms)就是中位。
很多新手会把STM32的定时器初始化搞混,PWM频率不对导致舵机剧烈抖动。排查方法是先用示波器量引脚波形,看频率是不是50Hz,高电平是不是在1~2ms的范围。如果频率对但舵机没劲,多半是电源问题——舵机供电不稳定,加个大电容或者独立供电即可。
5.4 按键按下没反应:从引脚模式开始查
STM32的GPIO引脚默认是浮空输入模式,如果忘了配置成上拉输入,按键接GND时引脚电平就可能处于不确定状态。我建议所有按键引脚都配置为上拉输入模式(GPIO_IPD),按键一端接引脚、一端接GND,按下时引脚被拉低读取到低电平。如果按键接VCC那边,就配置成下拉输入。
另外要注意的是,在Proteus仿真中按键模型默认的弹跳很小,而在实物中弹跳明显,代码里消抖逻辑就必须有。我用的10ms定时扫描加30ms连续确认的消抖方案,在实际按键上测试大约能过滤掉99%以上的抖动干扰。
5.5 系统运行一段时间后卡死:看门狗要不要上
智能药盒是长期运行的设备,代码里如果一个模块卡住,灯不闪、蜂鸣器不响、屏幕固定不变,老人面对这种情况会完全束手无策。所以我加了独立看门狗(IWDG),主循环每200ms喂狗一次,如果循环卡死超过1秒,芯片自动复位重新运行。
这个改动看起来不起眼,但长期运行的设备有没有看门狗,稳定性完全两个档次。如果你的代码用到了一个耗时超过1秒的操作(比如擦写Flash),记得在这段操作前暂停狗或在代码设计时避开,否则会误复位。
6. 开源文件说明与快速上手指引
开源包里的文件不算多,我按使用顺序介绍一下,方便你拿到手就知道从哪里开始:
- Hardware/STM32F103C8T6_Smart_Med_Box.pdf:完整原理图,包含最小系统、DS1302、LCD1602、按键、蜂鸣器、LED、舵机、DS18B20、串口及GSM预留接口;
- Software/:Keil5工程源码,包含所有驱动文件和应用层代码,可直接编译生成hex;
- Simulation/Med_Box.pdsprj:Proteus仿真工程文件,需用Proteus 8.9以上版本打开,加载hex后可直接运行;
- Docs/引脚分配表.pdf:一张表格列出了STM32所有引脚的外设分配,硬件接线和写代码时对照查就行;
- Docs/教程.md:从零开始搭建环境、编译、烧录、仿真的完整图文教程。
拿到项目的建议路径是:先打开引脚分配表,确认每个外设接到哪个引脚;再打开原理图,对照引脚表过一遍电路;然后打开Proteus仿真文件,加载hex运行一遍;最后打开Keil源码,从main.c开始,逐步读到App层的用药状态机。按这个顺序走,整套代码和硬件的对应关系就能建立起来了。
7. 后续还能怎么扩展
这套系统目前能完成基本的用药提醒、确认和告警,但我做的时候已经预留了一些扩展口,后面你可以按需升级。
最大的扩展方向是通信升级。现在告警走的是串口,在仿真里只能看到虚拟终端输出,实物接GSM模块后可以发短信。如果家里有WiFi,可以把串口接到ESP8266,告警信息推送到手机App或者微信小程序。用MQTT协议的话,药盒作为客户端发布消息,手机端订阅,延迟可以控制在一秒以内。
第二是出药机构的扩展。现在的舵机只做了PWM控制逻辑,实物上可以设计一个转盘式药仓,舵机转到对应格子自动落下药丸。这个机械结构不用太复杂,关键是仓位编码要和用药计划表对应上,代码里给每个用药时间组加一个仓位编号就行。
第三是数据记录和健康管理。用药确认时间可以存到外部Flash芯片(比如W25Q32),每周通过串口导出服药统计,监护人能清楚地看到这周有没有漏服、有没有准时服。再往后还可以结合心率、血压传感器,做成一个真正意义上的健康管理终端。
我个人做这个项目最大的体会是:嵌入式系统设计里,硬件和软件从来都是一体的。原理图上多一颗电容,代码里就少一个诡异Bug;代码里多一个状态机,硬件上就少一堆互相干扰的标志位。智能药盒这个项目难度适中,覆盖的外设类型又比较全面——时钟芯片、显示、按键、传感器、PWM控制、通信接口全都有,做完一遍基本能把STM32的主流外设都摸熟。如果你正想找个嵌入式项目练手,或者准备做毕业设计,从这套开源方案开始是个不错的选择。