大模型学习全路径:从零基础小白到实战高手,收藏这份系统化进阶指南!
2026/4/4 1:26:59
在嵌入式系统开发中,电子时钟是最经典的练手项目之一。这次我们要用STM32F103单片机配合Proteus仿真软件,实现一个同时驱动LCD1602液晶屏和数码管的双显电子时钟。这种设计既能锻炼GPIO和定时器的使用,又能学习不同显示设备的驱动逻辑。
硬件选择上,我推荐使用STM32F103C8T6这款性价比极高的芯片,它内置RTC实时时钟模块,72MHz主频完全够用。显示部分采用常见的LCD1602(16字符x2行)和4位共阳数码管组合。仿真环境用Proteus 8.9以上版本,实际开发可以用STM32CubeMX生成初始化代码。
注意:Proteus中的STM32模型与实际硬件存在时序差异,仿真时定时器参数需要特别调整,后文会具体说明。
LCD1602采用标准的4位数据线接法(DB4-DB7),节省GPIO资源。具体连接:
数码管使用动态扫描方式驱动,四个位选信号接PB8-PB11,段选信号通过74HC595串转并芯片连接,只需占用STM32的三个引脚(PB12-PB14)。这种设计在真实项目中能有效减少线材数量。
int main(void) { HAL_Init(); SystemClock_Config(); LCD_Init(); DigitalTube_Init(); TIM3_Init(10, 884); // 1ms定时 RTC_Init(); while(1) { Key_Process(); // 按键扫描 Time_Update(); // 时间计算 LCD_Display(); // LCD刷新 Tube_Display(); // 数码管刷新 } }使用TIM3产生1ms中断作为系统时基:
void TIM3_Init(uint16_t arr, uint16_t psc) { TIM_HandleTypeDef htim3; htim3.Instance = TIM3; htim3.Init.Prescaler = psc; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = arr; HAL_TIM_Base_Init(&htim3); HAL_TIM_Base_Start_IT(&htim3); }采用状态机方式优化显示刷新,避免频繁操作影响实时性:
void LCD_Refresh(void) { static uint8_t state = 0; switch(state) { case 0: LCD_SetCursor(0,0); LCD_WriteString("Time:"); state++; break; case 1: LCD_SetCursor(6,0); printf("%02d:%02d:%02d", hours, mins, secs); state++; break; case 2: LCD_SetCursor(0,1); LCD_WriteString("Date:"); state++; break; case 3: LCD_SetCursor(6,1); printf("%04d-%02d-%02d", year, month, day); state = 0; break; } }在定时器中断中实现扫描,避免闪烁:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint8_t pos = 0; DigitalTube_Clear(); switch(pos) { case 0: Show_Hour_Tens(); break; case 1: Show_Hour_Units(); break; case 2: Show_Min_Tens(); break; case 3: Show_Min_Units(); break; } DigitalTube_Select(pos); pos = (pos+1)%4; }使用STM32内置RTC模块,需先配置LSE时钟源:
void RTC_Init(void) { RTC_TimeTypeDef sTime = {0}; sTime.Hours = 12; sTime.Minutes = 0; sTime.Seconds = 0; HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); }采用状态机实现多功能按键:
void Key_Process(void) { if(KEY1_Pressed()) { mode = (mode+1)%3; // 切换设置模式 } if(mode == 1) { if(KEY2_Pressed()) hours++; if(KEY3_Pressed()) hours--; } // 其他模式类似... }这个项目我做过多次迭代,发现最关键的优化点是显示刷新策略。通过将LCD的刷新分散到不同周期,并合理设置数码管扫描频率,可以显著降低CPU占用率。实际测试中,这个方案即使在加入温度显示等功能后,仍然能保持流畅运行。