在保存和检索机制之间包含命令的代码

Code that hugs commands between save and retrieve mechanism

最近在用c库实现STM32F0系列单片机的内部外设

当我在研究 ST 如何用他们的 HAL 库做同样的事情时,我进入了这段代码...

  1 /*--------------------- EXTI Mode Configuration ------------------------*/
  2 /* Configure the External Interrupt or event for the current IO */
  3 if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE) 
  4 {
  5  /* Enable SYSCFG Clock */
  6  __HAL_RCC_SYSCFG_CLK_ENABLE();

  7  temp = SYSCFG->EXTICR[position >> 2];
  8  CLEAR_BIT(temp, (0x0FU) << (4U * (position & 0x03U)));
  9  SET_BIT(temp, (GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
  10 SYSCFG->EXTICR[position >> 2] = temp;

  11 /* Clear EXTI line configuration */
  12  temp = EXTI->IMR;
  13  CLEAR_BIT(temp, (uint32_t)iocurrent);
  14  if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
  15  {
  16   SET_BIT(temp, iocurrent); 
  17  }
  18  EXTI->IMR = temp;

我想了解的是 为什么 7-1012-18 行中的配对命令 被使用。 为什么有?为什么要存储一些东西然后写入它然后从内存中检索丢弃的写入内容? 是关于多任务处理和竞争条件吗?没看懂!

我看不出有什么奇怪的:

第7-10行和第12-18行将寄存器的内容复制到局部变量中,对其进行一些操作,然后将结果存储回原始寄存器。

发生这种情况的原因可能有多种,但我的猜测是代码的作者不希望 MCU 在修改寄存器时处于中间状态。

例如,第 8 行之后第 9 行之前的核心发生了什么?

就是要进行运算"atomic"。一次写入操作中对寄存器的所有更改。

这用于优化速度并避免内存映射寄存器中出现不需要的状态。
想象一些示例内存映射变量 EXT->ABC。想象一下这样夸张的例子情况:

  • 我们有一些硬件内存地址可以通过内存映射 EXT->ABC 变量访问
  • 我们知道在 EXT->ABC
  • 中位 1 已设置,位 2 已清除
  • 我们要清除第 1 位并设置第 2 位,并保持 EXT->ABC 中的所有其他位不变
  • 硬件不允许同时清除或设置 EXT->ABC 寄存器中的位 1 和位 2。这种状态是被禁止的,可能会导致未定义的行为(这意味着任何行为,例如软件重置)。
  • 读取和写入 EXT->ABC 寄存器非常非常慢。

如果直接对EXT->ABC变量进行操作:

CLEAR_BIT(EXT->ABC, 1); // expands to: EXT->ABC = EXT->ABC & ~1;
SET_BIT(EXT->ABC, 2);   // expands to: EXT->ABC = EXT->ABC | 2;

这将导致两条线之间出现禁止状态,因为第 1 位和第 2 位将被设置。此外,在这两行中,EXTI->IMR 被访问了 4 次。 为了解决这个问题,我们需要将EXTI->IMR寄存器中的值存储在某个地方,修改需要的位,然后将修改后的值写入EXTI->IMR寄存器。所以我们将:

uint32_t temp;
temp = EXT->ABC; // store
CLEAR_BIT(temp, 1);
SET_BIT(temp, 2);
EXT->ABC = temp; // write

我们从内存映射变量中读取一次,修改并使用该值,做任何我们想做的事。之后,我们将 once 写入内存映射变量。这样 EXT->ABC 寄存器只被触及两次并且代码被安全写入,因此不会出现 undefined/forbidden/unwanted 状态。
例如:至于第 7-10 行:要访问 SYSCFG->EXTICR[position >> 2U] 你需要多次 cpu 操作(计算 position >> 2U,乘以 sizeof(SYSCFG->EXTICR),添加到 SYSCFG->EXTICR 地址, 取消引用)。也许开发人员打算更快地执行这些行。或者您需要在写入 (GPIO_GET_INDEX(GPIOx))<<(4*position&0x03).

时清除位 0x0F<<(4*(position&0x03)