我是 STM32 微控制器的新手。我想知道启用 RCC 外设时钟后如何正确实现 2 个时钟周期延迟。
https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicro electronics.pdf 的第 5.2.16 节(第 134 页)
使能位具有同步机制,可为外设创建无干扰时钟。设置使能位后,时钟激活之前有 2 个时钟周期的延迟,软件必须考虑到这一点。我看到下面的解释,它似乎是正确的。
https://github.com/lab11/stm32l0-base/blob/master/sdk/STM32Cube_FW_LO_V1.5.0/Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_rcc.c>
RCC 限制 ##### 评论部分。此延迟取决于外设映射。 如果外设映射到AHB:延迟为2个AHB时钟周期 在硬件寄存器上设置时钟使能位后 如果外设映射到 APB:延迟为 2 个 APB 时钟周期 在硬件寄存器上设置时钟使能位后我正在看几个例子。在第一个示例中,读回 RCC 外设时钟使能寄存器。
RCC->AHB1ENR |= (1 << RCC_AHB1ENR_GPIOAEN_Pos);
// do two dummy reads after enabling the peripheral clock, as per the errata
volatile uint32_t dummy;
dummy = RCC->AHB1ENR;
dummy = RCC->AHB1ENR;
在第二个示例中,外设寄存器被读回。
void clock_wait_bus_cycles(enum bus_type bus, uint32_t cycles)
{
volatile uint32_t unused __attribute__((unused));
if (bus == BUS_AHB) {
while (cycles--)
unused = STM32_DMA1_REGS->isr;
} else { /* APB */
while (cycles--)
unused = STM32_USART_BRR(STM32_USART1_BASE);
}
}
哪一种是正确的做法?
AHBENR
而不是
AHB1ENR
,并且 GPIOA 时钟在任何情况下都是通过
IOPENR
而不是
AHBENR
启用。 然而,技术是合法的,但需要具体到正确的部分。 第二种方法中的函数包括函数调用开销和循环的 CPU 周期数,因此会稍长一些,但这会为小东西带来麻烦。它可能不必要地复杂。 第二次读取的外设的选择似乎是任意的。 您同样可以像第一个示例一样读取适当的 RCC 使能寄存器。 要求只是读取具有依赖于关联总线或外设的等待状态的内容,以便在读取完成之前不会执行进一步的指令。
ST 提供 HAL 和 LL 外围库,它们可能是推荐方法的良好范例(或者至少是可能有效的方法 - 并且经过测试) - 即使您选择不使用这些库本身。
STM32G0x0 HAL 就是这样做的:
#define __HAL_RCC_GPIOA_CLK_ENABLE() do { \
__IO uint32_t tmpreg; \
SET_BIT(RCC->IOPENR, RCC_IOPENR_GPIOAEN); \
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->IOPENR, RCC_IOPENR_GPIOAEN); \
UNUSED(tmpreg); \
} while(0U)
它读取刚刚写入的各个使能位,这保证了正确的时钟:
#define READ_BIT(REG, BIT) ((REG) & (BIT))
在时钟激活之前读取不会完成,因此我认为单次读取就足够了。第一个示例中的代码不适用于您正在使用的部件,这一事实可能是使用 ST 提供的 HAL 库的一个论据 - 它避免了在多个 STM32 部件之间使用和移植代码的问题,这些部件在不同系列中差异很大。