Bootstrap

STM32超级终端RTC日历系统开发实战 | 零基础入门STM32第四十步

主题内容教学目的/扩展视频
RTC时钟的使用重点课程RTC时钟的原理,电路原理分析,固件库分析,驱动程序分析。在超级终端上显示时钟。做可修改的超级终端显示RTC的项目。

师从洋桃电子,杜洋老师


📑文章目录

    • 一、系统功能概述
    • 二、硬件系统架构
      • 2.1 核心硬件组成
      • 2.2 经典电路设计
    • 三、软件核心逻辑解析
      • 3.1 主程序流程图
      • 3.2 多任务处理机制
    • 四、RTC核心操作详解
      • 4.1 时钟初始化流程
      • 4.2 时间设置算法
    • 五、时间显示格式定制
      • 5.1 格式化输出实现
      • 5.2 星期显示算法
    • 六、关键代码解析
      • 6.1 主循环处理逻辑
      • 6.2 时间显示优化技巧
    • 七、开发经验总结
    • 八、相关资源


回顾上期🔍STM32实时时钟(RTC)代码深度解析 | 零基础入门STM32第三十九步


(图1:项目演示图)

一、系统功能概述

本系统实现通过串口终端实时监控和修改STM32内置RTC时钟,主要功能包括:

  • 实时时钟显示:每秒更新日期、时间及星期
  • 串口指令控制:支持"初始化时钟"、"设置时间"等指令
  • 多任务处理:在时钟更新同时响应外部指令
  • 错误检测机制:数据校验与错误提示

二、硬件系统架构

2.1 核心硬件组成

模块型号/参数功能说明
MCUSTM32F103C8T6主控制器
RTC时钟源32.768kHz晶振提供精准时间基准
串口转换芯片CH340CUSB转TTL通信
后备电池CR2032纽扣电池RTC掉电保护

2.2 经典电路设计

// 后备电源切换电路
VBAT引脚 → 100nF电容 → CR2033电池座
LSE晶振 → 6pF负载电容 ×2 → GND

三、软件核心逻辑解析

3.1 主程序流程图

系统初始化
串口接收中断
接收完成?
解析指令
执行对应操作
更新显示

3.2 多任务处理机制

状态机实现原理

// 串口接收状态寄存器
void USART1_IRQHandler(void){ //串口1中断服务程序(固定的函数名不能修改)	
	u8 Res;
	//以下是字符串接收到USART_RX_BUF[]的程序,(USART_RX_STA&0x3FFF)是数据的长度(不包括回车)
	//当(USART_RX_STA&0xC000)为真时表示数据接收完成,即超级终端里按下回车键。
	//在主函数里写判断if(USART_RX_STA&0xC000),然后读USART_RX_BUF[]数组,读到0x0d 0x0a即是结束。
	//注意在主函数处理完串口数据后,要将USART_RX_STA清0
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){  //接收中断(接收到的数据必须是0x0d 0x0a结尾)		
		Res =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		printf("%c",Res); //把收到的数据以 a符号变量 发送回电脑		
		if((USART1_RX_STA&0x8000)==0){//接收未完成			
			if(USART1_RX_STA&0x4000){//接收到了0x0d				
				if(Res!=0x0a)USART1_RX_STA=0;//接收错误,重新开始
				else USART1_RX_STA|=0x8000;	//接收完成了 
			}else{ //还没收到0X0D					
				if(Res==0x0d)USART1_RX_STA|=0x4000;
				else{
					USART1_RX_BUF[USART1_RX_STA&0X3FFF]=Res ; //将收到的数据放入数组
					USART1_RX_STA++;	//数据长度计数加1
					if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
	} 
} 

四、RTC核心操作详解

4.1 时钟初始化流程

void RTC_Config(void){ //实时时钟初始化
    //在BKP的后备寄存器1中,存了一个特殊字符0xA5A5
    //第一次上电或后备电源掉电后,该寄存器数据丢失,表明RTC数据丢失,需要重新配置
    if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){//判断寄存数据是否丢失       
        RTC_First_Config();//重新配置RTC        
        BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5
    }else{
		//若后备寄存器没有掉电,则无需重新配置RTC
        //这里我们可以利用RCC_GetFlagStatus()函数查看本次复位类型
        if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET){
            //这是上电复位
        }
        else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET){
            //这是外部RST管脚复位
        }       
        RCC_ClearFlag();//清除RCC中复位标志

        //虽然RTC模块不需要重新配置,且掉电后依靠后备电池依然运行
        //但是每次上电后,还是要使能RTCCLK
        RCC_RTCCLKCmd(ENABLE);//使能RTCCLK        
        RTC_WaitForSynchro();//等待RTC时钟与APB1时钟同步
}

4.2 时间设置算法

ASCII转数值算法

//将超级终端发过来的数据换算并写入RTC
ryear = (USART1_RX_BUF[0]-0x30)*1000+(USART1_RX_BUF[1]-0x30)*100+(USART1_RX_BUF[2]-0x30)*10+USART1_RX_BUF[3]-0x30;
rmon = (USART1_RX_BUF[4]-0x30)*10+USART1_RX_BUF[5]-0x30;//串口发来的是字符,减0x30后才能得到十进制0~9的数据
rday = (USART1_RX_BUF[6]-0x30)*10+USART1_RX_BUF[7]-0x30;
rhour = (USART1_RX_BUF[8]-0x30)*10+USART1_RX_BUF[9]-0x30;
rmin = (USART1_RX_BUF[10]-0x30)*10+USART1_RX_BUF[11]-0x30;
rsec = (USART1_RX_BUF[12]-0x30)*10+USART1_RX_BUF[13]-0x30;
bya=RTC_Set(ryear,rmon,rday,rhour,rmin,rsec); //将数据写入RTC计算器的程序

写入流程

  1. 校验输入有效性(范围检查)
  2. 转换为Unix时间戳
  3. 写入RTC计数器

五、时间显示格式定制

5.1 格式化输出实现

printf("%d-%02d-%02d %02d:%02d:%02d", 
       ryear, rmon, rday, rhour, rmin, rsec);

格式说明符

  • %02d:两位数字显示,不足补零
  • \r\n:回车换行符

5.2 星期显示算法

// 基姆拉尔森计算公式优化版
u8 Get_Week(u16 year, u8 month, u8 day) {
    if(month < 3) { month += 12; year--; }
    return (day + 2*month + 3*(month+1)/5 + year + year/4 
           - year/100 + year/400) % 7;
}

六、关键代码解析

6.1 主循环处理逻辑

int main (void){//主程序
	u8 bya;
	RCC_Configuration(); //系统时钟初始化
	RTC_Config(); //实时时钟初始化
	LED_Init();//LED初始化
	KEY_Init();//按键初始化
	BUZZER_Init();//蜂鸣器初始化
	USART1_Init(115200); //串口初始化,参数中写波特率
	USART1_RX_STA=0xC000; //初始值设为有回车的状态,即显示一次欢迎词
	while(1){

		if(USART1_RX_STA&0xC000){ //如果标志位是0xC000表示收到数据串完成,可以处理。
			if((USART1_RX_STA&0x3FFF)==0){ //单独的回车键再显示一次欢迎词
				if(RTC_Get()==0){ //读出时间值,同时判断返回值是不是0,非0时读取的值是错误的。
					printf(" 洋桃开发板STM32实时时钟测试程序   \r\n");
					printf(" 现在实时时间:%d-%d-%d %d:%d%d:%d%d  ",ryear,rmon,rday,rhour,rmin/10,rmin%10,rsec/10,rsec%10);//显示日期时间
					if(rweek==0)printf("星期日   \r\n");//rweek值为0时表示星期日
					if(rweek==1)printf("星期一   \r\n");
					if(rweek==2)printf("星期二   \r\n");
					if(rweek==3)printf("星期三   \r\n");
					if(rweek==4)printf("星期四   \r\n");
					if(rweek==5)printf("星期五   \r\n");
					if(rweek==6)printf("星期六   \r\n");
					printf(" 单按回车键更新时间。输入字母C初始化时钟 \r\n");
					printf(" 请输入设置时间,格式20170806120000,按回车键确定! \r\n");
				}else{
					printf("读取失败!\r\n");
				}
			}else if((USART1_RX_STA&0x3FFF)==1){ //判断数据是不是2个
				if(USART1_RX_BUF[0]=='c' || USART1_RX_BUF[0]=='C'){
					RTC_First_Config(); //键盘输入c或C,初始化时钟
					BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//配置完成后,向后备寄存器中写特殊字符0xA5A5
					printf("初始化成功!      \r\n");//显示初始化成功
				}else{
					printf("指令错误!          \r\n"); //显示指令错误!
				} 
			}else if((USART1_RX_STA&0x3FFF)==14){ //判断数据是不是14个
				//将超级终端发过来的数据换算并写入RTC
				ryear = (USART1_RX_BUF[0]-0x30)*1000+(USART1_RX_BUF[1]-0x30)*100+(USART1_RX_BUF[2]-0x30)*10+USART1_RX_BUF[3]-0x30;
				rmon = (USART1_RX_BUF[4]-0x30)*10+USART1_RX_BUF[5]-0x30;//串口发来的是字符,减0x30后才能得到十进制0~9的数据
				rday = (USART1_RX_BUF[6]-0x30)*10+USART1_RX_BUF[7]-0x30;
				rhour = (USART1_RX_BUF[8]-0x30)*10+USART1_RX_BUF[9]-0x30;
				rmin = (USART1_RX_BUF[10]-0x30)*10+USART1_RX_BUF[11]-0x30;
				rsec = (USART1_RX_BUF[12]-0x30)*10+USART1_RX_BUF[13]-0x30;
				bya=RTC_Set(ryear,rmon,rday,rhour,rmin,rsec); //将数据写入RTC计算器的程序
				if(bya==0)printf("写入成功!      \r\n");//显示写入成功 
				else printf("写入失败!       \r\n"); //显示写入失败
			}else{ //如果以上都不是,即是错误的指令。
				printf("指令错误!          \r\n"); //如果不是以上正确的操作,显示指令错误!
			}
			USART1_RX_STA=0; //将串口数据标志位清0
		}
	}
}

6.2 时间显示优化技巧

// 分解数字位显示
printf("%d:%d%d", rhour, rmin/10, rmin%10);
// 输出示例:12:05 → 1 2 : 0 5

完整工程代码示例⏬超级终端显示日历程序


七、开发经验总结

  1. 抗干扰设计:在VBAT引脚并联100nF电容,提高RTC稳定性
  2. 输入验证:建议增加月份(1-12)、小时(0-23)等范围检查
  3. 低功耗优化:空闲时进入STOP模式,电流可降至2μA以下
  4. 扩展功能:可添加温度补偿算法提高计时精度

实测数据:使用外部晶振时,系统年误差小于±30秒。通过软件校准可进一步将误差控制在±5秒/年以内。


八、相关资源

[1] 洋桃电子B站课程-STM32入门100步
[2] STM32F103xx官方数据手册
[3] STM32F103X8-B数据手册(中文)
[4] STM32F103固件函数库用户手册(中文)
[5] 超级终端显示日历程序


💬 技术讨论(请在评论区留言~)

📌 下期预告:下一期将探讨RTC设置程序分析,欢迎持续关注!

点击查阅🔍往期【STM32专栏】文章

版权声明:本文采用[CC BY-NC-SA 4.0]协议,转载请注明来源
实测开发版:洋桃1号开发版(基于STM32F103C8T6)
更新日志

  • v1.0 初始版本(2025-02-28)
;