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

我需要处理一个计数器,让我为我的申请打勾。 计数器是32位,所以我需要知道的是如何在它包装时处理它。 例如:

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

谢谢

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

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

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是否已经过期?

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

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

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

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

 Start count: 0xfffffff End Count: 0x00000002 (incremented through 0,1,2 - ie three counts) End - Start == 0x00000002 - 0xfffffff == 0x00000003 

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

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

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

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

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

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

将无符号减法的结果转换为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); 

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

然后,您的future_scheduler函数将读取当前的纪元和时间,并为您的事件计算新的纪元和时间。

或者,您可以直接进行投注,并使您的计时function在每个计时器滴答时将您的事件计划计数降至零。

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

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

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

如果要查找两个事件之间的时间段,请说开始和结束

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

在等待事情发生的同时

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

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

总时间=开始 – 结束(这是一个向下计数器并注意这个数学即使在翻转时也能工作)

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

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

如果你可以保证你的事件将在2 ^ 32计数内发生,你现在不需要进行翻转,上次你只需要开始和结束的时间,即使计数器从0x00000000滚动到总计滴数=开始 – 结束也会有效开始和结束之间的0xFFFFFFFF。

假设你正在处理无符号类型,你可以很容易地检查包装 –

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

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

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

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