[APM32E1] APM32E103XE通过HSE校准LSI的方法

[复制链接]
435|4
onemoren 发表于 2025-9-12 17:22 | 显示全部楼层 |阅读模式
本帖最后由 onemoren 于 2025-9-12 17:27 编辑


#技术资源# #有奖活动# #申请原创# @21小跑堂

问题:
使用APM3E103XE芯片内部LSI时钟驱动内部RTC,每天误差超过1小时。是否可以通过外部时钟HSE 8MHz进行LSI校准,使RTC一天内的偏差不超过5分钟?

答案:
可以。AMP32E103XE可通过HSE作为参考,对LSI进行校准(RTC 24小时最多86.4秒)

实验效果
实验思路:PCBA上有外部HSE晶振,可以使用HSE晶振校准内部LSI。具体思路是使用内核SYSTICK定时器做计数校准。实验使用E103RET6芯片+DEMO板+SDK例程验证。
实验结果:在极海官网SDK例程的基础上修改程序后验证2PCS,确认不同芯片+不同主频情况下,LSI校准后RTC秒中断每秒最多偏差1ms,RTC 24小时最多偏差1ms*60*60*24=86.4秒。
1.png
2.png
校准方法原理、步骤、代码
1方法:main函数代码本文后面有提供,直接将提供的代码替换到极海APM32E103XE SDK的RTC例程中的main.c就可以验证。烧录后可以通过串口打印出秒中断时间。
3.png
2、校准原理:
LSI是内部低速RC振荡器(30k-60kHz,典型值40kHz),芯片间的一致性不高。校准的核心是以高精度的HSE作为参考时钟,通过内核自带的systick定时器获取基于LSI信号的秒中断时间,计算出其实际频率,再通过软件调整RTC预分频器参数实现正确的分频。
3详细步骤:具体如下,主要分4步:
① 配置SYSTICK,实现每1ms中断一次并统计系统时间。(实际使用中如果有RTOS,此步骤可跳过,直接获取RTOS系统时间戳使用)
② 配置串口,用于打印信息。(实际使用中此步骤可跳过)
③ 获取LSI实测频率。
④ 验证基于实测频率下的秒中断时间(实际使用中此步骤可跳过,直接按照实测频率配置RTC分频即可)
4.png      
4实际代码与注解
/************************************* Copyright(C)******************************
** @FileName   : main.c
** @brief      : 使用SYSTICK校准LSI
** @author     :
** @version    : V1.0
** @date       :
********************************************************************************/
/* Includes */
#include "main.h"
#include "stdio.h"
volatile uint32_t sys_ms=0;    //全局变量,系统时间
volatile uint32_t test_time;   //测试用时间
/****************************** BEGIN ********************************
**@name       : USART1_Init
**@Brief      : 配置串口,方便打印信息
**@Author     :
**@Version    : V1.0
**@time       :
******************************** END *********************************/
void USART1_Init(void)
{
    USART_Config_T USART_ConfigStruct;
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1);
    USART_ConfigStruct.baudRate = 115200;
    USART_ConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
    USART_ConfigStruct.mode = USART_MODE_TX_RX;
    USART_ConfigStruct.parity = USART_PARITY_NONE;
    USART_ConfigStruct.stopBits = USART_STOP_BIT_1;
    USART_ConfigStruct.wordLength = USART_WORD_LEN_8B;
    USART_Config(USART1, &USART_ConfigStruct);
    GPIO_Config_T GPIO_configStruct;
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
    GPIO_configStruct.mode = GPIO_MODE_AF_PP;
    GPIO_configStruct.pin = GPIO_PIN_9;
    GPIO_configStruct.speed = GPIO_SPEED_50MHz;
    GPIO_Config(GPIOA, &GPIO_configStruct);
    GPIO_configStruct.mode = GPIO_MODE_IN_FLOATING;
    GPIO_configStruct.pin = GPIO_PIN_10;
    GPIO_Config(GPIOA, &GPIO_configStruct);
    USART_Enable(USART1);
}
/****************************** BEGIN ********************************
**@Name       : LSI_Calibration
**@Brief      : LSI校准,返回LSI频率
**@Author     :
**@Version    : V1.0
**@Time       :
******************************** END *********************************/
uint32_t LSI_Calibration(void)
{
    uint32_t lsi_freq=32767;
    uint32_t time;
   
    //LSI RTC配置
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_PMU);
    PMU_EnableBackupAccess();
    RCM_EnableLSI();
    while (RCM_ReadStatusFlag(RCM_FLAG_LSIRDY) == RESET);
    RCM_ConfigRTCCLK(RCM_RTCCLK_LSI);
    RCM_EnableRTCCLK();
    RTC_WaitForSynchro();
    RTC_WaitForLastTask();
    RTC_ConfigPrescaler(lsi_freq);
    RTC_WaitForLastTask();
    RTC_ConfigCounter(0);
    RTC_WaitForLastTask();
   
    //获取1000ms实际计数
    time=sys_ms;
    RTC_ClearStatusFlag(RTC_FLAG_SEC);
    RTC_WaitForLastTask();
    while (RTC_ReadStatusFlag(RTC_FLAG_SEC)==0);
    time=sys_ms-time;
    printf("before_calibration_time: %dms\r\n", time);
    lsi_freq=lsi_freq*1000/time;
    printf("lsi_freq: %d\r\n", lsi_freq);
   
    //RTC disable
    RTC_ClearStatusFlag(RTC_FLAG_SEC);
    RTC_WaitForLastTask();
    RCM_DisableLSI();
    RCM_DisableAPB1PeriphClock((RCM_APB1_PERIPH_T)RCM_APB1_PERIPH_PMU);
    return lsi_freq;
}
/****************************** BEGIN ********************************
**@Name       : LSI_Verify
**@Brief      : LSI按照输入频率分频后验证秒中断时间
**@Author     :
**@Version    : V1.0
**@Time       :
******************************** END *********************************/
void LSI_Verify(uint32_t prescaler)
{
    RCM_EnableAPB1PeriphClock((RCM_APB1_PERIPH_T)RCM_APB1_PERIPH_PMU);
    PMU_EnableBackupAccess();
    RCM_EnableLSI();
    while (RCM_ReadStatusFlag(RCM_FLAG_LSIRDY) == RESET);
    RCM_ConfigRTCCLK(RCM_RTCCLK_LSI);
    RCM_EnableRTCCLK();
    RTC_WaitForSynchro();
    RTC_WaitForLastTask();
    RTC_ConfigPrescaler(prescaler);
    RTC_WaitForLastTask();
    RTC_ConfigCounter(0);
    RTC_WaitForLastTask();
    test_time=sys_ms;
   
    //开启中断
    RTC_EnableInterrupt(RTC_INT_SEC);
    RTC_WaitForLastTask();
    NVIC_EnableIRQRequest(RTC_IRQn, 0, 0);
}
int main(void)
{
    uint32_t lsi_freq;
    SysTick_Config(RCM_ReadSYSCLKFreq()/ 1000);    //配置systick,1ms中断一次
    USART1_Init();
   
    printf("LSI_CAL_TEST\r\n");
    printf("SYS CLK: %d MHZ\r\n", RCM_ReadSYSCLKFreq()/1000000);  
   
    lsi_freq=LSI_Calibration();
    LSI_Verify(lsi_freq);
    while (1)
    {
    }
}
void SysTick_Handler(void)
{
    sys_ms++;
}
void RTC_IRQHandler(void)
{
    if (RTC_ReadIntFlag(RTC_INT_SEC) != RESET)
    {
        printf("after_calibration_time: %dms\r\n", sys_ms-test_time);
        test_time=sys_ms;
        RTC_ClearIntFlag(RTC_INT_SEC);
        RTC_WaitForLastTask();
    }
}
int fputc(int ch, FILE* f)
{
    /* send a byte of data to the serial port */
    USART_TxData(USART1, (uint8_t)ch);
    /* wait for the data to be send  */
    while (USART_ReadStatusFlag(USART1, USART_FLAG_TXBE) == RESET);
    return (ch);
}
      

评论

通过SYSTICK定时器的计数功能,可以精确测算基于LSI运行的时间间隔,进而计算出需要调整的分频参数  发表于 2025-9-13 13:44
天体书记 发表于 2025-9-12 20:10 | 显示全部楼层
这个没有啥作用吧!?
LSI受温度影响最大了,环境温度要保持不变?
梦之一瞥 发表于 2025-9-13 09:37 | 显示全部楼层
LSI时钟还没有使用过呢!
WDT也嫌处理麻烦暂时没有使用
tpgf 发表于 2025-9-13 13:43 | 显示全部楼层
APM32E103XE通过HSE校准LSI的方法是一种利用高精度外部晶振作为参考时钟,对内部低速RC振荡器进行频率校正的技术
您需要登录后才可以回帖 登录 | 注册

本版积分规则

49

主题

69

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部