问题描述:
该问题由某客户提出,发生在STM32F103RBT6 器件上。据工程师讲述:其产品为车载GPS导航监控设备,其中使用了STM32 作为主控器件,负责管理整个设备。在该产品的设计中,使用了STM32的RTC,并将其计时显示在产品的屏幕上。计时显示的更新是由RTC 的秒中断来完成的,即由RTC的秒中断服务程序从RTC 中读出新的时间并更新到相关的变量中,再触发屏幕刷新程序更新屏幕上的显示。
在测试时发现屏幕上显示时间的秒部分走时不均匀,时快时慢,甚至会丢掉某个中间值而发生跳变。对该显示时间做长时间计时的测量,发现其长时间计时是准确的,即秒长度的平均值是准确的。将程序中的其它中断关掉,只保留RTC的秒中断,问题依旧。通过在RTC 秒中断服务程序中加入对GPIO 翻转的代码来测量RTC 秒中断响应的时间间隔,发现其是均匀的,如图(一)所示,说明并非RTC 的秒中断响应不及时而导致显示时间的波动。
用同样的方法测量从RTC 秒中断得到响应到完成屏幕上显示时间的更新所消耗的时间,结果为1.56mS,如图(二)所示。这一延时不足以对屏幕上显示时间造成的可察觉的波动。到此,不知下步该如何定位问题原因.
调研:
重复观察现象,如其所述。为了便于观察,修改代码,在每次从RTC 读取新的时间之后,将保存时间的变量通过UART 打印到终端软件上。其结果如图(三)所示,与设备屏幕上的显示时间是一致的:
进一步修改代码,在每次从RTC 读取时间数据之前加入1mS 的等待,如表(一)所示,其中变量TimeDisplay 由RTC 的秒中断服务程序置“1”,触发该任务更新屏幕显示时间.
Void Tim_Show(void)
{ /* infinite loop */
While(1)
{/* if 1 second has passed */
If (TimeDisplay==1)
{/*display current time */
Delay(1) // 从RTC读取时间之前延时1s
Time_Display(RTC_GetCounter());
TimeDisplay = 0;
GPIO_WriteBit(GPIO_LED,GPIO_Pin_6,Bit_RESET);
}
}
}
编译后重新测试,结果表明之前所述现象不再发生.
结论与处理:
用户程序对RTC 时间的读取与RTC 自身内部对时间数据的更新在时序上存在竞争关系,以至两者在时间上的操作顺序不确定。于是,读到的时间数据,时而是新数据,时而是旧数据,从而导致读回的时间数据的取值会发生跳变、或者不变的现象,而不是稳定的递增。
通过在读RTC 的时间数据之前加入一定延时,来保证其与RTC 内部数据更新之间保持一个固定的时间顺序,以便读回的都是更新后的数据。
深入分析:
在STM32 中,RTC 部分与CPU分别属于不同的时钟域,且两者的工作时钟频率相差较大,前者工作在32.768KHz 的频率下,而后者的工作频率通常在36MHz 以上。这一差别导致两者对同一事件做出响应的速度存在着明显的差距,CPU的响应速度快而RTC 响应速度慢。从下图所示的RTC系统架构中可以看到,由前置分频器产生的秒脉冲信号送给RTC 的计数器的同时,也通过RTC的中断请求控制单元送到了CPU 的NVIC 单元。
这样的信号传递关系决定了,在RTC 的计数器接到更新计数的信号的同时,RTC 的秒中断请求信号即已送达CPU,而不是在RTC 的计数器完成更新计数之后。这样一来,由于RTC 对这一事件的响应时间是RTC时钟域的1 个时钟周期,即30.5us,而CPU 对这一事件的响应时间才几个到几十个CPU 时钟域的时钟周期。假如CPU 工作在36MHz 的频率下, 一般来说这一时间在330ns左右,所以,CPU 要先于RTC 的计数器响应这一事件。然而,一般情况下软件的中断服务程序并非一开始就去读RTC的时间数据,而是要先做一些前期的判断和准备工作,甚至要等到中断服务程序结束后,由普通任务去读这一数据。由此,也会引发一个延时,通常这一延时的时长在10uS到50uS 之间。于是,读取RTC 的时间数据的事件和RTC 计数器更新计数值的事件在非常邻近的时间点上发生,且无固定顺序。综上所述,几个事件在时间上的关系,如图下所示:
为了消除RTC 计数器更新时间数据和软件读取RTC 时间数据这两个事件在时间上的竞争,可以让后一事件延后一段时间之后再发生,以确保两个事件有固定的先后顺序。
本文转载微信公众号:融创芯城(一站式电子元器件,PCB,PCBA购买与服务,项目众包、方案共享平台)