STM32中SysTick、FCLK、SYSCLK、HCLK

本文详细介绍了STM32微控制器的时钟系统结构,包括五个主要时钟源HSI、HSE、LSI、LSE和PLL的工作原理及配置方法。此外,还深入探讨了系统时钟SYSCLK及其分配给不同模块的过程,如AHB、APB1和APB2总线等。

http://rmingwang.com/the-stm32-systick-fclk-sysclk-hclk.html    转载原文


在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL。

①、HSI是高速内部时钟,RC振荡器,频率为8MHz。
②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接
外部时钟源,频率范围为4MHz~16MHz。
③、LSI是低速内部时钟,RC振荡器,频率为40kHz。
④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。
⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、
HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最
大不得超过72MHz。


其中40kHz的LSI(低速内部时钟)供独立看门狗IWDG使用,另外它还可以被选择为实时时钟RTC的时钟源。另外,实时时钟RTC的时钟源还可以选择LSE(低速外部时钟),或者是HSE(高速外部时钟)的128分频。RTC的时钟源通过RTCSEL[1:0]来选择。


STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz。(armjishu.com)

另外,STM32还可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。

系统时钟SYSCLK最大频率为72MHz,它是供STM32中绝大部分部件工作的时钟源。 系统时钟可由PLL、HSI或者HSE提供输出,并且它通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用:

①、送给AHB总线、内核、内存和DMA使用的HCLK时钟。
②、分频后送给STM32芯片的系统定时器时钟(Systick=Sysclk/8=9Mhz)
③、直接送给Cortex的自由运行时钟(free running clock)FCLK。【
ARMJISHU注:FCLK 为处理器的自由振荡的处理器时钟,用来采样中断和为调试模块计时。在处理器休眠时,通过FCLK 保证可以采样到中断和跟踪休眠事件。 Cortex-M3内核的“自由运行时钟(free running clock)”FCLK。“自由”表现在它不来自系统时钟HCLK,因此在系统时钟停止时FCLK 也继续运行。FCLK和HCLK 互相同步。FCLK 是一个自由振荡的HCLK。FCLK 和HCLK 应该互相平衡,保证进入Cortex-M3 时的延迟相同。】

④、送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给定时器(Timer)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4使用。
⑤、送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给定时器(Timer)1倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器1使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。


Fclk为供给CPU内核的时钟信号,我们所说的cpu主频为XXXXMHz,就是指的这个时钟信号,相应的,1/Fclk即为cpu时钟周期;Hclk为优秀的高性能总线(AHB bus peripherals)供给时钟信号(AHB为advanced high-performance bus);

HCLK :AHB总线时钟,由系统时钟SYSCLK 分频得到,一般不分频,等于系统时钟,HCLK是高速外设时钟,是给外部设备的,比如内存,flash。

Pclk为优秀的高性能外设总线(APB bus peripherals)供给时钟信号(其中APB为advanced peripherals bus)。

在以上的时钟输出中,有很多是带使能控制的,例如AHB总线时钟、内核时钟、各种APB1外设、APB2外设等等。当需要使用某模块时,记得一定要先使能对应的时钟。

需要注意的是定时器的倍频器,当APB的分频为1时,它的倍频值为1,否则它的倍频值就为2。

连接在APB1(低速外设)上的设备有:电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、窗口看门狗、Timer2、Timer3、Timer4。

注意:USB模块虽然需要一个单独的48MHz时钟信号,但它应该不是供USB模块工作的时钟,而只是提供给串行接口引擎(SIE)使用的时钟。USB模块工作的时钟应该是由APB1提供的。

连接在APB2(高速外设)上的设备有:UART1、SPI1、Timer1、
ADC1、ADC2、所有普通IO口(PA~PE)、第二功能IO口。



下图是STM32用户手册中的时钟系统结构图(位于《STM32F103CDE_DS_CH_V5.pdf》的第14页,或者《STM32F10XXX参考手册_CN.pdf》的第47页),通过该图可以从

STM32的时钟系统结构图

总体上掌握STM32的时钟系统。

原文地址:http://apps.hi.baidu.com/share/detail/34200598



下图是STM32用户手册中的时钟系统结构图(位于《STM32F103CDE_DS_CH_V5.pdf》的第14页,或者《STM32F10XXX参考手册_CN.pdf》的第47页),通过该图可以从

STM32的时钟系统结构图

总体上掌握STM32的时钟系统。

原文地址:http://apps.hi.baidu.com/share/detail/34200598


<think> 我们正在分析STM32F407 SysTick无法实现100us延时的原因。结合之前的讨论和引用内容,以下是关键点: 1. 引用[1]提到:当尝试将SysTick配置为10us中断时出现问题(100us尚可)。这说明在高频率下SysTick可能达到性能极限。 2. 引用[2][3]介绍了时钟系统:系统时钟SYSCLK最大72MHz(F407实际可达168MHz),经过分频后供给SysTickSysTick使用FCLK(与HCLK同步),在STM32F407中通常为168MHz。 3. SysTick是一个24位递减计数器,其重装载值(LOAD)范围为1到0xFFFFFF(16,777,215)。 问题分析: 1. 计算LOAD值: 对于100us延时,所需计数值 = (SystemCoreClock / 1000000) * 100 若SystemCoreClock = 168MHz,则: 计数值 = (168,000,000 / 1,000,000) * 100 = 16,800 这个值小于24位最大值(16,777,215),理论可行。 2. 为什么无法实现? a. 中断响应开销:如果使用中断方式(如引用[1]中修改RT_TICK_PER_SECOND),频繁中断(100us一次即10kHz)会导致CPU大部分时间处理中断,无法执行主程序。 b. 函数调用开销:在非中断方式的延时函数中,函数调用、寄存器配置等操作会消耗额外时间,导致实际延时大于预期。 c. 精度问题:SysTick的LOAD寄存器只能设置整数,计算时若使用整数除法截断,会导致误差积累。 3. 结合用户提供的函数: void cpu_us_delay(uint32_t us) { SysTick->LOAD = (SystemCoreClock/1000/1000) * us - 1UL; SysTick->VAL = 0UL; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; while((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)== 0); SysTick->CTRL = ~SysTick_CTRL_ENABLE_Msk; } 问题: a. 计算错误:`SystemCoreClock/1000/1000` 在整数除法中会被截断(如168,000,000/1,000,000=168,但若时钟较低则可能截断为0)。 b. 未考虑初始化和循环本身的时间:配置寄存器需要几个时钟周期,此时计数器已经在运行,导致实际计数时间超过设定值。 c. 关闭SysTick的方式错误:`SysTick->CTRL = ~SysTick_CTRL_ENABLE_Msk;` 会覆盖整个CTRL寄存器,应该使用位清除操作:`SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;`。 4. 引用[5]展示了一种用SysTick中断做延时的方式,但同样存在中断频率过高的问题。 解决方案: 1. 确保SystemCoreClock设置正确(对于STM32F407,通常为168MHz)。 2. 修正计算: ticks = SystemCoreClock * us / 1000000; 注意:先乘后除,避免整数除法截断(但要注意乘法是否溢出,不过168e6*1000=168e9>2^32,所以us较大时可能溢出,但100us时us很小,安全)。 3. 使用更安全的LOAD值设置(考虑最小值和最大值)。 4. 优化代码以减少额外开销(使用内联汇编或确保编译器优化)。 5. 如果不必要,避免使用中断方式,而用轮询方式(就像用户函数那样)。 修改后的函数(轮询方式): ```c void cpu_us_delay(uint32_t us) { // 计算所需的时钟节拍数(先乘后除,避免整数截断) uint32_t ticks = (uint64_t)SystemCoreClock * us / 1000000; // 确保ticks在有效范围内(1到0xFFFFFF) if (ticks == 0) ticks = 1; // 至少1个节拍 if (ticks > 0xFFFFFF) ticks = 0xFFFFFF; // 最大值 // 配置SysTick(使用处理器时钟,不使能中断) SysTick->LOAD = ticks - 1; // 注意:计数器从LOAD值递减到0,需要n+1个周期(所以减1) SysTick->VAL = 0; // 清除当前值和COUNTFLAG // 使能SysTick(开始计数) SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; // 等待计数完成 while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0); // 关闭SysTick(只清除使能位) SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; } ``` 注意:即使这样,由于函数调用和寄存器配置需要几个时钟周期,所以延时时间会略大于100us。对于精确延时,需要补偿这些开销(通过测量或计算指令周期)。 另外,在中断频繁的环境中(如RT-Thread系统),使用轮询延时可能会被中断打断,导致延迟时间不准确。 如果系统使用了SysTick作为系统时钟(如RT-Thread的时基),则不能随意修改SysTick的配置(如LOAD值),否则会破坏系统时基。这种情况下,应使用其他定时器来实现精确延时。 总结:STM32F407 SysTick无法实现100us延时的可能原因: 1. 系统时钟配置错误(SystemCoreClock值不正确) 2. LOAD值计算错误(整数除法截断) 3. 中断方式下中断频率过高导致系统卡顿 4. 函数调用和配置的开销未补偿 5. 在操作SysTick之前,SysTick可能已被其他代码配置(如RTOS的系统时基) 建议: 1. 检查SystemCoreClock的实际值(在调试器中查看,或通过时钟树确认)。 2. 使用示波器测量延时函数的实际输出(例如翻转GPIO)来验证。 3. 如果在RTOS中使用,考虑使用其他硬件定时器(如TIM)进行高精度延时。 相关问题: 1. 如何正确配置STM32F407的系统时钟? 2. 如何补偿SysTick延时函数的指令开销? 3. 在RT-Thread系统中如何实现高精度延时? 4. SysTick与其他定时器(TIM)在延时功能上有何区别? 5. 如何测量STM32中代码执行的精确时间? </think> ### STM32F407 SysTick无法实现100us延时的原因分析 #### 1. **SysTick配置错误** SysTick重载值(LOAD)计算存在整数截断问题: ```c SysTick->LOAD = (SystemCoreClock/1000/1000)*us - 1; ``` 当`SystemCoreClock`小于1MHz时(如内部低速时钟LSI=32kHz): $$ \frac{32,000}{1,000,000} = 0 \quad \text{(整数除法)} $$ 导致LOAD值为: $$ 0 \times 100 - 1 = 4,294,967,295 $$ 相当于延时长达数小时。**解决方法**: ```c // 先乘后除防止截断 uint32_t ticks = (uint64_t)SystemCoreClock * us / 1000000; SysTick->LOAD = ticks ? (ticks - 1) : 0; ``` #### 2. **SysTick时钟源受限** SysTick可选的时钟源: - **内核时钟(HCLK)**:最高168MHz (STM32F407) - **外部时钟(HCLK/8)**:最高21MHz $$ \text{最小延时} = \frac{1}{\text{时钟频率}} $$ 当使用外部时钟源时: $$ \frac{1}{21,000,000} \approx 47.6ns \quad \text{→ 最小理论延时} \approx 48ns $$ 但实际需要: ```c SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk; // 必须明确选择内核时钟 ``` #### 3. **中断响应时间限制** 在RT-Thread系统中(如引用[1]): - 默认SysTick中断频率1000Hz (1ms) - 当设为10,000Hz (100us)时: $$ \text{中断处理时间} > 100\mu s $$ 导致: - CPU频繁处理中断 - 主程序无法执行 - 系统卡死 #### 4. **代码执行延迟** 配置SysTick的指令需要消耗时间: ```armasm STR SysTick->LOAD ; 2 CPU周期 STR SysTick->VAL ; 2 CPU周期 STR SysTick->CTRL ; 2 CPU周期 ``` 在168MHz下,6周期 ≈ 35.7ns 加上循环判断开销,实际延时: $$ 100\mu s + \text{配置时间} > 100\mu s $$ #### 5. **寄存器操作错误** 禁用SysTick的方式错误: ```c SysTick->CTRL = ~SysTick_CTRL_ENABLE_Msk; // 错误 ``` 这会覆盖整个控制寄存器。**正确做法**: ```c SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 仅清除使能位 ``` ### 解决方案 ```c void safe_us_delay(uint32_t us) { uint32_t ticks = SystemCoreClock / 1000000 * us; if(ticks < 100) ticks = 100; // 最小值保护 SysTick->LOAD = ticks - 1; SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk; // 仅设置时钟源 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // 使能计数器 while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 安全禁用 } ``` ### 性能优化建议 1. **时钟选择** 始终使用内核时钟源: $$ \text{延时精度} = \frac{1}{168\text{MHz}} \approx 5.95\text{ns} $$ 2. **DWT计数器替代方案** 更精确的延时方案(无需配置): ```c #define DWT_CYCCNT *(volatile uint32_t*)0xE0001004 void dwt_delay(uint32_t cycles) { uint32_t start = DWT_CYCCNT; while((DWT_CYCCNT - start) < cycles); } ``` 3. **中断冲突处理** 在RTOS中避免高频SysTick中断: ```c // rtconfig.h中设置合理频率 #define RT_TICK_PER_SECOND 1000 // ≤1000Hz ``` ### 相关问题 1. 如何验证SysTick延时的实际精度? 2. STM32F407的DWT计数器如何使用? 3. RT-Thread中如何平衡系统节拍与高精度延时? 4. SysTick与TIM定时器在延时应用中有何区别? 5. 多核系统中如何实现高精度延时同步? [^1][^2][^3][^4][^5]
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值