定时器翻转处理

时间:2014-06-25 20:05:41

标签: c timer embedded real-time atomic

我有一个32位硬件定时器,我希望在软件中扩展到64位有效长度。

在我的嵌入式系统中,我有一个32位硬件“核心定时器”(CT),其频率为~40 MHz,因此它会在大约107秒内翻转。

这对于精确的时间长达107秒非常有用。但是我想做更长时间的同样精确计时。

它还有一个32位“周期”寄存器 - 当CT值与周期寄存器匹配时,会产生一个中断。

我的ISR看起来像这样(为简洁起见而简化):

const UINT32 ONE_MILLISECOND = TICK_RATE/1000;

UINT64 SwRTC;

void CT_ISR(void) {
    PeriodRegister += ONE_MILLISECOND;
    SwRTC += ONE_MILLISECOND;
    ClearCTInterrupt();
}

所以,现在我有一个64位的“SwRTC”可用于测量更长的周期,但仅用于1毫秒的精度,加上精确到1/40 MHz(25纳秒)的32位硬件定时器)。两者都使用相同的单位(TICK_RATE)。

如何将两者结合起来以获得同样精确的64位定时器,同时仍能获得1000 Hz的中断?

我的第一次尝试看起来像这样:

UINT64 RTC(void){

    UINT64 result;

    DisableInterrupts(); // to allow atomic operations

    result = (SwRTC & 0xFFFFFFFF00000000ull) + ReadCoreTimer();

    EnableInterrupts();

    return result;
}

但这并不好,因为如果CT停止而中断被禁用,那么我将得到一个低位32位的小数字的结果,但没有高位比特被ISR递增

也许这样的事情会起作用 - 读两遍并返回更高的值:

UINT64 RTC(void){

    UINT64 result1, result2;

    DisableInterrupts(); // to allow atomic operations

    result1 = (SwRTC & 0xFFFFFFFF00000000ull) + ReadCoreTimer();

    EnableInterrupts();

    DisableInterrupts(); // again

    result2 = (SwRTC & 0xFFFFFFFF00000000ull) + ReadCoreTimer();

    EnableInterrupts();

    if (result1 > result2)
        return result1;
     else
        return result2;
}

我不确定这是否有用,或者我是否错过了隐藏的问题。

这样做的最佳方式是什么?

(有些人可能会问为什么我需要在一开始就这么长时间这么长时间。这主要是为了简单 - 我不想根据时间段使用2种不同的计时方法;我更愿意使用同样的方法。)

2 个答案:

答案 0 :(得分:1)

我认为我几乎自己解决了这个问题:

UINT64 Rtc(void){

    UINT64 softwareTimer = SwRTC;
    UINT32 lowOrderBits = softwareTimer;                        // just take low-order 32 bits
    UINT64 coreTimer = ReadCoreTimer();

    if (lowOrderBits > coreTimer)                               // if CT has rolled over since SwRTC was updated
        softwareTimer += 0x100000000;                           // then increment high-order 32 bits of software count

    return (softwareTimer & 0xFFFFFFFF00000000ull) + coreTimer; 
}

首先读取64位软件定时器,然后读取32位硬件定时器。

硬件定时器(每25 nS更新一次)应始终>> =软件定时器的低32位(仅每1 mS更新一次)。

如果不是,则表示自从读取软件定时器以来硬件定时器已翻转。

因此,在这种情况下,我递增软件定时器的高位字。

然后只将软件时间的高32位与硬件定时器的低32位组合。

一个很好的副作用是不需要禁用中断。

我能看到的唯一问题是,如果编译器优化重新排序代码以便首先读取硬件计时器怎么办?然后我可以得到一个中断,在我有机会读取它之前递增软件定时器。

起初我以为我可以通过在读取两个定时器时禁用中断来解决这个问题,但是如果编译器重新排序代码以使DisableInterrupts()来得太晚会怎么样?

答案 1 :(得分:0)

如果它是一个upcounter,例如我通常会

elapsed = ((nowtime-starttime)&MASK)+(rollovers<<SIZE);

只要你经常采样(每次翻转很多次),对于现在的上升时间(我刚刚采样的时间)小于上次时间(上次是先前的时间)然后它就会翻身。

假设它正在计算每个值而不是从0xFF..FFF跳到0x00 ... 01。

Downcounter只是在starttime-nowtime的对面做所有事情。 nowtime&gt;上次。

有些定时器有一个翻转中断,如果你愿意,你有时可以轮询而不是中断,只要你可以保证每次翻转一次或多次采样,你可以简单地使用它来标记翻转计数。

如果你在任何时候错过翻滚,那么你自然会被4千兆计数或其他任何东西的计时器大小所取消。

某些硬件可能允许您使用一个定时器来生成输出时钟,然后您可以将其作为输入反馈到另一个定时器并以此方式级联(有时它们在芯片上执行此操作)。