用于阅读的原子块与ARM SysTicks

时间:2015-01-17 09:27:27

标签: c gcc arm interrupt-handling arduino-due

我目前正在将我的DCF77 library(您可能会发现source code at GitHub)从Arduino(基于AVR)移植到Arduino Due(ARM Cortex M3)。我是ARM平台的绝对初学者。

使用基于AVR的Arduino,我可以使用avr-libc来获取原子块。基本上,这会在块期间阻止所有中断,并且稍后将允许中断。对于AVR,这很好。现在对于ARM Cortex而言,事情变得复杂起来。

首先:对于库的当前用途,这种方法也可以。所以我的第一个问题是:是否有类似于" ATOMIC" ARM的avr-libc宏?显然其他人已经想到something in this directions.因为我正在使用gcc,我可以增强这些宏,使其几乎完全像avr-libv ATOMIC macor一样工作。我已经找到了一些CMSIS documentation但是这似乎只提供了一个" enable_irq"宏而不是" restore_irq"宏。

问题1:那里有没有库(gcc)吗?

因为ARM有不同的优先级中断,所以我也可以用不同的方式建立原子性。就我而言," atomic"块只能确保它们不会被systick中断打断。所以实际上我不需要阻止所有的东西来制作我的块"足够原子"。进一步搜索我找到了ARM synchronization primitives article in the developer infocenter。尤其是lockless programming的暗示。根据这篇文章,这是一个先进的概念,并且有许多出版物。在网上搜索我发现只有概念的一般解释,例如here。我认为无锁实现会非常酷,但此时我对ARM没有足够的信心从头开始实现这一点。

问题2:有没有人对ARM Cortex M3上的内存块无锁读取有一些提示?

正如我已经说过的,我只需要保护sysTicks中的低优先级线程。所以另一种选择是短暂禁用sysTicks。由于我正在实现时序敏感时钟算法,因此从长远来看,这不能减慢整个sysTick频率。虽然引入一些小抖动是可以的。在这个时候,我会发现这最有吸引力。

问题3:有没有什么好方法可以阻止sysTick中断而不会丢失任何滴答声?

我还找到了CMSIS documentation for semaphores。不过我有点不知所措。特别是我想知道我是否应该使用CMSIS以及如何在Arduino Due上执行此操作。

问题4:什么是我最好的选择?或者我应该在哪里继续阅读?

部分答案: 我实施了Notlikethat的提示

#if defined(ARDUINO_ARCH_AVR)
    #include <util/atomic.h>
    #define CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE)

#elif defined(ARDUINO_ARCH_SAM)
    // Workaround as suggested by Stackoverflow user "Notlikethat"
    // http://stackoverflow.com/questions/27998059/atomic-block-for-reading-vs-arm-systicks

    static inline int __int_disable_irq(void) {
        int primask;
        asm volatile("mrs %0, PRIMASK\n" : "=r"(primask));
        asm volatile("cpsid i\n");
        return primask & 1;
    }

    static inline void __int_restore_irq(int *primask) {
        if (!(*primask)) {
            asm volatile ("" ::: "memory");
            asm volatile("cpsie i\n");
        }
    }
    // This critical section macro borrows heavily from
    // avr-libc util/atomic.h
    // --> http://www.nongnu.org/avr-libc/user-manual/atomic_8h_source.html
    #define CRITICAL_SECTION for (int primask_save __attribute__((__cleanup__(__int_restore_irq))) = __int_disable_irq(), __ToDo = 1; __ToDo; __ToDo = 0)

#else
    #error Unsupported controller architecture
#endif

这个宏或多或少地做了我需要的东西。但是我发现还有改进的余地,因为这会阻止所有中断,尽管仅阻止狙击手就足够了。问题3仍然是开放的。

1 个答案:

答案 0 :(得分:4)

您所引用的大部分内容都是关于同步多个CPU之间的内存访问,或同一CPU上的预先调度的线程,鉴于所述情况,这似乎完全不合适。 &#34;原子性&#34;在这个意义上,指的是保证当一个观察者更新内存时,任何观察者读取内存都会看到初始状态或更新状态,但从不在某个部分之间。

&#34;原子性&#34; 关于中断遵循相同的原则 - 即确保如果发生中断,一系列代码要么根本不运行,要么完全运行 - 但是概念上不同的是 1 < / SUP>。只有两件事保证是原子的w.r.t.中断:单个指令 2 ,或者在中断禁用的情况下执行的指令序列。

&#34;对&#34;实现这一目标的方法确实来自CPSID / CPSIE指令,这些指令包含在__disable_irq() / __enable_irq()内在函数中。请注意,有两个&#34;阶段&#34;系统中的中断处理:M3内核本身只有一个IRQ信号 - 外部NVIC的工作是将系统IRQ的所有路由/多路复用/优先级划分为这一行。当CPU想要进入临界区时,它需要做的就是用CPSID掩盖自己的IRQ输入,做它需要的东西,然后用CPSIE取消屏蔽,此时任何挂起的IRQ来自NVIC将立即采取。

对于嵌套/重入临界区的情况,内在函数提供了一个方便的int __disable_irq(void)形式,它返回先前的状态,因此您可以有条件地取消屏蔽它。

对于没有提供此类内在函数的其他编译器,它可以直接推出自己的内容,例如:

static inline int disable_irq(void) {
    int primask;
    asm volatile("mrs %0, PRIMASK\n"
                 "cpsid i\n" : "=r"(primask));
    return primask & 1;
}

static inline void enable_irq(int primask) {
    if (primask)
        asm volatile("cpsie i\n");
}

[1]一个令人困惑的重叠是后者的意义通常用于在单CPU多任务中实现前者 - 如果中断关闭,在你完成之前没有其他线程可以安排,因此将永远不会看到部分更新的记忆。

[2]除了加载/存储多条指令之外 - 在低延迟中断配置中,这些可以中断,并在重新启动或返回时继续。< /子>

相关问题