1. 项目概述
作为一名嵌入式开发工程师,我最近完成了一个基于STM32的智能药盒提醒系统项目。这个系统专为解决老年人或慢性病患者漏服药物的问题而设计,通过精准的定时提醒和便捷的远程管理功能,帮助用户按时服药。
系统采用STM32F103C8T6作为主控制器,结合DS3231高精度RTC时钟模块、HC-05蓝牙模块和24C02 EEPROM存储芯片,实现了以下核心功能:
- 高精度定时提醒(误差≤±1秒/天)
- 支持10组以上定时任务设置
- 声光双重提醒(蜂鸣器+RGB LED)
- 手机APP远程管理(通过蓝牙)
- 本地按键操作
- 掉电数据保存
- 低电量提醒
整个系统的硬件成本控制在100元以内,非常适合个人DIY或小批量生产。下面我将详细介绍这个项目的设计思路、硬件搭建、软件开发和实际使用经验。
2. 硬件设计与实现
2.1 核心硬件选型
在选择硬件组件时,我主要考虑了以下几个因素:
- 性价比:作为个人项目,成本控制很重要
- 易用性:方便初学者理解和搭建
- 可靠性:医疗相关设备需要稳定工作
最终确定的硬件清单如下:
| 模块 | 型号 | 关键参数 | 备注 |
|---|---|---|---|
| 主控 | STM32F103C8T6 | Cortex-M3, 72MHz, 64KB Flash | 性价比极高的ARM芯片 |
| RTC | DS3231 | ±2ppm精度, I2C接口 | 比STM32内置RTC精度高很多 |
| 蓝牙 | HC-05 | 串口透传, 10米距离 | 经典蓝牙模块, 易用 |
| 存储 | 24C02 | 256字节, I2C接口 | 足够存储定时任务 |
| 蜂鸣器 | 有源3.3V | 85dB声压级 | 声音足够响亮 |
| LED | RGB共阴 | 20mA电流 | 多种颜色指示不同任务 |
| 电池 | 3.7V锂电池 | 1000mAh | 配合TP4056充电模块 |
2.2 电路设计与接线
电路设计采用模块化思路,各功能模块相对独立。下面是关键部分的接线说明:
电源部分
- 锂电池正极接TP4056的BAT+
- TP4056输出接3.3V稳压模块
- 稳压输出供给所有模块的VCC
- 所有GND共地
特别注意:HC-05蓝牙模块必须接3.3V,接5V会烧毁!
I2C总线
- STM32的PB6(SCL)接DS3231和24C02的SCL
- STM32的PB7(SDA)接DS3231和24C02的SDA
- 两个设备的地址不同(DS3231:0x68, 24C02:0xA0),不会冲突
蓝牙模块
- HC-05的TX接STM32的PA10(RX)
- HC-05的RX接STM32的PA9(TX)
- VCC接3.3V,GND接GND
声光提醒
- 蜂鸣器正极接PB0,负极接GND
- RGB LED的R接PB1,G接PB2,B接PB3,共阴端接GND
- 每个LED引脚串联220Ω限流电阻
按键输入
- 三个按键一端分别接PA0、PA1、PA2
- 另一端全部接GND
- 内部上拉,按下时为低电平
2.3 PCB设计建议
虽然面包板可以快速搭建原型,但长期使用建议设计PCB。以下是PCB设计要点:
- 电源走线要宽,至少20mil
- I2C总线要短,必要时加上拉电阻(4.7kΩ)
- 蜂鸣器远离模拟电路,避免干扰
- 为蓝牙模块预留天线区域
- 加入复位按钮和Boot选择跳线,方便调试
3. 软件开发详解
3.1 开发环境搭建
项目使用STM32CubeMX + Keil MDK开发环境:
- 安装STM32CubeMX 6.9.0+
- 安装Keil MDK 5.36+
- 安装STM32F1xx系列支持包
- 配置串口调试工具(如Putty)
3.2 STM32CubeMX配置
关键外设配置如下:
I2C配置
- 模式:I2C
- 速度:Fast Mode(400kHz)
- 地址:0x00(主机模式)
- 引脚:PB6(SCL), PB7(SDA)
USART配置
- 波特率:9600
- 字长:8位
- 停止位:1
- 无校验
- 引脚:PA9(TX), PA10(RX)
GPIO配置
- PB0:输出,控制蜂鸣器
- PB1-PB3:输出,控制RGB LED
- PA0-PA2:输入,上拉,连接按键
- PA3:ADC输入,电池电压检测
3.3 核心代码实现
RTC时间处理
DS3231使用BCD码存储时间,需要转换:
c复制uint8_t BCD_To_Dec(uint8_t bcd) {
return (bcd / 16 * 10) + (bcd % 16);
}
uint8_t Dec_To_BCD(uint8_t dec) {
return (dec / 10 * 16) + (dec % 10);
}
HAL_StatusTypeDef DS3231_Read_Time(RTC_TimeDef *time) {
uint8_t buf[7];
HAL_I2C_Mem_Read(&hi2c1, DS3231_ADDR, 0x00, I2C_MEMADD_SIZE_8BIT, buf, 7, 100);
time->second = BCD_To_Dec(buf[0] & 0x7F);
time->minute = BCD_To_Dec(buf[1]);
time->hour = BCD_To_Dec(buf[2] & 0x3F);
// 其他字段类似...
return HAL_OK;
}
定时任务检查
每秒检查一次是否有任务触发:
c复制uint8_t TimerTask_Check(SystemStateDef *state) {
for(uint8_t i=0; i<MAX_TASK_NUM; i++) {
TimerTaskDef *task = &state->task_list[i];
if(!task->enable) continue;
if(task->hour == state->current_time.hour &&
task->minute == state->current_time.minute) {
// 检查重复周期
switch(task->repeat) {
case 0: return i; // 每天
case 1: // 工作日
if(state->current_time.week >=1 && state->current_time.week <=5)
return i;
break;
// 其他情况...
}
}
}
return 0xFF; // 无匹配
}
蓝牙指令解析
支持多种APP指令:
c复制void Bluetooth_Cmd_Parse(SystemStateDef *state, uint8_t *cmd, uint16_t len) {
if(strstr(cmd, "SET_TIME:")) {
// 设置时间
RTC_TimeDef time;
sscanf(cmd+9, "%d-%d-%d %d:%d:%d",
&time.year, &time.month, &time.day,
&time.hour, &time.minute, &time.second);
DS3231_Set_Time(&time);
}
else if(strstr(cmd, "ADD_TASK:")) {
// 添加任务
TimerTaskDef task;
sscanf(cmd+9, "%d,%d,%d,%d,%d",
&task.enable, &task.hour, &task.minute,
&task.repeat, &task.led_color);
// 存储到EEPROM...
}
// 其他指令...
}
4. 系统调试与优化
4.1 常见问题排查
在实际开发中,我遇到了以下几个典型问题:
-
I2C通信失败
- 现象:无法读取DS3231时间
- 排查:用逻辑分析仪抓取I2C波形
- 解决:发现上拉电阻过大(10kΩ),改为4.7kΩ后正常
-
蓝牙连接不稳定
- 现象:APP偶尔断开连接
- 排查:检查电源发现电压波动
- 解决:在蓝牙模块VCC加100μF电容
-
RTC时间不准
- 现象:每天快约10秒
- 排查:DS3231精度应该很高
- 解决:发现是软件读取时间后未及时处理,优化代码逻辑
4.2 性能优化技巧
-
低功耗优化
- 主循环加入HAL_Delay(100),降低CPU占用
- 不提醒时关闭LED和蜂鸣器电源
- 蓝牙模块设置低功耗模式
-
代码优化
- 使用DMA传输I2C数据
- 关键函数添加__inline优化
- 频繁调用的变量加register修饰
-
用户体验改进
- 按键加入防抖处理
- 提醒声音采用间歇模式(响0.5秒,停0.5秒)
- 不同药品用不同颜色LED区分
5. 项目扩展方向
基础功能实现后,可以考虑以下扩展:
5.1 硬件扩展
- 增加WiFi模块(ESP8266),实现远程提醒
- 加入RFID模块,记录服药情况
- 添加小型显示屏,本地查看任务
5.2 软件功能
- 开发iOS/Android APP,提供更友好界面
- 增加服药记录统计功能
- 实现多设备联动(如智能家居系统)
5.3 产品化改进
- 设计3D打印外壳
- 优化PCB布局,减小体积
- 通过相关医疗设备认证
6. 实际使用体验
经过一个月的实际使用测试,系统表现出色:
- 时间精度:实测每天误差<0.5秒
- 电池续航:1000mAh电池可使用约15天
- 稳定性:连续工作30天无重启
用户反馈:
- 老人能轻松识别声光提醒
- 家属通过APP远程设置很方便
- 按键操作作为备用方案很实用
需要改进的点:
- 提醒声音可以更柔和些
- 电池续航还能优化
- APP界面需要更简洁
7. 关键代码片段
7.1 主循环逻辑
c复制while (1) {
// 每秒检测一次定时任务
if(HAL_GetTick() - last_check_time >= 1000) {
last_check_time = HAL_GetTick();
// 读取当前时间
DS3231_Read_Time(&System_State.current_time);
// 检查定时任务
uint8_t task_idx = TimerTask_Check(&System_State);
if(task_idx != 0xFF && !System_State.remind_flag) {
Remind_Control(&System_State, task_idx, 1);
}
// 检查提醒超时
if(System_State.remind_flag &&
(HAL_GetTick()/1000 - System_State.remind_start_time >= REMIND_DURATION)) {
Remind_Control(&System_State, 0, 0);
}
// 每5秒检测电池
if(HAL_GetTick() % 5000 == 0) {
Battery_Check(&System_State);
}
}
// 按键扫描
Key_Scan(&System_State);
// 蓝牙数据处理
if(ble_rx_len > 0 && ble_rx_buf[ble_rx_len-1] == '\r') {
Bluetooth_Cmd_Parse(&System_State, ble_rx_buf, ble_rx_len);
}
HAL_Delay(100); // 降低CPU负载
}
7.2 声光提醒控制
c复制void Remind_Control(SystemStateDef *state, uint8_t task_idx, uint8_t enable) {
if(enable) {
state->remind_flag = 1;
state->remind_start_time = HAL_GetTick() / 1000;
// 设置LED颜色
switch(state->task_list[task_idx].led_color) {
case 0: // 红
HAL_GPIO_WritePin(LED_PORT, LED_RED_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(LED_PORT, LED_GREEN_PIN|LED_BLUE_PIN, GPIO_PIN_RESET);
break;
// 其他颜色...
}
// 蜂鸣器鸣叫
HAL_GPIO_WritePin(BUZZER_PORT, BUZZER_PIN, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(BUZZER_PORT, BUZZER_PIN, GPIO_PIN_RESET);
} else {
// 关闭提醒
state->remind_flag = 0;
HAL_GPIO_WritePin(BUZZER_PORT, BUZZER_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LED_PORT, LED_RED_PIN|LED_GREEN_PIN|LED_BLUE_PIN, GPIO_PIN_RESET);
}
}
8. 项目总结
这个智能药盒提醒系统项目从构思到实现历时约两个月,期间遇到了不少挑战,也积累了很多嵌入式开发的经验。项目成功实现了最初设定的所有功能目标,并且在实际使用中表现可靠。
关键技术点总结:
- 高精度RTC的应用确保了定时准确性
- 蓝牙通信实现了便捷的远程管理
- EEPROM存储保证了数据不掉失
- 低功耗设计延长了电池寿命
对于想要复现或改进这个项目的开发者,我有以下几点建议:
- 先从基础功能开始,逐步添加复杂功能
- 合理规划硬件资源,留出扩展接口
- 注重用户体验,特别是对老年用户要简单易用
- 做好异常处理,增强系统鲁棒性
这个项目不仅具有实用价值,也是一个很好的STM32学习案例,涵盖了GPIO、定时器、I2C、USART、ADC等常用外设的应用。通过这个项目,我对嵌入式系统的开发流程有了更深入的理解,也为后续更复杂的项目打下了坚实基础。
