本帖最后由 L-MCU 于 2024-6-25 14:38 编辑
1、实时时钟(RTC) 关于CH32L103 RTC具体介绍,可看CH32L103应用手册。 关于RTC的预分频系数,最高可设置为2的20次方,但一般根据所用RTC时钟源设置。如LSE频率为32.768KHz,设置预分频器重装值寄存器(RTC_PSCRL)值为0x7FFF,则RTC就1s计数一次。寄存器具体介绍如下。 关于RTC的时钟源,可选为LSE或LSI或HSE128分频,如下图。一般选择LSE或LSI。 关于RTC的复位,其中预分频,预分频重装值,主计数器和闹钟,只能通过后备域的复位信号复位,而RTC控制寄存器由系统复位或电源复位控制。
2、RTC应用 CH32L103 EVT提供了RTC日历例程,程序中,使用TIM1进行RTC校准,TIM定时器设置1us计数一次,重装载值设置为65535,即65536us进一次中断,进一次更新中断计数加1。RTC秒中断中,对TIM计数器的值进行判断,理论上1s=1000000us,将TIM计数器的值与1000000进行比较,根据这之间的差值进行校准。具体的校准函数可参考RTC例程。 RTC例程配置使用LSE作为时钟源,此外还可以配置LSI或HSE/128作为时钟源,注意使用HSE/128作为时钟源时,要配置时钟控制寄存器(RCC_CTLR)使能开启HSE振荡器,可以使用HSE配置系统主频。 下代码为在RTC日历例程基础上修改的可配置使用LSI或HSE/128作为RTC时钟源,完整代码可见附件例程。 - /********************************** (C) COPYRIGHT *******************************
- * File Name : main.c
- * Author : WCH
- * Version : V1.0.0
- * Date : 2024/02/21
- * Description : Main program body.
- *********************************************************************************
- * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
- * Attention: This software (modified or not) and binary are used for
- * microcontroller manufactured by Nanjing Qinheng Microelectronics.
- *******************************************************************************/
- /**
- * @note
- * The code initializes the TIM1 timer and the RTC module, and then performs calibration on the RTC
- * based on a calibration value.
- *
- * this code block is checking if the `CalibrationVal` is less than 1000000. If it is, it
- * calculates the `FastSecPer30days` value by subtracting `CalibrationVal` from 1000000 and then
- * multiplying it by the number of seconds in 30 days (3600 seconds * 24 hours * 30 days) divided
- * by 1000000. This `FastSecPer30days` value is then passed to the `RTC_Calibration` function for
- * further processing.
- */
- #include "debug.h"
- #define RTC_LSE 1
- #define RTC_LSI 2
- #define RTC_HSE_128 3
- #define Mode RTC_HSE_128
- /* Global define */
- #define PPM_PER_STEP 0.9536743 // 10^6/2^20.
- #define PPM_PER_SEC 0.3858025 // 10^6/(30d*24h*3600s).
- /* Global Variable */
- volatile uint8_t CalibrationFlag = 0;
- volatile uint32_t CalibrationTIMCir = 0, CalibrationVal = 0;
- typedef struct
- {
- vu8 hour;
- vu8 min;
- vu8 sec;
- vu16 w_year;
- vu8 w_month;
- vu8 w_date;
- vu8 week;
- } _calendar_obj;
- _calendar_obj calendar;
- 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};
- /* Exported_Functions */
- u8 RTC_Init(void);
- u8 Is_Leap_Year(u16 year);
- u8 RTC_Alarm_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec);
- u8 RTC_Get(void);
- u8 RTC_Get_Week(u16 year, u8 month, u8 day);
- u8 RTC_Set(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec);
- volatile uint8_t Calibration_STA = 0;
- void RTC_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
- void TIM1_UP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
- /*********************************************************************
- * @fn RTC_IRQHandler
- *
- * [url=home.php?mod=space&uid=247401]@brief[/url] This function handles RTC Handler.
- *
- * [url=home.php?mod=space&uid=266161]@return[/url] none
- */
- void RTC_IRQHandler(void)
- {
- if (CalibrationFlag)
- {
- if (RTC_GetITStatus(RTC_IT_SEC) != RESET) /* Seconds interrupt */
- {
- RTC_Get();
- }
- if (RTC_GetITStatus(RTC_IT_ALR) != RESET) /* Alarm clock interrupt */
- {
- RTC_ClearITPendingBit(RTC_IT_ALR);
- RTC_Get();
- }
- printf("year/month/day/week/hour/min/sec:\r\n");
- printf("%d-%d-%d %d %d:%d:%d\r\n", calendar.w_year, calendar.w_month, calendar.w_date,
- calendar.week, calendar.hour, calendar.min, calendar.sec);
- }
- else
- {
- if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
- {
- if (Calibration_STA == 0)
- {
- CalibrationTIMCir = 0;
- TIM1->CNT = 0;
- TIM1->CTLR1 |= TIM_CEN;
- Calibration_STA = 1;
- }
- else if (Calibration_STA == 1)
- {
- TIM1->CTLR1 &= ~TIM_CEN;
- CalibrationVal = TIM1->CNT + CalibrationTIMCir * 65536;
- CalibrationVal < 1000000 ? printf("Calibration val = %ld\n", 1000000 - CalibrationVal) : printf("Calibration val = %ld\n", CalibrationVal - 1000000);
- ;
- TIM1->CNT = 0;
- Calibration_STA = 0;
- CalibrationFlag = 1;
- }
- }
- }
- RTC_ClearITPendingBit(RTC_IT_SEC | RTC_IT_OW);
- RTC_WaitForLastTask();
- }
- void TIM1_UP_IRQHandler()
- {
- if (!CalibrationFlag)
- CalibrationTIMCir += 1;
- TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
- }
- /*********************************************************************
- * @fn TIM1_OutCompare_Init
- *
- * @brief Initializes TIM1 output compare.
- *
- * @param arr - the period value.
- * psc - the prescaler value.
- * ccp - the pulse value.
- *
- * @return none
- */
- void TIM1_Base_Init(u16 arr, u16 psc)
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure = {0};
- NVIC_InitTypeDef NVIC_InitStructure = {0};
- RCC_PB2PeriphClockCmd(RCC_PB2Periph_TIM1, ENABLE);
- TIM_TimeBaseInitStructure.TIM_Period = arr;
- TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
- TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
- TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
- NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
- NVIC_Init(&NVIC_InitStructure);
- TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
- TIM1->CNT = 0;
- }
- /*********************************************************************
- * @fn RTC_NVIC_Config
- *
- * @brief Initializes RTC Int.
- *
- * @return none
- */
- static void RTC_NVIC_Config(void)
- {
- NVIC_InitTypeDef NVIC_InitStructure = {0};
- NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
- }
- /*********************************************************************
- * @fn RTC_Init
- *
- * @brief Initializes RTC collection.
- *
- * @return 1 - Init Fail
- * 0 - Init Success
- */
- u8 RTC_Init(void)
- {
- u8 temp = 0;
- RCC_PB1PeriphClockCmd(RCC_PB1Periph_PWR | RCC_PB1Periph_BKP, ENABLE);
- PWR_BackupAccessCmd(ENABLE);
- /* Is it the first configuration */
- if (BKP_ReadBackupRegister(BKP_DR1) != 0xA1A1)//从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
- {
- BKP_DeInit();
- #if(Mode==RTC_LSE)
- RCC_LSEConfig(RCC_LSE_ON);
- while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && temp < 250)
- {
- temp++;
- Delay_Ms(20);
- }
- if (temp >= 250)
- return 1;
- RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
- #elif(Mode==RTC_LSI)
- RCC_LSICmd(ENABLE);
- while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET && temp < 250)
- {
- temp++;
- Delay_Ms(20);
- }
- if (temp >= 250)
- return 1;
- RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
- #elif(Mode==RTC_HSE_128)
- RCC_RTCCLKConfig(RCC_RTCCLKSource_HSE_Div128);
- #endif
- RCC_RTCCLKCmd(ENABLE);
- RTC_WaitForLastTask();
- RTC_WaitForSynchro();
- RTC_ITConfig(RTC_IT_SEC, ENABLE);
- RTC_WaitForLastTask();
- RTC_EnterConfigMode();
- /*Deliberately speeding up the clock*/
- #if(Mode==RTC_HSE_128)
- RTC_SetPrescaler(62500);
- #elif(Mode==RTC_LSE||Mode==RTC_LSI)
- RTC_SetPrescaler(32766);
- #endif
- RTC_WaitForLastTask();
- RTC_Set(2019, 10, 8, 13, 58, 55); /* Setup Time */
- RTC_ExitConfigMode();
- BKP_WriteBackupRegister(BKP_DR1, 0XA1A1);
- }
- else
- {
- RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
- RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
- RTC_WaitForLastTask();//等待最近一次对RTC寄存器的写操作完成
- }
- RTC_NVIC_Config();
- RTC_Get();
- return 0;
- }
- /*********************************************************************
- * @fn Is_Leap_Year
- *
- * @brief Judging whether it is a leap year.
- *
- * @param year
- *
- * @return 1 - Yes
- * 0 - No
- */
- u8 Is_Leap_Year(u16 year)
- {
- if (year % 4 == 0)
- {
- if (year % 100 == 0)
- {
- if (year % 400 == 0)
- return 1;
- else
- return 0;
- }
- else
- return 1;
- }
- else
- return 0;
- }
- /*********************************************************************
- * @fn RTC_Set
- *
- * @brief Set Time.
- *
- * @param Struct of _calendar_obj
- *
- * @return 1 - error
- * 0 - success
- */
- 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;
- }
- seccount += (u32)(sday - 1) * 86400;
- seccount += (u32)hour * 3600;
- seccount += (u32)min * 60;
- seccount += sec;
- RCC_PB1PeriphClockCmd(RCC_PB1Periph_PWR | RCC_PB1Periph_BKP, ENABLE);
- PWR_BackupAccessCmd(ENABLE);
- RTC_SetCounter(seccount);
- RTC_WaitForLastTask();
- return 0;
- }
- /*********************************************************************
- * @fn RTC_Alarm_Set
- *
- * @brief Set Alarm Time.
- *
- * @param Struct of _calendar_obj
- *
- * @return 1 - error
- * 0 - success
- */
- 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;
- }
- seccount += (u32)(sday - 1) * 86400;
- seccount += (u32)hour * 3600;
- seccount += (u32)min * 60;
- seccount += sec;
- RCC_PB1PeriphClockCmd(RCC_PB1Periph_PWR | RCC_PB1Periph_BKP, ENABLE);
- PWR_BackupAccessCmd(ENABLE);
- RTC_SetAlarm(seccount);
- RTC_WaitForLastTask();
- return 0;
- }
- /*********************************************************************
- * @fn RTC_Get
- *
- * @brief Get current time.
- *
- * @return 1 - error
- * 0 - success
- */
- 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;
- 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)
- {
- 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;
- }
- /*********************************************************************
- * @fn RTC_Get_Week
- *
- * @brief Get the current day of the week.
- *
- * @param year/month/day
- *
- * @return week
- */
- u8 RTC_Get_Week(u16 year, u8 month, u8 day)
- {
- u16 temp2;
- u8 yearH, yearL;
- yearH = year / 100;
- yearL = year % 100;
- if (yearH > 19)
- yearL += 100;
- temp2 = yearL + yearL / 4;
- temp2 = temp2 % 7;
- temp2 = temp2 + day + table_week[month - 1];
- if (yearL % 4 == 0 && month < 3)
- temp2--;
- return (temp2 % 7);
- }
- /*********************************************************************
- * @fn RTC_Calibration
- *
- * @brief The function `RTC_Calibration` calculates a calibration step value based on a given fast seconds per
- * 30 days value.
- *
- * @param FastSecPer30days The `FastSecPer30days` parameter represents the number of fast seconds in a
- * 30-day period. This value is used to calculate the calibration step for the RTC (Real-Time Clock)
- * based on the deviation from the ideal timekeeping.
- *
- * @return none
- */
- void RTC_Calibration(uint16_t FastSecPer30days)
- {
- float Deviation = 0.0;
- u8 CalibStep = 0;
- Deviation = FastSecPer30days * PPM_PER_SEC;
- Deviation /= PPM_PER_STEP;
- CalibStep = (u8)Deviation;
- if (Deviation >= (CalibStep + 0.5))
- CalibStep += 1;
- if (CalibStep > 127)
- CalibStep = 127;
- BKP_SetRTCCalibrationValue(CalibStep);
- printf("Calibration cab: %d\n", CalibStep);
- }
- /*********************************************************************
- * @fn main
- *
- * @brief Main program.
- *
- * @return none
- */
- int main(void)
- {
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- SystemCoreClockUpdate();
- Delay_Init();
- USART_Printf_Init(115200);
- printf("SystemClk:%d\r\n", SystemCoreClock);
- printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID());
- printf("RTC Calibration Test\r\n");
- /* The code is initializing the TIM1 timer and the RTC (Real-Time Clock) module. */
- TIM1_Base_Init(65535, SystemCoreClock / 1000000 - 1);
- RTC_Init();
- while (CalibrationFlag == 0)
- ;
-
- printf("1CalibrationVal:%d\r\n", CalibrationVal);
- if (CalibrationVal < 1000000)
- {
- printf("CalibrationVal:%d\r\n", CalibrationVal);
- uint16_t FastSecPer30days = (1000000 - CalibrationVal)*3600*24*30/1000000;
- printf("FastSecPer30days:%d\r\n", FastSecPer30days);
- RTC_Calibration(FastSecPer30days);
- }
- while (1)
- {
- Delay_Ms(100);
- }
- }
注意EVT例程在MCU复位后时间会重置,重新从初始化配置时间开始运行,若不想MCU复位之后时间重置,可进行如下操作:在RTC初始化时,会有一个对后备数据寄存器的写入,后备数据寄存器可在VDD掉电后靠VBAT电源保存数据。可在RTC初始化时候加一个对后备数据寄存器读出数据的判断。若读出数据与写入数据一致,则时间不重置,继续RTC计数,若不一致,则重置重新开始计数。具体代码见上,如下图为RTC继续计数打印结果:
|