3

许多 ARM 处理器包含允许在 I/O 和/或 RAM 上进行各种形式的读-修改-写操作的特殊功能。然而,似乎没有一种方法可以在 Cortex-M3 和 Cortex-M0 设备的范围内始终如一地工作。

当我针对 Cortex-M3 设备时,我编写了一些方法MaskedSet32(uint32_t *target, uint32_t mask, uint32_t newval),例如使用 load-linked/compare-exclusive 以中断安全的方式原子地修改 32 位数字的一部分。然而,Cortex M0 不提供这些指令。我也许可以让代码保存中断状态、禁用中断、执行读取-修改-写入并重新启用中断,但这似乎有点笨拙。一些 Cortex 处理器提供位带或其他功能,因此可以使用单个原子内存操作来执行诸如以原子方式设置单个位之类的操作,但不同的处理器似乎做事不同。此外,我不确定哪种抽​​象可以实现最佳程度的可移植性。

对于 RAM 中的东西,我的猜测是,当速度不重要时,最好的方法是为诸如MaskedSet32, Increment, BottomPegI32(的原子版本*dest = (*dest < limit) ? limit : *dest;)之类的东西设置子例程。这种方法也可以与 I/O 一起使用,但不利用某些处理器提供的一些专用硬件来提高此类效率似乎是一种耻辱。例如,飞思卡尔 KL-25 可以执行如下操作:

MaskedSet32(&IOWhatever, 0x0F00, 0x0500); // Update bits 8-11 with a value of 0101

通过将值 5 写入某个 I/O 地址。5加载一个 I/O 地址和两个其他参数,然后调用一个子程序来完成可以通过加载一个 I/O 地址和一个常量并执行单个内存存储来完成的工作,这似乎很浪费。

虽然我看到的很多处理器演示代码只使用普通的布尔运算符,但我觉得这样做的想法令人反感

IoWhatever = (IoWhatever & ~0x0F00) | 0x0500;

在任何情况下,中断可能想要写入 I/O 地址的一部分,而主线代码正在写入另一部分。使用原子操作作为一种常见的做法似乎是一种更安全的方法,但我想以尽可能少的方式使代码陷入困境。

我是否应该定义一个 SetIoBitField 宏,它需要一个地址(将被转换为uint32_t*)、字段偏移量、字段宽度和新数据,要求字段偏移量和宽度是恒定的,然后使用原子读取-修改-写入方法(使用基于常量预先计算的掩码),内联加载链接/存储独占循环(同样),或写入特定于处理器的特殊 I/O 空间,或者我应该定义和使用这样的宏仅在具有此类原语并在其他处理器上使用 MaskedSet32 的处理器上[认为没有位域支持的处理器上的 I/O 位分配无论如何都会有所不同],或者哪种方法看起来最清晰和最便携?

4

2 回答 2

2

标准且明显的方法是编写代码,以便根据 ARM 代号有条件地编译:

#if ARM_GEN >= 3
   (use advanced ARM primitives)
#else
   (use clunkier ways to accomplish the same things)
#endif

whereARM_GEN由构建定义。如果代码库将由未密切沟通的人处理,那么在未定义符号的情况下提供编译时警告或错误并不是一个坏主意:

#ifndef ARM_GEN
 #error Define ARM_GEN as 1, 2, 3, ... 9 of the ARM architecture (ARM3, ARM9, etc.)
#endif
于 2013-03-11T17:51:23.757 回答
0

使用预处理器选择实现是解决问题的好方法。尽管如此,在这种情况下,禁用/启用中断可能不是处理并发问题的最佳设计模式。

正如您所意识到的,并非每个 MCU 都会支持您以原子方式执行屏蔽写入,而且,使用这种特定支持会使代码变得不可移植。作为固件设计人员,我喜欢使用标准化模式,因此,如果确实无法消除系统同一内存地址上的并发性,则应该使用互斥锁和/或 FIFO。

我相信您可以更改系统的布局以避免并发。内存并发是完全没有必要的,因为现代芯片以低成本拥有大量字节。IO 端口并发是一个非常常见的问题,大多数 MCU 制造商都实现了硬件支持来设置(或清除)单个端口位,而无需读取 IO 状态,从而无需软件并发处理。

于 2013-03-11T19:01:55.710 回答