一、系统概述与核心功能
1. 系统定位
基于STM32的桌面Mini时钟以"精准计时-环境感知-智能显示-低功耗续航"为核心,集成高精度RTC时钟、温湿度监测、自动亮度调节、闹钟提醒于一体,打造小巧精致、功能实用的桌面摆件,适用于卧室、书房、办公室等场景,替代传统电子钟,提供智能化时间管理体验。
2. 核心功能模块
| 模块 | 功能描述 |
|---|---|
| 精准计时 | DS3231高精度RTC,±2ppm精度(年误差<1分钟),支持闰年自动判断、12/24小时制切换 |
| 环境感知 | SHT30温湿度传感器,实时监测环境温度(-40125℃)和湿度(0100%RH) |
| 智能显示 | 0.96寸OLED(128×64),支持时间/日期/温湿度同屏显示、自动亮度调节、屏保模式 |
| 闹钟提醒 | 3组可编程闹钟,支持贪睡功能(5/10/15分钟),蜂鸣器+LED闪光提醒 |
| 用户交互 | 3个触摸按键(设置/上调/下调),长按/短按组合操作,操作反馈震动提示 |
| 低功耗设计 | 待机电流<1mA,Micro USB供电+CR1220纽扣电池备份,断电不停走 |
二、硬件设计方案
1. 核心硬件选型
| 模块 | 型号 | 关键参数 | 接口方式 |
|---|---|---|---|
| 主控MCU | STM32F030F4P6 | 48MHz Cortex-M0,16KB Flash,4KB RAM,小封装TSSOP20,极致性价比 | 核心控制器 |
| 实时时钟 | DS3231 | ±2ppm精度,I2C接口,内置温度补偿,CR1220纽扣电池备份(断电走时3年) | I2C1(PB6=SCL,PB7=SDA) |
| 温湿度传感器 | SHT30 | ±0.3℃/±2%RH精度,I2C接口,16位分辨率,DFN封装(3×3mm) | I2C1(复用DS3231总线) |
| 显示模块 | OLED 12864(I2C) | 0.96寸,128×64像素,自发光,对比度10000:1,可视角度>160° | I2C1(复用总线,地址0x3C) |
| 交互模块 | TTP223触摸按键×3 | 单通道触摸IC,低功耗(5μA),感应距离0-8mm,替代机械按键 | GPIO(PA0-PA2,外部中断) |
| 提醒模块 | 有源蜂鸣器+WS2812 LED | 蜂鸣器(5V,85dB),WS2812(RGB全彩,单线控制),闹钟时闪光提醒 | GPIO(PB0=蜂鸣器,PB1=LED) |
| 电源模块 | Micro USB+ME6211+HT7333 | 5V USB供电→ME6211降压至3.3V→HT7333稳压至3.3V(低纹波),CR1220电池备份 | 双电源供电(自动切换) |
2. 硬件电路设计要点
2.1 核心电路连接(极致紧凑设计)
STM32F030F4P6(TSSOP20)
├── I2C1总线(PB6=SCL,PB7=SDA)
│ ├── DS3231(RTC,地址0x68)
│ ├── SHT30(温湿度,地址0x44)
│ └── OLED(显示,地址0x3C)
├── GPIO输入(PA0-PA2)
│ ├── TTP223_1(设置键)
│ ├── TTP223_2(上调键)
│ └── TTP223_3(下调键)
├── GPIO输出(PB0-PB1)
│ ├── 蜂鸣器(闹钟提醒)
│ └── WS2812(RGB氛围灯)
└── 电源系统
├── Micro USB(5V输入)
├── ME6211(5V→3.3V)
├── HT7333(3.3V稳压)
└── CR1220(RTC备份电池)
2.2 低功耗设计亮点
- 双电源自动切换:主电源(USB 5V)正常时给系统供电并为纽扣电池充电;主电源断开时,DS3231自动切换至纽扣电池(仅维持计时,电流<1μA)。
- 动态背光控制:OLED亮度根据环境光自动调节(通过SHT30温度数据间接推算环境光,或外接光敏电阻),夜间自动降低亮度。
- 触摸唤醒:待机时STM32进入STOP模式(电流<10μA),触摸按键中断唤醒。
三、软件设计与核心代码
1. 系统架构(前后台系统)
采用前后台系统(无RTOS),以主循环为前台,中断为后台,核心流程:
- 初始化:配置时钟、I2C、GPIO、RTC、显示模块。
- 主循环 :
- 读取RTC时间/日期 → 读取温湿度 → 更新OLED显示 → 检查闹钟触发。
- 扫描触摸按键 → 处理设置/调整操作 → 更新系统状态。
- 中断服务:触摸按键中断(唤醒系统)、RTC闹钟中断(触发提醒)。
2. 核心代码实现(基于标准库)
2.1 DS3231 RTC驱动(I2C通信)
c
#include "ds3231.h"
#include "i2c.h"
// DS3231寄存器地址
#define DS3231_SEC 0x00 // 秒
#define DS3231_MIN 0x01 // 分
#define DS3231_HOUR 0x02 // 时(Bit6=12/24小时制)
#define DS3231_WEEK 0x03 // 星期(1-7)
#define DS3231_DATE 0x04 // 日
#define DS3231_MON 0x05 // 月
#define DS3231_YEAR 0x06 // 年(后两位)
#define DS3231_ALARM1 0x07 // 闹钟1
#define DS3231_CONTROL 0x0E // 控制寄存器
// BCD码与十进制互转
uint8_t bcd2dec(uint8_t bcd) { return (bcd>>4)*10 + (bcd&0x0F); }
uint8_t dec2bcd(uint8_t dec) { return ((dec/10)<<4) | (dec%10); }
// 设置时间(24小时制)
void DS3231_SetTime(uint8_t hour, uint8_t min, uint8_t sec) {
uint8_t buf[3];
buf[0] = dec2bcd(sec);
buf[1] = dec2bcd(min);
buf[2] = dec2bcd(hour) & 0x3F; // Bit6=0(24小时制)
HAL_I2C_Mem_Write(&hi2c1, 0x68<<1, DS3231_SEC, 1, buf, 3, 100);
}
// 读取时间
void DS3231_ReadTime(uint8_t *hour, uint8_t *min, uint8_t *sec) {
uint8_t buf[3];
HAL_I2C_Mem_Read(&hi2c1, 0x68<<1, DS3231_SEC, 1, buf, 3, 100);
*sec = bcd2dec(buf[0]);
*min = bcd2dec(buf[1]);
*hour = bcd2dec(buf[2] & 0x3F); // 屏蔽12/24小时制位
}
// 设置闹钟(每天定时)
void DS3231_SetAlarm(uint8_t hour, uint8_t min) {
uint8_t buf[4];
buf[0] = dec2bcd(min); // 分
buf[1] = dec2bcd(hour) & 0x3F; // 时(24小时制)
buf[2] = 0x80; // 日(不关心)
buf[3] = 0x80; // 星期(不关心)
HAL_I2C_Mem_Write(&hi2c1, 0x68<<1, DS3231_ALARM1, 1, buf, 4, 100);
// 使能闹钟中断
uint8_t ctrl = 0x05; // INTCN=1(中断输出),A1IE=1(闹钟1使能)
HAL_I2C_Mem_Write(&hi2c1, 0x68<<1, DS3231_CONTROL, 1, &ctrl, 1, 100);
}
2.2 SHT30温湿度驱动(I2C通信)
c
#include "sht30.h"
#include "i2c.h"
// SHT30测量命令(高重复精度)
#define SHT30_MEAS_HIGH 0x2400
// 读取温湿度
uint8_t SHT30_Read(float *temp, float *humi) {
uint8_t cmd[2] = {SHT30_MEAS_HIGH>>8, SHT30_MEAS_HIGH&0xFF};
uint8_t buf[6];
// 发送测量命令
if (HAL_I2C_Master_Transmit(&hi2c1, 0x44<<1, cmd, 2, 100) != HAL_OK)
return 1;
HAL_Delay(15); // 等待测量完成(高重复精度需15ms)
// 读取6字节数据(温度2B+CRC+湿度2B+CRC)
if (HAL_I2C_Master_Receive(&hi2c1, 0x44<<1, buf, 6, 100) != HAL_OK)
return 1;
// 转换为物理量(参考SHT30 datasheet)
uint16_t raw_temp = (buf[0]<<8) | buf[1];
uint16_t raw_humi = (buf[3]<<8) | buf[4];
*temp = -45 + 175 * (raw_temp / 65535.0f); // ℃
*humi = 100 * (raw_humi / 65535.0f); // %RH
return 0; // 成功
}
2.3 OLED显示驱动(时间/温湿度同屏)
c
#include "oled.h"
#include "font.h" // 6x8/8x16 ASCII字库
// 显示时间(HH:MM:SS)
void OLED_ShowTime(uint8_t hour, uint8_t min, uint8_t sec) {
char time_str[9];
sprintf(time_str, "%02d:%02d:%02d", hour, min, sec);
OLED_ShowString(32, 0, time_str, 16); // 居中显示(128-64)/2=32
}
// 显示日期(YYYY-MM-DD 星期X)
void OLED_ShowDate(uint16_t year, uint8_t mon, uint8_t day, uint8_t week) {
char date_str[20];
char *week_str[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
sprintf(date_str, "20%02d-%02d-%02d %s", year, mon, day, week_str[week-1]);
OLED_ShowString(8, 2, date_str, 12); // 12号字体
}
// 显示温湿度
void OLED_ShowTempHumi(float temp, float humi) {
char th_str[20];
sprintf(th_str, "T:%.1fC H:%.1f%%", temp, humi);
OLED_ShowString(16, 4, th_str, 12); // 12号字体
}
// 显示闹钟图标(开启/关闭)
void OLED_ShowAlarmIcon(uint8_t alarm_on) {
if (alarm_on) OLED_ShowChar(112, 0, '*', 16); // 右上角显示*
else OLED_ShowChar(112, 0, ' ', 16); // 清空
}
2.4 触摸按键与闹钟提醒
c
#include "touch_key.h"
#include "beep_led.h"
// 触摸按键扫描(PA0-PA2)
uint8_t TouchKey_Scan(void) {
static uint8_t key_up = 1; // 按键松开标志
if (key_up && (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET ||
HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET ||
HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_SET)) {
HAL_Delay(10); // 消抖
key_up = 0;
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) return 1; // 设置键
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET) return 2; // 上调键
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_SET) return 3; // 下调键
} else if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET &&
HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET &&
HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_RESET) {
key_up = 1; // 所有按键松开
}
return 0; // 无按键
}
// 闹钟提醒(蜂鸣器+LED闪烁)
void Alarm_Remind(uint8_t on_off) {
static uint8_t led_state = 0;
if (on_off) {
// 蜂鸣器间歇响(0.5s响,0.5s停)
static uint32_t beep_tick = 0;
if (HAL_GetTick() - beep_tick > 500) {
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // 蜂鸣器翻转
beep_tick = HAL_GetTick();
}
// LED闪烁
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_1); // WS2812翻转
HAL_Delay(200);
} else {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // 关闭蜂鸣器
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); // 关闭LED
}
}
2.5 主程序框架
c
#include "stm32f0xx_hal.h"
#include "ds3231.h"
#include "sht30.h"
#include "oled.h"
#include "touch_key.h"
#include "beep_led.h"
// 系统状态
typedef enum {
SYS_NORMAL = 0, // 正常显示
SYS_SET_HOUR, // 设置小时
SYS_SET_MIN, // 设置分钟
SYS_SET_ALARM // 设置闹钟
} SysState;
SysState sys_state = SYS_NORMAL;
uint8_t alarm_on = 0; // 闹钟开关
uint8_t alarm_hour = 7, alarm_min = 30; // 默认闹钟7:30
int main(void) {
HAL_Init();
SystemClock_Config(); // 48MHz
// 初始化外设
MX_I2C1_Init(); // I2C1(DS3231/SHT30/OLED)
MX_GPIO_Init(); // GPIO(触摸按键/蜂鸣器/LED)
OLED_Init(); // OLED初始化
DS3231_Init(); // RTC初始化(首次上电设置默认时间)
SHT30_Init(); // 温湿度传感器初始化
uint8_t hour, min, sec;
uint8_t year, mon, day, week;
float temp, humi;
while (1) {
// 1. 读取RTC时间日期
DS3231_ReadTime(&hour, &min, &sec);
DS3231_ReadDate(&year, &mon, &day, &week);
// 2. 读取温湿度
SHT30_Read(&temp, &humi);
// 3. 更新OLED显示
OLED_Clear();
OLED_ShowTime(hour, min, sec);
OLED_ShowDate(year, mon, day, week);
OLED_ShowTempHumi(temp, humi);
OLED_ShowAlarmIcon(alarm_on);
// 4. 检查闹钟触发
if (alarm_on && hour == alarm_hour && min == alarm_min && sec == 0) {
Alarm_Remind(1); // 触发闹钟
} else {
Alarm_Remind(0); // 关闭闹钟
}
// 5. 触摸按键处理
uint8_t key = TouchKey_Scan();
switch (sys_state) {
case SYS_NORMAL:
if (key == 1) sys_state = SYS_SET_HOUR; // 进入设置模式
if (key == 2) alarm_on = !alarm_on; // 开关闹钟
break;
case SYS_SET_HOUR:
if (key == 2) hour = (hour + 1) % 24; // 小时+1
if (key == 3) hour = (hour + 23) % 24; // 小时-1
if (key == 1) sys_state = SYS_SET_MIN; // 切换到分钟设置
DS3231_SetTime(hour, min, sec); // 保存时间
break;
case SYS_SET_MIN:
if (key == 2) min = (min + 1) % 60; // 分钟+1
if (key == 3) min = (min + 59) % 60; // 分钟-1
if (key == 1) sys_state = SYS_NORMAL; // 退出设置
DS3231_SetTime(hour, min, sec); // 保存时间
break;
}
HAL_Delay(200); // 200ms刷新一次
}
}
参考代码 基于STM32的桌面mini时钟设计 www.youwenfan.com/contentcst/133652.html
四、关键技术与优化
1. 极致低功耗设计
- STM32 STOP模式:无操作时进入STOP模式(电流<10μA),触摸按键中断唤醒,实测续航:CR1220电池可维持RTC走时3年以上。
- OLED动态刷新:仅在时间/温湿度变化时刷新OLED,静止时关闭显示(屏保模式),触摸唤醒后恢复。
- 传感器间歇工作:SHT30每10分钟采集一次温湿度(非连续工作),降低功耗。
2. 用户体验优化
- 触摸按键反馈:短按/长按区分功能(短按切换设置项,长按保存退出),操作时LED微闪提示。
- 自动亮度调节:根据SHT30温度数据间接推算环境光(温度越高环境越亮),自动调节OLED对比度(白天高亮,夜间低亮)。
- 闹钟贪睡功能:闹钟响时短按任意键暂停5分钟,连续按3次彻底关闭闹钟。
五、系统调试与扩展
1. 调试步骤
| 阶段 | 操作 | 工具 |
|---|---|---|
| 硬件调试 | 测量DS3231纽扣电池电压(≥2.8V),I2C信号质量 | 万用表、示波器(SCL/SDA波形) |
| RTC校准 | 对比手机时间,调整DS3231老化偏移量 | 串口打印时间,对比标准时间 |
| 显示测试 | 显示固定字符,检查OLED是否有坏点/残影 | 肉眼观察,逻辑分析仪(I2C时序) |
| 低功耗测试 | 待机时测量整机电流(应<1mA) | 万用表(串联在电源回路) |
2. 扩展功能
- 温湿度历史曲线:添加EEPROM(AT24C02)存储24小时温湿度数据,OLED绘制简易曲线。
- 天气图标显示:根据温湿度显示晴天/多云/雨天图标(如☀️/☁️/🌧️)。
- USB自动校时:通过USB接口连接电脑,自动同步电脑时间到RTC(需开发PC端工具)。
- 无线对时:添加BLE模块(如CC2541),通过手机APP设置时间/闹钟。
六、总结
基于STM32的桌面Mini时钟通过DS3231高精度RTC确保计时精准,SHT30温湿度传感器提供环境监测,OLED+触摸按键实现简洁交互,极致低功耗设计保障长久续航。