STM32G474之CALIB输出源是1Hz和512Hz的时钟源。通过测试输出波形,计算RTC输入时钟和理论值之间的误差,为“校准”服务的。
1、CALIB输出原理
2、CALIB输出测试程序
#include "RTC.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "LED.h"
#include "delay.h"
void RTC_Init(void);
void RTC_Display(void);
void RTC_Set_Date_And_Time(uint16_t syear,uint8_t smon,uint8_t sday,uint8_t week,uint8_t hour,uint8_t min,uint8_t sec,uint32_t Format);
uint8_t RTC_Set_Alarm(uint8_t mode,uint8_t sWeekDay,uint8_t hour,uint8_t min,uint8_t sec,uint8_t subSecond,uint32_t Format);
void RTC_CALIB_Output_On_RTCOUTx_Pin(RTC_HandleTypeDef *hrtc,uint8_t OutputPin);
void RTC_Init(void)
{
RTC_HandleTypeDef hrtc;
RCC_OscInitTypeDef RCC_OscInitStruct; //配置LSE/LSI时钟
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;//用来为RTC选择时钟源
__HAL_RCC_PWR_CLK_ENABLE();//Enable write access
HAL_PWR_EnableBkUpAccess();//Enable the power clock
#ifdef RTC_CLOCK_SOURCE_LSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
//配置LSE时钟,关闭LSI时钟
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
//选择LSE时钟为RTC时钟源
#elif defined (RTC_CLOCK_SOURCE_LSI)
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
//配置LSI时钟
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
//选择LSI时钟为RTC时钟源
#endif /*RTC_CLOCK_SOURCE_LSE*/
__HAL_RCC_RTCAPB_CLK_ENABLE();//使能RTC APB外部设备时钟,Enable RTC peripheral Clocks
__HAL_RCC_RTC_ENABLE();//使能RTC时钟,Enable RTC Clock
hrtc.Instance = RTC; //选择RTC
hrtc.Init.HourFormat = RTC_HOURFORMAT_24; //指定RTC小时的格式
hrtc.Init.AsynchPrediv = RTC_ASYNCH_PREDIV;//指定RTC异步预分法器的值。
hrtc.Init.SynchPrediv = RTC_SYNCH_PREDIV; //指定RTC同步预分配器的值。
RTC_CALIB_Output_On_RTCOUTx_Pin(&hrtc,1);//“RTC唤醒”输出从RTC_OUT1引脚输出
// RTC_CALIB_Output_On_RTCOUTx_Pin(&hrtc,2);//“RTC唤醒”输出从RTC_OUT2引脚输出
HAL_RTC_Init(&hrtc);
if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != 0x32F2)
{//读TAMP_BKPxR寄存器,因为RTC_BKP_DR0=0,所以是读TAMP_BKP0R寄存器
// Test_RTC_Set_Date_And_Time();//设置时间为24年2月29日星期四23:59:50:00
// RTC_Set_Date_And_Time(0x24,0x02,0x29,0x04,0x23,0x59,0x50,RTC_FORMAT_BCD);
//设置时间为24年2月29日星期四23:59:50:00
RTC_Set_Date_And_Time(24,2,29,4,23,59,50,RTC_FORMAT_BIN);
//设置时间为24年2月29日星期四23:59:50:00
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0x32F2);
//写TAMP_BKPxR寄存器,因为RTC_BKP_DR0=0,所以是写TAMP_BKPxR寄存器
//将0x32F2写入TAMP_BKP0R寄存器
//Writes a data in a RTC Backup data Register0
}
HAL_RTCEx_SetCalibrationOutPut(&hrtc,RTC_CALIBOUTPUT_512HZ);
// HAL_RTCEx_SetCalibrationOutPut(&hrtc,RTC_CALIBOUTPUT_1HZ);
}
//函数功能:显示“年月日和星期几”以及“时分秒”
void RTC_Display(void)
{
RTC_DateTypeDef sdatestructureget;//用来保存“年月日和星期几”
RTC_TimeTypeDef stimestructureget;//用来保存“时分秒”
printf("RTC_IT_SEC\r\n");
HAL_RTC_GetTime(NULL, &stimestructureget, RTC_FORMAT_BIN);//读取"RTC时间"
//读RTC_TR寄存器bit21:20(HT[1:0]),HT[1:0]表示小时的十位数值
//读RTC_TR寄存器bit19:16(HU[3:0]),HU[3:0]表示小时的个位数值
//读RTC_TR寄存器bit14:12(MNT[2:0]),MNT[2:0]表示分钟的十位数值
//读RTC_TR寄存器bit11:8(MNU[3:0]),MNU[3:0]表示分钟的个位数值
//读RTC_TR寄存器bit6:4(ST[2:0]),ST[2:0]表示秒的十位数值
//读RTC_TR寄存器bit3:0(SU[3:0]),SU[3:0]表示秒的个位数值
//HAL库耍牛氓,我也耍牛氓
HAL_RTC_GetDate(NULL, &sdatestructureget, RTC_FORMAT_BIN);//读取"RTC日期"
//读RTC_DR寄存器bit23:20(YT[3:0]),YT[3:0]表示年的十位数值
//读RTC_DR寄存器bit19:16(YU[3:0]),YU[3:0]表示年的个位数值
//读RTC_DR寄存器bit15:13(WDU[2:0]),WDU[2:0]=001b表示星期1;WDU[2:0]=010b表示星期2......WDU[2:0]=111b表示星期日
//读RTC_DR寄存器bit12(MT),MT表示月的十位数值
//读RTC_DR寄存器bit11:8(MU[3:0]),MU[3:0]表示月的个位数值
//读RTC_DR寄存器bit5:4(DT[1:0]),DT[1:0]表示日的十位数值
//读RTC_DR寄存器bit3:0(DU[3:0]),DU[3:0]表示日的个位数值
//HAL库耍牛氓,我也耍牛氓
printf("%02d-%02d-%02d day:%02d ", sdatestructureget.Year,sdatestructureget.Month,sdatestructureget.Date,sdatestructureget.WeekDay);
printf("%02d:%02d:%02d\r\n", stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds);
//显示时间格式为 : YY-MM-DD hh:mm:ss
}
//函数功能:设置时钟,24小时格式
//RTC_FORMAT_BIN:年月日时分秒的格式是十进数
//RTC_FORMAT_BCD:年月日时分秒的格式是BCD码
//RTC_Set_Date_And_Time(0x21,0x02,0x29,0X04,0x23,0x59,0x30,RTC_FORMAT_BCD);
//RTC_Set_Date_And_Time(21,2,29,4,23,59,30,RTC_FORMAT_BIN);
void RTC_Set_Date_And_Time(uint16_t syear,uint8_t smon,uint8_t sday,uint8_t week,uint8_t hour,uint8_t min,uint8_t sec,uint32_t Format)
{
uint32_t tmpreg;
uint32_t datetmpreg;
uint32_t sTimeFormat;
__HAL_RTC_WRITEPROTECTION_DISABLE(NULL);
//RTC->WPR = 0xCAU;RTC->WPR = 0x53U;
//在“备份域”被修改后,一些RTC寄存器会受到写保护,因此,再次配置,需要解锁。
//解锁受保护的RTC寄存器的步骤:先将0xCA写入RTC_WPR寄存器,然后将0x53RTC_WPR寄存器
//HAL库耍牛氓,我也耍牛氓
RTC_EnterInitMode(NULL);
//进入修改“RTC_TR和RTC_DR”模式
//HAL库耍牛氓,我也耍牛氓
CLEAR_BIT(RTC->CR, RTC_CR_FMT);
//设置当前时间格式为“24小时格式”
//RTC_CR寄存器bit6(FMT),令FMT=0表示24小时格式,FMT=1表示12小时格式
sTimeFormat = 0x00U;//记录当前时间格式为“24小时格式”
if (Format == RTC_FORMAT_BIN)//十进制数据
{
tmpreg = (uint32_t)(((uint32_t)RTC_ByteToBcd2(hour) << RTC_TR_HU_Pos) | \
((uint32_t)RTC_ByteToBcd2(min) << RTC_TR_MNU_Pos) | \
((uint32_t)RTC_ByteToBcd2(sec) << RTC_TR_SU_Pos) | \
(((uint32_t)sTimeFormat) << RTC_TR_PM_Pos));
//RTC_ByteToBcd2()将两位十进制数转换成BCD格式的数据
datetmpreg = (((uint32_t)RTC_ByteToBcd2(syear) << RTC_DR_YU_Pos) | \
((uint32_t)RTC_ByteToBcd2(smon) << RTC_DR_MU_Pos) | \
((uint32_t)RTC_ByteToBcd2(sday) << RTC_DR_DU_Pos) | \
((uint32_t)week << RTC_DR_WDU_Pos));
}
else
{//数据格式为BCD码:RTC_FORMAT_BCD
tmpreg = (((uint32_t)(hour) << RTC_TR_HU_Pos) | \
((uint32_t)(min) << RTC_TR_MNU_Pos) | \
((uint32_t)(sec) << RTC_TR_SU_Pos) | \
((uint32_t)(sTimeFormat) << RTC_TR_PM_Pos));
datetmpreg = ((((uint32_t)syear) << RTC_DR_YU_Pos) | \
(((uint32_t)smon) << RTC_DR_MU_Pos) | \
(((uint32_t)sday) << RTC_DR_DU_Pos) | \
(((uint32_t)week) << RTC_DR_WDU_Pos));
}
WRITE_REG(RTC->TR, (tmpreg & RTC_TR_RESERVED_MASK));//修改“时分秒”
WRITE_REG(RTC->DR, (uint32_t)(datetmpreg & RTC_DR_RESERVED_MASK));//修改“年月日和星期几”
CLEAR_BIT(RTC->CR, RTC_CR_BKP);
//将RTC_CR寄存器的bit18(BKP),令BKP=0,取消记忆“夏令时被修改过”
SET_BIT(RTC->CR, (RTC_DAYLIGHTSAVING_NONE | RTC_STOREOPERATION_RESET));
//RTC_CR寄存器bit16(ADD1H),ADD1H=0,当前时间不增加1小时
//RTC_CR寄存器bit18(BKP),BKP=0,不使用“夏令时”
CLEAR_BIT(RTC->ICSR, RTC_ICSR_INIT);
//RTC_ICSR寄存器bit7(INIT),INIT=0进入“自由运行模式”
if (READ_BIT(RTC->CR, RTC_CR_BYPSHAD) == 0U)
{//读RTC_CR寄存器bit5(BYPSHAD),BYPSHAD=0表示“日历值从影子寄存器读取”
if (HAL_RTC_WaitForSynchro(NULL) != HAL_OK)
{//等待“日历影子寄存器同步”
}
}
else
{//RTC_CR寄存器bit5(BYPSHAD),若BYPSHAD=1表示“日历值从日历计数器中读取”
/* Clear BYPSHAD bit */
CLEAR_BIT(RTC->CR, RTC_CR_BYPSHAD);
//RTC_CR寄存器bit5(BYPSHAD),令BYPSHAD=0“日历值从影子寄存器读取”
if (HAL_RTC_WaitForSynchro(NULL) != HAL_OK)
{//等待“日历影子寄存器同步”
}
/* Restore BYPSHAD bit */
SET_BIT(RTC->CR, RTC_CR_BYPSHAD);
//RTC_CR寄存器bit5(BYPSHAD),若BYPSHAD=1表示“日历值从日历计数器中读取”
}
__HAL_RTC_WRITEPROTECTION_ENABLE(NULL);
//RTC->WPR = 0xFFU;//RTC寄存器上锁,使能写保护
//HAL库耍牛氓,我也耍牛氓
}
//在调用HAL_RTC_Init()之前调用RTC_CALIB_Output_On_RTCOUTx_Pin()
//RTC_CALIB_Output_On_RTCOUTx_Pin(&hrtc,1);//“RTC唤醒”输出从RTC_OUT1引脚输出
//RTC_CALIB_Output_On_RTCOUTx_Pin(&hrtc,2);//“RTC唤醒”输出从RTC_OUT2引脚输出
//配置RTC_OUT1输出“RTC唤醒溢出标志”,其电平时间和WUTF的保持时间有关
void RTC_CALIB_Output_On_RTCOUTx_Pin(RTC_HandleTypeDef *hrtc,uint8_t OutputPin)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
hrtc->Init.OutPutPolarity = RTC_OUTPUT_POLARITY_LOW; //报警TAMPALRM输出低电平;
hrtc->Init.OutPutType = RTC_OUTPUT_TYPE_PUSHPULL; //TAMPALRM为推挽输出
hrtc->Init.OutPutPullUp = RTC_OUTPUT_PULLUP_ON; //TAMPALRM输出上拉
if(OutputPin==1)
{
__HAL_RCC_GPIOC_CLK_ENABLE(); //GPIOC时钟使能
GPIO_InitStruct.Pin = GPIO_PIN_13; //选择引脚号码为13
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出模式
// GPIO_InitStruct.Pull = GPIO_NOPULL; //引脚上拉和下拉都没有被激活
GPIO_InitStruct.Pull = GPIO_PULLUP; //设置上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
//根据GPIO_InitStruct结构变量指定的参数初始化GPIOC的外设寄存器
hrtc->Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE; //RTC_OUT1输出使能,RTC_OUT2输出不使能;
}
else if(OutputPin==2)
{
__HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB外设时钟
GPIO_InitStruct.Pin = GPIO_PIN_2;//选择引脚编号2
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; //复用功能推挽模式
// GPIO_InitStruct.Pull = GPIO_NOPULL; //引脚上拉和下拉都没有被激活
GPIO_InitStruct.Pull = GPIO_PULLUP; //设置上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的输出速度为120MHz
GPIO_InitStruct.Alternate = GPIO_AF0_RTC_50Hz; //将引脚复用为RTC_OUT2
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
hrtc->Init.OutPutRemap = RTC_OUTPUT_REMAP_POS1; //RTC_OUT1输出不使能,RTC_OUT2输出使能;
}
hrtc->Init.OutPut = RTC_OUTPUT_DISABLE; //设置“RTC唤醒”“RTC报警A”“RTC报警B”均不输出
}