15

我需要处理一个计数器,它可以为我的应用程序提供标记。计数器是 32 位的,所以我需要知道的是如何处理它。例如:

我有一个返回 a 的函数,(timestamp + shifttime)我还有一个返回 1 或 0 的函数,具体取决于时间是否已过,但我的计数器有可能会换行,我该如何处理?


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

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

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 位长,以便于计算。

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

请记住,我不一定会一直检查以查看当前计数器是什么以及某些时间戳是否已过期。我希望这能说明我遇到的问题是什么。

4

11 回答 11

37

只要开始计数和结束计数之间的差小于 2 32,并且执行无符号 32 位算术,即使计数值跨越回绕点,这无关紧要。(如果使用有符号算术,跨度必须小于 2 32 /2)

例如:

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

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

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

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

于 2010-06-22T22:24:40.267 回答
14

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

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

于 2010-06-22T17:50:32.560 回答
5

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

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);
于 2010-06-22T22:56:40.423 回答
5

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

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

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

于 2010-06-23T00:23:14.730 回答
2

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

于 2010-06-22T17:44:19.343 回答
1

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

您首先需要知道达到 2^32 个刻度所需的时间,并且需要确保您对其进行过采样。

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

start = 读取计时器 lasttime = 开始翻转 = 0

在等待事情发生时

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

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

总时间 = 开始 - 结束(这是一个递减计数器,请注意,即使在翻转时这个数学也有效)

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

如果你有一个向上的柜台,那么现在

如果您可以保证您的事件将在 2^32 计数内发生,那么您现在不需要进行翻转,上一次您只需要 start 和 end 并且总滴答声 = start - end 即使计数器从 0x00000000 滚动到开始和结束之间的 0xFFFFFFFF。

于 2010-06-23T17:46:28.073 回答
1

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

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

或者,您可以直接下注,并让您的计时功能在每个计时器滴答时将您的事件时间表计数为零。

于 2010-06-22T17:44:56.743 回答
1

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

于 2010-06-22T17:45:46.213 回答
0

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

于 2010-06-22T17:50:49.490 回答
0

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

在您的计数器正在滴答作响的函数中,每当此计数器达到其最大计数时,您的 Wrap 计数器就会增加。因此,当您读取返回计时器是否已过的函数时,您还读取了 Wrap 计数器,以检查它包裹了多少次。重要的是,也要这样做:每次阅读换行计数器时,都想清除它,以供下次阅读。

于 2010-06-23T00:32:00.210 回答
0

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

if (timestamp + shifftime < timestamp) 
    it_wrapped();
于 2010-06-22T17:43:51.210 回答