你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32H7时钟树RCC分析--- HAL库配置(二)

[复制链接]
STMCU小助手 发布时间:2022-3-23 14:00
启动流程
H7的启动流程我们再说一下:

系统上电复位,进入启动文件 startup_stm32h743xx.s,在这个文件里面执行复位中断服务程序。
在复位中断服务程序里面执行函数 SystemInit,此函数在文件 system_stm32h7xx.c 里面。
之后是调用启动文件__main,最终进入到 main函数
进入到 main 函数就可以开始用户应用程序编程了。在程序运行之前需要:
MPU 初始化,需要用到库文件 stm32h7xx_hal_cortex.c 和 stm32h7xx_hal_cortex.h。
Cache 初始化,需要用到 core_cm7.h 文件。
HAL 库初始化函数 HAL_Init,对Systick滴答定时器初始化,需要用到文件 stm32h7xx_hal.c。
系统时钟初始化,需要用到库文件 stm32h7xx_hal_rcc.c

要注意的是,Hal库的SystemInit函数对于时钟只做了:复位 RCC 时钟配置为默认复位值(默认开启 HSI) 并没有做其他的配置,所以时钟初始化还要用户自己配置。

我们今天关心的就是最后一步的系统时钟初始化

上面的四步,在执行完HAL_Init之后,系统还在用内部高速时钟 HSI,对于 H7 来说,HSI 主频是 64MHz

修改时钟配置

那么我们来看下如何修改时钟的配置:

第 1 步:在 stm32h7xx_hal_conf.h 文件配置 HSE_VALUE
配置的大小要跟板子的实际晶振大小匹配。

我使用的外部晶振是25Mhz的 所以配置为25000000
  1. #if !defined (HSE_VALUE)
  2. #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
  3. #endif /* HSE_VALUE */
复制代码

第 2 步:然后我们初始化时钟,最主要的就是两个结构体参数和两个函数:
  1. RCC_ClkInitTypeDef RCC_ClkInitStruct;  // 配置时钟源相关参数
  2. RCC_OscInitTypeDef RCC_OscInitStruct;  //配置系统时钟源及各个外设分频系数
复制代码
  1. HAL_RCC_OscConfig()  //配置时钟源相关参数
  2. HAL_RCC_ClockConfig() //配置系统时钟源及各个外设分频系数
复制代码

HAL_RCC_OscConfig()

HAL_RCC_OscConfig() , 该 函 数 在 HAL 库 关 键 头 文 件stm32h7xx_hal_rcc.h 中声明,在文件 stm32h7xx_hal_rcc.c 中定义。首先我们来看看该函数声明:

  1. __weak HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);
复制代码

只有一个RCC_OscInitTypeDef 结构体参数,我们来看下这个结构体:

  1. typedef struct
  2. {
  3. uint32_t OscillatorType; //需要选择配置的振荡器类型
  4. uint32_t HSEState; //HSE 状态
  5. uint32_t LSEState; //LSE 状态
  6. uint32_t HSIState; //HIS 状态
  7. uint32_t HSICalibrationValue; //HIS 校准值
  8. uint32_t LSIState; //LSI 状态
  9. uint32_t HSI48State //HSI48 的状态
  10. uint32_t CSIState; //CSI 状态
  11. uint32_t CSICalibrationValue; //CSI 校准值
  12. RCC_PLLInitTypeDef PLL; //PLL 配置
  13. }RCC_OscInitTypeDef;
复制代码

第一个就是选择振荡器的类型,比如我们要开启 HSE,那么我们会设置 OscillatorType 的值为 RCC_OSCILLATORTYPE_HSE,然后设置 HSEState 的值为 RCC_HSE_ON 开启 HSE。对于其他时钟源 HSI,LSI 和 LSE,配置方法类似

最后有一个 RCC_PLLInitTypeDef PLL; //PLL 配置

这个就是配置锁相环了,结构体参数如下

  1. typedef struct
  2. {
  3. uint32_t PLLState; //PLL 状态
  4. uint32_t PLLSource; //PLL 时钟源
  5. uint32_t PLLM; //PLL 分频系数 M
  6. uint32_t PLLN; //PLL 倍频系数 N
  7. uint32_t PLLP; //PLL 分频系数 P
  8. uint32_t PLLQ; //PLL 分频系数 Q
  9. uint32_t PLLR; //PLL 分频系数 R
  10. uint32_t PLLRGE; //PLL1 时钟输入范围
  11. uint32_t PLLVCOSEL; //PLL1 时钟输出范围
  12. uint32_t PLLFRACN; //PLL1 VCO 乘数因子的小数部分
  13. }RCC_PLLInitTypeDef;
复制代码

这里的分频系数,全部都是PLL锁相环内部控制,我们要做的就是明白具体的公式就行
Fvco:VCO频率
Fsys:系统时钟频率,也是PLL1的p分频输出时钟频率 pll1_p_ck
FpllqLL1的q分频输出时钟频率 pll1_q_ck
ref1_ck: PLL输入时钟频率,可以是HSI,CSI,HSE等.


pllnLL1倍频系数(PLL倍频),取值范围:4~512.
pllmLL1预分频系数(**LL之前的分频),取值范围:2~63.
pllpLL1的p分频系数(PLL之后的分频),分频后作为系统时钟,取值范围:2~128.(且必须是2的倍数)
pllqLL1的q分频系数(PLL之后的分频),取值范围:1~128.

FOXV5CS$TF$EC}LD$F6K4S2.png

  1. VCO频率: Fvco= ref1_ck*(plln/pllm);
  2. pll1_p_ck频率: Fsys=Fvco/pllp= ref1_ck*(plln/(pllm*pllp));
  3. pll1_q_ck频率: Fpllq=Fvco/pllq= ref1_ck*(plln/(pllm*pllq));
复制代码

{_{)ZU9EI]$R3}8ORBES7_B.png

具体的公式如下,上一篇也有讲解

YO4U@_534{J@C}0L]}KMDI6.png

HAL_RCC_ClockConfig()
接下来我们就要看 HAL_RCC_ClockConfig()函数,声明如下:

  1. HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct,
  2. uint32_t FLatency);
复制代码

一共有两个参数:第一个入口参数 RCC_ClkInitStruct 是结构体 RCC_ClkInitTypeDef
指针类型,用来设置 SYSCLK 时钟源以及 SYSCLK、AHB,APB1、APB2、APB3 和 APB4 的分
频系数。第二个入口参数 FLatency 用来设置 FLASH 延迟

我们开看下RCC_ClkInitTypeDef 具体位置在stm32h7xx_hal_rcc.h 中声明

  1. typedef struct
  2. {
  3.   uint32_t ClockType;             /*!< 要配置的时钟。
  4.                               该参数可以是@ref RCC_System_Clock_Type 的值     */

  5.   uint32_t SYSCLKSource;          /*!< 用作系统时钟的时钟源(SYSCLKS)*/

  6.   uint32_t SYSCLKDivider;         /*!< 系统时钟分频    */

  7.   uint32_t AHBCLKDivider;         /*!< AHB分频 */

  8.   uint32_t APB3CLKDivider;        /*!APB3分频*/

  9.   uint32_t APB1CLKDivider;        /*!APB1分频*/
  10.   uint32_t APB2CLKDivider;        /*!APB2分频*/
  11.   uint32_t APB4CLKDivider;      /*!APB4分频*/
  12. }RCC_ClkInitTypeDef;
复制代码

第一个参数 ClockType 配置说明我们要配置的是 SYSCLK,HCLK、D1PCLK1(PCLK3)、
PCLK1、PCLK2 和 D3PCLK1(PCLK4)六个时钟。
第二个参数 SYSCLKSource 配置选择系统时钟源为 PLL。
第三个参数 SYSCLKDivider 配置 SYSCLK 分频系数
第四个参数 AHBCLKDivider 配置 AHB 分频系数
第五个参数 APB1CLKDivider 配置 APB1 分频系数
第六个参数 APB2CLKDivider 配置 APB2 分频系数
第七个参数 APB3CLKDivider 配置 APB3 分频系数
第八个参数 APB4CLKDivider 配置 APB4 分频系数

这个在下面有详细的介绍

然后我们看第二个参数:FLatency 用来设置 FLASH 延迟和调压器VOS

那就需要知道Flash 读操作

Flash 读操作 是每次读取完Flash数据,需要延时一定时间,确保之后的数据传输正常

VOS:在STM32H7中,系统Flash的受到调压器输出电压级别选择(VOS)的影响(在电源控制寄存器中)

为了得到最高的FLASH读取速度,我们需要设置VOS级别为1 然后等待数设置为4 就可以得到最高的Flash读取速度,ST 官方例程使用的就是 4 个 等待状态数

`~PBE4ZQVW`UAVP%~(Q[@8X.png

STM32H7xx参考手册 112页

最终代码如下:

都做了注释



  1. /*
  2. *********************************************************************************************************
  3. *        函 数 名: SystemClock_Config
  4. *        功能说明: 初始化系统时钟
  5. *                    System Clock source            = PLL (HSE)
  6. *                    SYSCLK(Hz)                     = 400000000 (CPU Clock)
  7. *                   HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
  8. *                    AHB Prescaler                  = 2
  9. *                    D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
  10. *                    D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
  11. *                    D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
  12. *                    D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
  13. *                    HSE Frequency(Hz)              = 25000000
  14. *                    VDD(V)                         = 3.3
  15. *                    Flash Latency(WS)              = 4
  16. *        形    参:
  17. *           plln:PLL1倍频系数(PLL倍频),取值范围:4~512.
  18. *                        pllm:PLL1预分频系数(**LL之前的分频),取值范围:2~63.
  19. *                        pllp:PLL1的p分频系数(PLL之后的分频),分频后作为系统时钟,取值范围:2~128.(且必须是2的倍数)
  20. *                        pllq:PLL1的q分频系数(PLL之后的分频),取值范围:1~128.
  21. *
  22. *
  23. *            Fvco:VCO频率
  24. *            Fsys:系统时钟频率,也是PLL1的p分频输出时钟频率 pll1_p_ck
  25. *            Fpllq:PLL1的q分频输出时钟频率  pll1_q_ck
  26. *            ref1_ck: PLL输入时钟频率,可以是HSI,CSI,HSE等.
  27. *
  28. *
  29. *            plln:PLL1倍频系数(PLL倍频),取值范围:4~512.
  30. *                         pllm:PLL1预分频系数(**LL之前的分频),取值范围:2~63.
  31. *                     pllp:PLL1的p分频系数(PLL之后的分频),分频后作为系统时钟,取值范围:2~128.(且必须是2的倍数)
  32. *                         pllq:PLL1的q分频系数(PLL之后的分频),取值范围:1~128.
  33. *
  34. *
  35. *
  36. *                         VCO频率: Fvco= ref1_ck*(plln/pllm);
  37. *                         pll1_p_ck频率: Fsys=Fvco/pllp= ref1_ck*(plln/(pllm*pllp));
  38. *                         pll1_q_ck频率: Fpllq=Fvco/pllq= ref1_ck*(plln/(pllm*pllq));
  39. *
  40. *                        CPU频率(rcc_c_ck)=pll1_p_ck频率=400Mhz
  41. *                        rcc_aclk=rcc_hclk3=200Mhz
  42. *                        AHB1/2/3/4(rcc_hclk1/2/3/4)=200Mhz  
  43. *                        APB1/2/3/4(rcc_pclk1/2/3/4)=100Mhz  
  44. *                        FMC时钟频率=pll2_r_ck=((25/25)*512/2)=256Mhz
  45. *
  46. *
  47. *          外部晶振为25M的时候,推荐值:plln=160,pllm=5,pllp=2,pllq=2.
  48. *          得到:Fvco=25*(160/5)=800Mhz
  49. *          CPU频率Fsys= pll1_p_ck频率=800/2=400Mhz
  50. *          pll1_q_ck频率=800/2=400Mhz
  51. *        返 回 值: 无

  52. *********************************************************************************************************
  53. */
  54. void SystemClock_Config(u32 plln,u32 pllm,u32 pllp,u32 pllq)
  55. {
  56.         HAL_StatusTypeDef ret=HAL_OK;
  57.         RCC_ClkInitTypeDef RCC_ClkInitStruct;
  58.         RCC_OscInitTypeDef RCC_OscInitStruct;
  59.          /*使能供电配置更新 */
  60.         MODIFY_REG(PWR->CR3,PWR_CR3_SCUEN, 0);
  61.         
  62.         /*
  63.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
  64.          详情看参考手册的Table 12的表格。
  65.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
  66.     */
  67.         __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  68.         while ((PWR->D3CR & (PWR_D3CR_VOSRDY)) != PWR_D3CR_VOSRDY) {}

  69.                
  70.                 /* 使能HSE,并选择HSE作为PLL时钟源 */
  71.         RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_HSE;
  72.         RCC_OscInitStruct.HSEState=RCC_HSE_ON;
  73.         RCC_OscInitStruct.HSIState=RCC_HSI_OFF;
  74.         RCC_OscInitStruct.CSIState=RCC_CSI_OFF;
  75.         RCC_OscInitStruct.PLL.PLLState=RCC_PLL_ON;
  76.         RCC_OscInitStruct.PLL.PLLSource=RCC_PLLSOURCE_HSE;

  77.         RCC_OscInitStruct.PLL.PLLN=plln;
  78.         RCC_OscInitStruct.PLL.PLLM=pllm;
  79.         RCC_OscInitStruct.PLL.PLLP=pllp;
  80.         RCC_OscInitStruct.PLL.PLLQ=pllq;

  81.         RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  82.         RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
  83.         ret=HAL_RCC_OscConfig(&RCC_OscInitStruct);
  84.         if(ret!=HAL_OK) while(1);

  85.                
  86.                
  87.                 /*
  88.        选择PLL的输出作为系统时钟
  89.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
  90.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
  91.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
  92.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
  93.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
  94.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
  95.                 AHB 分频系数为 2,故其频率为HCLK=SYSCLK/2=200MHz。
  96.                 APB1 分频系数为 2,故其频率为 PCLK1=HCLK/2=100MHz。
  97.                 APB2分频系数为 2,故其频率为 PCLK2=HCLK/2=200/2=100MHz,
  98.                 APB3 分频系数为 2,故其频率PCLK3=HCLK/2=200/2=100MHz,
  99.                 APB4 的分频系数为 2,故其频率 PLCK4=HCLK/2=200/2=100MHz
  100.     */
  101.                
  102.         RCC_ClkInitStruct.ClockType=(RCC_CLOCKTYPE_SYSCLK|\
  103.                                 RCC_CLOCKTYPE_HCLK |\
  104.                                 RCC_CLOCKTYPE_D1PCLK1 |\
  105.                                 RCC_CLOCKTYPE_PCLK1 |\
  106.                                 RCC_CLOCKTYPE_PCLK2 |\
  107.                                 RCC_CLOCKTYPE_D3PCLK1);

  108.         RCC_ClkInitStruct.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;
  109.         RCC_ClkInitStruct.SYSCLKDivider=RCC_SYSCLK_DIV1;
  110.         RCC_ClkInitStruct.AHBCLKDivider=RCC_HCLK_DIV2;
  111.         RCC_ClkInitStruct.APB1CLKDivider=RCC_APB1_DIV2;
  112.         RCC_ClkInitStruct.APB2CLKDivider=RCC_APB2_DIV2;
  113.         RCC_ClkInitStruct.APB3CLKDivider=RCC_APB3_DIV2;  
  114.         RCC_ClkInitStruct.APB4CLKDivider=RCC_APB4_DIV2;
  115.         
  116.         /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
  117.         ret=HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
  118.         if(ret!=HAL_OK) while(1);



  119.    /*   使用IO的高速模式,要使能IO补偿,即调用下面三个函数
  120.       (1)使能CSI clock
  121.       (2)使能SYSCFG clock
  122.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
  123.     */
  124.         __HAL_RCC_CSI_ENABLE() ;

  125.         __HAL_RCC_SYSCFG_CLK_ENABLE() ;

  126.         HAL_EnableCompensationCell();

  127. }
复制代码


主函数调用:

  1.         SystemClock_Config(160,5,2,4);            //设置时钟400Mhz
复制代码




收藏 评论0 发布时间:2022-3-23 14:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版