如何在一个简单的嵌入式系统中跟踪时间,假设您需要以秒为单位的时间的定点表示,并且您之间的时间间隔不能以该定点格式精确表达?在这些情况下,如何避免累积错误。
这个问题是对slashdot的this article的反应。
0.1秒不能整齐地表示为二进制定点数,正如1/3不能整齐地表示为十进制定点数。任何二进制定点表示都有一个小错误。例如,如果在该点之后有8个二进制位(即使用按256缩放的整数值),则0.1乘256为25.6,将四舍五入为25或26,导致-2.3%的错误或分别为+ 1.6%。在该点之后添加更多二进制位会减小此错误的比例,但无法消除它。
重复添加后,错误会逐渐累积。
如何避免这种情况?
答案 0 :(得分:7)
一种方法不是试图通过重复添加0.1秒常数来计算时间,而是保持简单的整数时钟滴答计数。这个滴答计数可以根据需要以秒为单位转换为定点时间,通常使用乘法后跟除法。给定中间表示中的足够位,该方法允许任何有理缩放,并且不会累积错误。
例如,如果当前的滴答计数为1024,我们可以得到当前时间(在点后8位的固定点)乘以256,然后除以10 - 或等效地乘以128然后除以5.无论哪种方式,都存在错误(除法中的余数),但错误是有界的,因为余数总是小于5.没有累积误差。
答案 1 :(得分:2)
另一种方法在整数乘法和除法被认为成本太高的情况下可能是有用的(这些日子应该变得非常罕见)。它从Bresenhams line drawing algorithm借用了一个想法。您将当前时间保持在固定点(而不是刻度计数),但您还保留一个错误术语。当误差项变得太大时,可以对时间值应用校正,从而防止误差累积。
在8位后点示例中,0.1秒的表示为25(256/10),误差项(余数)为6.在每一步,我们向误差累加器添加6。基于此,到目前为止,前两个步骤是......
Clock Seconds Error
----- ------- -----
25 0.0977 6
50 0.1953 12
在第二步,错误值溢出 - 超过10.因此,我们递增时钟并从错误中减去10。每次错误值达到10或更高时都会发生这种情况。
因此,实际的序列是......
Clock Seconds Error Overflowed?
----- ------- ----- -----------
25 0.0977 6
51 0.1992 2 Yes
76 0.2969 8
102 0.3984 4 Yes
几乎总是存在错误(仅当错误值为零时,时钟才精确正确),但误差受一个小常数的限制。时钟值没有累积误差。
答案 2 :(得分:0)
仅硬件解决方案是安排硬件时钟周期快速运行 - 精确到足以补偿由重复添加的滴答持续时间值的舍入引起的累积损失。也就是说,调整硬件时钟滴答速度,使定点滴答持续时间值精确正确。
仅当时钟只使用一种定点格式时才有效。
答案 3 :(得分:0)
为什么不用0.1秒计数器,每十次递增你的秒计数器,并将0.1计数器包回0?
答案 4 :(得分:0)
在这个特定的例子中,我只是将时间计数保持在十分之一秒(或毫秒,或适合应用程序的任何时间范围)。我一直在小型系统或控制系统中这样做。
因此,100小时的时间值将存储为3_600_000
个刻度 - 零错误(除了可能由硬件引入的错误)。
这种简单技术引入的问题是:
我还必须使用不是“常规”的计时器来玩其他一些技巧。例如,我在一台需要每秒发生300次数据采集的设备上工作。硬件定时器每毫秒触发一次。没有办法缩放毫秒计时器以获得正好1/300秒的单位。因此,我们必须拥有能够在每3个,3个和4个刻度执行数据采集的逻辑,以防止采集过度漂移。
如果您需要处理硬件时间错误,那么您需要多个时间源并一起使用它们以保持总体时间同步。根据您的需要,这可能很简单或非常复杂。
答案 5 :(得分:0)
过去我见过的东西:增量值不能用定点格式精确表示,但可以表示为分数。 (这类似于“跟踪错误值”解决方案。)
实际上在这种情况下问题略有不同,但在概念上类似 - 问题不是这样的定点表示,而是从时钟源导出不是完美倍数的定时器。我们的硬件时钟频率为32,768 Hz(基于钟表晶体的低功耗定时器通用)。我们想要一个毫秒计时器。
毫秒计时器应该每32.768个硬件标记递增一次。第一个近似值是每33个硬件标记递增一次,标称值为0.7%。但是,注意到0.768是768/1000或96/125,你可以这样做:
毫秒计数器上会出现一些短期“抖动”(32对33硬件标记),但长期平均值为32.768硬件标记。