拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于51单片机的智能药盒设计与实现:从定时器到DS1302

简介本资源是一套基于51单片机的智能药盒完整嵌入式开发工程面向嵌入式初学者、电子类课程设计学生及老年健康设备开发者解决用药提醒不及时、操作复杂等实际问题。压缩包共36个文件含6个核心C源文件main.c、DS1302.c、uart.c、gsm.c、lcd1602.c、common.c、6个头文件.h、6个编译中间文件.obj、6个列表文件.lst及Keil工程配置文件.uvproj、.uvopt等总大小仅111KB结构规范便于理解模块划分与工程构建流程。已有2036人学习下载。读者可直接获取可烧录的.hex固件、带注释的多模块驱动代码实时时钟、GSM短信提醒、LCD1602显示、UART通信、语音播报WT588D支持、完整Keil工程环境配置及编译日志特别适合通过真实项目掌握51单片机外设协同开发与低功耗健康管理设备实现逻辑。 我妈有高血压药每天固定两顿可她就总是嘴上说“吃了”实际上转头就忘。我在购物平台上看过那种智能药盒少说两三百功能还死板。后来我干脆自己动手用一块51单片机搭了一个智能药盒能设定好几个吃药时间到点蜂鸣器响、指示灯闪还能在屏幕上显示现在几点了、该吃什么药。这个项目做完花了不到一百块从画电路到硬焊再到调程序全都是我一个人搞定的放在单片机课程设计里也绝对是拿得出手的那种。51单片机智能药盒这个题材技术点非常经典几乎把单片机的核心外设都串起来了GPIO控制、定时器计数器、中断、串行通信协议DS1302的三线时序、按键扫描、状态机编程全都有。如果你正在准备51单片机课程设计或者单纯想给家里老人做个实用的小电器这篇内容应该能帮你省下不少弯路。1. 项目整体设计与方案选型1.1 智能药盒的核心问题它到底在解决什么做任何项目之前先别急着焊板子先把问题想清楚。药盒的核心痛点特别简单老人的吃药时间靠人记记不住就漏。纸质的药单写得再清楚老人可能看不清、看不懂、或者根本不看。所以智能药盒本质上是一个“时间提醒 服药确认”的小型嵌入式系统需要干三件事准确计时随时知道现在是几点几分。在预设时间触发提醒声音和光都得有光不能太刺眼、声音不能太柔和老人耳背。操作要极其简单老人只需要在响铃时按一个“确认”键不用去编程、不用管菜单。至于吃药记录、远程提醒、语音播报这些都是加分项。我第一版没有做先把最核心的跑通。在设计目标上我给自己定了几个硬指标成本控制在100元以内单次充电或插电后能长期运行时间断电不丢失按键不能太多最多三个供老年人使用界面显示的字要大。这些指标直接影响后续选型比如为什么要选LCD1602而不是OLED就是因为1602的字大、还便宜。1.2 方案选型为什么用51单片机而不是STM32或者Arduino很多人一听到智能药盒第一个念头就是上STM32甚至树莓派这其实是用大炮打蚊子。药盒需要做的事情——显示、计时、按键、响铃——处理的数据量极小逻辑也不复杂压根不需要Cortex-M4级别的算力更不需要Linux系统。51单片机在这个场景里反而是最优解原因有三成本极低STC89C52零售也就几块钱算上外围器件全套做下来成本不过百。资料极多郭天祥的《十天学会单片机》、江科大B站UP主的定时器计数器视频、普中的开发板例程几乎横扫所有新手问题。你搜“51单片机定时器计数器工作原理”出来的资料够看一个月。51的外设简单没有复杂的企业级工具链Keil STC-ISP就能搞定所有流程非常适合个人DIY和课程设计交付。有人可能担心51单片机性能不够128字节RAM够用吗我用实际项目回答你够。我程序里同时跑了DS1302时间读取、LCD1602显示刷新、按键扫描、蜂鸣器控制RAM还剩下几十个字节。关键不是芯片性能多强而是代码规划是否合理。主控我选的是STC89C52RC普中开发板上那块Flash 8KRAM 512字节。如果你手头已经有开发板直接在上面做原型验证是最快的后面想做成正式产品再画一块最小系统板也行。2. 硬件设计与核心模块拆解2.1 硬件框架与关键引脚分配整个系统硬件上其实是五部分主控、时钟模块、显示模块、按键输入、提醒输出。我用了一个普中51单片机开发板当底座然后外接DS1302时钟模块和LCD1602显示屏。引脚分配如下模块引脚连接说明LCD1602数据线P0.0 - P0.78位并行数据P0需接上拉电阻LCD1602 RSP2.0寄存器选择0指令/1数据LCD1602 RWP2.1读/写选择接地简化LCD1602 EP2.2使能信号DS1302 CEP1.0片选DS1302 SCLKP1.1时钟线DS1302 IOP1.2数据线蜂鸣器P1.7通过三极管驱动按键设置P3.2进入菜单/切换按键加P3.3数值加按键确认P3.4确认/停止响铃供电采用USB 5V进来LCD1602直接吃5VDS1302模块板载3.3V稳压。这里有个细节要注意DS1302模块上的CR2032纽扣电池是保持时间的关键断电后靠它供电时间才不会清零药盒装在床头常年插电偶尔拔掉也不会丢时间。2.2 时钟模块DS1302的选型与使用要点时间模块是整个药盒的心脏时间不准提醒就全乱套。市面上常见的实时时钟芯片有DS1302和DS3231两种我第一版用的是DS1302原因只有一个便宜模块不到五块钱。DS1302是Dallas现在叫Maxim出的实时时钟芯片走三线串行接口内置了秒分时日月周年寄存器数据是BCD码格式。外部接32.768kHz晶振配合纽扣电池主系统断电它自己照样走时。三线接口分别是CE片选、SCLK串行时钟、IO数据线时序和SPI有点像但又不完全一样稍微细看就能掌握。这里有一个关键注意点DS1302的32.768kHz晶振对负载电容很敏感模块上通常会预焊两颗6pF电容如果买到的模块没有电容走时会偏得离谱一天可能差几分钟。我一开始买过一个裸芯片自己焊晶振没加电容结果一天慢了三分钟后来换成带电容的模块才稳定。如果你要自己画板子晶振旁边这两颗电容绝对不能省。DS3231则是另一个选择自带温度补偿晶振走时精度比DS1302高一个数量级一个月误差不超过一分钟但是模块价格大概十几块。课程设计要求不高的话用DS1302就够了给老人做真正长期使用的产品建议一步到位上DS3231。2.3 显示与人机交互LCD1602和三个按键显示方面我选了LCD160216列2行蓝底白字字大清晰老人隔着半米也看得清。第一行显示药品名称和状态比如“降压药 已吃”第二行显示时间“08:30 周二”。这里有个经验LCD1602如果接到P0口必须接上拉电阻因为P0是开漏结构直接驱动会显示不出来。开发板上一般已经集成了排阻独立做板子的时候要记得加10K排阻。按键方面只设计三个设置键、加键、确认键。设置键进入时间设置模式加键调整数值确认键保存并退出同时响铃状态下按确认键可以停止响铃表示“这顿药我吃了”。没有做减键是为了减少老人误操作的可能性。在设置模式下长按加键可以让数字连续增加设置速度更快这个逻辑在软件里用一个标志位定时器扫描实现。界面菜单设计越少层越好最好一层搞定。按下设置键屏幕上某个数字开始闪烁按加键变数字再按设置键切到下一个要修改的字段最后按确认退出。这样就不用做什么多级菜单老人学一遍就记住了。2.4 提醒输出从蜂鸣器到舵机出药机构最基础的提醒方案是蜂鸣器加LED灯。蜂鸣器我选的无源蜂鸣器模块它需要给一个方波信号才响频率不同声音也不同。驱动电路上蜂鸣器不能直接接单片机IO口因为51单片机IO口驱动能力很弱要加一个NPN三极管S8050来放大电流。基极串一个1K电阻接P1.7集电极接蜂鸣器负极蜂鸣器正极接VCC这样就够响了。无源蜂鸣器发声的代码在定时器里讲思路是在中断里反转IO电平比如要发2kHz的声音就让IO每250微秒翻转一次。蜂鸣器响三十秒后自动停避免老人正好不在旁边、一直干响浪费电也避免吓到人。进阶一点的提醒方案是加一个SG90舵机做“出药抽屉”。到点后舵机带动一个药用抽屉往外推一点老人取走药后把抽屉推回去系统通过一个微动开关检测抽屉是否复位如果没复位蜂鸣器继续响。这一版我做了原型效果挺好但要注意SG90舵机启动瞬间电流很大最好单独供电和单片机部分共地就行不能直接从开发板5V口取电否则系统会重启。3. 软件实现定时器、时间驱动与状态机3.1 定时器计数器的底层配置如何用定时器0织出系统心跳51单片机定时器计数器是这套系统的时间基础。这里我强烈建议先把原理搞透而不是只抄代码。定时器的本质是内部一个16位计数器每个机器周期加一从初值加到65536溢出时产生一次中断。溢出一次要多久由初值决定。以12MHz晶振、标准12T模式为例一个机器周期正好是1微秒所以要得到1毫秒的中断周期初值就是65536 - 1000 64536转十六进制是0xFC18。我把定时器0配置成了1毫秒中断一次然后在中断服务函数里对毫秒累加每1000次置一个1秒标志位。主循环检测到标志位就去刷新时间、判断是否该响铃这样做的好处是繁杂的显示刷新和按键处理都在主循环里不在中断里中断保持短小精悍不会因为占用时间过长而出乱子。void Timer0_Init(void) { TMOD 0x01; // 定时器0模式116位 TH0 0xFC; // 1ms定时初值高字节 TL0 0x18; // 1ms定时初值低字节 ET0 1; EA 1; TR0 1; } unsigned int msCnt 0; bit sysSecTick 0; void Timer0_ISR(void) interrupt 1 { TH0 0xFC; TL0 0x18; msCnt; if(msCnt 1000) { msCnt 0; sysSecTick 1; } }注意一个坑网上很多例程用的是定时器0模式0也就是13位计数器初值算法和16位不一样容易算错。直接统一用模式116位最省心。如果你用的是STC 1T系列单片机机器周期是12分频之后的1/12微秒同样想要1毫秒中断初值算法要改这点在选型时就要想清楚。3.2 DS1302驱动三线时序读时间BCD码转换DS1302的驱动是整个程序里最容易写错的部分。它的时序是先拉高CE然后送一个8位命令字节命令字节最高位固定为1bit0代表读写方向接下来就是读写数据。写时序是SCLK上升沿时数据写入读时序是SCLK下降沿时数据输出。核心读写函数如下void DS1302_WriteByte(unsigned char dat) { unsigned char i; for(i 0; i 8; i) { DS1302_IO dat 0x01; dat 1; DS1302_SCLK 0; DS1302_SCLK 1; } } unsigned char DS1302_ReadByte(void) { unsigned char i, dat 0; for(i 0; i 8; i) { dat 1; DS1302_IO 1; if(DS1302_IO) dat | 0x80; DS1302_SCLK 0; DS1302_SCLK 1; } return dat; }读时间的时候寄存器地址从0x80开始秒寄存器0x82是分0x84是时依次往下。DS1302内部存放的数据是BCD码格式比如十进制的23在芯片里存的是0x23所以读出来的高4位是十位、低4位是个位需要用BCD转十进制的函数处理unsigned char BcdToDec(unsigned char bcd) { return (bcd 4) * 10 (bcd 0x0F); } unsigned char DecToBcd(unsigned char dec) { return ((dec / 10) 4) | (dec % 10); }设置时间的函数要把十进制时间先通过DecToBcd转成BCD码然后按寄存器地址依次写入。写之前先关掉写保护向0x8E寄存器写0x00写完之后再打开写保护写0x80防止后续程序误改时间。这块如果写反了时间会写不进去排查半天最后发现是写保护没打开。读时间的操作我建议把秒分时一起连读出来并且在读之前先停一下DS1302的秒更新把秒寄存器的halt位置1读完再清零否则刚好卡在进位瞬间读可能读到“08:59:60”这种脏数据。上一版代码没做这个处理用一段时间后偶尔时间会跳变排查了很久。3.3 主循环状态机按键扫描、菜单切换与响铃确认逻辑主程序如果全写成顺序结构后期加功能就乱套了。我一开始就定义了一个非常简单的状态机只有三个状态正常显示、设置时间、设置闹钟。typedef enum { MODE_NORMAL, MODE_SET_ALARM, MODE_SET_TIME } UIMode; UIMode uiMode MODE_NORMAL;主循环里不断根据当前状态调用不同的处理函数状态只有通过按键触发切换互不干扰这比一长串if else清晰得多也好调试。按键处理必须消抖。最简单的方案就是检测到按键按下后延时20毫秒再检测一次如果还是低电平就认为是有效按键。这块代码看着简单但不能省不加消抖的后果是按下一次设置键状态直接跳了两三个你必须关机重来。如果发现按键偶尔连跳除了消抖还可以考虑在按键松开时再触发一次逻辑但我实际用下来延时消抖已经够用了。响铃的判断逻辑核心在一个“确认标志”上bit alarmConfirmed 0; if(sysSecTick) { sysSecTick 0; DS1302_ReadTime(); if(curHour alarmHour curMin alarmMin !alarmConfirmed) { AlarmOn(); // 开蜂鸣器置响铃标志 } if(curMin ! alarmMin) { alarmConfirmed 0; // 分钟变了重置确认状态 } }这个逻辑可以防止一个常见问题如果响铃后老人按了确认键alarmConfirmed置1但下一分钟判断条件又成立蜂鸣器又会响一次。所以每当分钟数改变时要把确认标志清零这样下一轮的整点提醒还能正常工作。响铃过程中定时器还要继续计时所以响应30秒后自动停止的逻辑可以放在主循环里用另一个计数器实现。3.4 舵机PWM的实现和出药动作控制如果你有兴致做出药机构舵机控制是绕不开的。SG90舵机的控制信号是50Hz的PWM波周期20毫秒高电平脉宽在0.5毫秒到2.5毫秒之间对应0到180度。51单片机没有硬件PWM模块我直接用定时器1模拟1毫秒中断一次在中断里对脉宽计数同时用一个变量控制电平翻转。要更精细地控制舵机位置可以改成0.1毫秒中断把脉宽精度提上去。这里有个经验舵机在快速转动时瞬间电流可以飙到几百毫安如果电源太弱单片机会被拉低电压表现为“按钮按下整个系统都重启”。我的做法是给舵机单独一个供电线5V电源直接进舵机舵机信号线再接单片机的P3.4地线共地不能和工作电路直接用一个稳压IC。出药动作我设计得很保守到点后蜂鸣器响的同时舵机缓慢转到90度把一个装药的小抽屉往前推大约2厘米老人把药取走后把抽屉推回去舵机再转回初始位置。整个过程很直观老人使用不需要任何学习成本。4. 常见问题与排查技巧实录4.1 程序下载失败、芯片不识别STC单片机下载用的是串口STC-ISP软件有经典要求先点“下载”按钮然后再给单片机上电这个叫冷启动。如果总提示“仍在连接中请给MCU上电”多半是操作顺序错了。另外检查USB转串口芯片的驱动是否装好设备管理器里能看到COM口才正常。我踩过一次坑是下载线太长导致信号衰减换了一根短线就成功了。4.2 LCD1602白屏或显示乱码先别怀疑代码。第一件事是调对比度电位器背部那个蓝色的可调电阻旋转到屏幕上出现清晰的黑色字体。第二件事检查P0口上拉电阻如果没有上拉1602基本什么都不显示。第三件事检查初始化时序初始化指令0x38、0x0C、0x06之间必须加上15毫秒以上的延时不能连发否则LCD反应不过来。4.3 时钟走时不准、每隔几天就偏DS1302对晶振极其敏感。最有效的排查是看模块有没有6pF负载电容没有就焊上去。其次避免把DS1302模块放在电源变压器旁边高频干扰会直接影响走时。如果这些调完还是偏干脆换DS3231模块一个月误差不超过1分钟省心。4.4 蜂鸣器不响、声音沙哑无源蜂鸣器不会自己响必须由程序给方波。如果你听到“滋滋”的沙哑声说明驱动频率不对或者IO翻转速度不均匀尤其是用延时写方波时其他中断会插进来导致频率不稳定。我建议把方波生成放在定时器中断里有一个简单的变量控制翻转主循环只负责开/关蜂鸣器问题就解决了。4.5 舵机乱转、定时器互相干扰先检查是不是PWM周期不稳定。51单片机开多个定时器时中断优先级和重装初值要特别小心定时器0和定时器1都要在中断开头重新赋初值否则累计误差会越来越大最终舵机抖动。硬件层面检查电源容量舵机启动电流大加一个470uF电解电容在舵机电源引脚旁边能明显改善抖动。5. 扩展方向与个人心得5.1 值得做的三个升级语音提醒、远程设置、服药日志蜂鸣器响虽然响但老人分不清是“该吃哪种药”。我后来加了一块ISD1820语音模块预先录一段“该吃药啦”触发提醒时直接放语音效果比纯蜂鸣器好太多而且模块非常便宜接线也只有电源、地、触发脚三根。强烈建议加上。远程设置这个方向最实用的是加一块ESP8266模块通过串口和51单片机通信。手机连上WiFi后在微信小程序里设置好吃药时间通过局域网发给ESP8266再通过串口把数据写到51单片机内部的Flash或外挂的EEPROM里。虽然涉及一点上位机开发但整个数据链路并不复杂做完以后药盒的可玩性完全不一样。服药日志这块可以在Rx按键按下时把当前时间和药品编号写到AT24C02里攒一周的数据通过串口发到电脑上就能看到连续七天的吃药情况。这功能对医生调整用药方案有很大帮助如果是给家里老人做的正式版本我建议一定加上。5.2 这个项目教会我的几件事做这个智能药盒下来我最深的体会有三件事。第一硬件和软件必须一起设计不能先画完所有硬件再写程序。我第一版把蜂鸣器接在P3.0上到写软件时才发现P3.0同时是串口RXD引脚每次用串口下载程序都要把蜂鸣器线拔掉。后来改到P1.7才解决所以引脚分配时先想想哪些引脚要复用。第二状态机是单片机程序的基本思维模型。把所有运行状态列清楚画出状态转换关系再动手写代码你会发现程序逻辑特别清晰出bug的概率大幅下降。我现在做任何单片机项目第一步都是画状态表而不是抄例程。第三供电问题是最大的隐形杀手。各种“程序莫名跑飞”“按键失灵”“舵机乱转”的场景最后查出来基本都是电源纹波大、电流不够、地线没共好。遇到问题先把示波器或者万用表戳到电源引脚上测一测能省掉一半排查时间。这个药盒后来一直放在家里用除了隔几个月要换一次DS1302纽扣电池基本没再动过。至于外壳我用亚克力板拼了一个小盒子正面开个口装1602屏侧面三个按键露出来整体不大放床头柜上正合适。如果你也准备做一个我的建议是第一版就按最简单的方案先跑通蜂鸣器能用就先用蜂鸣器所有高级功能等基本逻辑稳定了再加别一上来就野心太大做一半卡住才是最打击人的。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门