哪种方式在微控制器中初始化寄存器更快?

Which way is faster in initializing registers in a microcontroller?

在为 PIC 微控制器编写固件时,我想到了这个问题。

我知道有两种方法可以初始化微控制器中的寄存器。举个例子,如果我们将端口初始化为输出,一种方法是编写如下命令,它将 1 分配给 TRISx 寄存器

中的每个位

方法一

TRISX = 0xFF;

同样的事情可以通过单独分配位来完成。

方法二

_TRISX0 = 1;
_TRISX1 = 1;
_TRISX2 = 1;
...
_TRISX7 = 1;

我的问题是,它会被编译器视为相同吗?完成这两个操作所花费的时间是否相同?还是方法 1 需要一个时钟周期而方法 2 需要 8 个时钟周期(我的意思是慢 ~8 倍)?

我尝试阅读 X16 compiler guide 但找不到任何提示。

一件事是,您没有提供 C 代码来说明这些位实际上是如何被引用的。但是假设它是通过 bit-fields.

的联合和结构

最好的方法是实际检查编译器生成的 ASM。您确实需要了解您的硬件架构,但您仍然需要查看生成的 ASM 才能真正了解。

要仅分配一个位,请说 _TRISX0=1; vs TRISX = 0x01;,取决于 arch 和编译器,编译器可能会生成比整个寄存器更高效(更少的周期,可能更少的指令)的代码,仅用于单个位赋值。 至少有一个这样的 MCU/DSP 处理器和编译器,来自 TI,我知道这是真的。

如果您有多个 (>1) 语句,您的方法 2,具有单独的位分配,您的 one-liner 寄存器分配可能会更有效或同样有效:如果编译器推断 - 错误或不 - 所有这些位分配按顺序分​​配给同一个寄存器,它可以用 one-liner 替换它们,就像你在方法 1.

中那样

我没有特别想到 PIC。我建议您在需要时检查任何 MCU 的 ASM。

硬件寄存器始终是 volatile 合格的,不允许编译器优化包含 volatile 访问的代码。因此,如果您向他们写入 8 次,那么您将获得 8 次写入。这当然比 1 写慢得多。

此外,连续多次写入寄存器是非常糟糕的做法,就好像它们是 RAM 中的临时变量一样。硬件寄存器往往具有各种微妙的方式 side-effects。它们可以具有 "write-once" 属性或仅接受某些模式下的写入。通过分几步给他们写信,你养成了一种习惯,因为不正确的寄存器设置会导致各种疯狂、微妙的问题。

正确的做法是只写入一次寄存器,或者尽可能少地写入。

例如,您可能认为示例中的数据方向寄存器非常愚蠢,没有 side-effects。但是 GPIO 硬件通常需要一些时间来切换端口电路,从写入数据方向寄存器到访问 I/O 端口。因此,多次写入可能会不必要地停止端口。

假设REGISTER是memory-mapped,volatile限定的硬件寄存器的名字,那么...

不要这样做:

MASK1 = calculation();
REGISTER |= MASK1;
MASK2 = calculation();
REGISTER |= MASK2;

这样做:

uintx_t reg_val=0; // temp variable in RAM
MASK1 = calculation();
reg_val |= MASK1;
MASK2 = calculation();
reg_val |= MASK2;
REGISTER = reg_val; // single write to the actual register

这将取决于处理器指令集和编译器。以PIC18F45K20为例,sdcc编译器编译如下

TRISDbits.TRISD0 = 1;

BSF _TRISDbits, 0

编译时

TRISD = 0xFF;

MOVLW   0xff
MOVWF   _TRISD

所以在这种情况下设置单个位会更快,因为它不涉及在工作寄存器中放置临时值。

然而,并非所有指令集都包含 BSF 指令,并且某些体系结构不需要为后一个任务使用工作寄存器。

P.S。以上示例基于 sdcc 编译器的输出,但我想 xc8xc16 编译器会产生类似的结果。

P.P.S。检查生成的程序集时,请记住某些指令比其他指令消耗更多的处理器周期。有关详细信息,请参阅数据表。