gd32的rtc使用心得

本文介绍了在GD32微控制器中,当外部低速振荡器(LSE)无法启动时,如何切换到内部低速振荡器(LSI)并保持RTC时钟精度的解决方案。通过配置RTC设置和监控系统运行时间,确保在LSE故障时无缝过渡到LSI模式。
AI助手已提取文章相关产品:

gd32的rtc用起来真是不好用,对晶振要求太高,不容易起振。本文章旨在解决外部LSE不起振时,使用LSI并保证精度的解决方案。

struct RTC_MONITOR
{
uint8_t monitor_on;//0:on,1:off
uint8_t rtcclk_source;//0:lse,1:lsi

uint8_t rtc_backsecond;
uint8_t rtc_second;

uint32_t timer_period;
uint32_t sysrun_bytimer;
uint32_t sysrun_bytimers;
uint32_t sysrun_byrtc;

uint8_t get_second;
uint8_t get_minute;
uint8_t get_hour;
uint8_t get_day;
uint8_t get_month;
uint32_t get_year;
uint32_t get_counter;
};


struct RTC_MONITOR rtc_monitor;

int main(void)

{

//init hardware

RTC_Configuration();

init_rtc_monitor();

while(1)

{

rtc_monitor_on();  

}

}

uint32_t lwoff_timeout,rsf_timeout,lsifrequency;
void RTC_Configuration(void)
{
  uint32_t WaitForOscSource;


  
  /*Enables the clock to Backup and power interface peripherals    */
  RCC_APB1PeriphClock_Enable(RCC_APB1PERIPH_BKP | RCC_APB1PERIPH_PWR,ENABLE);
  /* RTC clock source configuration ------------------------------------------*/
  /* Allow access to BKP Domain */
  PWR_BackupAccess_Enable(ENABLE);

/*Enable 40 kHz internal oscillator */
RCC_LSI_Enable(ENABLE);//´ò¿ªLSI


while(RCC_GetBitState(RCC_FLAG_LSISTB)==RESET);//µÈ´ýÖ±µ½LSIÎȶ¨

//read back
rtc_monitor.get_day  = BKP_ReadBackupRegister(BKP_DR3);
  rtc_monitor.get_month = BKP_ReadBackupRegister(BKP_DR2);
  rtc_monitor.get_year  = BKP_ReadBackupRegister(BKP_DR4);
rtc_monitor.get_second   = BKP_ReadBackupRegister(BKP_DR5);
  rtc_monitor.get_minute = BKP_ReadBackupRegister(BKP_DR6);
  rtc_monitor.get_hour  = BKP_ReadBackupRegister(BKP_DR7);
rtc_monitor.get_counter = RTC_GetCounter();

if(BKP_ReadBackupRegister(BKP_DR1)==CONFIGURATION_RESET)
{
    /* Backup Domain Reset */
//rtc clk init
BKP_DeInit(); 
PWR_BackupAccess_Enable(ENABLE);


while((PWR->CTLR & PWR_CTLR_BKPWE) == RESET);
/*Enable 32.768 kHz external oscillator */
RCC_LSEConfig(RCC_LSE_EN);
for(WaitForOscSource=0;WaitForOscSource<5000;WaitForOscSource++)
{
}


RCC_RTCCLKConfig(RCC_RTCCLKSOURCE_LSE);
/* RTC Enabled */
RCC_RTCCLK_Enable(ENABLE);

//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}

/*Wait for RTC registers synchronisation */
//RTC_WaitRSF();
/* Clear RSF flag */
RTC->CTLR2 &= ~((uint16_t)RTC_FLAG_RSF);
/* Loop until RSF flag is set */
rsf_timeout = LSE_STARTUP_TIMEOUT;
while (((RTC->CTLR2 & RTC_FLAG_RSF) == (uint16_t)RESET)&&(rsf_timeout))
{
rsf_timeout--;
}
//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}



/* Setting RTC Interrupts-Seconds interrupt enabled */
/* Enable the RTC Second */
RTC_INT_Enable(RTC_INT_SF , ENABLE);
/* Wait until last write operation on RTC registers has finished */
//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}
/* Set RTC prescaler: set RTC period to 1 sec */
RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
/* Prescaler is set to 32766 instead of 32768 to compensate for
lower as well as higher frequencies*/
/* Wait until last write operation on RTC registers has finished */
//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}

Day = (rtc_monitor.get_day == 0)?(DEFAULT_DAY):(rtc_monitor.get_day);
Month = (rtc_monitor.get_month == 0)?(DEFAULT_MONTH):(rtc_monitor.get_month);
Year = rtc_monitor.get_year;
Hour = rtc_monitor.get_hour;
Minute = rtc_monitor.get_minute;
Second = rtc_monitor.get_second;

BKP_WriteBackupRegister(BKP_DR2,Month);
BKP_WriteBackupRegister(BKP_DR3,Day);
BKP_WriteBackupRegister(BKP_DR4,Year);
BKP_WriteBackupRegister(BKP_DR5,Second);
BKP_WriteBackupRegister(BKP_DR6,Minute);
BKP_WriteBackupRegister(BKP_DR7,Hour);


    BKP_WriteBackupRegister(BKP_DR1, CONFIGURATION_DONE);
  }
else
{
/* Allow access to BKP Domain */
PWR_BackupAccess_Enable(ENABLE);
while((PWR->CTLR & PWR_CTLR_BKPWE) == RESET);

//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}
/* Enable the RTC Second */
RTC_INT_Enable(RTC_INT_SF , ENABLE);
/* Wait until last write operation on RTC registers has finished */
//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}
}

  /* Check if how many days are elapsed in power down/Low Power Mode-
   Updates Date that many Times*/
  CheckForDaysElapsed();

  BKP_RTCOutputConfig(BKP_RTCOUTPUT_NULL);
}




void RTC_LSI_Configuration(void)
{
uint32_t WaitForOscSource;
//lse can not work use lsi
RCC_APB1PeriphClock_Enable(RCC_APB1PERIPH_BKP | RCC_APB1PERIPH_PWR,ENABLE);


BKP_DeInit();
for(WaitForOscSource=0;WaitForOscSource<50000;WaitForOscSource++)
{
}
PWR_BackupAccess_Enable(ENABLE);


while((PWR->CTLR & PWR_CTLR_BKPWE) == RESET);

/*Enable 40 kHz internal oscillator */
RCC_LSI_Enable(ENABLE);//´ò¿ªLSI


while(RCC_GetBitState(RCC_FLAG_LSISTB)==RESET);//µÈ´ýÖ±µ½LSIÎȶ¨


RCC_RTCCLKConfig(RCC_RTCCLKSOURCE_LSI);
/* RTC Enabled */
RCC_RTCCLK_Enable(ENABLE);

//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}

/*Wait for RTC registers synchronisation */
//RTC_WaitRSF();
/* Clear RSF flag */
RTC->CTLR2 &= ~((uint16_t)RTC_FLAG_RSF);
/* Loop until RSF flag is set */
rsf_timeout = LSE_STARTUP_TIMEOUT;
while (((RTC->CTLR2 & RTC_FLAG_RSF) == (uint16_t)RESET)&&(rsf_timeout))
{
rsf_timeout--;
}
//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}



/* Setting RTC Interrupts-Seconds interrupt enabled */
/* Enable the RTC Second */
RTC_INT_Enable(RTC_INT_SF , ENABLE);
/* Wait until last write operation on RTC registers has finished */
//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}

lsifrequency = GetLSIFrequency();
/* Set RTC prescaler: set RTC period to 1 sec */
RTC_SetPrescaler(lsifrequency); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
/* Prescaler is set to 32766 instead of 32768 to compensate for
lower as well as higher frequencies*/
/* Wait until last write operation on RTC registers has finished */
//RTC_WaitLWOFF();
lwoff_timeout = LSE_STARTUP_TIMEOUT;
/* Loop until RTOFF flag is set */
while (((RTC->CTLR2 & RTC_FLAG_LWOFF) == (uint16_t)RESET)&&(lwoff_timeout))
{
lwoff_timeout--;
}

Day = (rtc_monitor.get_day == 0)?(DEFAULT_DAY):(rtc_monitor.get_day);
Month = (rtc_monitor.get_month == 0)?(DEFAULT_MONTH):(rtc_monitor.get_month);
Year = rtc_monitor.get_year;

Hour = rtc_monitor.get_hour;
Minute = rtc_monitor.get_minute;
Second = rtc_monitor.get_second;


Set_RTCTime();//set counter
}


uint32_t GetLSIFrequency(void)
{
TIMER_BaseInitPara  TimerBaseStructure;
TIMER_ICInitPara TIMInput_Config;
NVIC_InitPara NVIC_InitStructure;

extern __IO uint32_t uwLsiFreq;


extern  __IO uint32_t uwCaptureNumber;

RCC_APB1PeriphClock_Enable(RCC_APB1PERIPH_TIMER5, ENABLE);
  
RCC_LSI_Enable(ENABLE);//´ò¿ªLSI


  while(RCC_GetBitState(RCC_FLAG_LSISTB)==RESET);//µÈ´ýÖ±µ½LSIÎȶ¨

TIMER_Enable(TIMER5, DISABLE);
RCC_GetClocksFreq(&RCC_Clocks);
/* Configure the Priority Group to 2 bits */
NVIC_PRIGroup_Enable(NVIC_PRIGROUP_4);

/* Enable the TIMER5 gloabal Interrupt */
NVIC_InitStructure.NVIC_IRQ = TIMER5_IRQn;
NVIC_InitStructure.NVIC_IRQPreemptPriority = 0;
NVIC_InitStructure.NVIC_IRQSubPriority = 0;
NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
NVIC_Init(&NVIC_InitStructure);



TimerBaseStructure.TIMER_Prescaler = 0;
TimerBaseStructure.TIMER_Period = 0xffff;
TimerBaseStructure.TIMER_ClockDivision = TIMER_CDIV_DIV1;
TimerBaseStructure.TIMER_CounterMode = TIMER_COUNTER_UP;


TIMER_BaseInit(TIMER5, &TimerBaseStructure);

RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_AF, ENABLE);
GPIO_PinRemapConfig(GPIO_REMAP_TIMER5CH4_LSI,ENABLE);


/* Configure the Input Capture of TIM_CHANNEL_4 */
TIMInput_Config.TIMER_CH  = TIMER_CH_4;
  TIMInput_Config.TIMER_ICPolarity  = TIMER_IC_POLARITY_RISING;
  TIMInput_Config.TIMER_ICSelection = TIMER_IC_SELECTION_DIRECTTI;
  TIMInput_Config.TIMER_ICPrescaler = TIMER_IC_PSC_DIV8;
  TIMInput_Config.TIMER_ICFilter    = 0;


TIMER_ICInit(TIMER5,&TIMInput_Config);

uwCaptureNumber = 0;

TIMER_INTConfig(TIMER5,TIMER_INT_CH4,ENABLE);
TIMER_Enable(TIMER5, ENABLE);

/* Wait until the TIM5 get 2 LSI edges */
  while(uwCaptureNumber != 2)
  {
  }
TIMER_INTConfig(TIMER5,TIMER_INT_CH4,DISABLE);

/* Disable the TIMER5 gloabal Interrupt */
NVIC_InitStructure.NVIC_IRQ = TIMER5_IRQn;
NVIC_InitStructure.NVIC_IRQPreemptPriority = 0;
NVIC_InitStructure.NVIC_IRQSubPriority = 0;
NVIC_InitStructure.NVIC_IRQEnable = DISABLE;
NVIC_Init(&NVIC_InitStructure);

return uwLsiFreq;
}



void init_rtc_monitor(void)
{
rtc_monitor.monitor_on = 1;
rtc_monitor.rtcclk_source = 0;

rtc_monitor.rtc_backsecond = Second;
rtc_monitor.rtc_second = Second;

rtc_monitor.timer_period = 50;//50ms
rtc_monitor.sysrun_bytimer = 0;
rtc_monitor.sysrun_byrtc = 0;
}


void rtc_monitor_on(void)
{
if(rtc_monitor.rtc_backsecond != Second)
{
rtc_monitor.rtc_backsecond = Second;
rtc_monitor.sysrun_byrtc++;
}
rtc_monitor.sysrun_bytimers = (rtc_monitor.sysrun_bytimer*rtc_monitor.timer_period)/1000;
//compare system runtime by timer and rtc only in lse condition
if(rtc_monitor.rtcclk_source == 0)
{
//only if sysrun_byrtc == 0 and bytimer >= 3,indicate that lse donot work, turn into lsi mode
if((rtc_monitor.sysrun_byrtc == 0)&&(rtc_monitor.sysrun_bytimers >= 3))
{
rtc_monitor.rtcclk_source = 1;

RTC_LSI_Configuration();

rtc_monitor.sysrun_byrtc = (rtc_monitor.sysrun_bytimer*rtc_monitor.timer_period)/1000;
}
}
}



需要配合2个TIMER实时监控RTC的运行情况

void TIMER3_IRQHandler(void)
{
if(TIMER_GetIntBitState(TIMER3, TIMER_INT_UPDATE) == SET)
{

TIMER_ClearIntBitState(TIMER3, TIMER_INT_UPDATE);

rtc_monitor.sysrun_bytimer++;

uint16_t tmpCCTIM_CHANNEL_4[2] = {0, 0};
__IO uint32_t uwLsiFreq = 0;


__IO uint32_t uwCaptureNumber = 0;
__IO uint32_t uwPeriodValue = 0;

void TIMER5_IRQHandler(void)
{
if(TIMER_GetIntBitState(TIMER5, TIMER_INT_CH4) == SET)
{
TIMER_ClearIntBitState(TIMER5, TIMER_INT_CH4);

/* Get the Input Capture value */
tmpCCTIM_CHANNEL_4[uwCaptureNumber++] = TIMER_GetCapture4(TIMER5);//HAL_TIM_ReadCapturedValue(&Input_Handle, TIM_CHANNEL_4);

if (uwCaptureNumber >= 2)
{
if ( tmpCCTIM_CHANNEL_4[0] > tmpCCTIM_CHANNEL_4[1] )
{
/* Compute the period length */
uwPeriodValue = (uint16_t)(0xFFFF - tmpCCTIM_CHANNEL_4[0] + tmpCCTIM_CHANNEL_4[1] + 1);
}
else
{
/* Compute the period length */
uwPeriodValue = (uint16_t)(tmpCCTIM_CHANNEL_4[1] - tmpCCTIM_CHANNEL_4[0] + 1);
}
/* Frequency computation */ 
uwLsiFreq = (uint32_t) SystemCoreClock / uwPeriodValue;
uwLsiFreq *= 8;
}
}
}


您可能感兴趣的与本文相关内容

<think>嗯,用户想了解GD32RTC时钟的配置和使用教程。首先,我需要回忆一下之前提供的引用内容,里面提到了GD32RTC结构、电源域、配置时的常见问题,比如外部时钟的问题,以及时钟配置错误导致的超频情况。用户可能遇到了配置RTC时的问题,比如时间不准或者无法使用外部时钟,需要一步步的指导。首先,我应该先介绍RTC的基本概念和结构,这样用户能有个整体认识。然后分步骤讲解配置流程,包括时钟源选择、备份域设置、初始化步骤等。这部分需要结合引用中的信息,特别是引用[1]提到的备份域和VDD电源域的区别,确保配置时电源正确。引用[2]提到外部低速时钟的问题,例程可能有误,所以在配置外部时钟时要特别注意检查电路和代码是否正确。引用[3]提到时钟配置错误导致超频,所以在设置分频系数时要仔细计算,避免超过芯片的最大频率。 接下来,代码示例部分需要展示如何初始化RTC,包括使能时钟、配置时钟源、设置预分频器、设置时间日期等。这里要确保代码语法正确,特别是寄存器的名称和操作步骤是否与GD32的库函数一致。同时,要提醒用户备份域写保护的问题,需要先解除保护才能配置RTC,这点在引用[1]和[2]中都有涉及。 然后,常见问题部分需要总结用户可能遇到的陷阱,比如外部晶振不起振、时间走得过快、日期不更新等。这些问题可能和预分频系数、时钟源选择错误有关,需要逐一排查。例如,时间走得快可能是因为分频系数设置错误,导致计数频率过高,引用[2]中提到过类似现象。日期不更新可能和闹钟寄存器配置有关,或者软件中的处理逻辑有误。 最后,生成的相关问题要覆盖用户可能进一步的需求,比如备份域的其他功能、低功耗模式下的RTC使用、如何校准RTC等。这些问题可以帮助用户深入理解RTC的应用场景和高级配置,同时引用中的信息可以作为扩展阅读的提示。 整个过程中,需要确保使用正确的中文表达,避免技术术语错误,并且按照用户的要求,将数学公式和独立公式用正确的LaTeX格式表示。虽然当前问题主要涉及代码和配置步骤,但如果有涉及数学计算的地方,比如分频系数的计算,也需要正确应用行内公式的格式,例如$f_{rtc}= f_{clock} /(prescaler+1)$。总结一下,回答的结构应该是:基本概念→配置步骤→代码示例→常见问题→相关问题。每个部分都要参考已有的引用内容,确保信息的准确性和针对性,帮助用户解决实际的配置难题。</think>### GD32 RTC时钟配置及使用教程 #### 一、RTC基本概念 GD32RTC模块属于备份域,即使系统复位或从低功耗模式唤醒,其配置和时间数据仍可保留[^1]。RTC核心由以下部分组成: 1. 32位累加计数器(用于时间戳) 2. 预分频器(调整时钟频率) 3. 闹钟寄存器 4. 时钟源选择(内部低速RC/LSE外部低速晶振) #### 二、配置步骤 1. **时钟源选择** - 外部低速晶振(LSE):需硬件连接32.768kHz晶振,并配置寄存器$RCC\_BDCTRL$使能LSE[^2] - 内部低速RC(LSI):默认频率约40kHz,需校准 ```c // 使能LSE(外部晶振) rcu_osci_on(RCU_LSE); while (rcu_osci_stab_wait(RCU_LSE) == ERROR); ``` 2. **备份域解锁** RTC寄存器位于备份域,需先解除写保护: ```c rcu_periph_clock_enable(RCU_PMU); // 使能PMU时钟 pmu_backup_write_enable(); // 允许写入备份域 ``` 3. **RTC初始化** ```c // 选择时钟源为LSE rcu_rtc_clock_config(RCU_RTCSRC_LSE); rcu_periph_clock_enable(RCU_RTC); // 配置预分频器(假设LSE=32.768kHz) rtc_prescaler_set(32767); // 分频后频率=1Hz rtc_counter_set(0); // 初始化计数器 ``` 4. **时间设置** ```c // 设置日期时间为2023年10月1日12:00:00 rtc_date_t date = {2023, 10, 1}; rtc_time_t time = {12, 0, 0}; rtc_date_time_set(&date, &time); ``` #### 三、常见问题 1. **外部晶振无法启动** - 检查硬件电路是否连接正确 - 参考引用[2],部分例程需修改LSE启动超时时间 2. **时间走得过快** - 预分频系数错误,例如LSE=32.768kHz时需设置$prescaler = 32767$[^3] - 使用LSI时需校准频率 3. **日期不更新** - 确保闹钟寄存器未覆盖主计数器 - 检查备份域电源是否稳定 #### 四、完整代码示例 ```c #include "gd32f3x0.h" void rtc_init(void) { // 解锁备份域 rcu_periph_clock_enable(RCU_PMU); pmu_backup_write_enable(); // 配置LSE rcu_osci_on(RCU_LSE); while(rcu_osci_stab_wait(RCU_LSE) != SUCCESS); // 选择RTC时钟源 rcu_rtc_clock_config(RCU_RTCSRC_LSE); rcu_periph_clock_enable(RCU_RTC); // 初始化RTC rtc_prescaler_set(32767); // 1Hz时钟 rtc_counter_set(0); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值