STM32的电子钟功能实现
一、系统架构设计
1.1 硬件组成框图
graph TD
A[STM32F103C8T6] --> B[OLED0.96]
A --> C[按键矩阵]
A --> D[蜂鸣器]
B --> I2C总线
C --> EXTI中断
1.2 核心模块划分
- 时间管理:SysTick定时器中断(1ms精度)
- 显示驱动:SSD1306 OLED中文显示
- 用户交互:4x4矩阵键盘+外部中断
- 电源管理:低功耗模式(待机模式)
二、硬件配置实现
2.1 时钟配置(SystemClock_Config)
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 72MHz
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInit, FLASH_LATENCY_2);
}
2.2 OLED驱动配置(I2C接口)
// SSD1306初始化
void SSD1306_Init(void)
{
SSD1306_WriteCommand(0xAE); // 关闭显示
SSD1306_WriteCommand(0x20); // 设置内存地址模式
SSD1306_WriteCommand(0x00); // 水平地址模式
SSD1306_WriteCommand(0xB0); // 设置页起始地址
SSD1306_WriteCommand(0xC8); // 设置COM输出扫描方向
SSD1306_WriteCommand(0x00); // 设置低位列地址
SSD1306_WriteCommand(0x10); // 设置高位列地址
SSD1306_WriteCommand(0x40); // 设置起始行地址
SSD1306_WriteCommand(0x81); // 设置对比度
SSD1306_WriteCommand(0xFF); // 对比度最大值
SSD1306_WriteCommand(0xA1); // 设置段重定向
SSD1306_WriteCommand(0xA6); // 正常显示
SSD1306_WriteCommand(0xA8); // 设置多路复用率
SSD1306_WriteCommand(0x3F); // 1/64 duty
SSD1306_WriteCommand(0xD3); // 设置显示偏移
SSD1306_WriteCommand(0x00); // 无偏移
SSD1306_WriteCommand(0xD5); // 设置显示时钟分频
SSD1306_WriteCommand(0x80); // 建议比值
SSD1306_WriteCommand(0xD9); // 设置预充电周期
SSD1306_WriteCommand(0xF1); // 预充电周期值
SSD1306_WriteCommand(0xDA); // 设置COM硬件引脚配置
SSD1306_WriteCommand(0x12); // 配置值
SSD1306_WriteCommand(0xDB); // 设置VCOMH
SSD1306_WriteCommand(0x40); // VCOMH电压值
SSD1306_WriteCommand(0x8D); // 设置电荷泵
SSD1306_WriteCommand(0x14); // 启用电荷泵
SSD1306_WriteCommand(0xAF); // 开启显示
}
三、时间管理系统
3.1 SysTick中断服务程序
volatile uint32_t ms_ticks = 0;
void SysTick_Handler(void)
{
HAL_IncTick();
ms_ticks++;
}
// 时间结构体
typedef struct {
uint8_t hours;
uint8_t minutes;
uint8_t seconds;
} TimeTypeDef;
TimeTypeDef current_time = {0, 0, 0};
// 时间更新函数
void Update_Time(void)
{
if(ms_ticks >= 1000) {
ms_ticks = 0;
current_time.seconds++;
if(current_time.seconds >= 60) {
current_time.seconds = 0;
current_time.minutes++;
if(current_time.minutes >= 60) {
current_time.minutes = 0;
current_time.hours++;
if(current_time.hours >= 24) {
current_time.hours = 0;
}
}
}
}
}
四、OLED中文显示实现
4.1 GB2312字库加载
// 定义字库数组(示例:0x00-0x0F为数字和字母)
const uint8_t Font_GB2312[] = {
0x00, 0x7E, 0x11, 0x11, 0x11, 0x7E, // 0
0x38, 0x44, 0x44, 0x44, 0x38, 0x00, // 1
// ... 其他字符数据
};
// 显示字符串函数
void OLED_ShowString(uint8_t x, uint8_t y, char* str)
{
while(*str) {
OLED_DisplayChar(x, y, *str++);
x += 8;
}
}
// 显示中文字符(需扩展字库)
void OLED_DisplayChinese(uint8_t x, uint8_t y, uint16_t code)
{
uint8_t page, col;
for(page=0; page<8; page++) {
OLED_SetCursor(x, y+page);
OLED_WriteData(Font_GB2312[code*16 + page]);
}
}
五、按键交互系统
5.1 矩阵键盘扫描
#define KEY_MATRIX_ROWS 4
#define KEY_MATRIX_COLS 4
GPIO_TypeDef* row_ports[4] = {GPIOA, GPIOA, GPIOA, GPIOA};
uint16_t row_pins[4] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3};
GPIO_TypeDef* col_ports[4] = {GPIOB, GPIOB, GPIOB, GPIOB};
uint16_t col_pins[4] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3};
// 扫描函数
char Matrix_Key_Scan(void)
{
for(uint8_t col=0; col<4; col++) {
// 拉低当前列
HAL_GPIO_WritePin(col_ports[col], col_pins[col], GPIO_PIN_RESET);
for(uint8_t row=0; row<4; row++) {
if(HAL_GPIO_ReadPin(row_ports[row], row_pins[row]) == GPIO_PIN_RESET) {
while(HAL_GPIO_ReadPin(row_ports[row], row_pins[row]) == GPIO_PIN_RESET); // 消抖
return (col<<4) | row; // 返回键值
}
}
HAL_GPIO_WritePin(col_ports[col], col_pins[col], GPIO_PIN_SET);
}
return 0xFF; // 无按键
}
5.2 外部中断配置
// EXTI回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY_ADJUST_PIN) {
// 进入时间调整模式
time_adjust_mode = 1;
adjust_counter = 0;
}
}
六、主程序流程
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
SSD1306_Init();
// 初始化时间
current_time.hours = 12;
current_time.minutes = 0;
current_time.seconds = 0;
while(1)
{
// 时间更新
Update_Time();
// 显示时间
OLED_Clear();
OLED_ShowString(0,0, "Time:");
OLED_DisplayString(32,0, "HH:MM:SS");
OLED_DisplayString(0,2, "Current:");
OLED_DisplayString(32,2, "XX:XX:XX");
// 按键处理
char key = Matrix_Key_Scan();
if(key != 0xFF) {
switch(key) {
case KEY_MODE: // 模式切换
time_adjust_mode ^= 1;
break;
case KEY_UP: // 增加时间
Adjust_Time(1);
break;
case KEY_CONFIRM: // 确认设置
time_adjust_mode = 0;
break;
}
}
}
}
参考代码 STM32电子钟功能 www.youwenfan.com/contentcnh/56377.html
七、扩展功能实现
8.1 闹钟功能
typedef struct {
uint8_t enable;
uint8_t hours;
uint8_t minutes;
} AlarmTypeDef;
AlarmTypeDef alarm = {0, 7, 30}; // 7:30闹钟
// 闹钟检测
void Check_Alarm(void)
{
if(!alarm.enable) return;
if(current_time.hours == alarm.hours &&
current_time.minutes == alarm.minutes &&
current_time.seconds == 0) {
BEEP_ON(); // 触发蜂鸣器
HAL_Delay(30000); // 持续30秒
BEEP_OFF();
}
}
8.2 日期显示扩展
typedef struct {
uint8_t year;
uint8_t month;
uint8_t day;
} DateTypeDef;
DateTypeDef current_date = {24, 9, 2025}; // 2025-09-24
// 日期显示函数
void OLED_ShowDate(uint8_t x, uint8_t y)
{
char date_str[11] = "Date:2025-09-24";
OLED_DisplayString(x, y, date_str);
}

浙公网安备 33010602011771号