很多人第一次看到“智能药盒/老人用药管理系统”这个项目时第一反应是“不就是个定时提醒闹钟嘛”但实际上手后才发现真正麻烦的不是“定时”本身而是怎么把吃药这件事做得足够可靠让老人愿意用、不会忘、不会错。这个基于STM32的开源项目从底层驱动到上位机逻辑都做了完整闭环还配了原理图和Proteus仿真工程非常适合作为单片机综合项目练手也适合学生做课程设计或毕业设计参考。我拿到这套资料后完整梳理了一遍代码结构清晰外设用得比较全面但又不过度堆料——基本就是STM32F103系列、OLED/LCD显示、按键输入、蜂鸣器提醒、LED指示外加RTC实时时钟作为核心时间基准。这套设计最大的好处是所有模块都能在Proteus里仿真跑通意味着你即使手头没有实物硬件也能把整个系统的逻辑、状态切换、异常处理全部验证一遍。接下来我把整个项目的设计思路、硬件原理、代码架构和实操踩坑经验一次性讲透。1. 项目整体设计与功能拆解1.1 为什么选择STM32作为主控药盒系统的核心需求是“按时、按量、不错、不漏”所以主控必须有可靠的时间基准、足够多的I/O控制各种外设、以及一定的低功耗潜力。STM32F103C8T6在这几个维度上表现得非常均衡ARM Cortex-M3内核72MHz主频内带RTC配合外部32.768kHz晶振可做实时时钟还带了丰富的定时器、I2C、SPI、UART和ADC价格便宜资料也多到爆炸。更重要的是ST官方提供了HAL库和标准外设库两套开发方式配合STM32CubeMX可以图形化配置引脚和时钟树大幅降低入门门槛。这个开源项目采用的是标准库风格代码虽然看起来比HAL库“啰嗦”但对于学习者来说反而能看清楚每一个寄存器操作背后的含义也方便在Proteus中直接烧录hex文件进行仿真。从系统架构上说STM32扮演的是“总调度”的角色它负责读取当前时间与预设的服药时间表比对到了设定时间就触发声光提醒同时可以通过按键完成“已服药”确认、暂停提醒、时间校准等操作。所有状态都在LCD屏幕上显示老人或照护者一眼就能看懂下一次吃药的时间点。1.2 功能需求整理与模块划分我在整理这个项目时习惯先把需求拆成一个个独立模块再逐个实现。这套智能药盒的完整功能可以分成下面几个层次时间管理上电后显示当前日期、时间、星期支持按键调整时间内置RTC独立走时断电后靠备用电池或法拉电容维持。用药计划可以预设多个服药时点比如早上8点、中午12点、晚上6点每个时点对应相应的药仓编号和用药数量。提醒执行到点后同时触发蜂鸣器鸣叫、LED闪烁、屏幕弹窗提醒并进入“待确认”状态如果老人按下“确认”键则记录本次服药完成如果超过一定时间没有确认则提高提醒强度或通过串口向上位机发送“漏服”信息。人机交互LCD显示菜单和状态按键负责时间调整、方案选择、服药确认实践中也可以换成触摸屏但会增加成本和程序复杂度本项目按键方案更稳。扩展接口预留串口和I/O口后续可接ESP8266/WiFi模块把漏服数据推送到手机或者接GSM模块发短信给子女。这也是这个项目得名“管理系统”的原因它不只是本地提醒而是一个可以远程监控的终端节点。从模块划分来看这个项目涉及RTC、GPIO、外部中断、定时器、I2COLED、按键消抖、蜂鸣器PWM控制等多个知识点恰好覆盖了STM32初学到进阶的常用外设。2. 核心硬件电路设计与原理图解读2.1 最小系统电路与电源设计项目提供的原理图里最小系统部分比较标准STM32F103C8T6作为主控8MHz晶振通过OSC_IN/OSC_OUT引脚接入两个20pF负载电容并联到地配置正确的话系统时钟可以稳定倍频到72MHz。复位电路采用10kΩ上拉电阻100nF电容到地NRST引脚低电平复位这是最经典的阻容复位电路。BOOT0通过10kΩ电阻下拉到地确保从Flash启动。电源部分的设计有一个细节值得注意系统主电源采用USB的5V输入经过一个AMS1117-3.3稳压芯片降到3.3V给MCU和其他外设供电。输入输出各接10uF和100nF电容做滤波稳压器输出端还加了一个LED做电源指示。如果你打算用电池供电或者加电机震动模块建议另外单独做电机驱动电源不要直接从MCU的3.3V引脚取大电流否则电压跌落会导致系统重启。RTC的备用电源电路很多人会忽略但药盒这种设备一旦关机再开机时间如果回到初始值所有定时逻辑就全乱了。项目里预留了VBAT引脚可以接一颗CR1220纽扣电池或者一个法拉电容。我用的是5.5V/0.33F的法拉电容实测断电后维持RTC走时超过24小时性价比比纽扣电池高而且不用担心没电更换。2.2 显示、按键与提醒电路的关键设计显示部分采用的是I2C接口的0.96寸OLEDSSD1306。I2C只需要SCL和SDA两根线极大节省引脚4个引脚VCC、GND、SCL、SDA插上就能用。原理图上要注意给SCL和SDA分别接4.7kΩ上拉电阻这是I2C总线规范要求的。如果漏了上拉电阻通信时会出现乱码甚至完全无响应。OLED的VCC可以直接接3.3V不要接5V虽然大多数模块带了稳压但长期5V供电容易发热损坏。按键电路常见的方案有两种一种是一端接GND、一端接IOIO内部上拉另一种是接VCC、IO内部下拉。项目里用的是第一种同时外接10kΩ上拉到3.3V以提高抗干扰能力。每个按键都并联了一个100nF电容做硬件消抖这个细节在仿真中可能不太明显但在实物中非常关键尤其是在电源纹波较大的时候。蜂鸣器提醒电路用的是一片NPN三极管如S8050驱动有源蜂鸣器。STM32的GPIO灌/拉电流能力有限直接驱动蜂鸣器会烧引脚所以必须加三极管或MOS管做开关。基极串一个1kΩ限流电阻发射极接地集电极接蜂鸣器到VCC。这样MCU引脚只要输出高电平三极管导通蜂鸣器发声。如果是无源蜂鸣器则可以用定时器输出PWM驱动特定频率但提醒用的有源蜂鸣器更省事。2.3 原理图绘制与PCB布局要点拿到项目原理图后建议用立创EDA或者Altium Designer打开重新梳理一遍而不要直接拿去打板。为什么要重新梳理因为开源项目的原理图和PCB往往是作者当时的实验版本有些定位孔、丝印、封装可能和你手头的器件不完全一致。我按这套原理图自己画过一版PCB几个经验值得分享晶振要靠近MCU走线尽量短不要在晶振下方走其它信号线否则容易引入杂散电容导致起振失败。去耦电容靠近电源引脚每个VDD引脚旁边放一个100nF电容电源入口放一个10uF钽电容。STM32对电源纹波不敏感但ADC和RTC对电源稳定性要求较高。按键和OLED接口放在板边方便固定在药盒外壳上。如果要做成实物尽量留出插针或者杜邦线接口方便拆装。高压和低压部分严格分开蜂鸣器驱动、电机驱动这类有感性负载的电路要与MCU引脚保持距离地线单独走防止反电动势干扰复位。原理图中的标识符号也要注意晶振用“Y”开头、电阻“R”、电容“C”、按键“S”、LED“D”、三极管“Q”这是一般规范。我见过不少学生画的原理图不看位号后面焊接对不上坐标那叫一个痛苦。拿到开源资料先给所有器件做编号核对封装和原理图符号再去layout能省一半时间。3. 软件架构与核心代码逻辑实现3.1 工程文件结构与开发环境准备项目代码是在Keil MDK环境下开发的目标芯片是STM32F103C8T6我在实际打开工程前先做了两件事一是安装Keil.STM32F1xx_DFP固件包二是用ST-Link或者J-Link作为调试器。如果你没有实物只想仿真那么编译生成hex文件后直接加载到Proteus的STM32芯片模型里就可以运行。打开工程后你会发现代码结构挺清晰大概分成了这几个部分main.c主函数包括系统时钟配置、外设初始化调用、主循环状态机。rtc.c / rtc.hRTC初始化和时间读写函数。oled.c / oled.hSSD1306驱动包含屏幕初始化和字符/数字显示函数。key.c / key.h按键扫描与消抖处理。buzzer.c / buzzer.h蜂鸣器控制。med_plan.c / med_plan.h服药计划存储、比较和状态管理。usart.c / usart.h串口打印和扩展通信可选。这种分层设计的好处是每个模块功能单一后续想改显示设备、换按键类型、加WiFi模块都只需要改动对应文件不影响整体逻辑。在实际项目中我一般还会加一个“config.h”统一存放引脚宏定义和数据结构体但这个项目里是分散在各个头文件中的问题不大自己看懂就行。3.2 RTC时间基准与服药计划的数据结构时间系统是整个药盒的心脏。STM32F103的RTC本质上是一个独立的定时器它依靠外部32.768kHz晶振分频产生1Hz的计数脉冲然后通过一组32位寄存器保存当前时间秒、分钟、小时、日、月、年。代码里通过读取RTC寄存器后转换成BCD或十进制格式再在OLED上格式化成“YYYY-MM-DD HH:MM:SS”。这个项目里服药计划是用结构体数组来组织的我强烈建议你在学习时也这样定义typedef struct { uint8_t hour; // 服药时点小时 uint8_t min; // 服药时点分钟 uint8_t medIndex; // 药仓编号A/B/C等 uint8_t dose; // 用药量例如2片 uint8_t taken; // 是否已确认服药0否 1是 } MedPlan_t; #define MAX_PLAN_NUM 4 MedPlan_t plan[MAX_PLAN_NUM];主循环每秒读一次当前时间然后与plan数组里的每个元素比对如果小时和分钟相等且taken标志为0则触发提醒函数同时把taken置为1避免重复触发。当用户按下“确认”键时将对应项的taken状态清除进入下一轮。当然这个逻辑是简化版真实的项目还需要处理“跨天重置”问题——比如第二天的早上8点应该重新允许提醒所以每次日期变化时要把所有plan项的taken清零。这里我补充一个常见bug很多初学会把时间比较写成“时和分完全相等”但这存在两个问题。第一如果主循环是轮询方式秒数可能跳过导致判断不到正点第二判断窗口太窄万一系统卡顿错过秒数提醒就漏了。更稳妥的写法是记录“上次检查的秒数”当秒数从59跳到00时再检查上一分钟是否匹配或者直接定时器中断里做秒计数然后在秒为0的时刻做请求标志位。3.3 按键扫描、消抖与菜单逻辑按键部分是这个项目里最考验代码功底的地方。机械按键按下瞬间会产生约5-10ms的电平抖动直接用GPIO读电平判断大概率会一次按下触发多次。项目代码里采用延时消抖检测到引脚低电平后延时10-20ms再次读取如果仍然低电平才判定为有效按下。但是只做延时消抖还有一个隐患如果在等待消抖期间有其他按键按下会丢失事件。更好的做法是用定时器中断做状态机扫描每1ms扫一次按键矩阵维护每个按键的按下计数和释放计数达到阈值才触发动作。这个项目用的是简单延时方式考虑到交互逻辑不复杂完全够用。如果你后续想扩展更多按键建议把这段重构成状态机扫描。菜单设计上项目采用了两级逻辑主界面显示“当前时间 下一次服药倒计时”长按“设置”键进入设置界面可以按“加/减”键调整时间按“确认”键保存退出。按键的短按和长按检测可以通过计时变量实现代码里用了一个简单的计时器按下时开始计数超过800ms判定为长按。在OLED上画菜单时要注意字符刷新方式建议先清屏再整体重绘避免局部刷新出现残影。3.4 报警提醒与漏服处理逻辑到了服药时间点提醒模块需要做三件事亮灯、响铃、显示。项目用GPIO控制LED闪烁、用PWM或IO翻转控制蜂鸣器同时OLED切换到提醒页面显示“TIME TO TAKE MEDICINE”和具体的药仓编号。比较合理的是采用“间歇式提醒”——响3秒停2秒循环持续一分钟不是一直长鸣因为长鸣反而容易被忽略或者惹人烦。接着是漏服处理如果提醒发出后2分钟内没有任何按键操作系统会自动将本次状态标记为“漏服”并通过串口输出一条格式化记录比如“2025-01-18 08:00 MED_A MISSED”。如果接了ESP8266可以改成UDP/HTTP上报。这个设计思路很重要药盒的价值不仅仅是现场提醒更是为家属或后台系统提供可追溯数据所以在代码里我建议把所有事件开机、确认、漏服、校时都打上时间戳记录在结构体或EEPROM中方便后续导出分析。4. 基于Proteus的仿真与调试实录4.1 仿真工程搭建与元件连接项目附带的仿真文件是Proteus 8版本打开后你会看到STM32F103C8T6芯片、OLED、按键、蜂鸣器、LED等器件的连接图。如果你从零开始搭需要注意的几件事Proteus中STM32的仿真模型需要加载hex文件才能运行默认上电后不会执行任何程序双击芯片在Program File路径中选择Keil编译生成的hex文件然后设置外部晶振频率为8MHz如果程序里配置的是HSE。OLED在Proteus里通常用“SSD1306 OLED Display”模型I2C地址默认0x78即0x3C左移一位。如果仿真时屏幕不亮或者花屏大概率是I2C通信不稳定可以把I2C频率降低一点比如把HAL库里的I2C时钟设为100kHz标准模式不要用400kHz快速模式仿真模式下快速模式有时会出毛刺。蜂鸣器和LED的限流电阻在仿真中看起来无所谓但必须保留因为在真实电路里少了限流电阻就是烧器件。仿真是不能替代硬件测试的但能帮你验证逻辑流程正确性特别是状态机跳转和按键边界情况。4.2 高频报错“no stm32 target found”是怎么回事在实际下载调试时很多初学者会在Keil的下载设置里看到“Error: Flash Download failed - Target DLL has been cancelled”或者“No STM32 Target Found! If your product embeds debug authentication, please...”这类的报错信息。别慌这不是代码问题几乎总是调试器、接线或芯片设置导致的。最常见的场景是用ST-Link连接STM32时Keil的Debug设置里没选对调试器或者选择了但驱动没装好。解决方法分三步走确认ST-Link驱动插上ST-Link后打开设备管理器确认“STLink dongle”出现在端口设备里。如果显示未知设备或感叹号需要重新安装ST-Link驱动在ST官网或ST-Link Utility安装包里可以找到。Keil设置检查点魔术棒Options for Target进入Debug选项卡左侧选择“ST-Link Debugger”再进入Settings确认能识别到IDCODE和Device Name。如果这里显示“No target connected”多半是接线问题。接线核实ST-Link的SWDIO接芯片SWDIO、SWCLK接SWCLK、GND接GND、3.3V接VTref目标板参考电压。注意STM32F103C8T6的SWDIO是PA13、SWCLK是PA14别接错了。如果你用的是20针JTAG转SWD的转接板要看清引脚顺序转接线接反是最常见的人祸。如果以上都检查了还是同样的报错还有一种可能是芯片内部debug authentication被锁住了——这常见于你之前烧录过保护级别的程序比如RDP Level 1此时需要先用ST-Link Utility里的“Connect under reset”模式去解锁或者直接按着复位键再点击下载在下载瞬间松手。项目里是没有加读保护的所以你复现时不会遇到这种情况但以后调试自己的板子会碰到。4.3 仿真中常见时序问题排查在Proteus里跑这个项目最常遇到三种现象一是OLED不刷新二是一直处在“系统时间不动”的状态三是按键按了没反应。OLED不刷新90%是因为I2C时序不对或者地址不对。代码里如果初始化函数里写的是“0x78”那么对应的是0x3C左移一位如果你手上的OLED实际是0x3D那组地址就需要修改宏定义。Proteus模型通常支持指定地址双击OLED元器件查看“I2C Address”属性。RTC时间不走的根源一般是仿真模型对32.768kHz晶振的支持不够好。Proteus仿真RTC时建议直接跳过外部晶振强行用内部LSI时钟代码里把RTC时钟源选择为LSI仿真速度也更快。如果一定要在仿真中体现外部晶振可以给晶振两端并一个1MΩ电阻并确保负载电容匹配这样更容易起振。按键没反应的排查顺序是先看GPIO上下拉配置是否正确再看代码中按键触发电平是否有误最后看Proteus中按键模型是否接地/接电源。很多人在Proteus里放了一个按键但是引脚没有连到正确的网络名导致电平始终浮空程序自然读不到变化。交叉查看工程里的网络标号比对着原理图逐一排查一般几分钟就能定位。5. 项目资源使用与扩展改造建议5.1 开源资料包内容说明这套开源项目压缩包解压后核心目录大致如下Hardware/ schematic.pdf // 原理图PDF版方便直接查阅 schematic_source/ // 原理图工程源文件可能为AD或立创EDA格式 PCB/ // PCB layout工程可打样 Software/ stm32_project/ // Keil工程源码 hex/ // 编译好的固件可直接烧录 Simulation/ medicine_box.pdsprj // Proteus仿真工程 README.txt // 仿真使用说明 Doc/ 使用说明.pdf // 软件操作、时间设置说明我建议这样的使用顺序先看仿真工程——再编译源码——最后动手搭实物。仿真工程能让你在5分钟内看到效果建立直观认识编译源码可以帮你理解每段程序的作用实物制作则是对整体能力的考验。当然如果你是学生赶时间交设计报告也可以直接烧录hex看效果但那样确实很亏因为代码注释和模块划分都相当清晰不学一遍有点暴殄天物。5.2 从药盒到一个“用药管理终端”的扩展思路这个项目最棒的地方是它的“留白”——基础版本做到了定时提醒和状态显示但系统架构和预留接口允许你做很多二次开发。我个人尝试过几个扩展方向都跑通了分享给大家参考。扩展1增加语音提示。用一个小型语音播放模块比如DY-SV5W或者老一点的JQ8900把“该吃药了”“请取A仓药物”“早间用药已完成”等音频文件放进TF卡STM32通过串口发指令播放。对不习惯看屏幕的老人来说语音提醒的效果远好于蜂鸣器。接法很简单模块TX/RX分别接STM32的一个USART引脚供电5V音频输出接喇叭。扩展2接入ESP8266实现远程提醒。用ESP8266-01S接在STM32的USART1上设置为透传模式或使用AT指令。STM32在“漏服”或者“已确认”时通过串口发送JSON字符串ESP8266用MQTT/HTTP POST到服务器或微信小程序。考虑到成本ESP8266模块只要十块钱左右两种模块共用一个3.3V电源注意ESP8266的瞬时电流可能到300mA以上供电要足。扩展3机械自动出药结构。STM32可以驱动步进电机或舵机配合3D打印的药仓结构实现自动掉药。我做过一个方案用28BYJ-48步进电机做药仓旋转盘每次旋转一个工位电位器或者红外对管做零点定位。代码里只需要在确认服药后发送脉冲给电机驱动板即可逻辑不算复杂但机械部分调起来真的很花时间建议先从仿真把流程打磨好再做实物。扩展4记录日志到EEPROM。对于家人来说老人按时吃药的历史记录比一次两次提醒更重要。STM32F103C8T6内部没有EEPROM可以外挂一个AT24C02I2C接口或者用STM32内部Flash模拟EEPROM。按每天最多8条记录计算AT24C02的256字节空间可以存好几天的日志。如果要存更久用SD卡模块或者接入无线把数据同步出去更合理。5.3 基于该项目的毕业设计/课程设计包装建议如果读者是准备做毕设或者课程设计这个项目其实可以延展出多个题目方向“基于STM32的智能药盒设计与实现”最直接“基于STM32和物联网的老人用药远程监测系统”加ESP8266“基于STM32的语音交互智能药盒”加语音模块“基于STM32与OneNet云平台的智能药箱”加云平台包装不是堆功能而是要有明确的痛点分析和系统架构图。比如“物联网版”可以将步骤分成感知层STM32药量监测、传输层WiFi/4G、应用层微信小程序/云平台这样论文和答辩时的思路都会清晰很多。核心代码逻辑其实完全复用这个开源项目的框架只需要把每一层的接口写明白。注意如果你要在设计报告里放代码一定要去理解每一段代码的功能而不只是把github上的代码贴进来。导师提问时最喜欢问的就是“如果现在时间是13:59你如何设计逻辑确保14:00准时提醒”“漏服的标志位存在哪里断电后是否丢失”这类边界问题。这个项目里RTC和服药计划的结构体设计已经能回答大部分问题但你必须自己先吃透。6. 常见问题与排查技巧速查表下面把我在实际复现这个项目过程中比较高频的问题整理成速查表方便你在调试时按图索骥。现象可能原因解决办法Keil编译报错缺少头文件未添加相应的固件库路径在C/C选项卡Include Paths中加入源码目录下的Libraries/CMSIS等路径烧录时出现No STM32 Target FoundST-Link驱动、接线、Debug设置异常检查驱动、SWD接线和Debug选项卡里的目标芯片型号OLED显示乱码I2C地址不匹配、上拉电阻缺失确认OLED地址宏检查SCL/SDA上拉电阻RTC不走时晶振未起振或RTC时钟源选择错误仿真中改用LSI实物检查32.768kHz晶振负载电容按键按下多次触发消抖不彻底加硬件电容或在代码中增加10-20ms消抖延时蜂鸣器不响GPIO配置错误或三极管驱动问题测量基极和集电极电平确认NPN三极管极性仿真中程序不运行未加载hex文件或时钟频率不匹配双击芯片加载hex外部晶振频率设为8MHz系统复位后时间丢失VBAT没有接备用电源接纽扣电池或法拉电容并检查RTC备份寄存器逻辑串口打印乱码波特率不一致或时钟配置倍频错误确认程序和串口助手的波特率及系统时钟为72MHz菜单状态错乱按键事件未进行优先级/防重入处理按键事件置标志位主循环里统一处理不中断嵌套修改状态这几个问题其实也是STM32嵌入式开发的通用雷区排查经验在这些项目里都会反复用到。比如“烧录失败”和“程序不运行”是两个截然不同的阶段前者要看调试器连接后者要看代码和硬件配置不要把时间浪费在互相怀疑上。7. 实操心得与最后的小技巧最后按惯例分享一点我在复现这个项目过程中的个人体会。第一仿真通过不等于实物一定正常。Proteus帮你验证了逻辑但像电源纹波干扰、按键抖动、OLED上电时序这些问题仿真里是看不见的。所以做实物时建议一步一步来先烧一个最简的“LED闪烁”程序确认最小系统能跑再逐个接入OLED、按键、蜂鸣器每加一个模块都单独测一次这样出了问题能快速锁定是硬件还是软件。第二代码里加日志打印真的会救命。项目原本的串口输出功能很基础我在调试时改了一下在RTC初始化成功、服药提醒触发、按键按下、漏服判定这几个关键节点都加上了一行串口日志。别小看这几行文本当你遇到“药盒半夜自己响”这种诡异问题时日志能直接告诉你到底是时间校验错了还是plan数据结构被意外篡改了。第三如果想送给家里老人用别忘了考虑易用性。按键要够大屏幕字要够大字体要和背景对比度高最好还能有语音提示。很多技术佬做的第一版药盒自己玩得转但长辈根本不会用因为界面和交互方式没有从老人视角出发。开源项目给你提供了核心逻辑交互细节一定要自己多琢磨、多让目标用户测试。第四这个项目后续最值得扩展的方向我个人认为不是“自动化出药”这种看起来很酷但可靠性很难保证的机械结构而是“数据闭环”——也就是把每次服药记录存下来并同步给子女端。毕竟对于需要长期服药的老人来说持续、准确、可追溯的用药记录才是真正的痛点。我在实际跑完整个流程后最大的感受是像这种“控制逻辑完整有原理图有仿真”的开源项目是性价比极高的学习素材系统性过一遍胜过自己瞎啃几个月手册。建议你也像我一样先花一个晚上看原理图再花一个周末把代码和仿真摸透然后试着改一个功能进去——比如加一个“每周重复”的服药计划或者改成双色LED区分“正常提醒”和“漏服报警”。只要你能独立做出其中一个改动这个项目就真正变成你自己的东西了。
