\begingroup

我对如何使用 ATSAM3X8E 上的寄存器感到困惑。例如,在PIO 的中,几乎所有东西都有两个寄存器。

例如,有PIO_PERPIO_PDR寄存器用于启用和禁用 PIO 寄存器。那么,如果我想启用 GPIO 线路 1,我应该同时设置和取消设置这两个寄存器吗,还是只需设置PIO_PER就足够了?

启用

PIO_PER = (1U << 1);

或者

PIO_PER = (1U << 1);
PIO_PDR = ~(1U << 1);

另外,两者的顺序重要吗?拥有两个独立的寄存器的目的是什么?

\endgroup


最佳答案
2

\begingroup

如果要启用某个引脚,则将要启用的引脚写入位 1。位 0 不执行任何操作。

因此,您使用单一启用的示例是可以的。

根据您想要执行的操作,您的第二个启用可能完全错误。

第一行只启用您想要的位。第二行禁用除您刚刚启用的引脚之外的所有其他引脚。这可能是您想要的,也可能不是,但它将禁用所有其他引脚。顺序本身并不重要,除非您想按该顺序执行操作。

因此,为什么要有单独的设置和清除寄存器的更大概念是原子性。

假设您想对某个寄存器的某个位执行某些操作。如果您熟悉“传统”的做事方式,例如打开 LED 意味着您需要读取数据寄存器、设置位并写入数据寄存器 – 即读取-修改-写入操作。

再想象一下,您想通过定时器中断控制第二个 LED,而这发生在读取数据寄存器之后但在写入数据寄存器以点亮第一个 LED 之前。定时器中断还需要对同一寄存器执行读取-修改-写入操作。第二个 LED 在中断中打开,但主代码返回寄存器的值,其中第二个 LED 熄灭但第一个 LED 亮起。显然,在对第一个 LED 执行读取-修改-写入操作时应该防止定时器中断发生,这样定时器中断也可以正确地读取-修改-写入第二个 LED。也许可以通过禁用中断来实现。

因此,拥有单独的清除和设置寄存器是原子的,并提供“硬件加速”,您无需花时间在无用的读取 – 修改 – 写入序列上,也无需担心操作的原子性。

例如,您可以通过告诉硬件要设置或清除哪些位以及要保持哪些位不变,在一次操作中设置或清除某些位。

\endgroup

\begingroup

补充一下@Justme 的回答,这对于内容被微控制器修改的寄存器尤其重要。

想象一下一个中断状态寄存器(包含多个中断源),它由微控制器更新,需要在中断服务例程中清除。如果微控制器在读取/修改/写入过程中更新了不同的中断状态,则使用读取/修改/写入方法清除中断将导致问题。

\endgroup