【STM32】晶振,主时钟,外设频率介绍
首先,我用的是STM32F407,下方所有图片都是出自这芯片的文档,如果型号和我不同,需要找到对应的芯片说明文档,也许会有出入
先看一张时钟图

这里会着重说明高速的部分,低速(不管内部还是外部)只给RTC时钟使用
题外话,MCO1、MCO2,你可以往外面输出时钟
以下开始正题
图片红圈处是主时钟,供给许多东西使用,例如外设(UART、SPI...),简直就像大型音乐演奏的指挥者
所有你想用到的外设,初始化第一步,就是使能时钟(向主时钟请求)
例如下方的SPI初始化代码
void SPI3_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);//使能GPIOC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);//使能SPI3时钟 ...
...
}
是不是能看到clock(时钟)的字样?又看到ENABLE(使能)了吧?
再回到时钟图,红圈处的主时钟,总共有三种来源
【1】HSI内部高速晶振
【2】HSE外部高速晶振
【3】PLL锁相环
其中PLL的来源,还是要由『内部高速晶振』或是『外部高速晶振』提供
主时钟的SW,可以选择来源是三种的哪一种
内部晶振固定是16M,外部的话,要取决你接的晶振是多少,当然,有个范围,图上写了,4M~26M
选择了『内部高速晶振』或是『外部高速晶振』,这没什么好说的,主时钟的频率就和晶振一样
下面说明,如果选择了PLL,应该要设定哪些东西
首先很重要的一点,提供给PLL的时钟一定要是1M,不可以是2M、4M...之类的
所以呢,就有了图上的绿圈,除M(代码为PLL_M)
如果是内部晶振(16M)提供,这个除M的值,就必须设16,16M / 16 = 1M
如果是外部晶振提供,假设接了8M的晶振,这个除M的值,就要设8,8M / 8 = 1M
这是规定
最终,PLL就一定会得到1M的时钟,然后才开始做倍频的工作
倍频,会经过一个乘N(代码为PLL_N)
之后,再经过一个除P(代码为PLL_P),分频后,就会把结果提供给主时钟了
分频Q(除Q)(代码为PLL_Q),会给另一个东西提供时钟,其他的忘了,但是SDIO就是其中一个
下面来看代码
『代码图片』和『代码块』,都是出自sysytem_stm32f4xx.c
代码块的代码有点多,但是只要认真看,其实会发现,ST把所有STM32F4系列的都加进来了
只要看和自己有关的就好了,我一开始也说了,我用的是STM32F407,我只关注宏定义『STM32F40_41xxx』即可

static void SetSysClock(void)
{
#if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx) || defined (STM32F401xx)
/******************************************************************************/
/* PLL (clocked by HSE) used as System clock source */
/******************************************************************************/
__IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* Enable HSE */
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; #if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx)
/* PCLK2 = HCLK / 2*/
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; /* PCLK1 = HCLK / 4*/
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
#endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx */ #if defined (STM32F401xx)
/* PCLK2 = HCLK / 2*/
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; /* PCLK1 = HCLK / 4*/
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
#endif /* STM32F401xx */ /* 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)
{
} #if defined (STM32F427_437xx) || defined (STM32F429_439xx)
/* Enable the Over-drive to extend the clock frequency to 180 Mhz */
PWR->CR |= PWR_CR_ODEN;
while((PWR->CSR & PWR_CSR_ODRDY) == 0)
{
}
PWR->CR |= PWR_CR_ODSWEN;
while((PWR->CSR & PWR_CSR_ODSWRDY) == 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;
#endif /* STM32F427_437x || STM32F429_439xx */ #if defined (STM32F40_41xxx)
/* 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;
#endif /* STM32F40_41xxx */ #if defined (STM32F401xx)
/* 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;
#endif /* STM32F401xx */ /* 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);
{
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
#elif defined (STM32F411xE)
#if defined (USE_HSE_BYPASS)
/******************************************************************************/
/* 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 | RCC_CR_HSEBYP); /* 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 / 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) |
(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);
{
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
#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_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);
{
}
#endif /* USE_HSE_BYPASS */
#endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx */
}

在函数setSysClock(设置系统时钟)里面,一开始就会发现一行代码
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
这是选择使用HSE(外部晶振),但,这还不是主时钟的三个选择之一,只是选择制造时钟的来源,下面的代码才是真正决定主时钟是使用HSE、HSI、PLL哪一个
/* Select the main PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
上面写着RCC_CFGR_SW_PLL,配合文章一开始的图片,红圈处,不是有个SW吗?这就是选择主时钟来源
再结合这三行代码『RCC->CR』『RCC->CFGR』这些代码(注释 『/* Enable HSE */ 』这些不算代码啊)
意思是:我使用外部晶振,然后外部晶振提供给PLL去倍频,最后主时钟采用PLL提供的频率
在函数setSysClock里面,还可以找到关于PLL的设置,也就是
PLL_M、PLL_N、PLL_P
这些功用之前也说了,对照一开始的图片,找到PLL里面的『除M』『乘N』『除P』
也就理解了
在STM32F407里面,没有关于/R的东西,看看其他F4系列的,应该能找到
接下来,我会把重点放在外设上,也就是红圈处主时钟的右边,APBx分频这部分(有些外设需要的时钟不同,但主时钟只能提供一种频率,这时要靠各自的分频,实现不同的频率)
但是在APBx之前,还有一个AHB分频
整个思路是这样的,假设我要用SPI:『主时钟提供时钟』->『经过AHB分频』->『经过APBx分频,APBx总线拥有自己的一个时钟频率』->『SPI自己还可以再一次分频』
相关代码如下(此代码还是上面那个很长的代码,里面的一小部分,注释是我自己加上的)
/* HCLK = SYSCLK / 1*/
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB分频 #if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx)
/* PCLK2 = HCLK / 2*/
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2分频 /* PCLK1 = HCLK / 4*/
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1分频 #endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx */
这里就看出了两个分频了,一个是AHB,另一个是APBx(APB1和APB2暂且算同一个)
上面以SPI为例,那么『SPI自己还可以再一次分频』,这代码在哪呢?
这就要自己写SPI初始化函数了,设定相关的参数,其中一条,就是分频,如下图绿色底那行所示

在STM32F4系列,有两个APB,分别是APB1、APB2
APB是总线,至于哪些外设,分别是挂在哪个总线上呢?
我截取一张头文件stm32f4xx_rcc.h的图片来看,就知道了

这就是外设的分布,例如串口2(USART2),在410行,也就是挂在APB1上
串口1(USART1),在433行,它就是挂在APB2上了
文章上面一点,我不是贴了一个SPI 1的初始化代码的图片吗(图片显示行号4~39)?
SPI 1是挂在APB2上的,时钟使能写的函数名称和参数,都写着APB2的
另外,APB2的上限速度,是比APB1块的
一开始的时钟图,也写了APB2的上限是84M,比APB1整整多了一倍
总算结束了,但是请注意,本篇是以STM32F407为例,不同型号的芯片,最好还是参考各自的说明文档,以免出错
谢谢你的观看,如果有错,也希望能提出来,一起交流进步
【STM32】晶振,主时钟,外设频率介绍的更多相关文章
- stm32 晶振不起振
1. STM32f103有内部晶振.刚刚上电时,所有Clock都是源于内部晶振,所以当片内没有程序或内部程序没有使能外部晶振时,外部晶振是不会起振的.2. STM32f103有内部复位电路,只有当检测 ...
- STM32 晶振 系统时钟8MHZ和72Mhz的原因
首先问题描述: 1.自己画的板子和淘宝买的最小系统板 系统时钟不一致,自己画的是8Mhz,HSE失败:最小系统板72Mhz 2.最小系统板在程序1运行仿真的时候,查看peripherals->P ...
- 石英晶振频率后面带的PPM是什么单位
PPM是石英晶振的基本单位之一,表示晶振的精度和相对偏差, PPM代表着百万分之一,它表明晶体的频率可能会偏离标称值多少.晶振频率是以MHZ(10的6次方)和KHZ(10的3次方)为基本单位的,标称频 ...
- MCU的心脏-晶振
晶振是石英晶体谐振器(quartzcrystal oscillator)的简称,它被称为电路系统的心脏,它为整个系统提供"心跳".中央处理器(CPU)一切指令的执行都是建立在这个& ...
- 痞子衡嵌入式:浅谈i.MXRT10xx系列MCU外接24MHz晶振的作用
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT10xx系列MCU外接24MHz晶振的作用. 痞子衡之前写过一篇关于时钟引脚的文章 <i.MXRT1xxx系列MCU时 ...
- 时钟晶振32.768KHz为什么是15分频?
实时时钟晶振为什么选择是32768Hz的晶振,在百度上搜索的话大部分的答案都是说2的15次方是32768,使用这个频率的晶振,人们可以很容易的通过分频电路得到1Hz的计时脉冲.但是话有说回来了,2的整 ...
- [转载]关于晶振ppm
写得不错,小白的我学习了 原文地址:关于晶振ppm作者:thomaswangbj XXppm就是说频率的误差=(xx/百万)*振荡器的标称频率 eg1:120ppm,27M的晶振,频率的误差 = 12 ...
- 关于STM32外接4—16MHz晶振主频处理方法
由于STM32F10x库官方采用的是默认的外接8MHz晶振,因此造成很多用户也采用了8MHz的晶振,但是,8MHz的晶振不是必须的,其他频点的晶振也是可行的,只需要在库中做相应的修改就行. 在论 ...
- 换晶振导致stm32串口数据飞码的解决办法(补充)
今天(2014.4.21)把stm32f107的程序下载到stm32f103的板子上,发现串口收不到数据,突然想起晶振频率没有修改,#define HSE_VALUE ((uint32_t)13 ...
随机推荐
- 【数据结构&算法】02-复杂度分析之执行效率和资源消耗
目录 前言 复杂度 分析方法 大 O 复杂度表示法 例子-评估累加和的各种算法执行效率 算法 1(for 循环): 算法 2(嵌套 for 循环): 大 O 表示 时间复杂度分析 关注执行最多的一段代 ...
- Laravel/Lumen 分组求和问题 where groupBy sum
在Laravel中使用分组求和,如果直接使用Laravel各数据库操作方法,应该会得出来如下代码式: DB::table('table_a') ->where('a','=',1) ->g ...
- 攻防世界 WEB 高手进阶区 upload1 Writeup
攻防世界 WEB 高手进阶区 upload1 Writeup 题目介绍 题目考点 文件上传漏洞 一句话木马 中国菜刀类工具的使用 Writeup 使用burpsuite抓包 可见只是对上传文件的后缀进 ...
- vue脚手架项目如何在控制台打印组件实例
需要在浏览器上安装拓展程序vue开发工具,安装好后在控制台上输入$vm即可打印vue组件实例对象. Vue2.3开发工具都有,可自行下载 百度网盘链接提取码:si5l
- geoserver控制服务访问权限-类似百度地图的key
目录 缘起 可行性分析 如何实现key验证访问 如何控制key能访问哪些地图服务? 如何实现服务器ip白名单 流程梳理 申请key 访问地图 实施步骤 拦截器设置 配置key验证规则 配置服务拦截规则 ...
- 如何系统学习C 语言(下)之 文件篇
我们所编写的程序源代码以及编译生成的可执行文件都属于文件.所以文件的实质就是存储在外部存储介质上的一段连续的二进制数据. c 语言程序对文件的处理采用文件流的形式,程序运行在内存中,而文件是存储在外部 ...
- Java学习(九)
今天先学习了内联框架的知识,使用iframe的标签,还有超链接的知识. 做了个小实践 <!DOCTYPE html> <head> <meta charset=" ...
- Jquery的常用使用方法
1.获取单个checkbox选中项(三种写法)$("input:checkbox:checked").val()或者$("input:[type='checkbox']: ...
- CefSharp-基于C#的客户端开发框架技术栈开发全记录
CefSharp简介 源于Google官方 CefSharp用途 CefSharp开发示例 CefSharp应用--弹窗与右键 不弹出子窗体 禁用右键 CefSharp应用--High DPI问题 缩 ...
- 直播预告 | 猪齿鱼V1.1发布,线上新功能详解邀您参加
2021年11月11日,数智化效能平台猪齿鱼 Choerodon发布 V1.1版本,多项功能新增或优化,多管齐下,全面提升团队工作效能! 通过提供体系化方法论和协作.测试.DevOps及容器工具,猪齿 ...