Embedded 为什么我必须一次启用一个外围时钟?

Embedded 为什么我必须一次启用一个外围时钟?,embedded,arm,stm32,Embedded,Arm,Stm32,在我编写的将字符写入USART1的最小STM32应用程序中,当我尝试同时启用所有需要的时钟时,USART似乎不起作用: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE); 但当我一次启用一个时钟时,它会工作: RCC_APB2PeriphClockCmd(R

在我编写的将字符写入USART1的最小STM32应用程序中,当我尝试同时启用所有需要的时钟时,USART似乎不起作用:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA
                       | RCC_APB2Periph_AFIO
                       | RCC_APB2Periph_USART1, ENABLE);
但当我一次启用一个时钟时,它会工作:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,  ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,   ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
为什么会这样?这些时钟是否有特定的启用顺序?(如果有,记录在哪里?)

(我省略了下面所有初始化GPIO引脚、设置USART和开始发送内容的代码,因为每个应用程序中都是相同的。如果相关,请告诉我,我会将其包括在内。)

我使用的设备是STM32F103VET6


既然有人对所涉及的大会感兴趣,就在这里。对于同时出现的所有三个时钟:

00000000 <main>:
   0:   b590            push    {r4, r7, lr}
   2:   b089            sub     sp, #36 ; 0x24
   4:   af00            add     r7, sp, #0
   6:   f244 0014       movw    r0, #16389      ; 0x4005
   a:   2101            movs    r1, #1
   c:   f7ff fffe       bl      0 <RCC_APB2PeriphClockCmd>

0x40021000
是RCC外围设备的基址;
#24
偏移量指向
RCC#U APB2ENR
寄存器,该寄存器对每个启用的时钟都有一个位。(有关详细信息,请参阅的第109页。)

您可能希望让我们确切知道您正在使用的设备和/或查看该设备的勘误表。例如,STM32L100x6/8/B-A(和其他)设备的勘误表包含以下内容():

2.6.1 RCC外围时钟启用后的延迟

描述

RCC外围时钟启用和有效时钟之间的延迟 为了进行管理,应考虑外围设备启用 外围设备读/写寄存器

此延迟取决于外围设备的映射:

  • 如果外围设备映射到AHB上:延迟应等于2个AHB周期
  • 如果外设映射到APB上:延迟应等于1+(AHB/APB预分频器)周期
变通办法

  • 使用DSB指令暂停Cortex-M CPU管道,直到指令完成
  • 在RCC启用位写入和外围寄存器写入之间插入“n”NOP(对于AHB外围设备,n=2,n=1+AHB/APB APB外围设备情况下的预分频器)

  • 这听起来不像是您的问题,但可能与此有关(可能一次启用一次会引入一个延迟,这是必要的)。

    好吧,我想我已经解决了,结果根本不是硬件问题。。。我的工具链配置存在许多问题:

  • 我正在设置
    -nostlib
    。这导致无法生成某些全局初始化代码。我不确定这有多重要,但其他问题包括:

  • 我没有将
    -mthumb
    和其他CPU选项传递给链接器。这导致生成的一些启动代码成为垃圾

  • 我的启动文件不包含对
    \uuu libc\u init\u数组的调用。这导致在链接时删除更多的初始化代码


  • 我仍然不知道为什么拆分外围时钟初始化能够解决这个问题。也许代码量的变化将某些东西撞向了正确的对齐方式?无论如何,到目前为止,解决基本问题似乎已经解决了问题(尽管我仍然对一些剩余的启动代码有点怀疑)。

    我使用的特定设备是STM32F103VET6。我没有在勘误表()中看到这个问题,我也不确定这在这里会如何应用,因为我还没有使用任何外围设备,只是启用了它们!是的,在我看来这不是你的问题,但我想我会发帖以防万一。作为一个实验,您可能希望在调用“combined”
    RCC_APB2PeriphClockCmd()
    之后添加一个小延迟,看看这是否会产生某种不同。我已经尝试过了-不幸的是,它似乎没有什么不同(两个选项的反汇编都是什么样子的?你能为每个选项发布反汇编吗?程序集非常无聊-
    RCC\u APB2PeriphClockCmd()
    是一个非常正常的函数(它写入
    APB2ENR
    );这两个选项都会导致使用不同的参数调用它。汇编永远不会令人厌烦;它是嵌入式编程的关键所在;)!!因此,选项A只是一个“mov立即(dest)”,选项B是3个“mov立即(dest)”,同样值得注意;APB巴士是一种痛苦;也许这是一个内置的外围同步“feauture”ST,它通过一个公共共享元素将这些USART耦合在一起。因此,作为第一步,反汇编是有用的。如果时钟树使用PLL(向上时钟),那么在启用其他同级时钟之前,通常需要让它锁定。我对这一部分一无所知。然而,通常要求首先启动父时钟。一个模块可能已启用,开始计时可能会导致它试图访问另一个模块,这可能会导致某种锁存;在启动时钟之前,确保模块处于禁用状态[这可能不可能,因为寄存器可能需要时钟响应]。
    00000000 <main>:
       0:   b590            push    {r4, r7, lr}
       2:   b089            sub     sp, #36 ; 0x24
       4:   af00            add     r7, sp, #0
       6:   2004            movs    r0, #4
       8:   2101            movs    r1, #1
       a:   f7ff fffe       bl      0 <RCC_APB2PeriphClockCmd>
       e:   2001            movs    r0, #1
      10:   2101            movs    r1, #1
      12:   f7ff fffe       bl      0 <RCC_APB2PeriphClockCmd>
      16:   f44f 4080       mov.w   r0, #16384      ; 0x4000
      1a:   2101            movs    r1, #1
      1c:   f7ff fffe       bl      0 <RCC_APB2PeriphClockCmd>
      ...
    
    00000000 <RCC_APB2PeriphClockCmd>:
       0:   4b04            ldr     r3, [pc, #16]   ; (14 <RCC_APB2PeriphClockCmd+0x14>)
       2:   699a            ldr     r2, [r3, #24]
       4:   b109            cbz     r1, a <RCC_APB2PeriphClockCmd+0xa>
       6:   4310            orrs    r0, r2
       8:   e001            b.n     e <RCC_APB2PeriphClockCmd+0xe>
       a:   ea22 0000       bic.w   r0, r2, r0
       e:   6198            str     r0, [r3, #24]
      10:   4770            bx      lr
      12:   bf00            nop
      14:   40021000        .word   0x40021000