打印
[STM32F1]

基于STM32F103的RTC功能实现

[复制链接]
218|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
l63t89|  楼主 | 2023-12-23 14:43 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
前言:
最近心血来潮,想用stm32f103c8t6这块小板子实现定时的功能,但是发现网上没有太多的资料,所以自己弄了一个。
有几点需要大家注意的是:
由于这个核心板没有外部晶振,所以在RTC初始化时用的是LSI(内部低速晶振),频率约为40KHZ。
由于没有纽扣电池,故断电后无法继续计时。

实现功能:

串口显示日期和时间
串口设置日期和时间
串口设置闹钟
所需元器件:

stm32f103c8t6核心板
USB转串口模块
—————————

实物展示:

使用特权

评论回复
沙发
l63t89|  楼主 | 2023-12-23 14:44 | 只看该作者
结果展示:

使用特权

评论回复
板凳
l63t89|  楼主 | 2023-12-23 14:45 | 只看该作者
代码实现:
main,c文件
#include "sys.h"

int main(void)
{
        delay_init();      //延时函数初始化
        LED_GPIO_Config(); //LED引脚配置
        My_USART1();       //串口初始化
        printf("串口初始化完成\r\n");

        RTC_Init();      //RTC初始化
        //RTC_Alarm_Set(2021,12,30,17,35,20); //闹钟设置
       
        while(1)
        {
                GPIO_ResetBits(GPIOC, GPIO_Pin_13);
                delay_ms(500);
                GPIO_SetBits( GPIOC, GPIO_Pin_13);  
                delay_ms(500);
        }
}

使用特权

评论回复
地板
l63t89|  楼主 | 2023-12-23 14:45 | 只看该作者
RTC.C文件#include "rtc.h"
#include "sys.h"
#include "string.h"
#include "led.h"
#include <stdlib.h>
const char *pt = __TIME__;  //20:15:05
const char *pd = __DATE__;  //Dec 30 2021

u8 month[13][5] = {"NUL","Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
struct SET_ALARM alarm;

_calendar calendar;//时钟结构体

static void RTC_NVIC_Config(void)
{       
        NVIC_InitTypeDef NVIC_InitStructure;
        NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;                //RTC全局中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;        //先占优先级1位,从优先级3位
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        //先占优先级0位,从优先级4位
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //使能该通道中断
        NVIC_Init(&NVIC_InitStructure);                //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
/*******************************************************************************
* 函 数 名         : RTC_Init_LSI
* 函数功能                   : RTC初始化
* 输    入         : 无
* 输    出         : 0,初始化成功
                                 1,LSI开启失败
解决复位之后RTC_WaitForSynchro();卡死问题:此句在if外面开启时钟,RCC_LSICmd(ENABLE);
内部晶振低速时钟40KHZ
注意:使用内部低速时钟断电后无法继续走时,即使有备用电池也不行
LSI需由主电源VDD供电,而VBAT只能使LSE起振。
*******************************************************************************/
u8 RTC_Init_LSI(void)
{
        //检查是不是第一次配置时钟
        u8 temp=0;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
        PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
        RCC_LSICmd(ENABLE); //设置内部低速晶振(LSI)
        if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050) //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
        {
                BKP_DeInit(); //复位备份区域
                //RCC_LSEConfig(RCC_LSE_ON);        //设置外部低速晶振(LSE),使用外设低速晶振
                while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET&& temp < 250) //检查指定的RCC标志位设置与否,等待低速晶振就绪
                {
                        temp++;
                        delay_ms(10);
                }
                if(temp>=250)return 1;//初始化时钟失败,晶振有问题
                //RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
                RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); //设置RTC时钟(RTCCLK),选择LSI作为RTC时钟
                RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
                RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
                RTC_WaitForSynchro(); //等待RTC寄存器同步
                RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR, ENABLE); //使能RTC秒中断、闹钟中断
                RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
                RTC_EnterConfigMode();/// 允许配置
                //RTC_SetPrescaler(32767); //设置RTC预分频的值
                RTC_SetPrescaler(40000); //设置RTC预分频的值
                RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成

                //RTC_Set(2017,3,6,0,0,0); //设置时间
                get_time();
                RTC_Set(calendar.w_year+2000-1 ,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);  //设置时间
               
                RTC_ExitConfigMode(); //退出配置模式
                BKP_WriteBackupRegister(BKP_DR1, 0X5050); //向指定的后备寄存器中写入用户程序数据
        }
        else//系统继续计时
        {
                RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
                RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR, ENABLE); //使能RTC秒中断、闹钟中断
                RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
        }
        RTC_NVIC_Config();//RCT中断分组设置
        RTC_Get();//更新时间
        return 0; //ok
}

//RTC时钟中断
//每秒触发一次  
void RTC_IRQHandler(void)
{                 
        if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
        {                                                       
                RTC_Get();//更新时间  
                printf("RTC Time:%d-%d-%d %d:%d:%d\r\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间       
                               
        }
        if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
        {
                RTC_ClearITPendingBit(RTC_IT_ALR);                //清闹钟中断                 
                RTC_Get();                                //更新时间   
                printf("Alarm Time:%d-%d-%d %d:%d:%d\r\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间       
               
          }                                                                                                    
        RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);                //清闹钟中断
        RTC_WaitForLastTask();                                                                                           
}
//判断是否是闰年函数
//月份   1  2  3  4  5  6  7  8  9  10 11 12
//闰年   31 29 31 30 31 30 31 31 30 31 30 31
//非闰年 31 28 31 30 31 30 31 31 30 31 30 31
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 year)
{                          
        if(year%4==0) //必须能被4整除
        {
                if(year%100==0)
                {
                        if(year%400==0)return 1;//如果以00结尾,还要能被400整除           
                        else return 0;   
                }else return 1;   
        }else return 0;       
}                                   

//月份数据表                                                                                         
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表          
//平年的月份日期表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};

/*******************************************************************************
* 函 数 名         : RTC_Set
* 函数功能                   : RTC设置日期时间函数(以1970年1月1日为基准,把输入的时钟转换为秒钟)
                                                1970~2099年为合法年份
* 输    入         : syear:年  smon:月  sday:日
                                        hour:时   min:分         sec:秒                       
* 输    出         : 0,成功
                                 1,失败
*******************************************************************************/
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
        u16 t;
        u32 seccount=0;
        if(syear<1970||syear>2099)return 1;          
        for(t=1970;t<syear;t++)        //把所有年份的秒钟相加
        {
                if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
                else seccount+=31536000;                          //平年的秒钟数
        }
        smon-=1;
        for(t=0;t<smon;t++)           //把前面月份的秒钟数相加
        {
                seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
                if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数          
        }
        seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
        seccount+=(u32)hour*3600;//小时秒钟数
    seccount+=(u32)min*60;         //分钟秒钟数
        seccount+=sec;//最后的秒钟加上去

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);        //使能PWR和BKP外设时钟  
        PWR_BackupAccessCmd(ENABLE);        //使能RTC和后备寄存器访问
        RTC_SetCounter(seccount);        //设置RTC计数器的值

        RTC_WaitForLastTask();        //等待最近一次对RTC寄存器的写操作完成         
        return 0;            
}

//初始化闹钟                  
//以1970年1月1日为基准
//1970~2099年为合法年份
//syear,smon,sday,hour,min,sec:闹钟的年月日时分秒   
//返回值:0,成功;其他:错误代码.
u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
        u16 t;
        u32 seccount=0;
        if(syear<1970||syear>2099)return 1;          
        for(t=1970;t<syear;t++)        //把所有年份的秒钟相加
        {
                if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
                else seccount+=31536000;                          //平年的秒钟数
        }
        smon-=1;
        for(t=0;t<smon;t++)           //把前面月份的秒钟数相加
        {
                seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
                if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数          
        }
        seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
        seccount+=(u32)hour*3600;//小时秒钟数
    seccount+=(u32)min*60;         //分钟秒钟数
        seccount+=sec;//最后的秒钟加上去                             
        //设置时钟
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);        //使能PWR和BKP外设时钟   
        PWR_BackupAccessCmd(ENABLE);        //使能后备寄存器访问  
        //上面三步是必须的!
        RTC_SetAlarm(seccount);
        RTC_WaitForLastTask();        //等待最近一次对RTC寄存器的写操作完成         
       
        return 0;            
}

//得到当前的时间
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{
        static u16 daycnt=0;
        u32 timecount=0;
        u32 temp=0;
        u16 temp1=0;          
    timecount=RTC_GetCounter();         
        temp=timecount/86400;   //得到天数(秒钟数对应的)
        if(daycnt!=temp)//超过一天了
        {          
                daycnt=temp;
                temp1=1970;        //从1970年开始
                while(temp>=365)
                {                                 
                        if(Is_Leap_Year(temp1))//是闰年
                        {
                                if(temp>=366)temp-=366;//闰年的秒钟数
                                else {temp1++;break;}  
                        }
                        else temp-=365;          //平年
                        temp1++;  
                }   
                calendar.w_year=temp1;//得到年份
                temp1=0;
                while(temp>=28)//超过了一个月
                {
                        if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
                        {
                                if(temp>=29)temp-=29;//闰年的秒钟数
                                else break;
                        }
                        else
                        {
                                if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
                                else break;
                        }
                        temp1++;  
                }
                calendar.w_month=temp1+1;        //得到月份
                calendar.w_date=temp+1;          //得到日期
        }
        temp=timecount%86400;                     //得到秒钟数             
        calendar.hour=temp/3600;             //小时
        calendar.min=(temp%3600)/60;         //分钟       
        calendar.sec=(temp%3600)%60;         //秒钟
        calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期   
        return 0;
}       

//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//输入参数:公历年月日
//返回值:星期号                                                                                                                                                                                 
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{       
        u16 temp2;
        u8 yearH,yearL;
       
        yearH=year/100;        yearL=year%100;
        // 如果为21世纪,年份数加100  
        if (yearH>19)yearL+=100;
        // 所过闰年数只算1900年之后的  
        temp2=yearL+yearL/4;
        temp2=temp2%7;
        temp2=temp2+day+table_week[month-1];
        if (yearL%4==0&&month<3)temp2--;
        return(temp2%7);
}                          

void get_time()
{
                 //Dec  3 2021
                if( pd[4] == ' ' )
                {
                        calendar.w_date = pd[5]-'0';//得到日
                }
                else   //Dec 30 2021
                {
                        calendar.w_date= (pd[4]-'0')*10 + (pd[5]-'0');//得到日
                }
               
                calendar.w_year = (pd[9]-'0')*10 + (pd[10]-'0');//得到年       
                //printf("年:%d\r\n",calendar.w_year );
                u8 i;
                for(i = 1; i <= 12; i++)
                {
                        if(strcmp((const char *)pd, (char *)month[i]) == 0)
                        {
                                break;//找到月份了
                        }
                }
                calendar.w_month = i;//得到月  

                calendar.hour = (pt[0]-'0')*10 + (pt[1]-'0');//得到小时
                calendar.min = (pt[3]-'0')*10 + (pt[4]-'0');//得到分钟
                calendar.sec = (pt[6]-'0')*10 + (pt[7]-'0');//得到秒
}

使用特权

评论回复
5
l63t89|  楼主 | 2023-12-23 14:46 | 只看该作者
my_usart1.c文件#include "sys.h"
#include "my_usart1.h"
#include <string.h>
#include <stdlib.h>

void My_USART1(void)
{
        //定义结构体变量,注意:只能放在{后面
        GPIO_InitTypeDef  GPIO_InitStruct;
        USART_InitTypeDef USART1_InitStruct;
        NVIC_InitTypeDef  NVIC_InitStruct;
        /*1串口时钟、GPIO时钟初始化*/
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
    /*2GPIOA端口模式设置*/
        //配置引脚TX
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;             //推挽复用输出
        GPIO_InitStruct.GPIO_Pin = USART1_GPIO_PIN_TX;            //PA9
        GPIO_InitStruct.GPIO_Speed =  GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStruct);
        //配置引脚RX
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;      //浮空输入
        GPIO_InitStruct.GPIO_Pin = USART1_GPIO_PIN_RX;           //PA10
        //GPIO_InitStruct.GPIO_Speed =  GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStruct);
        /*3串口参数初始化*/
        USART1_InitStruct.USART_BaudRate = 9600;                                       //波特率
        USART1_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  //流控
        USART1_InitStruct.USART_Mode = USART_Mode_Tx |USART_Mode_Rx ;                   //串口模式
        USART1_InitStruct.USART_Parity = USART_Parity_No;                              //校验位
        USART1_InitStruct.USART_StopBits = USART_StopBits_1 ;                           //停止位
        USART1_InitStruct.USART_WordLength = USART_WordLength_8b;                      //数据位
       
        USART_Init(USART1,&USART1_InitStruct);
       
        /*4开启中断并且初始化NVIC*/
         USART_ITConfig( USART1, USART_IT_RXNE, ENABLE);//开启接收中断

        NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;     //选择中断源
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;  //抢占优先级
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;      //响应优先级
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;       //中断使能
        NVIC_Init(&NVIC_InitStruct);        

        /*5使能串口*/
        USART_Cmd(USART1,ENABLE);
       
        /*6编写中断处理函数*/
}

/*实现发送字符串的功能描述:
依次发送字符串中的字符,每发送一个检查下TXE标志位,修改库函数中的USART_SendData()函数即可
发送完全部字符以后,最后检查TC标志位 ,*/
void USART_SendByte(USART_TypeDef* USARTx, uint16_t Data)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_DATA(Data));
   
  /* Transmit Data */
  USARTx->DR = (Data & (uint16_t)0x01FF);
        while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE)==RESET);
}

void USART_SendString(USART_TypeDef* USARTx, char *str)
{       
        while(*str!='\0')
        {
                USART_SendByte(USARTx,*str++);
        }
        while( USART_GetFlagStatus(USARTx, USART_FLAG_TC)==RESET);
}

uint8_t USART_ReceiveByte(USART_TypeDef* USARTx)
{
        while(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE)==RESET);
        return (uint8_t) USART_ReceiveData(USART1);

}

static volatile uint8_t  g_usart1_buf[128]={0};
static volatile uint32_t g_usart1_cnt=0;
void USART1_IRQHandler(void)                        //串口1中断服务程序
{
        uint8_t d=0;
        int i = 0;
        u16 alarm_buf[64]={0};
        u16 rtc_buf[64]={0};
        if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        {
                //接收串口数据
                d = USART_ReceiveData(USART1);       
               
                g_usart1_buf[g_usart1_cnt] = d;
               
                g_usart1_cnt++;

                //设置闹钟
                if(d == 'A'|| g_usart1_cnt>= sizeof(g_usart1_buf))
                {                
                        char *s = strtok((char *)g_usart1_buf,"- :");  //分割符是- :
                         while(s!=NULL)
                        {   
                            alarm_buf[i] = atoi(s);    //2022-1-10 23:50:5A
                                i++;
                                s = strtok(NULL,"- :");
                                g_usart1_cnt = 0;
                        }
                        RTC_Alarm_Set(alarm_buf[0],alarm_buf[1],alarm_buf[2],alarm_buf[3], alarm_buf[4],alarm_buf[5]); //闹钟设置
                        printf("%d-%d-%d %d:%d:%d  设置闹钟成功!\r\n",alarm_buf[0],alarm_buf[1],alarm_buf[2],alarm_buf[3],alarm_buf[4],alarm_buf[5]);
                }       
                //设置时间
                else if(d == 'R'|| g_usart1_cnt>= sizeof(g_usart1_buf))
                {                
                        char *s = strtok((char *)g_usart1_buf,"- :");  //分割符是- :
                         while(s!=NULL)
                        {   
                            rtc_buf[i] = atoi(s);    //2022-1-10 23:50:5R
                                i++;
                                s = strtok(NULL,"- :");
                                g_usart1_cnt = 0;
                        }
                        RTC_Set(rtc_buf[0],rtc_buf[1],rtc_buf[2],rtc_buf[3], rtc_buf[4],rtc_buf[5]); //设置时间
                        printf("%d-%d-%d %d:%d:%d  设置时间成功!\r\n",rtc_buf[0],rtc_buf[1],rtc_buf[2],rtc_buf[3],rtc_buf[4],rtc_buf[5]);
                }       
                //清空串口接收中断标志位
                USART_ClearITPendingBit(USART1, USART_IT_RXNE);
        }
}

/*不勾选微库则需要这个*/
#pragma import(__use_no_semihosting)
struct __FILE
{
        int handle;
};

FILE __stdout;
void _sys_exit(int x)
{
        x = x;
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
                /* 发送一个字节数据到串口 */
                USART_SendData(USART1, (uint8_t) ch);
               
                /* 等待发送完毕 */
                while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);               
                return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
                /* 等待串口输入数据 */
                while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

                return (int)USART_ReceiveData(USART1);
}

使用特权

评论回复
6
l63t89|  楼主 | 2023-12-23 14:46 | 只看该作者
追加内容:
如果有3V纽扣电池和32.768KHz的石英晶振,就可以实现断电后RTC继续走时的功能。

使用特权

评论回复
7
l63t89|  楼主 | 2023-12-23 14:46 | 只看该作者
根据数据手册可知,外部低速时钟电路可以这样搭建:
其中 CL1 和 CL2 为 5pF~15pF 之间的瓷介电容器,OSC32_IN为引脚PC14,OSC32_OUT为引脚PC15。

使用特权

评论回复
8
l63t89|  楼主 | 2023-12-23 14:46 | 只看该作者

使用特权

评论回复
9
l63t89|  楼主 | 2023-12-23 14:47 | 只看该作者
纽扣电池电路可以这样搭建:
当接电池和有v3.3电源时,就会选择v3.3供电。当接电池和没有v3.3电源时,就会选择电池供电,即3v3掉电后RTC也能照常工作,备用的纽扣电池。当不接电池和有v3.3电源时也会选择v3.3供电。

使用特权

评论回复
10
l63t89|  楼主 | 2023-12-23 14:47 | 只看该作者

使用特权

评论回复
11
l63t89|  楼主 | 2023-12-23 14:47 | 只看该作者
代码实现如下:
除RTC的初始化不一样外,其他都一样。
/*******************************************************************************
* 函 数 名         : RTC_Init_LSE
* 函数功能                   : RTC初始化
* 输    入         : 无
* 输    出         : 0,初始化成功
                                 1,LSE开启失败
外部低速时钟
注意:使用外部低速时钟断电后如果有备用电池可以继续走时
*******************************************************************************/
u8 RTC_Init_LSE(void)
{
        //检查是不是第一次配置时钟
        u8 temp=0;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
        PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
        //RCC_LSICmd(ENABLE); //设置内部低速晶振(LSI)
        if (BKP_ReadBackupRegister(BKP_DR1) != 0x6060) //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
        {
                BKP_DeInit(); //复位备份区域
                RCC_LSEConfig(RCC_LSE_ON);        //设置外部低速晶振(LSE),使用外设低速晶振
                while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&& temp < 250) //检查指定的RCC标志位设置与否,等待低速晶振就绪
                {
                        temp++;
                        delay_ms(10);
                }
                if(temp>=250)return 1;//初始化时钟失败,晶振有问题
                RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
                //RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); //设置RTC时钟(RTCCLK),选择LSI作为RTC时钟
                RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
                RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
                RTC_WaitForSynchro(); //等待RTC寄存器同步
                RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR, ENABLE); //使能RTC秒中断、闹钟中断
                RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
                RTC_EnterConfigMode();/// 允许配置
                 RTC_SetPrescaler(32767); //设置RTC预分频的值
                //RTC_SetPrescaler(40000); //设置RTC预分频的值
                RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成

                //RTC_Set(2017,3,6,0,0,0); //设置时间
                get_time();
                RTC_Set(calendar.w_year+2000-1 ,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);  //设置时间
               
                RTC_ExitConfigMode(); //退出配置模式
                BKP_WriteBackupRegister(BKP_DR1, 0X6060); //向指定的后备寄存器中写入用户程序数据
        }
        else//系统继续计时
        {
                RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
                RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR, ENABLE); //使能RTC秒中断、闹钟中断
                RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
        }
        RTC_NVIC_Config();//RCT中断分组设置
        RTC_Get();//更新时间
        return 0; //ok
}

使用特权

评论回复
12
l63t89|  楼主 | 2023-12-23 14:49 | 只看该作者
最后:
需要代码的可以自行下载。
下载操作:

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

90

主题

798

帖子

1

粉丝