STM32-寄存器时钟配置指南

目录

启动

SystemInit

SetSysClock

 总结


启动

从startup_stm32f0xx.s内的开头的Description可以看到

;* Description        : STM32F051 devices vector table for EWARM toolchain.
;*                      This module performs:
;*                      - Set the initial SP
;*                      - Set the initial PC == iar_program_start,
;*                      - Set the vector table entries with the exceptions ISR 
;*                        address
;*                      - Configure the system clock
;*                      - Branches to main in the C library (which eventually
;*                        calls main()).
;*                      After Reset the Cortex-M0 processor is in Thread mode,
;*                      priority is Privileged, and the Stack is set to Main.

可以得知STM32的启动流程是首先初始化SP(堆栈种指针),初始化PC(程序计数器)指针指向__iar_program_start,设置向量表,初始化时钟系统

可以看到初始化时钟系统是在设置堆栈后,并且运行__iar_program_start设置了硬之后,这是必要的。

startup的汇编内容

这也就是所有MCU启动过程中的第一步:使用汇编语言编写的启动第一部分,设置堆栈和硬件为第二部分的启动铺垫。

第二部是使用C语言编写的启动第二部分

接下来直接跳转到SystemInit

SystemInit

1.在初始化的第一步

void SystemInit (void)
{    /* Set HSION bit */RCC->CR |= (uint32_t)0x00000001;

通过数据手册可以看到

打开了HSI时钟

为什么第一步先要打开HSI时钟呢?

         在许多微控制器中,HSI (High Speed Internal) 时钟通常是第一个被启用的时钟源,这是因为HSI时钟是内置在微控制器内部的,不需要外部晶振或陶瓷谐振器就能工作,因此它是最容易且快速可用的时钟源

2.

/* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */ 
RCC->CFGR &= (uint32_t)0x08FFB80C;
//0000 0100 1000 1111 1111 1011 1000 0000 1100

在时钟配置寄存器中,从低位开始看

SW(00)使用HSI作为系统时钟

SWS(11)不使用系统时钟切换状态

HPRE(0000)SYSCLK不分频

PPRE(0000)HCLK不分频

PLLSRC(1)HSE/PREDIV作为PLL输入时钟

PLLXTPRE(1)HSE 2分频

PLLMUL(1111)PLL倍频系数,PLL输入时钟的16倍频

MCO(100)控制器时钟输出为系统时钟SYSCLK

总体可以看到在systemInit中使用了HSI作为系统时钟,HSE作为PLL输入时钟,HSI提供了即时性,HSE提供了可靠性

3.

  /* Reset HSEON, CSSON and PLLON bits */RCC->CR &= (uint32_t)0xFEF6FFFF;
//1111 1110 1111 0110 1111 1111 1111 1111

从低位开始可以得知

HSION(1)启动HSI振荡器

HSEON(1)启动HSE振荡器

HSEBYP(1)HSE晶体振荡器有旁路

CSSON(0)时钟检测器关闭

PLLON(0)PLL关闭

PLLRDY(1)PLL锁定

注意在这里关闭并且锁定了PLL

4.

  /* Reset HSEBYP bit */RCC->CR &= (uint32_t)0xFFFBFFFF;
//1111 1111 1111 1011 1111 1111 1111 1111

可以看确保HSE振荡器不是在旁路模式下工作,而是使用内部电路来产生时钟信号。

需要使用HSE振荡器作为系统时钟的一部分时。在启动阶段,可能首先使用HSI(高速内部振荡器)作为时钟源,随后配置HSE振荡器,并可能使用HSE作为PLL(锁相环)的输入,以产生更高频率的系统时钟

5.

  /* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */RCC->CFGR &= (uint32_t)0xFFC0FFFF;

将PLLSRC位清零,意味着PLL的输入时钟将默认为HSI经过预分频后的时钟(如果HSI可用并且已经使能)。
将PL LXTPRE位清零,如果选择了HSE作为PLL输入时钟,那么它不会通过预分频器。
将PLLMUL[3:0]位清零,PLL的乘法因子将被重置为其默认或最小值。 

6.

  /* Reset PREDIV1[3:0] bits */RCC->CFGR2 &= (uint32_t)0xFFFFFFF0;

将PREDIV1[3:0]位清零,将预分频器1的分频比数重置为其最小值或默认值,不进行分频

7.

  /* Reset USARTSW[1:0], I2CSW, CECSW and ADCSW bits */RCC->CFGR3 &= (uint32_t)0xFFFFFEAC;

分别配置了USART,I2C,HDMI CEC, ADC时钟源

PCLK作为USART时钟源

HSI作为I2C1的时钟源

HSI/244作为HDMI CEC时钟源

PCLK 2或4分频作为ADC时钟

8.

  /* Reset HSI14 bit */RCC->CR2 &= (uint32_t)0xFFFFFFFE;

禁用了HSI14

HSI14 和 HSI 不完全相同,尽管它们都是 STM32 微控制器内部集成的 RC(电阻-电容)振荡器,但它们有各自的特点和用途:

HSI (High Speed Internal Oscillator)
HSI 是一个典型的内部振荡器,通常提供大约 8 MHz 或 16 MHz 的时钟频率,具体取决于微控制器的型号。HSI 在上电或复位后默认启用,可以作为系统时钟源,为整个微控制器提供时钟信号。
HSI 通常用于在没有外部时钟源时快速启动系统,或者在低功耗模式下作为时钟源。
HSI14 (High Speed Internal 14 MHz Oscillator)
HSI14 提供一个大约 14 MHz 的时钟频率,专为 USB 和某些高级定时器(如 TIM1 和 TIM8)设计。在 STM32 微控制器中,HSI14 主要用于 USB 全速(Full Speed)应用和高级定时器,因为它们需要一个稳定的时钟源。
HSI14 的主要优点在于它可以直接为 USB 设备提供所需的确切时钟频率,而无需额外的时钟调节或 PLL 放大。
总结来说,HSI 和 HSI14 都是内部振荡器,但它们的频率和用途不同。HSI 更倾向于作为系统时钟源,而 HSI14 则专门用于需要特定时钟频率的外设,如 USB 和某些高级定时器。在某些 STM32 系列中,HSI14 不是默认启用的,需要在软件中显式地配置和启用。

9.

  /* Disable all interrupts */RCC->CIR = 0x00000000;

失能所有的中断

之后就进入下面函数来设置时钟预频率 总线预分频 和 FLASH设置

  /* Configure the System clock frequency, AHB/APBx prescalers and Flash settings */SetSysClock();

SetSysClock

这段代码是STM32微控制器中用于配置系统时钟的一个函数,名为`SetSysClock()`。下面是对这段代码详细步骤的解释:

1. 初始化变量:
   `StartUpCounter` 和 `HSEStatus` 分别用于跟踪HSE启动超时计数和HSE状态。

  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

2. 启用HSE:
   通过设置RCC_CR寄存器的HSEON位来启动高速外部振荡器(HSE)。

  /* Enable HSE */    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

3. 等待HSE就绪:
   使用一个循环检查RCC_CR寄存器的HSERDY位,以确认HSE是否已经稳定。如果HSE在指定时间内未能准备好,`StartUpCounter`将增加,直至达到`HSE_STARTUP_TIMEOUT`常量,此时将退出循环。

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

4. 检查HSE状态:
   如果HSE成功启动,`HSEStatus`设置为0x01,否则设置为0x00。

5. 配置Flash预取缓冲区和等待周期:

  if ((RCC->CR & RCC_CR_HSERDY) != RESET){HSEStatus = (uint32_t)0x01;}else{HSEStatus = (uint32_t)0x00;}  


   如果HSE成功启动,接下来配置Flash访问控制寄存器(FLASH_ACR)以启用预取缓冲区,并设置适当的等待周期。

6. 配置AHB和APB总线时钟:
   设置RCC_CFGR寄存器的HPRE和PPRE位,以确定AHB和APB总线的预分频因子。在这个例子中,HCLK(AHB总线时钟)和PCLK(APB总线时钟)都被设置为与SYSCLK(系统时钟)相同。

/* Enable Prefetch Buffer and set Flash Latency */FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;/* HCLK = SYSCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;/* PCLK = HCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;

7. 配置PLL:
   清除RCC_CFGR寄存器中与PLL源选择、PLL输入时钟预分频和PLL倍频因子相关的位。
   配置PLL以使用HSE作为输入时钟源,经过预分频器,并将PLL的倍频因子设置为6,这意味着最终的PLL输出频率将为HSE频率的6倍(假设HSE为8 MHz,PLL输出将为48 MHz)。

  /* PLL configuration = HSE * 6 = 48 MHz */RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6);

8. 启用PLL:

    /* Enable PLL */RCC->CR |= RCC_CR_PLLON;

    通过设置RCC_CR寄存器的PLLON位来启动PLL。

9. 等待PLL就绪:
   使用循环检查RCC_CR寄存器的PLLRDY位,以确认PLL是否已经稳定。

/* Wait till PLL is ready */while((RCC->CR & RCC_CR_PLLRDY) == 0){}

10. 选择PLL作为系统时钟源:
    将RCC_CFGR寄存器的SW位设置为PLL,选择PLL作为系统时钟源。

    /* Select PLL as system clock source */RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

11. 等待PLL作为系统时钟源:
    使用循环检查RCC_CFGR寄存器的SWS位,以确认PLL是否已经被选为系统时钟源。

    /* Wait till PLL is used as system clock source */while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL){}

12. 处理HSE启动失败:
    如果HSE未能启动,函数将到达此处,这里可以添加代码来处理这种错误情况,例如重启系统或进入错误处理程序。

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 */}  

SetSysClock()函数的主要目的是配置系统时钟,使其能够以期望的频率运行。在这个例子中,它首先启用并确认HSE的运行,然后使用HSE作为PLL的输入,通过PLL生成48 MHz的时钟信号,并最终选择PLL作为系统时钟源,从而为整个微控制器提供一个稳定且足够高的时钟频率。

 总结

可以看到STM32的时钟启动方案是在SystemInit中,首先开启了快速易用的HSI作为系统时钟,并且关闭了所有中断,关闭了HSI14,之后在SetSysClock中重启PLL选择了HSE作为系统时钟。

初始化的后期启用HSE并使用PLL来进一步提高系统时钟频率,这种方案有几个明显的好处:

在STM32的启动方案中,首先使用HSI作为系统时钟,然后在初始化的后期启用HSE并使用PLL来进一步提高系统时钟频率,这种方案有几个明显的好处:

1. 快速启动:
   HSI作为内部RC振荡器,不需要外部元件,上电后立即可用这使得系统可以迅速启动并执行基本的初始化,如设置堆栈、初始化硬件寄存器等,而无需等待外部时钟源稳定。

2. 可靠性与容错性:
   HSI提供了系统启动时的可靠时钟源,即使外部晶振或时钟源出现问题,系统仍然可以使用HSI运行,虽然可能在精度和稳定性上有所折衷,但至少可以确保基本的功能性和安全性

3. 性能提升:
   一旦系统初步初始化完成,可以启用更稳定、精度更高的HSE振荡器,并通过PLL进一步提高时钟频率。这可以显著提高系统的性能,因为PLL可以将时钟频率放大到远高于HSI所能提供的频率,从而允许CPU和外设以更高的速度运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/50689.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序支付流程

前端需要做的事情: 生成平台订单:前端调用接口,向后端传递购买的商品信息、收货人信息,(后端生成平台订单,返回订单编号)获取预付单信息:将订单编号发送给后端后,&#x…

Hadoop单机版环境搭建

一 . 案例信息 Hadoop 的安装部署的模式一共有三种: 本地模式,默认的模式,无需运行任何守护进程( daemon ),所有程序都在单个 JVM 上执行。由 于在本机模式下测试和调试 MapReduce 程序较为方便&#x…

leetocde662. 二叉树最大宽度,面试必刷题,思路清晰,分点解析,附代码详解带你完全弄懂

leetocde662. 二叉树最大宽度 做此题之前可以先做一下二叉树的层序遍历。具体题目如下: leetcode102二叉树的层序遍历 我也写过题解,可以先看看学习一下,如果会做层序遍历了,那么这题相对来说会简单很多。 具体题目 给你一棵…

[OJ]水位线问题,1.采用回溯法(深度优先遍历求解)2.采用广度优先遍历求解

1.深度优先遍历 使用回溯法,深度优先遍历利用栈先进后出的特点,在加水控制水量失败时, 回到最近一次可对水进行加水与否的位置1.对于给定水量k,是否在[l,r]之间, 是:是否加水(加水y,用掉x,是否在[l,r]之间)(不加水y,用掉x,是否在[l,r]之间)先尝试加水,如…

NVIDIA全面转向开源GPU内核模块

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

FastAPI(七十八)实战开发《在线课程学习系统》接口开发-- 评论

源码见:"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 梳理下思路 1.判断是否登录 2.课程是否存在 3.如果是回复,查看回复是否存在 4.是否有权限 5.发起评论 首先新增pydantic模型 class Cour…

音视频入门基础:WAV专题(2)——WAV格式简介

注:本文有部分内容引用了维基百科:https://zh.wikipedia.org/wiki/WAV 一、引言 Waveform Audio File Format(缩写WAVE或WAV)是微软与IBM公司所开发在个人电脑存储音频流的编码格式,在Windows平台的应用软件受到广泛的…

AI/机器学习(计算机视觉/NLP)方向面试复习3

1. Pooling 有哪些方式?pytorch的实现? Pooling可以分成:最大池化,平均池化,全局平均池化,随机池化,空间金字塔池化。 1. 最大池化(Max Pooling) 最大池化是最常用的池…

union的特性和大小端

一、union在c和c语言中的特性 1.共享内存空间:union的所有成员共享同一块内存空间。意味着在同一时刻,union 只能存储其成员 中的一个值。当你修改了union中的一个成员,那么其它成员的值也会被改变,因为它们实际上都是指向同一块…

JS逆向高级爬虫

JS逆向高级爬虫 JS逆向的目的是通过运行本地JS的文件或者代码,以实现脱离他的网站和浏览器,并且还能拿到和浏览器加密一样的效果。 10.1、编码算法 【1】摘要算法:一切从MD5开始 MD5是一个非常常见的摘要(hash)逻辑. 其特点就是小巧. 速度快. 极难被破解. 所以,…

skywalking docker部署

skywalking-oap # 拉取skywalking-oap镜像 docker pull apache/skywalking-oap-server:9.7.0# 启动容器 docker run --name oap \ -d \ -p 11800:11800 \ -p 12800:12800 \ apache/skywalking-oap-server:9.7.0skywalking-ui # 摘取skywalking-ui镜像 docker pull apache/sky…

大屏使用技巧——如何实现数据分发

当多个组件需共用同一数据源时,为了减少重复请求,需要进行数据分发。那如何实现接一次数据就能让多个组件映射同一数据源中的不同数据字段呢? 实现思路 目标组件的静态数据中添加标记字段,数据过滤器内通过 data 参数获取到对应…

加密micropython写的程序为.mpy的方法

2024年7月26日 用虚拟机安装一个Linux,本例为CentOS7的Linux系统。 1.保证Linux能够连接网络。 2.进入root用户,使用下面的命令行安装gcc编译器: yum install gcc 3.安装完成后,查看gcc是否安装成功,用下面的命令…

家政项目小程序的设计

管理员账户功能包括:系统首页,个人中心,用户管理,家政人员管理,家政服务管理,咨询信息管理,咨询服务管理,家政预约管理,留言板管理,系统管理 微信端账号功能…

前端开发知识-vue

大括号里边放键值对,即是一个对象。 一、vue可以简化前端javascript的操作。 主要特点是可以实现视图、数据的双向绑定。 使用vue主要分为三个步骤: 1.javascript中引入vue.js 可以src中可以是vue的网址,也可以是本地下载。 2.在javasc…

FastAPI(七十九)实战开发《在线课程学习系统》接口开发-- 加入课程和退出课程

源码见:"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 加入课程 我们先看下加入课程 1.是否登录 2.课程是否存在 3.是否已经存在 4.添加 首先实现逻辑 def get_student_course(db: Session, course: int…

C++——QT:保姆级教程,从下载到安装到用QT写出第一个程序

登录官网,在官网选择合适的qt版本进行下载 这里选择5.12.9版本 点击exe文件下载,因为服务器在国外,国内不支持,所以可以从我的网盘下载 链接: https://pan.baidu.com/s/1XMILFS1uHTenH3mH_VlPLw 提取码: 1567 --来自百度网盘超级…

本地部署Graphhopper路径规划服务(graphhopper.sh启动版)

文章目录 文章参考源码获取一、配置Java环境变量二、配置Maven环境变量三、构建graphhopper步骤1. 下载数据2. 配置graphhopper配置文件config-example.yml3. 在项目中启动命令行执行./graphhopper.sh build3.1|、遇到的问题3.1.1、pom.xml中front-maven-plugin-无法下载npm6.1…

土耳其云手机提升TikTok电商效率

在数字化飞速发展的今天,TikTok不仅是一个社交平台,更是一个巨大的电商市场。随着TikTok电商功能在全球范围内的扩展,土耳其的商家和内容创作者正面临着前所未有的机遇。本文将详细介绍土耳其云手机怎样帮助商家抓住机遇,实现业务…

项目都做完了,领导要求国际化????--JAVA后端篇

springboot项目国际化相信各位小伙伴都会,很简单,但是怎么项目都做完了,领导却要求国际化文件就很头疼了 国际化的SpringBoot代码: 第一步:创建工具类 /*** 获取i18n资源文件** author bims*/ public class Message…