Embedded 为什么我必须一次启用一个外围时钟?
在我编写的将字符写入USART1的最小STM32应用程序中,当我尝试同时启用所有需要的时钟时,USART似乎不起作用: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
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预分频器)周期
这听起来不像是您的问题,但可能与此有关(可能一次启用一次会引入一个延迟,这是必要的)。好吧,我想我已经解决了,结果根本不是硬件问题。。。我的工具链配置存在许多问题:
-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