CH58x嘀嗒定时器(SysTick)

一、基本概念 

SysTick是一个定时器,只是它放在了NVIC中,主要的目的是为了给操作系统提供一个硬件上的中断(号称滴答中断)。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。SysTick是基于32M高频晶振工作,所以32M不停止,则嘀嗒定时器一直存在。

CH592的嘀嗒定时器是内核自带的64位计数器。

设置好嘀嗒时间后,计数满会进入到中断并需要手动清除标志

SysTick代码参考:

#include "CH58x_common.h"

void DebugInit(void){
    GPIOA_SetBits(GPIO_Pin_9);
    GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
    GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
    UART1_DefInit();
}

int main(){
    SetSysClock(CLK_SOURCE_PLL_60MHz);
    DebugInit();        /* 配置串口调试 */
    PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);

    SysTick_Config(FREQ_SYS);    //设置为60M,则1s进一次中断。设置为FREQ_SYS/1000,则1ms进入一次中断。

    while(1){
        PRINT("SYS_GetSysTickCnt = %d\r\n", SYS_GetSysTickCnt());    //获取计数时间,计数满会进入中断并清0。
    }
}

__INTERRUPT
__HIGH_CODE
void SysTick_Handler()          /***嘀嗒定时器中断函数***/
{
    SysTick->SR = 0;                    //清除中断标志
}

通过嘀嗒定时器计算运行时间:

 先使能嘀嗒定时器,然后函数获取时间即可。注意进入中断会清0,所以592计算时间如果是反过来,说明已经清0过,需要加上进入中断前清0的时间。

SysTick_Config(FREQ_SYS);
PRINT("%d\r\n", SYS_GetSysTickCnt());
noptest();
PRINT("%d\r\n", SYS_GetSysTickCnt());

__INTERRUPT
__HIGH_CODE
void SysTick_Handler()          /***嘀嗒定时器中断函数***/
{
    SysTick->SR = 0;                    //清除中断标志
}

定时器获取时间与嘀嗒定时器获取用法相同,接口函数:printf("time:%d\n", TMR0_GetCurrentTimer());

嘀嗒定时器在经过睡眠唤醒后,计数会被清除。需要重新使能。

二、扩展:32M校准32K,射频调整

直接通过示波器抓取32K的波形误差比较大,抓取32M的波形误差比较小。
32M可以产生嘀嗒定时器,如果32M的波形非常稳定,则嘀嗒定时器可以产生非常稳定的1s的波形。
32K可以产生RTC定时器,通过1s1次的RTC定时器与滴答定时器进行对比。
从而通过滴答定时器校准RTC定时器,这样可以使32K的波形变稳定。

1、通过蓝牙连接苹果手机调整32M波形

首先需要确保32M的波形是准确的,因此可以通过程序里添加一个TMOS任务,跟苹果手机连接后开启这个任务,然后看一下打印的值是多少,如果不在0附近,可以尝试调整频偏,调整频偏函数:HSECFG_Capacitance(HSECap_20p);
需要调整为打印出来的值为0附近,然后测试一下连接的情况。

原理:苹果手机(较新的手机)的蓝牙频偏比较准,蓝牙连接后可以大致估算出从机蓝牙的频偏,进行匹配电容的调整。使用仪器测试更加准确。

if ( events & ReadCfo_EVT )
{
    int16 readcfo = 0;
    readcfo = BLE_ReadCfo();
    PRINT("ReadCfo_Count = %d\r\n", readcfo);
    tmos_start_task( Peripheral_TaskID, ReadCfo_EVT, 1600 );      
    return (events ^ ReadCfo_EVT);
} 

2、通过32M校准32K

32MHZ晶振:10PPM、12PF(立创商城YXC扬兴科技3225型号)
32768晶振:20PPM、12.5PF

如果参数如上,可以直接尝试将32M的匹配电容通过程序调整为20pf(差不多在0附近)。

调整32M的波形:HSECFG_Capacitance(HSECap_20p);
调整32K:LSECFG_Capacitance(LSECap_20p);

#include "CH58x_common.h"

void DebugInit(void){
    GPIOA_SetBits(GPIO_Pin_9);
    GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
    GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
    UART1_DefInit();
}

void HAL_TimeInit( void ){
#if( CLK_OSC32K )                                      //使用内部的32K RC作为RTC的时钟,由于rc振荡器是误差比较大,所以wch提供了校准函数,并且根据需求是校准到32Khz 还是32.768khz
    R8_SAFE_ACCESS_SIG = 0x57;
    R8_SAFE_ACCESS_SIG = 0xa8;
    R8_CK32K_CONFIG &= ~(RB_CLK_OSC32K_XT|RB_CLK_XT32K_PON);
    R8_CK32K_CONFIG |= RB_CLK_INT32K_PON;
    //Lib_Calibration_LSI();                           // 32K rc的校准,如果不需要高精度(比如一些非ble应用),可以注释掉这个代码,可以省去一些RAM和flash空间
    Calibration_LSI_FLASH();
    //Calibration_LSI_RAM();
#else
    R8_SAFE_ACCESS_SIG = 0x57;
    R8_SAFE_ACCESS_SIG = 0xa8;
    R8_CK32K_CONFIG |= RB_CLK_OSC32K_XT | RB_CLK_INT32K_PON | RB_CLK_XT32K_PON;
    R8_SAFE_ACCESS_SIG = 0;
#endif
    RTC_InitTime( 2020, 1, 1, 0, 0, 0 );                //RTC时钟初始化当前时间,这里实际上年月日时分秒的计算都是软件实现,如果不需要,可以注释掉.同样可以省区一些RAM和flash资源
    //TMOS_TimerInit( 0 );                              //tmos的时间初始化,当我么没有用到tmos时候,这里注释掉即可
}

void ch573_systick_init(void) {
    SysTick_Config(0xffffffff);
    //HCLK DIV 8
    //SysTick->CTLR &= ~(0x000001<<2);

    PFIC_DisableIRQ(SysTick_IRQn);

}

int main(){
    uint8_t i;
    SetSysClock(CLK_SOURCE_PLL_60MHz);
    /* 配置串口调试 */
    DebugInit();
    PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);
    HSECFG_Capacitance(HSECap_20p);

    LSECFG_Capacitance(LSECap_20p);

    HAL_TimeInit();
    PFIC_EnableIRQ(RTC_IRQn);
    RTC_TMRFunCfg(Period_1_S);
    ch573_systick_init();

    while(1);
}

__INTERRUPT
__HIGH_CODE
void RTC_IRQHandler(void){
    R8_RTC_FLAG_CTRL = RB_RTC_TMR_CLR;
    static uint32_t last_systick_cnt,current_systick_cnt;

    current_systick_cnt = (SysTick->CNT)&0xffffffff;

#if 0
    int32_t gap = (last_systick_cnt - current_systick_cnt)&0xffffffff;      //573
#else
    int32_t gap = (current_systick_cnt - last_systick_cnt)&0xffffffff;      //582
#endif

#if 0
    PRINT("gap = %d\r\n", gap);
    PRINT("last_systick_cnt = %d\r\n", last_systick_cnt);
    PRINT("current_systick_cnt = %d\r\n", current_systick_cnt);
#endif
    PRINT("rtc error = %5d ppm\n",(int32_t)(60e6- gap)/60);
    last_systick_cnt = current_systick_cnt;
}

 

posted @ 2023-02-27 11:34  SweetTea_lllpc  阅读(887)  评论(0编辑  收藏  举报