返回列表 发新帖我要提问本帖赏金: 50.00元(功能说明)

[APM32E0] 基于APM32E030解读APM库的高速时钟配置

[复制链接]
 楼主| Alden 发表于 2025-6-25 12:41 | 显示全部楼层 |阅读模式
<
本帖最后由 Alden 于 2025-6-25 12:44 编辑

#技术资源# #申请原创#
每一家MCU厂家的SDK写法和寄存器功能都有所不同,如果不熟悉的话就会配置错误,导致MCU运行不稳定。
接下来就已APM32E030的手册和SDK,解读下高速时钟的配置和相关注意事项。
实现了解MCU的高速时钟要先看下用户手册。
高速时钟源分内部时钟源和外部时钟源:
内部时钟源
内部时钟包含 HSICLK(高速内部时钟信号)和 LSICLK(低速内部时钟信号)。
HSICLK 时钟信号由内部 8MHz 的 RC 振荡器产生。不同芯片的 RC 振荡器频率不同,且同一颗芯片随着温度、电压的变化也会存在差异;每个芯片的 HSICLK 时钟频率在出厂前已经被厂家校准到 1%(25℃、VDD=VDDA=3.3V)
外部时钟源
外部时钟信号包括 HSECLK(高速外部时钟信号)和 LSECLK(低速外部时钟信号)。
外部的时钟源有两种:
⚫ 外部晶体/陶瓷谐振器(常规的无源晶振)
⚫ 用户外部时钟(有源晶振或者是其他芯片提供的时钟)
微信图片_20250625102853.png
从E030的用户手册可以看出,E030的最大主频也就是SYSCLK最大是72Mhz。系统时钟源可以从HSECLK(外部时钟的时钟),PLLCLK(PLL的时钟)、HSICLK(内部8M时钟)这三个中来选择。
HSECLK的输入时钟范围是4~32Mhz,可通过PLL的分频器和倍频器配置成PLL最大72M主频。
HSICLK的时钟频率是8M,并且需要固定2分频到PLL的倍频器,最大16倍频,所以最大主频只能配到64Mhz.

系统时钟会再经过AHBPSC和APBPSC分频配置后到各外设。其中TMR的时钟需要注意,所有 TMRxCLK(定时器时钟)频率分配由硬件按以下 2 种情况自动设置:
⚫如果相应的 APB 预分频系数是 1,定时器的时钟频率与所在 APB 总线频率一致。
⚫否则,定时器的时钟频率被设为与其相连的 APB 总线频率的 2 倍

具体的寄存器在用户手册中,主要是时钟控制寄存器 1(RCM_CTRL1)和时钟配置寄存器 1(RCM_CFG1),具体功能可以查看用户手册。

除了RCM相关的寄存器,还有Flash的等待周期与预取使能与时钟相关需要注意。
微信图片_20250625111015.png
理论部分主要就这些,更详细的建议查看用户手册,接下来是代码的相关部分。
芯片上电实现会运行到启动文件,初始化完中断向量表后会进入到SystemInit()函数进行默认的时钟初始化。
微信图片_20250625112616.png
在SystemInit()函数中会复位时钟相关的寄存器,然后进入SystemClockConfig();进行默认的时钟初始化。
微信图片_20250625112621.png
SystemClockConfig()会根据宏定义来进行时钟初始化。
微信图片_20250625113807.png
SDK默认配置的是8M外部无源晶振,配置主频72M。
微信图片_20250625112626.png
如果需要配置更低的主频,可以直接通过选择不同的宏定义来直接切换。

如果使用的外部高速晶振是其他频率,比如4M、12M、16M等,就不能直接修改宏定义来配置主频,还需要做如下修改。
1、将HSE_VALUE改成实际的晶振频率,例如使用12M晶振就修改成
  1. #define  HSE_VALUE  ((uint32_t)12000000)


微信图片_20250625115459.png
2、修改PLL倍频系数寄存器PLLMULCFG=4,对应数据手册可以看到是6倍频12M*6=72M
微信图片_20250625115830.png

如果产品应用对时钟精度要求不高,想不接外部晶振,使用内部晶振倍频到64M的操作。
1、屏蔽默认的使用外部晶振的宏定义
微信图片_20250625120338.png
2、编写如下使用内部晶振的时钟初始化函数,在main函数中调用
  1. void SystemClock_HSI_PLL_Init()
  2. {
  3.         
  4.                 RCM_Reset();
  5.         
  6.     /* Enable HSI */
  7.     RCM_EnableHSI();

  8.             /* Wait until HSI is ready */
  9.     while (RCM->CTRL1_B.HSIRDY** == RESET);
  10.         
  11.                 FMC_EnablePrefetchBuffer();
  12.         
  13.                 FMC_SetWS2();
  14.         
  15.                 RCM_ConfigAHB(RCM_SYSCLK_DIV_1);
  16.         
  17.                 RCM_ConfigAPB(RCM_HCLK_DIV_1);
  18.       /*  SYSCLKFreq = (HSI * 16) / 2 */
  19.     RCM_ConfigPLL(RCM_PLL_SEL_HSI_DIV2, RCM_PLLMF_16);

  20.     /* Enable PLL */
  21.     RCM_EnablePLL();
  22.     while (RCM->CTRL1_B.PLLRDY** == BIT_RESET);
  23.     /* Selct PLL as Sysclk */
  24.     RCM_ConfigSYSCLK(RCM_SYSCLK_SEL_PLL);
  25.     while (RCM->CFG1_B.SCLKSWSTS != 0x02);
  26. }
如果想要在程序运行中切换主频频率,比如将外部晶振72M的配置切换到36M
  1. void SystemClock_HSE_PLL_Init()
  2. {
  3.         
  4.             uint32_t i;
  5.     /* Select HSI as System Clock at first */
  6.     RCM_ConfigSYSCLK(RCM_SYSCLK_SEL_HSI);

  7.     /* Disable PLL */
  8.     RCM_DisablePLL();

  9.     /* Wait until Pll is ready */
  10.     while (RCM->CTRL1_B.PLLRDY** == SET);


  11.    RCM_ConfigHSE(RCM_HSE_OPEN);

  12.     for (i = 0; i < HSE_STARTUP_TIMEOUT; i++)
  13.     {
  14.         if (RCM->CTRL1_B.HSERDY**)
  15.         {
  16.             break;
  17.         }
  18.     }
  19.                     if (RCM->CTRL1_B.HSERDY**)
  20.     {
  21.                 FMC_EnablePrefetchBuffer();
  22.         
  23.                 FMC_SetWS2();
  24.         
  25.                 RCM_ConfigAHB(RCM_SYSCLK_DIV_1);
  26.         
  27.                 RCM_ConfigAPB(RCM_HCLK_DIV_1);
  28.     /* Config PLL source and multiplication factor
  29.         SYSCLKFreq = (HSE * 6) / 4 */
  30.     RCM_ConfigPLL(RCM_PLL_SEL_HSE, RCM_PLLMF_9);
  31.     RCM_ConfigCLKDIV(RCM_CLK_DIV_2);

  32.     /* Enable PLL */
  33.     RCM_EnablePLL();
  34.     while (RCM->CTRL1_B.PLLRDY** == BIT_RESET);
  35.     /* Selct PLL as Sysclk */
  36.     RCM_ConfigSYSCLK(RCM_SYSCLK_SEL_PLL);
  37.     while (RCM->CFG1_B.SCLKSWSTS != 0x02);
  38.                 }
  39.                 else
  40.                 {
  41.                 /*可增加HSE启动失败的处理程序*/
  42.                 }

  43. }
如果要知道系统现在的时钟配置是多少,可以参考SDK中的RCB-->RCM_ClockSwitch 例程
  1.     /* Initiatate the usart */
  2.     APM_TINY_COMInit(COM1);

  3.     printf("sysSource = %s ", RCM_SYSCLK_SEL_TAB[RCM_ReadSYSCLKSource()]);
  4.     printf("sysClock = %" PRId32 "\r\n", RCM_ReadSYSCLKFreq());
使用串口来打印当前时钟配置,注意这个打印是基于HSE_VALUE与实际相符的情况才是准确的,需要注意核对。
  1. int main(void)
  2. {
  3.     APM_TINY_LEDInit(LED2);
  4.     APM_TINY_LEDInit(LED3);
  5.     APM_TINY_PBInit(BUTTON_KEY1, BUTTON_MODE_EINT);
  6.     APM_TINY_PBInit(BUTTON_KEY2, BUTTON_MODE_EINT);
  7.     APM_TINY_COMInit(COM1);

  8.     ClockOutputInit();
  9.     printf("sysSource = %s ", RCM_SYSCLK_SEL_TAB[RCM_ReadSYSCLKSource()]);
  10.     printf("sysClock = %" PRId32 "\r\n", RCM_ReadSYSCLKFreq());

  11.                 SystemClock_HSE_PLL_Init();
  12.     for (;;)
  13.     {
  14.         Delay();
  15.         APM_TINY_LEDToggle(LED2);
  16.     }
  17. }
微信图片_20250625123934.png
这是启动文件不初始化时钟,在main中配置64M主频的测试结果

打赏榜单

21小跑堂 打赏了 50.00 元 2025-06-26
理由:恭喜通过原创审核!期待您更多的原创作品~

评论

APM32E030的时钟系统解读,通过时钟树搭配代码,详解单片机的时钟系统。  发表于 2025-6-26 14:08
 楼主| Alden 发表于 2025-6-25 12:42 | 显示全部楼层
观星者宁静 发表于 2025-6-27 09:56 | 显示全部楼层
文章写得还是蛮全的。
要是当时我学习的时候有人这么讲一下,可以节省不少不少的时间呢!
现在我的项目里面已经把system_init.c给删了,自己把这部分实现划到BSP里面了。
黄昏收获 发表于 2025-6-28 14:31 | 显示全部楼层
还别说,经过楼主这么简单的串讲下来,这时钟树明白多了。
谢谢 楼主
永恒回声 发表于 2025-6-28 16:19 | 显示全部楼层
楼主,看时钟树里面有systick的8分频,楼主这个要怎么配置啊?
HeartbeatEcho 发表于 2025-6-29 16:09 | 显示全部楼层
我一直使用官方的时钟初始化的代码,也没有自己修改过。
确实是一上电就跑到最大主频。
ZenithSeeker 发表于 2025-6-29 16:11 | 显示全部楼层
arm内核的应该都是这个样子的启动顺序吧
zcyxh12345 发表于 2025-7-4 10:53 | 显示全部楼层
好像跟STM32F030不兼容,能兼容就更好了
 楼主| Alden 发表于 2025-7-4 10:57 | 显示全部楼层
zcyxh12345 发表于 2025-7-4 10:53
好像跟STM32F030不兼容,能兼容就更好了

是兼容的哦
真的问题不大 发表于 2025-7-4 10:59 | 显示全部楼层
zcyxh12345 发表于 2025-7-4 10:53
好像跟STM32F030不兼容,能兼容就更好了

可以的吧,而且他们家我记得也有F030的系列,跟st完全兼容
您需要登录后才可以回帖 登录 | 注册

本版积分规则

49

主题

113

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部

49

主题

113

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部