目录

1.  SystemInit()及相关函数解析

1.1  SystemInit() 函数及注释

1.2  SystemInit() 函数的主要作用

1.3  SetSysClock() 函数

2.  STM32F411时钟设置代码

3.  M,N和P值的设置

4.  STM32F411使用外部时钟源HSE的方法

4.1   HSE的启动代码

4.2   关键代码解释

5.  STM32F411使用时钟源的切换及仿真

5.1  使用外部晶振HSE

5.2  使用内部晶振HSI


在标准库的system_stm32f4xx.c注释中,可以确定STM32F410和STM32F411默认的系统时钟源为PLL(HSI):

本篇的主要工作是将时钟源由HSI改为HSE,并通过仿真确定使用内部晶振和外部晶振时的系统时钟频率分别是多少。

1.  SystemInit()及相关函数解析

SystemInit() 是整个设置系统时钟的入口函数。如果我们使用ST提供的 STM32F4 固件库的话,会在系统启动之后先执行SystemInit() 函数实现系统相关时钟的设置。这个过程是在启动文件 startup_stm32f40_41xxx.s 中间设置的,启动代码如下:

1.1  SystemInit() 函数及注释

void SystemInit(void)
{/* 复位 RCC 时钟配置为默认配置  ------------*//* 打开 HSION 位  */RCC->CR |= (uint32_t)0x00000001;/* 复位 CFGR 寄存器 *//* *************************************** CFGR register bit1:0 * 由软件置1和清零,用于选择系统时钟源* 00:选择HSI作为系统时钟* 01:选择HSE作为系统时钟* 10:选择PLL作为系统时钟* 11:不允许* **************************************/RCC->CFGR = 0x00000000;/* 复位 HSEON, CSSON and PLLON 位  */RCC->CR &= (uint32_t)0xFEF6FFFF;/* 复位寄存器 PLLCFGR  */RCC->PLLCFGR = 0x24003010;/* 复位 HSEBYP 位  */RCC->CR &= (uint32_t)0xFFFBFFFF;/* 关闭所有中断  */RCC->CIR = 0x00000000;/* Configure the System clock source, PLL Multiplier and Divider factors, AHB/APBx prescalers and Flash settings ----------------------------------*/SetSysClock();/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAMSCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#elseSCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}

1.2  SystemInit()函数的主要作用

复位 PLLCFGR,CFGR 寄存器,同时通过设置 CR 寄存器的 HSI 时钟使能位来打开 HSI 时钟。默认情况下如果 CFGR 寄存器复位,那么是选择 HSI 作为系统时钟,当低两位配置为 00 的时候(复位之后),会选择 HSI 振荡器为系统时钟。也就是说,调用 SystemInit 函数之后,首先是选择 HSI 作为系统时钟。

1.3  SetSysClock() 函数

SetSysClock() 函数中包含有大量条件编译,因为使用的是STM32F411芯片,所以找到STM32F411xE的条件编译:

该条件编译下又跟了一个新的条件编译语句 :

#if defined(USE_HSE_BYPASS)

在system_stm32f4xx.c中有如下几句:

如果需要通过STM32F103的STLINK MCO引脚使用HSE旁路对STM32F410xx和STM32F411xE提供时钟,则取消注释以下行,频率固定在8MHz不能修改。

HES Bypass即HSE旁路时钟源,也就是不使用晶振/陶瓷振荡器,直接通过外部提供一个可靠的4~26MHz时钟作为HSE。由于我们使用的是外部晶振,不使用旁路时钟,所以不要取消注释。

2.  STM32F411时钟设置代码

SetSysClock()函数中关于STM32F411的时钟设置代码如下:

#else /* HSI will be used as PLL clock source *//* Select regulator voltage output Scale 1 mode */RCC->APB1ENR |= RCC_APB1ENR_PWREN;PWR->CR |= PWR_CR_VOS;/* HCLK = SYSCLK / 1*/RCC->CFGR |= RCC_CFGR_HPRE_DIV1;/* PCLK2 = HCLK / 2*/RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;/* PCLK1 = HCLK / 4*/RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;/* Configure the main PLL */RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | (PLL_Q << 24); /* Enable the main PLL */RCC->CR |= RCC_CR_PLLON;/* Wait till the main PLL is ready */while((RCC->CR & RCC_CR_PLLRDY) == 0){}/* Configure Flash prefetch, Instruction cache, Data cache and wait state */FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS; //FLASH_ACR_LATENCY_2WS
//  /* Select the main PLL as system clock source */RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));RCC->CFGR |= RCC_CFGR_SW_PLL;/* Wait till the main PLL is used as system clock source */while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);{}

该段代码表明STM32F411芯片默认是使用HSI作为时钟源的,也就是走的以下这条路:(编写代码使用的是标准库,CubeMX时钟树因为看起来更直观,仅用于参考)

3.  PLL_M,PLL_N和PLL_P值的设置

PLL时钟输出频率计算公式:

f(VCO 时钟) = f(PLL 时钟输入) × (PLLN / PLLM)

f(PLL 常规时钟输出) = f(VCO 时钟) / PLLP

f(USB OTG FS, SDIO, RNG 时钟输出) = f(VCO 时钟) / PLLQ

所以在使用PLL时,系统时钟的计算方式为:

SystemCoreClock = 时钟源 /M *N /P

关于PLL_M,PLL_N和PLL_P的设置同样在system_stm32f4xx.c中,代码如下:

因为我使用的是外部25MHz的晶振,设置的PLL_M,PLL_N,PLL_P值分别为13,104和2,有:

SystemCoreClock = 25/13*104/2 = 100MHz

但是由于没有开启外部时钟,系统启动时默认使用HSI作为时钟源,所以SystemCoreClock变成了:

SystemCoreClock = 16/13*104/2 = 64MHz

所以理论上的定时1秒实际约为1.56秒。

注意:时钟源/M的值要落在1~2之间,所以M值也需要根据晶振的频率来确定,不能随意取值。

4.  STM32F411使用外部时钟源HSE的方法

4.1   HSE的启动代码

将SetSysClock()函数中原有关于STM32F411条件编译部分使用HIS启动的代码注释掉,并添加以下代码:

/*******************************************************************************             如果不使用HSE_BYPASS模式,依然使用HSE作为时钟源*              PLL (clocked by HSE) used as System clock source                ******************************************************************************/__IO uint32_t StartUpCounter = 0, HSEStatus = 0;/* Enable HSE and HSE BYPASS */RCC->CR |= (uint32_t)RCC_CR_HSEON;/* Wait till HSE is ready and if Time out is reached exit */do{HSEStatus = RCC->CR & RCC_CR_HSERDY;StartUpCounter++;} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));if ((RCC->CR & RCC_CR_HSERDY) != RESET){HSEStatus = (uint32_t)0x01;}else{HSEStatus = (uint32_t)0x00;}if (HSEStatus == (uint32_t)0x01){/* Select regulator voltage output Scale 1 mode */RCC->APB1ENR |= RCC_APB1ENR_PWREN;PWR->CR |= PWR_CR_VOS;/* HCLK = SYSCLK/1     */RCC->CFGR |= RCC_CFGR_HPRE_DIV1;/* PCLK2 = HCLK/1    */RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;/* PCLK1 = HCLK/2   */RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;/* Configure the main PLL */RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);/* Enable the main PLL */RCC->CR |= RCC_CR_PLLON;/* Wait till the main PLL is ready */while((RCC->CR & RCC_CR_PLLRDY) == 0){}/* Configure Flash prefetch, Instruction cache, Data cache and wait state */FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;/* Select the main PLL as system clock source */RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));RCC->CFGR |= RCC_CFGR_SW_PLL;/* Wait till the main PLL is used as system clock source */while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);{}}

4.2   关键代码解释

RCC->CR |= (uint32_t)RCC_CR_HSEON;

作用:打开HSE振荡器

由宏定义

#define  RCC_CR_HSEON  ((uint32_t)0x00010000)

可知,该行代码是将CR寄存器的第16位置1,查看CR寄存器各个位的描述可知,该行代码的作用是打开HSE振荡器。

 do{HSEStatus = RCC->CR & RCC_CR_HSERDY;StartUpCounter++;} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

作用:等待HSE时钟稳定。

HSE振荡器开启时会有波动,所以需要一端时间等待其稳定,当HSE振荡器稳定后,CR寄存器的第17位会置1,表示HSE时钟已准备就绪。

    /* HCLK = SYSCLK/1  */RCC->CFGR |= RCC_CFGR_HPRE_DIV1;/* PCLK2 = HCLK/1    */RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;/* PCLK1 = HCLK/2       */RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;

作用:设置HCLK时钟为系统时钟(100M),PCLK2时钟为系统时钟(100M),PCLK1时钟为系统时钟的2分频(50MHz)

对应CubeMX时钟树:

RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |                         (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);

作用:将我们设定好的M,N,P,Q值写入到PLLCFGR寄存器。

Q值适用于USB OTG FS、 SDIO 和随机数发生器时钟。为使 USB OTG FS 能够正常工作,需要 48 MHz 的时钟。对于 SDIO 和随即数生成器,频率需要低于或等于 48 MHz 才可正常工作。我们并没有使用到该参数。

该句含义是在PLLCFGR寄存器中写入如下值:0000 0100 0100 0000 0001 1010 0000 1101

查看PLLCFGR寄存器各个位的描述可知,该句功能为:

  1. 设置PLLQ=4;
  2. 选择HSE时钟作为PLL时钟输入;
  3. 设置PLLP=2;
  4. 设置PLLN=104,
  5. 设置PLLM=13.

对应CubeMX时钟树:

RCC->CR |= RCC_CR_PLLON;

作用:打开主PLL

由宏定义

#define  RCC_CR_PLLON   ((uint32_t)0x01000000)

可知,该行代码将CR寄存器第24位置1,查看CR寄存器各个位的描述可知,该行代码的作用是主PLL使能。

RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;

作用:选择主PLL作为系统时钟源

由宏定义

#define  RCC_CFGR_SW            ((uint32_t)0x00000003)

#define  RCC_CFGR_SW_PLL   ((uint32_t)0x00000002)

可知,是将CFGR寄存器的位1:0置为10,即选择PLL作为系统时钟。

5.  STM32F411使用时钟源的切换及仿真

在主函数中加入如下两行代码,然后进行仿真。

RCC_ClocksTypeDef RCC_ClocksStatus;RCC_GetClocksFreq(&RCC_ClocksStatus);

5.1  使用外部晶振HSE

1)HSE=25MHz;M=13;N=104;P=2

计算值:SYSCLK=25/13*104/2=100MHz

仿真结果如下:

系统时钟频率SYSCLK,HCLK频率,PCLK2频率为0x05F5E0D0,十进制为99.999952MHz

PCLK1时钟是SYSCLK的2分频,为0X02FAF068,十进制为49.999976MHz

2)  HSE=25MHz;M=16;N=128;P=2

计算值:SYSCLK=25/16*128/2=100MHz

仿真结果如下:

系统时钟频率SYSCLK,HCLK频率,PCLK2频率为0x05F5E100,十进制为100MHz

PCLK1时钟是SYSCLK的2分频,为0X02FAF080,十进制为50MHz

5.2  使用内部晶振HSI

1)HSI=16MHz;M=8;N=100;P=2

计算值: SYSCLK=16/8*100/2=100MHz

仿真结果如下:

系统时钟频率SYSCLK,HCLK频率,PCLK2频率为0x05F5E100,十进制为100MHz

PCLK1时钟是SYSCLK的2分频,为0X02FAF080,十进制为50MHz

2)HSI=16MHz;M=13;N=104;P=2

计算值:SYSCLK=16/13*104/2=64MHz

仿真结果如下:

系统时钟频率SYSCLK,HCLK频率,PCLK2频率为0x03D08FF4,十进制为63.999988MHz

PCLK1时钟是SYSCLK的2分频,为0X01E847FA,十进制为31.999994MHz

由以上仿真结果可知,虽然通过公式计算出最终的SYSCLK都是100MHz,但是选择不同的M,N值,仿真频率的最终结果会略微出现偏差,所以M要尽量选择2的整数倍。因此重新选择M=16,N=128,P=2。

STM32F411的HSI和HSE启动方式切换及仿真相关推荐

  1. 10g物理standby主备switchover方式切换详述

    以下给大家展现一下10g物理standby主备之间通过switchover方式进行切换的详细步骤,供参考. 1.主库检查是否为"TO STANDBY"状态,若不是,需要重新启动一下 ...

  2. stm32运行java_STM32之——3种启动方式学习

    参考: https://blog.csdn.net/wwt18811707971/article/details/78678059 所谓启动,一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第 ...

  3. service两种启动方式的区别

    service,中文名称是服务,服务是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互,而且还要长期运行的任务. 服务运行不依赖于任何用户界面,即使程序切换到后台,或者 ...

  4. Wayland/Weston 启动方式简介

    前言 本文简单介绍 Weston 常用的几种 backend 启动方式.目前最新的 Weston 8.0.0 支持如下几种 backend: drm-backend fbdev-backend hea ...

  5. 库卡机器人bco运动_库卡KUKA机器人四种启动方式

    库卡机器人四大启动方式说明 1.自动运行方式 (1)自动运行说明 库卡机器人处于自动运行时,先要在手动状态时候选择需要运行的程序,然后通过钥匙开关切换机器人到自动运行状态,给机器人上电,然后启动机器人 ...

  6. 库卡机器人bco运动_库卡机器人四种启动方式说明——库卡机器人

    一.库卡机器人四大启动方式说明 1.自动运行方式 (1)自动运行说明 库卡机器人处于自动运行时,先要在手动状态时候选择需要运行的程序,然后通过钥匙开关切换机器人到自动运行状态,给机器人上电,然后启动机 ...

  7. linux--切换ubuntu启动方式 及 还原配置

    linux--切换ubuntu启动方式 及 还原配置 1 切换ubuntu启动方式 1.1 ubuntu 14 1.2 ubuntu 18 2 还原配置 2.1 tty1-6进入 2.2 单用户模式( ...

  8. 野火linux启动方式选择拨码开关,embed_linux_tutorial

    硬件资源介绍--野火EBF 6ULL 开发板 在PC上通过Ubuntu熟悉了Linux系统的基础使用后,我们尝试体验一下在嵌入式开发板上的Linux系统.先来了解一下开发板的硬件资源. 关于开发板的硬 ...

  9. 【教程】在UEFI启动方式下,通过GRUB2引导,直接从硬盘ISO文件安装Windows10和Ubuntu双系统

    本文为作者原创,允许转载,但必须注明原文地址: https://www.cnblogs.com/byronxie/p/9949789.html 动机 最近在自学MIT6.828 Operating S ...

最新文章

  1. Android 之窗口小部件详解--App Widget
  2. CentOS下TPC-W安装
  3. 95. Leetcode 1049. 最后一块石头的重量 II (动态规划-背包问题)
  4. electron ipcRenderer渲染进程发送事件, ipcMain主进程监听事件
  5. 关于VS无法启动程序,系统找不到指定的路径,最标准的回答
  6. js函数提示 vscode_工欲善其事,必先利其器,VSCode高效插件
  7. Gensee Android SDK(一)组成结构
  8. 教你七招提高.NET网站性能
  9. 安全多方计算(MPC)从入门到精通:简易教程
  10. 计算机组成原理综合题,2013计算机组成原理复习题.doc
  11. 补充轻量级持久层V2版本的测试页面模板与实体类模板
  12. 用计算机弹雅俗共赏,聊聊雅俗共赏:钢琴、饺子和面
  13. 莫烦python学习笔记之numpy.array,dtype,empty,zeros,ones,arrange,linspace
  14. 一大波Java来袭(四)String类、StringBuilder类、StringBuffer类对照
  15. 快速获得Google Chrome最新版本
  16. html 预加载图片,实现网页图片预加载的几个方法
  17. 一个android本地txt阅读器的思路与实现
  18. 阿里月薪6W招程序员,看到要求我傻眼了!
  19. Star Way To Heaven题解(防题目重复)
  20. 玩转MongoDB4.0(MongoDB基础总结)

热门文章

  1. YOLO5 旋转模型图片标注及训练
  2. 购物车实现原理(干货满满)
  3. 数据结构 图的邻接表和邻接矩阵实现———c语言
  4. js jq 图片上传功能
  5. 向Gitlab项目工程提交代码时出现无法推送的问题
  6. 与广东未来科技:怀揣初心,并肩同行
  7. SQL存储过程【笔记】一个较简单的库存月结
  8. BZOJ 2081 Beads Hash
  9. 用户sa登陆失败时,应该如何解决
  10. android 小米闹钟,为了叫不醒的早上,我买了个闹钟,小米小爱智能闹钟开箱