如何处理嵌入式C中的包装计数器

时间:2010-06-22 17:38:05

标签: c embedded integer-overflow

我需要处理一个计数器,它为我的应用程序提供了滴答声。计数器是32位,所以我需要知道的是如何在它包装时处理它。例如:

我有一个函数返回一个(timestamp + shifttime),我有另一个函数将返回1或0,这取决于时间是否已经过去,但我的计数器可能会包装如何处理有了这个 ?。

谢谢

非常感谢所有回复的人。我将在此编辑中提供更多详细信息。

我使用的是STM32 Cortex-M3。我想使用RTC计数器将其用作我的应用程序的滴答,以安排需要以特定间隔发生的任务。 RTC可以产生溢出中断,因此检测中断不是问题。我遇到的主要问题(或者至少我认为是一个问题)是某些任务得到(时间戳+班次),即


int main( void )
{
FlashLedTimeStamp = ReturnCounter( 20 );  // currentcounter value + a shift of 20
StatusLedTimeStamp = ReturnCounter( 3 );  // currentcounter value + a shift of 3

//then later on ....
while(1)
{
    /* other tasks could go here */

    if( HasTimeElapsed( FlashLedTimeStamp );
    {
       /* do something and get another timestamp value */
       FlashLedTimeStamp = ReturnCounter( 20 );  // currentcounter value + a shift of 20
    }

    if( HasTimeElapsed( StatusLedTimeStamp );
    {
       /* do something and get another timestamp value */
       FlashLedTimeStamp = StatusLedTimeStamp( 3 );  // currentcounter value + a shift of 3
    }
}   
}

让我们假设我的RTC计数器只有8位长,以便于数学运算。

如果我的当前计数器是250,当我得到我的时间戳,这意味着FlashLedTimeStamp = 14和StatusLedTimeStamp = 253我将如何检查FlashLedTimeStamp是否已过期?

请记住,我不一定要经常查看当前计数器是什么以及某个时间戳是否已过期。我希望这能说清楚我所遇到的问题是多少先进的。

11 个答案:

答案 0 :(得分:20)

只要开始和结束计数之间的差异小于(2 ^ 32)/ 2,并且假设执行2的补码32位算术(几乎普遍为真),即使计数值跨越,也无关紧要。包点。例如:

Start count: 0xfffffff
End Count:   0x00000002 (incremented through 0,1,2 - i.e. three counts)

End - Start == 0x00000002 - 0xfffffff == 0x00000003

只要计数器是内置整数类型的位宽,并且使用该类型,就可以实现正确的答案。如果计数器寄存器可能不是内置整数类型的宽度,则可以通过屏蔽高阶“溢出”位来实现相同的效果。

如果由于其他原因需要更大的计数,或者连续时间戳之间的差异太大,那么您可以简单地使用另一个在低阶计数器换行时递增的整数。该整数将形成一个较大整数的高位,因此第二个整数的LSB是这个较大整数的第33位。

答案 1 :(得分:13)

如果您读取两个时间戳读数并且您的第一次读数更大而不是第二次读数,那么您的计数器已经包装好了。这是检测包装计数器的基本方法。

但是,这不会检测计数器是否已多次包装,或者计数器已包装并且恰好大于第一次读数的情况。既然你说这是一个嵌入式系统,你的描述使你的“计数器”听起来像一个时钟,看看你是否可以设置一个中断,以便在时钟到零时触发(这样你每次时钟复位时都会得到一个中断)。当此中断触发时,增加一个单独的计数器。这应该有效地为您的时钟增加额外的精度,并允许您的计数器包装而不会造成问题。

答案 2 :(得分:4)

如果您使用无符号变量来存储计数器和计时器到期时间,那么您只需使用此测试:

if (current_time - expiry_time < 0x80000000UL)
    /* timer has expired */

这假设您每0x80000000个刻度至少测试一次到期,并且您的最长计时器设置为在将来过期时小于0x80000000。

答案 3 :(得分:2)

问题有点模糊。一种可能性是在您第一次注意到时间已过时设置标记。一种可靠的方法是添加第二个计数器,当第一个计数器溢出时,该计数器会递增。这实际上会创建一个不会溢出的64位计数器。

答案 4 :(得分:2)

将无符号减法的结果转换为signed并将其与零进行比较。当你经常检查它时,应该处理溢出(并且你的超时时间小于计时器范围的一半)。

uint32_t timer( void);             // Returns the current time value
uint32_t timeout;

timeout = timer() + offset;

// wait until timer() reaches or exceeds timeout value
while ((int32_t)(timeout - timer()) > 0);

答案 5 :(得分:1)

最简单的方法是创建一个“epoch counter”,明确计算翻转次数。 (例如:你有一个计算秒数0..59的硬件计数器。你的纪元计数器会计算几分钟,每次它注意到秒计数器已经翻转时递增。)

您的future_scheduler函数会读取当前的纪元和时间,并为您的活动计算新的纪元和时间。

或者,您可以直接进行,并使您的计时功能在每个计时器时间点将您的事件计划计数降至零。

答案 6 :(得分:1)

其中一种可能性是将两个变量都转换为64位长,然后进行求和。然后与最大32位值进行比较,以确定它是否被包裹。

答案 7 :(得分:1)

让我们假设计数器倒计时(许多倒计时以保存逻辑中的门)。

你需要首先知道达到2 ^ 32滴答所需的时间,并且需要确保你正好过度采样。

如果你想找到两个事件之间的时间段,比如说开始和结束

start =读取计时器 lasttime = start rollover = 0

等待事情发生

nowtime =读取计时器 if(nowtime&gt; lasttime)rollover + = 1(这是一个向下计数器) lasttime = nowtime

事件发生: 结束=读取计时器

总时间=开始 - 结束(这是一个向下计数器并注意这个数学运算即使翻滚也是如此)

总时间=从刻度到秒,分钟等的总时间/比例因子 总时间+ =翻转*秒/分钟/每2 ^ 32计数

如果你现在有一个向上计数器

如果你可以保证你的活动将在2 ^ 32计数内发生,你现在不需要做翻转,上次你只需要开始和结束的时间,而总滴答=开始 - 结束甚至可以工作如果计数器在开始和结束之间从0x00000000滚动到0xFFFFFFFF。

答案 8 :(得分:0)

假设您正在处理无符号类型,您可以非常轻松地检查包装 -

if (timestamp + shifftime < timestamp) 
    it_wrapped();

答案 9 :(得分:0)

在嵌入时,您可以访问CPU溢出位。当添加溢出它的寄存器时,将设置此项。用于添加AddCarry链接。

答案 10 :(得分:0)

我认为最简单的方法之一就是使用另一个计数器(让我们称之为Wrap计数器,让它成为计时器模块的静态全局),每次原始32位计数器包装时计数。

在您的计数器正在打开的功能中,每次此计数器达到其最大计数时,您的Wrap计数器增量。 因此,当您正在读取返回计时器是否已经过去的函数时,您还会阅读Wrap计数器,以检查它包裹的次数。重要的是,要做到这一点:每次你阅读包装计数器,你想清除它,为你的下一次阅读。