我是 STM32 微控制器的新手。我想知道如何在启用 RCC 外设时钟后正确实现 2 个时钟周期的延迟。
https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf的第 5.2.16 节(第 134 页)
使能位具有同步机制,可为外设创建无故障时钟。设置使能位后,时钟激活前会有 2 个时钟周期的延迟,软件必须考虑到这一点。
我偶然发现了下面的解释,它看起来是正确的。
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);
}
}
哪一个才是正确的方法?
第一个例子显然是针对除您所引用的 STM32G0x0 之外的 STM32 部件,因此从这个意义上说它是不正确的。STM32G0x0 有一个 AHB,所以
AHBENR
不是AHB1ENR
,并且 GPIOA 时钟在任何情况下都是通过IOPENR
not启用的AHBENR
。然而,该技术是合法的,但需要特定于该部件才能正确。第二种方法中的函数包括函数调用开销和循环的 CPU 周期数,因此会稍微长一些,但这会让人为小事操心。它可能不必要地复杂。第二种方法中读取的外设的选择似乎是任意的。您可以像第一个例子一样读取适当的 RCC 启用寄存器。要求只是读取依赖于相关总线或外设的等待状态的内容,以便在读取完成之前不会执行进一步的指令。
ST 提供 HAL 和 LL 外设库,它们可能是推荐方法的良好范例(或者至少是可能有效且经过测试的方法)——即使您选择不使用这些库本身。
例如,STM32G0x0 HAL 执行以下操作:
它读取刚刚写入的单独使能位,这保证了正确的时钟:
直到时钟激活时,读取才会完成,因此我认为单次读取就足够了。
事实上,第一个示例中的代码不适用于您正在使用的部件,这可能是使用 ST 提供的 HAL 库的一个理由——它避免了在多个 STM32 部件上使用和移植代码的问题,而这些部件在不同系列之间存在很大差异。