/*! * \file xc_drv_calib.c * * \brief Target xc calib driver implementation * * \copyright Revised BSD License, see section \ref LICENSE. * * \code * * _ __ _ ________ _ * | |/ /(_)___ / ____/ /_ (_)___ * | // / __ \/ / / __ \/ / __ \ * / |/ / / / / /___/ / / / / /_/ / * /_/|_/_/_/ /_/\____/_/ /_/_/ .___/ * /_/ * (C) 2022-2025 XinChip * * \endcode * * \author ( XinChip ) Alex-J * * \author ( XinChip ) */ /*----------------------------------------------------------------------------------- INCLUDE HEADE FILES ------------------------------------------------------------------------------------*/ #include "xc_drv_calib.h" /*------------------------------------------------------------------------------------ Macros -------------------------------------------------------------------------------------*/ #define RC_ADJ_VAL_MIN 0x0 #define RC_ADJ_VAL_MID 0x1fff #define RC_ADJ_VAL_MAX 0x3fff #define RANGE_LPO_RTM 0x400 #define GEARS_HZ 1.56f #define _32K_STAND_HZ 32000 #define CALIB_NUM 50 // ms #define CALIB_TIME_MS 10 // ms #define CALIB_DELAY_CHECK (CALIB_TIME_MS + 20) // ms #define CALIB_STEP_CYCLE_32K (CALIB_TIME_MS * 32) /*------------------------------------------------------------------------------------ Local Variables -------------------------------------------------------------------------------------*/ static volatile uint32_t mid = RC_ADJ_VAL_MID; // MID_LPO_RTM; /*------------------------------------------------------------------------------------ Global Variables -------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------ Func Prototype -------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------ Functions -------------------------------------------------------------------------------------*/ /** * @brief xc_rc32k_calib_init * @details RC32k calibration initialization * * @param uint16_t - rc_adj_val * @retval void */ void xc_rc32k_calib_init(uint16_t rc_adj_val) { if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_32M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_48M)) { cpr_ctl_pclk_grctl__ctl_pclk_gr_upd__setf(0x1UL); cpr_ctl_pclk_grctl__ctl_pclk_gr__setf(4U); } else if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_64M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_96M)) { cpr_ctl_pclk_grctl__ctl_pclk_gr__setf(2U); cpr_ctl_pclk_grctl__ctl_pclk_gr_upd__setf(0x1UL); } else if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_16M)) { cpr_ctl_pclk_grctl__ctl_pclk_gr_upd__setf(0x1UL); cpr_ctl_pclk_grctl__ctl_pclk_gr__setf(8U); } cpr_ctlapbclken_grctl__rtc_pclk_en__setf(0x1UL); cprao_aon_clken_grctl__rtc_clk_en__setf(0x1UL); rtc_rccal_lfcfg_set(0x100031); if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_16M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_32M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_64M)) { rtc_rccal_hfcfg_set(25000); rtc_rccal_lim1_set(3); rtc_rccal_lim2_set(13); } else if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_48M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_96M)) { rtc_rccal_hfcfg_set(37500); rtc_rccal_lim1_set(4); rtc_rccal_lim2_set(19); } rtc_rccal_adj_step_set(0x1404); rtc_rccal_adj_val_set(0x1fff); rtc_rccal_adj_cfg_set(0x1fff4000 | rc_adj_val); rtc_rccal_intsta_set(0x03); rtc_rccal_inten_set(0x01); for (uint16_t dly = 0; dly < 1000; dly++) ; rtc_rccal_en_set(0x01); } /** * @brief xc_flfclk_calcu * @details Frequency lfclk value calculation * * @param uint32_t - hfcnt_val * @retval uint16_t - f_lfclk */ uint16_t xc_flfclk_calcu(uint32_t hfcnt_val) { uint32_t hfcnt_load = rtc_rccal_hfcfg_get(); uint32_t lfdiv = rtc_rccal_lfcfg_get(); uint8_t hfcnt_dir = (hfcnt_val >> 31); uint16_t f_lfclk = 0; if (hfcnt_dir == 0) { if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_16M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_32M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_64M)) { f_lfclk = CLOCK_HFCLK_IN_16M * (lfdiv + 1) / (hfcnt_load - hfcnt_val); } else if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_48M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_96M)) { f_lfclk = 24000000 * (lfdiv + 1) / (hfcnt_load - hfcnt_val); } } else { if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_16M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_32M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_64M)) { f_lfclk = CLOCK_HFCLK_IN_16M * (lfdiv + 1) / (hfcnt_load + hfcnt_val - 0x80000000); } else if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_48M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_96M)) { f_lfclk = 24000000 * (lfdiv + 1) / (hfcnt_load + hfcnt_val - 0x80000000); } } return f_lfclk; } /** * @brief xc_rc32k_freq_scope * @details RC32k frequency scope * * @param uint16_t - freq * @retval eLFREQ_Sta - ret */ eLFREQ_Sta xc_rc32k_freq_scope(uint16_t freq) { eLFREQ_Sta ret = LFREQ_UNKNOW; uint16_t offset = (CLOCK_RC_LFCLK_IN_32K * 2 / 100); uint16_t rc32_offset_min = (CLOCK_RC_LFCLK_IN_32K - offset); uint16_t rc32_offset_max = CLOCK_RC_LFCLK_IN_32K; if (freq < rc32_offset_min) ret = LFREQ_LOWER; else if (freq >= rc32_offset_max) ret = LFREQ_HIGHER; else ret = LFREQ_GOOD; return ret; } /** * @brief RC32k_Calib_by_hw * @details Calibrating RC32k through hardware. * * @param uint16_t - freq * @retval eLFREQ_Sta - ret */ void xc_rc32k_calib_by_hw(void) { uint16_t f_lfclk = 0; uint8_t rccal_int_sta; eLFREQ_Sta f_lf_sta; xc_rc32k_calib_init(mid); while (1) { do { rccal_int_sta = rtc_rccal_intsta_get(); } while (!(rccal_int_sta & 0x01)); rtc_rccal_inten_set(0x00); rtc_rccal_intsta_set(rccal_int_sta); f_lfclk = xc_flfclk_calcu(rtc_rccal_hfcnt_val_get()); f_lf_sta = xc_rc32k_freq_scope(f_lfclk); if (LFREQ_GOOD != f_lf_sta) { rtc_rccal_en_set(0x00); // XC_RTC->RCCAL_EN = 0x00; if (f_lf_sta == LFREQ_LOWER) { mid += 100; if (mid > RC_ADJ_VAL_MAX) { mid = RC_ADJ_VAL_MIN; } } else { mid -= 10; if (mid < 10) { mid = RC_ADJ_VAL_MAX; } } rtc_rccal_adj_cfg_set(0x1fff4000 | mid); rtc_rccal_inten_set(0x01); rtc_rccal_en_set(0x01); } else { DEBUG("f_lfclk: %d\n", f_lfclk); break; } } rtc_rccal_en_set(0x00); rtc_rccal_adj_cfg_set(0x00001fff); rtc_rccal_adj_cfg__freq_adj_offset__setf(mid); rtc_rccal_adj_cfg__freq_adj_hw_clr__setf(0x01); rtc_rccal_intsta_set(0x03); for (uint16_t dly = 0; dly < 1000; dly++) ; rtc_rccal_en_set(0x01); } /** * @brief rc32k_calib_by_soft * @details Calibrate RC32k by software. * * @param uint16_t - freq * @retval eLFREQ_Sta - ret */ void xc_rc32k_calib_by_soft(void) { uint32_t f_lfclk = 0; uint32_t t32k_cnt = 0; uint8_t ao_intr; xc_rc32k_calib_init(mid); rtc_rccal_inten_set(0x00); rtc_rccal_intsta_set(0x03); while (1) { rtc_ao_timer_ctl_set(0); rtc_ao_timer_ctl__rtc_ao_timer_value__setf(CALIB_STEP_CYCLE_32K); rtc_ao_timer_ctl__rtc_freq_timer_en__setf(1); do { ao_intr = rtc_all_intr_ao_get(); } while ((ao_intr & 0x20) == 0); t32k_cnt = rtc_freq_32k_timer_val_get(); rtc_ao_timer_ctl__rtc_ao_timer_clr__setf(0x1UL); rtc_ao_timer_ctl_set(0); if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_16M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_32M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_64M)) { f_lfclk = (uint32_t)((((float)((16.0) * CALIB_STEP_CYCLE_32K)) / t32k_cnt) * 1000000); } else if ((xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_48M) || (xc_clock_hfclk_in_get() == CLOCK_HFCLK_IN_96M)) { f_lfclk = (uint32_t)((((float)((24.0) * CALIB_STEP_CYCLE_32K)) / t32k_cnt) * 1000000); } if (f_lfclk > _32K_STAND_HZ) { mid = mid - ((uint32_t)(((float)(f_lfclk - _32K_STAND_HZ)) / (float)GEARS_HZ)); } else if (f_lfclk < _32K_STAND_HZ) { mid = mid + ((uint32_t)(((float)(_32K_STAND_HZ - f_lfclk)) / (float)GEARS_HZ)); } // DEBUG("f_lfclk: %d\n", f_lfclk); rtc_rccal_en_set(0x00); rtc_rccal_adj_cfg_set(0x1fff4000 | mid); rtc_rccal_en_set(0x01); if (f_lfclk == _32K_STAND_HZ) break; } }