哪种方法可以更快地初始化微控制器中的寄存器?

时间:2019-06-18 05:48:11

标签: c embedded microcontroller

这个问题是在为PIC微控制器编写一些固件时想到的。

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

方法1

TRISX = 0xFF;

同一件事可以通过分别分配位来完成。

方法2

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

我的问题是,编译器会否将其视为相同,并且完成这两项操作所需的时间是否相同?还是方法1占用一个时钟周期,而方法2占用8个时钟周期(我的意思是慢8倍)?

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

3 个答案:

答案 0 :(得分:11)

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

此外,非常糟糕的做法是连续多次写入寄存器,就像它们是RAM中的临时变量一样。硬件寄存器往往具有各种微妙的副作用。它们可以具有“一次写入”属性,或仅接受某些模式下的写入。通过分几步写给他们,您就会养成习惯,以创建由错误的寄存器设置引起的各种疯狂的,细微的问题。

正确的做法是一次或多次写入寄存器。

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

假设REGISTER是具有存储器映射的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

答案 1 :(得分:7)

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

TRISDbits.TRISD0 = 1;

BSF _TRISDbits, 0

在编译时

TRISD = 0xFF;

MOVLW   0xff
MOVWF   _TRISD

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

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

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

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

答案 2 :(得分:1)

有一件事,您没有提供C代码来显示这些位是如何被实际引用的。但是,可以说这是通过位域的并集和结构实现的。

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

仅分配一个位,例如_TRISX0 = 1; vs TRISX = 0x01 ;,取决于架构和编译器,与整个寄存器相比,编译器仅针对单个位分配就可能生成更高效(更少的周期,可能更少的指令)的代码。 TI至少有一个这样的MCU / DSP处理器和编译器,我知道这是真的。

对于具有多个(> 1)语句,方法2(具有单独的位分配)的情况,单行寄存器分配可能会更高或更有效:如果编译器推论(错误或错误地)所有这些位分配都按顺序分配给同一寄存器,它可以像在方法1中那样用单线替换它们。

我没有特别考虑使用PIC。我建议您在需要时检查所有MCU的ASM。