关于嵌入式学习随笔->4《F7系统时钟》

1、STM32有5个时钟源:HSI、HSE、LSI、LSE、PLL。

--》HSI是高速内部时钟,RC振荡器,频率为16MHz,精度不高。可以直接作为系统时钟或者用作PLL时钟输入。

--》HSE是告诉外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~26MHz。

--》LSI是低速内部时钟,RC振荡器,频率为32KHz,提供低功耗时钟。LSI主要可以作为IWDG独立看门狗时钟,LPTimer低功耗定时器时钟以及RTC时钟。

--》LSE是低速外部时钟,接频率为32.768KHz的石英晶体。为RTC提供精准的时钟频率。

--》PLL为锁相环倍频输出。

STM32F767内部时钟最快可达216MHz,这就是用的PLL倍频输出。其实尤其时钟电路可以知道,系统时钟由HSI、HSE、PLL提供,如果HSE出现故障,那么就会由HSI提供系统时钟源,虽然很慢,但是仍然可以是系统正常运行。下边是STM32F767的时钟树,很用以理解。

主PLL时钟计算:PLCK = HSE * N / ( M * P ),这个公式可以由电路图导出。(一般用的是外部告诉时钟,xN倍频一般为50~432,R分频系数一般为2~7,Q分频系数一般为2~15)

2、任何一个外设在使用前必须首先要使能相应的时钟

其实任何一个电路的逻辑控制器都是通过一个寄存器来控制的。RCC控制时钟的相关寄存器定义在RCC_TypeDef中。

  1 typedef struct
  2 {
  3   __IO uint32_t CR;            /*!< RCC clock control register,                                  Address offset: 0x00 */
  4   __IO uint32_t PLLCFGR;       /*!< RCC PLL configuration register,                              Address offset: 0x04 */
  5   __IO uint32_t CFGR;          /*!< RCC clock configuration register,                            Address offset: 0x08 */
  6   __IO uint32_t CIR;           /*!< RCC clock interrupt register,                                Address offset: 0x0C */
  7   __IO uint32_t AHB1RSTR;      /*!< RCC AHB1 peripheral reset register,                          Address offset: 0x10 */
  8   __IO uint32_t AHB2RSTR;      /*!< RCC AHB2 peripheral reset register,                          Address offset: 0x14 */
  9   __IO uint32_t AHB3RSTR;      /*!< RCC AHB3 peripheral reset register,                          Address offset: 0x18 */
 10   uint32_t      RESERVED0;     /*!< Reserved, 0x1C                                                                    */
 11   __IO uint32_t APB1RSTR;      /*!< RCC APB1 peripheral reset register,                          Address offset: 0x20 */
 12   __IO uint32_t APB2RSTR;      /*!< RCC APB2 peripheral reset register,                          Address offset: 0x24 */
 13   uint32_t      RESERVED1[2];  /*!< Reserved, 0x28-0x2C                                                               */
 14   __IO uint32_t AHB1ENR;       /*!< RCC AHB1 peripheral clock register,                          Address offset: 0x30 */
 15   __IO uint32_t AHB2ENR;       /*!< RCC AHB2 peripheral clock register,                          Address offset: 0x34 */
 16   __IO uint32_t AHB3ENR;       /*!< RCC AHB3 peripheral clock register,                          Address offset: 0x38 */
 17   uint32_t      RESERVED2;     /*!< Reserved, 0x3C                                                                    */
 18   __IO uint32_t APB1ENR;       /*!< RCC APB1 peripheral clock enable register,                   Address offset: 0x40 */
 19   __IO uint32_t APB2ENR;       /*!< RCC APB2 peripheral clock enable register,                   Address offset: 0x44 */
 20   uint32_t      RESERVED3[2];  /*!< Reserved, 0x48-0x4C                                                               */
 21   __IO uint32_t AHB1LPENR;     /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */
 22   __IO uint32_t AHB2LPENR;     /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */
 23   __IO uint32_t AHB3LPENR;     /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */
 24   uint32_t      RESERVED4;     /*!< Reserved, 0x5C                                                                    */
 25   __IO uint32_t APB1LPENR;     /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */
 26   __IO uint32_t APB2LPENR;     /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */
 27   uint32_t      RESERVED5[2];  /*!< Reserved, 0x68-0x6C                                                               */
 28   __IO uint32_t BDCR;          /*!< RCC Backup domain control register,                          Address offset: 0x70 */
 29   __IO uint32_t CSR;           /*!< RCC clock control & status register,                         Address offset: 0x74 */
 30   uint32_t      RESERVED6[2];  /*!< Reserved, 0x78-0x7C                                                               */
 31   __IO uint32_t SSCGR;         /*!< RCC spread spectrum clock generation register,               Address offset: 0x80 */
 32   __IO uint32_t PLLI2SCFGR;    /*!< RCC PLLI2S configuration register,                           Address offset: 0x84 */
 33   __IO uint32_t PLLSAICFGR;    /*!< RCC PLLSAI configuration register,                           Address offset: 0x88 */
 34   __IO uint32_t DCKCFGR1;      /*!< RCC Dedicated Clocks configuration register1,                 Address offset: 0x8C */
 35   __IO uint32_t DCKCFGR2;      /*!< RCC Dedicated Clocks configuration register 2,               Address offset: 0x90 */
 36
 37 } RCC_TypeDef;

结构体:RCC_TypeDef

3、时钟系统初始化(程序角度HAL库)

在STM32的启动文件中都会由如下代码段(我们称之为引导代码):

  1 Reset_Handler    PROC
  2                  EXPORT  Reset_Handler             [WEAK]
  3         IMPORT  SystemInit
  4         IMPORT  __main
  5
  6                  LDR     R0, =SystemInit
  7                  BLX     R0
  8                  LDR     R0, =__main
  9                  BX      R0
 10                  ENDP;

很容易看出,在执行__main函数之前需要先执行SystemInit函数进行系统初始化,而不是我们自认为的会直接到

main函数开始执行(注意__main函数与main函数有区别,详细区别参见之后的STM32之启动文件讲解,暂时未出),总之我们这里只要知道在main函数执行之前系统要先执行SystemInit函数,其定义为:

  1 void SystemInit(void)
  2 {
  3   /* FPU settings ------------------------------------------------------------*/
  4   #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
  5     SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  6   #endif
  7   /* Reset the RCC clock configuration to the default reset state ------------*/
  8   /* Set HSION bit */
  9   RCC->CR |= (uint32_t)0x00000001;
 10
 11   /* Reset CFGR register */
 12   RCC->CFGR = 0x00000000;
 13
 14   /* Reset HSEON, CSSON and PLLON bits */
 15   RCC->CR &= (uint32_t)0xFEF6FFFF;
 16
 17   /* Reset PLLCFGR register */
 18   RCC->PLLCFGR = 0x24003010;
 19
 20   /* Reset HSEBYP bit */
 21   RCC->CR &= (uint32_t)0xFFFBFFFF;
 22
 23   /* Disable all interrupts */
 24   RCC->CIR = 0x00000000;
 25
 26   /* Configure the Vector Table location add offset address ------------------*/
 27 #ifdef VECT_TAB_SRAM
 28   SCB->VTOR = RAMDTCM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
 29 #else
 30   SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
 31 #endif
 32 }

SystemInit函数主要有三个功能:

--》首先是对FPU的设置,关于浮点运算的设置。

--》其次就是对一些寄存器的默认配置,也就是将其设置为初始值。

其中  RCC->CR |= (uint32_t)0x00000001;     /* Set HSION bit */  目的就是设置系统默认时钟为高速内部时钟(HSI),因为在刚开始时系统不确定你是否使用了外部时钟,在这里会进行初试默认值的设置,包括其他寄存器,在这里都有设置开始时的默认值。

--》最后是对中断像量表的地址的设置,包括他的基地址和偏移地址。

HAL库中没有对于系统时钟的设置,只是有个初始值,而在标准库(M3,M4所使用的)中则实在系统初始化函数中对系统时钟进行设置的,不需要用户单独去写时钟配置的函数。

HAL库中系统时钟配置的一般步骤(stm32_clock_init函数):

1—>使能PWR时钟,调用:

  1     /* Enable Power Clock*/
  2     __HAL_RCC_PWR_CLK_ENABLE();

2—>设置调压器输出电压级别,调用:

  1     /* Set Range */
  2     __HAL_PWR_VOLTAGESCALING_CONFIG(VoltageScaling);

3—>选择是否开启Over-Driver功能,调用:

  1 HAL_PWREx_EnableOverDrive(); //开启Over-Driver功能

4—>配置时钟源相关参数,调用:

  1 HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化

5—>配置系统时钟源以及AHB、APB1和APB2的分频系数,调用函数:

  1 HAL_RCC_ClockConfig();

最主要的两个配置函数:

  1 HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);//时钟源配置函数
  2 HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency);//时钟配置函数

时钟源配置函数入口参数时钟源结构体定义:

  1 typedef struct
  2 {
  3   uint32_t OscillatorType;       /*!< The oscillators to be configured.
  4                                       This parameter can be a value of @ref RCC_Oscillator_Type                   */
  5
  6   uint32_t HSEState;             /*!< The new state of the HSE.
  7                                       This parameter can be a value of @ref RCC_HSE_Config                        */
  8
  9   uint32_t LSEState;             /*!< The new state of the LSE.
 10                                       This parameter can be a value of @ref RCC_LSE_Config                        */
 11
 12   uint32_t HSIState;             /*!< The new state of the HSI.
 13                                       This parameter can be a value of @ref RCC_HSI_Config                        */
 14
 15   uint32_t HSICalibrationValue;   /*!< The HSI calibration trimming value (default is RCC_HSICALIBRATION_DEFAULT).
 16                                        This parameter must be a number between Min_Data = 0x00 and Max_Data = 0x1F */
 17
 18   uint32_t LSIState;             /*!< The new state of the LSI.
 19                                       This parameter can be a value of @ref RCC_LSI_Config                        */
 20
 21   RCC_PLLInitTypeDef PLL;        /*!< PLL structure parameters                                                    */
 22
 23 }RCC_OscInitTypeDef;

刚好有5个时钟源:HSI、HSE、LSI、LSE、PLL,其中在时钟源配置函数中要先对时钟源类型,在进行相应的时钟源配置,而不同的时钟原配置所需要的操作是不同的,因此这里就要先用时钟源类型来进行判断。要设置主PLL参数就又需要一个结构体来实现(RCC_PLLInitTypeDef ):

  1 typedef struct
  2 {
  3   uint32_t PLLState;   /*!< The new state of the PLL.
  4                             This parameter can be a value of @ref RCC_PLL_Config                      */
  5
  6   uint32_t PLLSource;  /*!< RCC_PLLSource: PLL entry clock source.
  7                             This parameter must be a value of @ref RCC_PLL_Clock_Source               */
  8
  9   uint32_t PLLM;       /*!< PLLM: Division factor for PLL VCO input clock.
 10                             This parameter must be a number between Min_Data = 2 and Max_Data = 63    */
 11
 12   uint32_t PLLN;       /*!< PLLN: Multiplication factor for PLL VCO output clock.
 13                             This parameter must be a number between Min_Data = 50 and Max_Data = 432  */
 14
 15   uint32_t PLLP;       /*!< PLLP: Division factor for main system clock (SYSCLK).
 16                             This parameter must be a value of @ref RCC_PLLP_Clock_Divider             */
 17
 18   uint32_t PLLQ;       /*!< PLLQ: Division factor for OTG FS, SDMMC and RNG clocks.
 19                             This parameter must be a number between Min_Data = 2 and Max_Data = 15    */
 20 #if defined (STM32F765xx) || defined (STM32F767xx) || defined (STM32F769xx) || defined (STM32F777xx) || defined (STM32F779xx)
 21   uint32_t PLLR;       /*!< PLLR: Division factor for DSI clock.
 22                             This parameter must be a number between Min_Data = 2 and Max_Data = 7    */
 23 #endif /* STM32F767xx || STM32F769xx || STM32F777xx || STM32F779xx */
 24
 25 }RCC_PLLInitTypeDef;

这些程序代码结合最上边的时钟树就可以很容易的理解了,其中PLLM、PLLN、PLLP、PLLQ、PLLR与时钟树中主PLL中参数M、N、P、Q、R一一对应。

以下代码为时钟源配置相关代码(HAL_RCC_OscConfig函数配置方法):

  1     RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE;    //时钟源为HSE
  2     RCC_OscInitStructure.HSEState=RCC_HSE_ON;                      //打开HSE
  3     RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON;				   //打开PLL
  4     RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE;          //PLL时钟源选择HSE
  5     RCC_OscInitStructure.PLL.PLLM=pllm;	//主PLL和音频PLL分频系数(PLL之前的分频)
  6     RCC_OscInitStructure.PLL.PLLN=plln; //主PLL倍频系数(PLL倍频)
  7     RCC_OscInitStructure.PLL.PLLP=pllp; //系统时钟的主PLL分频系数(PLL之后的分频)
  8     RCC_OscInitStructure.PLL.PLLQ=pllq; //USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频)
  9     ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化

时钟配置函数相关代码(HAL_RCC_ClockConfig函数配置方法):

  1     //选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
  2     RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
  3     RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;//设置系统时钟时钟源为PLL
  4     RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1;//AHB分频系数为1
  5     RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV4;//APB1分频系数为4
  6     RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV2;//APB2分频系数为2
  7     ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_7);//同时设置FLASH延时周期为7WS,也就是8个CPU周期。

这样就将系统时钟配置好了,注意与时钟树相结合,更容易理解。

而要想达到最大时钟频率216MHz那么就需要我们开启Over-Driver的功能,还要设置调压器的级别为1。这样还没有设置完全,还有一个等待周期的设置,一般电压范围确定后等待周期也就固定了,这个参数的配置其实就是时钟入口参数的第二个值FLatency。

原文地址:https://www.cnblogs.com/vcan123/p/10424901.html

时间: 2024-10-13 08:08:33

关于嵌入式学习随笔->4《F7系统时钟》的相关文章

Zephyr学习(四)系统时钟

每一个支持多进程(线程)的系统都会有一个滴答时钟(系统时钟),这个时钟就好比系统的“心脏”,线程的休眠(延时)和时间片轮转调度都需要用到它. Cortex-M系列的内核都有一个systick时钟,这个时钟就是设计用来支持操作系统的,是一个24位的自动重装载向下计数器,中断入口就位于中断向量表里面,定义在zephyr-zephyr-v1.13.0\arch\arm\core\cortex_m\vector_table.S: 1 SECTION_SUBSEC_FUNC(exc_vector_tabl

关于嵌入式学习随笔-&gt;5《Systick定时器》

1.什么是Systick定时器? --->Systick定时器是一个简单的滴答定时器,对于ST的M3.M4.M7内核芯片,都有滴答定时器. --->Systick滴答定时器常常用来做延迟,或者时时操作系统的心跳时钟.这样可以节省MCU的资源,不用另外浪费一个定时器.比如UCOS系统中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick滴答定时器做UCOS的心跳时钟. --->Systick定时器就是一个系统滴答定时器,一个24位的倒计数定时器,记到0时

关于嵌入式学习随笔-&gt;6《NVIC中断优先级管理》

1.中断技术概述 在单片机应用系统中,中断技术主要用于时时检测与控制,也就是要求单片机能感及时地响应中断请求源提出的服务请求,进行快速响应并及时处理,这些工作是由单片机片内的中断系统来实现的.当中断请求源发出中断请求时,如果中断请求得到允许,单片机暂时中止当前正在执行的主程序,转到中断服务处理程序处理中断请求,处理完中断服务请求后,再回到原来被中止的程序处(断点),继续执行被中断的主程序. 中断响应和处理过程如下图所示: 2.中断源 -->M4/M7内核支持256个中断,其中包含了16个内核中断

Linux嵌入式学习-远程过程调用-Binder系统

Binder系统的C程序使用示例IPC : Inter-Process Communication, 进程间通信RPC : Remote Procedure Call, 远程过程调用 这里我们直接只用android系统中已经实现好的Bindrt系统. 具体源代码在 frameworks\native\cmds\servicemanager\目录下. service_manager.c :a. binder_openb. binder_become_context_managerc. binder

关于嵌入式学习随笔-&gt;8《UART串行通信原理》

1.什么是串口 串口是MCU(Microcontroller Unit:微控制单元)重要的外部接口,同时也是软件开发重要的调试手段,现如今基本上所有的MCU都会带有串口.以STM32F767为例,它的串口资源相当丰富,功能也十分强大,STM32F767IGT6最多可以提供8路串口,支持8/16倍过采样.支持自动波特率检测.支持Modbus通信.支持同步单线通信和半双工单线通信.支持LIN.支持调制解调器操作.智能卡协议和IrDA SIR ENDEC规范.具有DMA等. 处理器与外部设备通信的两种

关于嵌入式学习随笔-&gt;13《STM32CubeMX应用4-FreeRTOS的配置》

使用FreeRTOS配置led闪烁 设置创建任务的名字和对应的弱函数名字 生成代码,在弱函数中写入相应的函数,刚生成的函授都在main.c中.点add可以添加任务. 原文地址:https://www.cnblogs.com/vcan123/p/12249995.html

关于嵌入式学习随笔-&gt;14《STM32CubeMX应用5-CAN总线的配置》

STM32CubeMX-CAN总线的配置 配置好后生成代码 关于HAL库中相关文件代码 1.HAL库滤波器设置c文件 #include "bsp_can.h" #include "main.h" extern CAN_HandleTypeDef hcan1; extern CAN_HandleTypeDef hcan2; void can_filter_init(void) { CAN_FilterTypeDef can_filter_st; can_filter_

嵌入式学习路线怎么学,如何学习嵌入式系统

随着互联网的快速发展,嵌入式也越来越火热,更多的人投入到嵌入式开发的行列中来,那么想要学习嵌入式,该从哪里入手学习,嵌入式学习路线图怎么学? 想要学习好嵌入式,想成为嵌入式软件工程师.那么当前企业需要哪些技术呢? 1.嵌入式上层的软件应用开发 需要:精通嵌入式Linux C语言编程.嵌入式Linux C语言数据结构.嵌入式Linux项目开发流程.嵌入式 Linux并发程序设计.嵌入式 Linux应用编程.嵌入式 Linux网络编程.嵌入式数据库开发;若是Android设备的应用开发,那就必须要了

嵌入式学习笔记101-uboot_1.1.6移植(1)

根据前篇博文(嵌入式学习笔记100-uboot1.1.6初体验)最后的结论,现在开始将其实现: a. 修改makefile的CROSS_COMPILE指定编译器 arm-linux-gcc -v –> gcc version 3.4.5 CROSS_COMPILE = /opt/EmbedSky/crosstools_3.4.5_softfloat/gcc-3.4.5-glibc-2.3.6/arm-linux/bin/arm-linux- chmod -R 777 u-boot-1.1.6/