RT-Thread的PM组件利用LPTIM来实现低功耗唤醒和系统tick补偿,而LPTIM的时钟源一般会选择LSI。LSI这玩意儿功耗虽然只有几百nA,但是频偏非常大,例如STML051手册上就说LSI的频率在26kHz ~ 56kHz之间,典型值为38kHz(HAL库里面是37kHz),如下图所示。
在一些对时间要求稍高的低功耗应用场景,LSI的精度无法满足需求。例如某设备需要每30秒唤醒一次,如果LSI的时钟偏高,那么设备的唤醒的周期肯定会小于30秒。虽然对LSI的实际精度我们无法校准,但是我们可以测算出LPTIM的实际时钟频率。在RT-Thread的drv_lptim驱动中,需要向上层提供LPTIM的频率,只要我们提供的这个频率贴近实际值,那么休眠时造成的时间误差就可以减小。
如何测算LPTIM的实际频率?实际上可以依靠高速时钟来计算,例如systick。高速时钟相比LSI而言,精度还是高很多的。具体思路如下:假设LSI的频率为37kHz,LPTIM的时钟为LSI的32分频,那么LPTIM的频率为(37000 >> 5),在这个频率下让LPTIM定时1秒,同时利用systick来记录实际经过的时间delta_time,那么就可以很方便地利用利用delta_time来反推LPTIM的实际频率。参考代码如下。LSI的精度受芯片自身和外部环境影响,建议定时给LPTIM校准一次。
void stm32lx_lptim_calibration(void)
{
rt_uint32_t delta_time = 0;
__HAL_LPTIM_CLEAR_FLAG(&LptimHandle, LPTIM_FLAG_CMPM);
/* 定时1秒,reload = 37000 / 32 = 1156 */
HAL_LPTIM_TimeOut_Start(&LptimHandle, 0xFFFF, 1156);
delta_time = rt_tick_get();
/* 等待lptim定时时间到 */
while(!__HAL_LPTIM_GET_FLAG(&LptimHandle, LPTIM_FLAG_CMPM))
{
rt_thread_delay(1);
}
delta_time = rt_tick_get() - delta_time;
/* 计算lptim的实际频率 */
lptim_freq = (LSI_VALUE >> 5) * RT_TICK_PER_SECOND / delta_time;
}