STM32F103——时钟配置

目录

1、认识时钟树

1.1 什么是时钟树

1.2 时钟系统解析

1.2.1 时钟源

1.2.2 锁相环PLL

1.2.3 系统时钟SYSCLK

1.2.4 时钟信号输出MCO 

2、如何修改主频 

2.1 STM32F1时钟系统配置

2.2 STM32F1 时钟使能和配置


下列进行举例的开发板是原子哥的战舰开发板STM32F103ZET6

1、认识时钟树

1.1 什么是时钟树

在学习时钟之前,我们得先要了解什么是时钟? 

简单来说,时钟是具有周期性的脉冲信号,最常用的是占空比50%的方波。如下图:

时钟是单片机的脉搏,搞懂时钟走向及关系,对单片机使用至关重要。

时钟树概念:STM32F103的时钟树是指该微控制器的整体时钟系统架构。它由一系列时钟源、时钟分频器和时钟分配器组成,用于提供不同模块的时钟信号,并确保它们以正确的速率和时间关系运行。

为什么是时钟树而不是时钟呢?一个 MCU 越复杂,时钟系统也会相应地变得复杂,如 STM32F1 的时钟系统比较复杂,不像简单的 51 单片机一个系统时钟就可以解决一切。对于 STM32F1 系列的芯片,正常工作的主频可以达到 72Mhz,但并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及 RTC 只需要几十 kHZ 的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。由一系列时钟源通过时钟配置器提供各个模块时钟信号,犹如一颗树。如下图:

1.2 时钟系统解析

STM32F1 时钟系统图如下: 

图中已经把我们主要关注几处标注出来。部分表示其它电路需要的输入源时钟信号;B 为一个特殊的振荡电路“PLL”,由几个部分构成;C 为我们重点需要关注的 MCU 内的主时钟“SYSCLK”;AHB 预分频器将 SYSCLK 分频或不分频后分发给其它外设进行处理,包括到 F 部分的 Cortex-M 内核系统的时钟。D、E 部分别为定时器等外设的时钟源APB1/APB2。G 是 STM32 的时钟输出功能。

1.2.1 时钟源

对于 STM32F1,输入时钟源(Input Clock)主要包括 HSI,HSE,LSI,LSE。其中,从时钟频率来分可以分为高速时钟源和低速时钟源,其中 HSI、HSE 高速时钟,LSI 和 LSE 是低速时钟。从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时钟源,其中 HSE 和 LSE 是外部时钟源;其他是内部时钟源,芯片上电即可产生,不需要借助外部电路。

下面我们看看 STM32 的时钟源。

(1)2个外部时钟源:

高速外部振荡器 HSE (High Speed External Clock signal):外接石英/陶瓷谐振器,频率为4MHz~16MHz。正点原子STM32F103战舰开发板使用的是8MHz。

低速外部振荡器 LSE (Low Speed External Clock signal):外接 32.768kHz 石英晶体,主要作用于 RTC 的时钟源。
(2)2个内部时钟源:
高速内部振荡器 HSI(High Speed Internal Clock signal):由内部 RC 振荡器产生,频率为 8MHz。
低速内部振荡器 LSI(Low Speed Internal Clock signal) :由内部 RC 振荡器产生,频率为 40kHz,可作为独立看门狗的时钟源。

芯片上电时默认由内部的 HSI 时钟启动,如果用户进行了硬件和软件的配置,芯片才会根据用户配置调试尝试切换到对应的外部时钟源,所以同时了解这几个时钟源信号还是很有必要的。如何设置时钟的方法会在后续提到。

1.2.2 锁相环PLL

锁相环是自动控制系统中常用的一个反馈电路,在 STM32 主控中,锁相环的作用主要有两个部分:输入时钟净化和倍频。前者是利用锁相环电路的反馈机制实现,后者我们用于使芯片在更高且频率稳定的时钟下工作。

在 STM32 中,锁相环的输出也可以作为芯片系统的时钟源。使用锁相环时只需要进行三个部分的配置。PLL 作为系统时钟源的配置部分图如下:

(1)PLLXTPRE:HSE 分频器作为 PLL 输入 (HSE divider for PLL entry)

在标注为①的地方,它专门用于 HSE,ST 设计它有两种方式,并把它的控制功能放在 RCC_CFGR 寄存器中。

PLLXTPRE:HSE分频器作为PLL输入(HSE divider for PLL entry)由软件置“1”或清“0”来分频HSE后作为PLL输入时钟。只能在关闭PLL时才能写入此位。

0:HSE不分频(1分频)

1:HSE 2分频

经过 HSE 分频器处理后的输出振荡时钟信号比直接输入的时钟信号更稳定。

(2)PLLSRC:PLL 输入时钟源 (PLL entry clock source)

图中②表示的是 PLL 时钟源的选择器。

PLLSRC:PLL输入时钟源(PLL entry clock source)由软件置“1”或清“0”来选择PLL输入时钟源。只能在关闭PLL时才能写入此位。

0:HSI振荡器时钟经2分频后作为PLL输入时钟

1:HSE时钟作为PLL输入时钟。

它有两种可选择的输入源:设计为 HSI 的二分频时钟,另一个是 A 处的 PLLXTPRE 处理后的 HSE 信号。

(3)PLLMUL:PLL 倍频系数 (PLL multiplication factor)

图中③所表示的配置锁相环倍频系数,同样地可以查到在 STM32F1 系列中,ST 设置它的有效倍频范围为 2~16 倍。

要实现 72MHz 的主频率,我们通过选择 HSE 不分频作为 PLL 输入的时钟信号,即输入 8Mhz,通过标号③选择倍频因子,可选择 2-16 倍频,我们选择 9 倍频,这样可以得到时钟信号为 8*9=72MHz。

1.2.3 系统时钟SYSCLK

STM32 的系统时钟 SYSCLK 为整个芯片提供了时序信号。我们已经大致知道 STM32 主控是时序电路链接起来的。对于相同的稳定运行的电路,时钟频率越高,指令的执行速度越快,单位时间能处理的功能越多。STM32 的系统时钟是可配置的,在 STM32F1 系列中,它可以为HSI、PLLCLK、HSE 中的一个,通过 CFGR 的位 SW[1:0]设置。 

讲解 PLL 作为系统时钟时,根据我们开发板的资源,可以把主频通过 PLL 设置为 72MHz。仍使用 PLL 作为系统时钟源,如果使用 HSI/2,那么可以得到最高主频 8MHz/2*16=64MHz,显然达不到72MHz。

AHB、APB1、APB2、内核时钟等时钟通过系统时钟分频得到。根据得到的这个系统时钟,下面我们结合外设来看一看各个外设时钟源。如下STM32F103 系统时钟图: 

上图,标号 C 为系统时钟输入选择,可选时钟信号有外部高速时钟 HSE(8M)、内部高速时钟 HSI(8M)和经过倍频的 PLL CLK(72M),选择 PLL CLK 作为系统时钟,此时系统时钟的频率为 72MHz。系统时钟来到标号 D 的 AHB 预分频器,其中可选择的分频系数为 1,2,4,8,16,32,64,128,256,我们选择不分频,所以 AHB 总线时钟达到最大的 72MHz。

下面介绍一下由 AHB 总线时钟得到的时钟:

APB1 总线时钟,由 HCLK 经过标号 E 的低速 APB1 预分频器得到,分频因子可以选择 1,2,4,8,16,这里我们选择的是 2 分频,所以 APB1 总线时钟为 36M。由于 APB1 是低速总线时钟,所以 APB1 总线最高频率为 36MHz,片上低速的外设就挂载在该总线上,例如有看门狗定时器、定时器 2/3/4/5/6/7、RTC 时钟、USART2/3/4/5、SPI2(I2S2)与 SPI3(I2S3)、I2C1 与 I2C2、CAN、USB 设备和 2 个 DAC。

APB2 总线时钟,由 HCLK 经过标号 F 的高速 APB2 预分频器得到,分频因子可以选择 1,2,4,8,16,这里我们选择的是 1 即不分频,所以 APB2 总线时钟频率为 72M。与 APB2 高速总线链接的外设有外部中断与唤醒控制、7 个通用目的输入/输出口(PA、PB、PC、PD、PE、PF和 PG)、定时器 1、定时器 8、SPI1、USART1、3 个 ADC 和内部温度传感器。其中标号 G 是ADC 的预分频器在后面 ADC 实验中详细说明。

此外,AHB 总线时钟直接作为 SDIO、FSMC、AHB 总线、Cortex 内核、存储器和 DMA 的HCLK 时钟,并作为 Cortex 内核自由运行时钟 FCLK。 

USB、RTC、MCO 相关时钟,如下图:

标号 H 是 USBCLK,是一个通用串行接口时钟,时钟来源于 PLLCLK。STM32F103 内置全速功能的 USB 外设,其串行接口引擎需要一个频率为 48MHz 的时钟源。该时钟源只能从PLL 输出端获取,可以选择为 1.5 分频或者 1 分频,也就是,当需要使用 USB 模块时,PLL 必须使能,并且时钟频率配置为 48MHz 或 72MHz。

标号 I 是 MCO 输出内部时钟,STM32 的一个时钟输出 IO(PA8),它可以选择一个时钟信号输出,可以选择为 PLL 输出的 2 分频、HSI、HSE、或者系统时钟。这个时钟可以用来给外部其他系统提供时钟源。

标号 J 是 RTC 定时器,其时钟源为 HSE/128、LSE 或 LSI。

1.2.4 时钟信号输出MCO 

STM32 允许通过设置,通过 MCO 引脚输出一个稳定的时钟信号。在下图中标注为“G”的部分。

以下四个时钟信号可被选作 MCO 时钟:

(1)SYSCLK

(2)HSI

(3)HSE

(4)除 2 的 PLL 时钟

时钟的选择由时钟配置寄存器(RCC_CFGR)中的 MCO[2:0]位控制。
我们可以通过 MCO 引脚来输出时钟信号,测试输出时钟的频率,或作为其它需要时钟信号的外部电路的时钟。

2、如何修改主频 

STM32F103 默认的情况下(比如:串口 IAP 时或者是未初始化时钟时),使用的是内部 8M的 HSI 作为时钟源,所以不需要外部晶振也可以下载和运行代码的。
下面我们来学习如何让 STM32F103 芯片在 72MHZ 的频率下工作,72MHz 是官方推荐使用的最高的稳定时钟频率。

2.1 STM32F1时钟系统配置

第 1 步:配置 HSE_VALUE

打开 STM32F1xx_hal_conf.h 文件,我们知道需要宏定义 HSE_VALUE 匹配我们实际硬件的高速晶振频率(这里是 8MHZ),代码中通过使用宏定义的方式来选择 HSE_VALUE 的值是 25M 或者 8M,这里我们不去定义 USE_STM3210C_EVAL 这个宏或者全局变量即可,选择定义 HSE_VALUE 的值为 8M。找到这一段代码,进行配置即可,不过一般官方已经配置好的。如下图:

第 2 步:调用 SystemInit 函数

我们介绍启动文件的时候就知道,在系统启动之后,程序会先执行 SystemInit 函数,进行系统一些初始化配置。启动代码调用 SystemInit 函数如下:

下面我们来看看 system_stm32f1xx.c 文件下定义的 SystemInit 程序。

从上面代码可以看出,SystemInit 主要做了如下两个方面工作:

(1)外部存储器配置
(2)中断向量表地址配置
然而我们的代码中实际并没有定义 DATA_IN_ExtSRAM 和 USER_VECT_TAB_ADDRESS这两个宏,实际上 SystemInit 对于原子哥的例程并没有起作用,但原子哥保留了这个接口。从而避免了去修改启动文件。另外,是可以把一些重要的初始化放到 SystemInit 这里,在 main 函数运行前就把重要的一些初始化配置好(如 ST 这里是在运行 main 函数前先把外部的 SRAM 初始化),这个我们一般用不到,直接到 main 函数中处理即可,但也有厂商(如 RT-Thread)就采取了这样的做法,使得 main 函数更加简单,但对于初学者,我们暂时不建议这种用法。
HAL 库的 SystemInit 函数并没有任何时钟相关配置,所以后续的初始化步骤,我们还必须编写自己的时钟配置函数。

3 步:在 main 函数里调用用户编写的时钟设置函数

这里我们随便打开原子哥 HAL 库例程实验 1 跑马灯实验,看看在工程目录 Drivers\SYSTEM 分组下面定义的 sys.c 文件中的时钟设置函数 sys_stm32_clock_init 的内容:

函数 sys_stm32_clock_init 就是用户的时钟系统配置函数,除了配置 PLL 相关参数确定SYSCLK 值之外,还配置了 AHB、APB1 和 APB2 的分频系数,也就是确定了 HCLK,PCLK1和 PCLK2 的时钟值。

首先来看看使用 HAL 库配置 STM32F1 时钟系统的一般步骤:
(1)配置时钟源相关参数:调用函数 HAL_RCC_OscConfig()。
(2)配置系统时钟源以及 SYSCLK、AHB、APB1 和 APB2 的分频系数:调用函数HAL_RCC_ClockConfig()。

下面我们详细讲解这个 2 个步骤。

步骤 1:配置时钟源相关参数,使能并选择 HSE 作为 PLL 时钟源,配置 PLL1,我们调用的函数为 HAL_RCC_OscConfig(),该函数在 HAL 库头文件 STM32F1xx_hal_rcc.h 中声明,在文件 STM32F1xx_hal_rcc.c 中定义。首先我们来看看该函数声明:HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);

该函数只有一个形参,就是结构体 RCC_OscInitTypeDef 类型指针。接下来我们看看结构体RCC_OscInitTypeDef 的定义:

typedef struct
{
    uint32_t OscillatorType; /* 需要选择配置的振荡器类型 */
    uint32_t HSEState; /* HSE 状态 */
    uint32_t HSEPredivValue; /* HSE 预分频值 */
    uint32_t LSEState; /* LSE 状态 */
    uint32_t HSIState; /* HIS 状态 */
    uint32_t HSICalibrationValue; /* HIS 校准值 */
    uint32_t LSIState; /* LSI 状态 */
    RCC_PLLInitTypeDef PLL; /* PLL 配置 */
}RCC_OscInitTypeDef;

该结构体前面几个参数主要是用来选择配置的振荡器类型。比如我们要开启 HSE,那么我们会设置 OscillatorType 的值为 RCC_OSCILLATORTYPE_HSE,然后设置 HSEState 的值为RCC_HSE_ON 开启 HSE。对于其他时钟源:HIS、LSI、LSE,配置方法类似。

RCC_OscInitTypeDef 这个结构体还有一个很重要的成员变量是 PLL,它是结构体RCC_PLLInitTypeDef 类型。它的作用是配置 PLL 相关参数,我们来看看它的定义:

typedef struct
{
    uint32_t PLLState; /* PLL 状态 */
    uint32_t PLLSource; /* PLL 时钟源 */
    uint32_t PLLMUL; /* PLL 倍频系数 M */
}RCC_PLLInitTypeDef;

从 RCC_PLLInitTypeDef;结构体的定义很容易看出该结构体主要用来设置 PLL 时钟源以及相关分频倍频参数。这个结构体的定义的相关内容请结合时钟树中红色框的内容一起理解。

接下来我们看看我们的时钟初始化函数 sys_stm32_clock_init 中的配置内容:

/* 使能 HSE,并选择 HSE 作为 PLL 时钟源,配置 PLLMUL */
RCC_OscInitTypeDef rcc_osc_init = {0};
rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE; /* 使能 HSE */
rcc_osc_init.HSEState = RCC_HSE_ON; /* 打开 HSE */
rcc_osc_init.HSEPredivValue = RCC_HSE_PREDIV_DIV1; /* HSE 预分频 */
rcc_osc_init.PLL.PLLState = RCC_PLL_ON; /* 打开 PLL */
rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE; /* PLL 时钟源为 HSE */
rcc_osc_init.PLL.PLLMUL = plln; /* 主 PLL 倍频因子 */
ret = HAL_RCC_OscConfig(&rcc_osc_init); /* 初始化 */

通过函数的该段程序,我们开启了 HSE 时钟源,同时选择 PLL 时钟源为 HSE,然后把sys_stm32_clock_init 的形参直接设置作为 PLL 的参数 M 的值,这样就达到了设置 PLL 时钟源相关参数的目的。设置好 PLL 时钟源参数之后,也就是确定了 PLL 的时钟频率。

步骤 2:配置系统时钟源,以及 SYSCLK、AHB、APB1 和 APB2 相关参数,用函数HAL_RCC_ClockConfig(),声明如下:

HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct,
uint32_t FLatency);

该函数有两个形参,第一个形参 RCC_ClkInitStruct 是结构体 RCC_ClkInitTypeDef 类型指针变量,用于设置 SYSCLK 时钟源以及 SYSCLK、AHB、APB1 和 APB2 的分频系数。第二个形参 FLatency 用于设置 FLASH 延迟。

RCC_ClkInitTypeDef 结构体类型定义比较简单,我们来看看其定义:

typedef struct
{
     uint32_t ClockType; /* 要配置的时钟 */
     uint32_t SYSCLKSource; /* 系统时钟源 */
     uint32_t AHBCLKDivider; /* AHB 分频系数 */
     uint32_t APB1CLKDivider; /* APB1 分频系数 */
     uint32_t APB2CLKDivider; /* APB2 分频系数 */
}RCC_ClkInitTypeDef;

在 sys_stm32_clock_init 函数中的实际应用配置内容如下:

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

sys_stm32_clock_init 函数中的 RCC_ClkInitTypeDef 结构体配置内容:

第一个参数 ClockType 配置表示我们要配置的是 SYSCLK、HCLK、PCLK1 和 PCLK 四个时钟。
第二个参数 SYSCLKSource 配置选择系统时钟源为 PLL。
第三个参数 AHBCLKDivider 配置 AHB 分频系数为 1。
第四个参数 APB1CLKDivider 配置 APB1 分频系数为 2。
第五个参数 APB2CLKDivider 配置 APB2 分频系数为 1。

根据我们在 mian 函数中调用 sys_stm32_clock_init(RCC_PLL_MUL9)时设置的形参数值,我们可以计算出,PLL 时钟为 PLLCLK = HSE * 9 = 8MHz * 9 = 72MHz。

同时我们选择系统时钟源为 PLL,所以系统时钟 SYSCLK=72MHz。AHB 分频系数为 1,故频率为 HCLK=SYSCLK/1=72MHz。APB1 分频系数为 2,故其频率为 PCLK1=HCLK/2=36MHz。APB2 分频系数为 1,故其频率为 PCLK2=HCLK/1=72MHz。我们总结一下通过调用函数sys_stm32_clock_init(RCC_PLL_MUL9)之后的关键时钟频率值:

SYSCLK(系统时钟) =72MHz
PLL 主时钟 =72MHz
AHB 总线时钟(HCLK=SYSCLK/1) =72MHz
APB1 总线时钟(PCLK1=HCLK/2) =36MHz
APB2 总线时钟(PCLK2=HCLK/1) =72MHz

最后我们来看看函数 HAL_RCC_ClockConfig 第二个入口参数 FLatency 的含义,为了使FLASH 读写正确(因为 72Mhz 的时钟比 Flash 的操作速度 24Mhz 要快得多,操作速度不匹配容易导致 Flash 操作失败),所以需要设置延时时间。对于 STM32F1 系列,FLASH 延迟配置参数值是通过下表来确定的。

从上可以看出,我们设置值为 FLASH_LATENCY_2,也就是 2WS,也就是 3 个 CPU 周期,为什么呢?因为经过上面的配置之后,系统时钟频率达到了最高的 72MHz,对应的就是两个等待状态,所以选择 FLASH_LATENCY_2。

2.2 STM32F1 时钟使能和配置

在配置好时钟系统之后,如果我们要使用某些外设,例如 GPIO,ADC 等,我们还要使能这些外设时钟。这里大家必须注意,如果在使用外设之前没有使能外设时钟,这个外设是不可能正常运行的。STM32 的外设时钟使能是在 RCC 相关寄存器中配置的。因为 RCC 相关寄存器非常多,有兴趣的可以直接打开原子哥《STM32F10xxx 参考手册_V10(中文版).pdf》6.3 小节查看所有 RCC 相关寄存器的配置。

接下来我们来讲解通过 STM32F1 的 HAL 库使能外设时钟的方法。 

在 STM32F1 的 HAL 库中,外设时钟使能操作都是在 RCC 相关固件库文件头文件STM32F1xx_hal_rcc.h 定义的大家打开 STM32F1xx_hal_rcc.h 头文件可以看到文件中除了少数几个函数声明之外大部分都是宏定义标识符。外设时钟使能在 HAL 库中都是通过宏定义标识符来实现的。首先,我们来看看 GPIOA 的外设时钟使能宏定义标识符: 

#define __HAL_RCC_GPIOA_CLK_ENABLE() do { \
                       __IO uint32_t tmpreg; \
                       SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);\
                       tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);\
                       UNUSED(tmpreg); \
 } while(0U)

这段代码主要是定义了一个宏定义标识符__HAL_RCC_GPIOA_CLK_ENABLE(),它的核心操作是通过下面这行代码实现的:

SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);

这行代码的作用是,设置寄存器 RCC->APB2ENR 的相关位为 1,至于是哪个位,是由宏定义标识符 RCC_APB2ENR_IOPAEN 的值决定的,而它的值为:

#define RCC_APB2ENR_IOPAEN_Pos (0U)
#define RCC_APB2ENR_IOPAEN_Msk (0x1UL << RCC_APB2ENR_IOPAEN_Pos) 
#define RCC_APB2ENR_IOPAEN RCC_APB2ENR_IOPAEN_Msk

上面三行代码很容易计算出来 RCC_APB2ENR_IOPAEN= (0x00000001<<2),因此上面代码的作用是设置寄存器 RCC->APB2ENR 寄存器的位 2 为 1。我们可以从 STM32F1 的参考手册中搜索 APB2ENR 寄存器定义,位 2 的作用是用来使用 GPIOA 时钟。APB2ENR 寄存器的位 2 描述如下:

位 0 IOPAEN:IO 端 A 时钟使能(I/O port A clock enable)
由软件置‘1’或清‘0’
0:IO 端口 A 时钟关闭
1:IO 端口 A 时钟开启

那么我们只需要在我们的用户程序中调用宏定义标识符就可以实现 GPIOA 时钟使能。使用方法为:

__HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能 GPIOA 时钟 */

对于其他外设,同样都是在 STM32F1xx_hal_rcc.h 头文件中定义,大家只需要找到相关宏定义标识符即可,这里我们列出几个常用使能外设时钟的宏定义标识符使用方法:

__HAL_RCC_DMA1_CLK_ENABLE(); /* 使能 DMA1 时钟 */
__HAL_RCC_USART2_CLK_ENABLE(); /* 使能串口 2 时钟 */
__HAL_RCC_TIM1_CLK_ENABLE(); /* 使能 TIM1 时钟 */

我们使用外设的时候需要使能外设时钟,如果我们不需要使用某个外设,同样我们可以禁止某个外设时钟。禁止外设时钟使用方法和使能外设时钟非常类似,同样是头文件中定义的宏定义标识符。我们同样以 GPIOA 为例,宏定义标识符为:

#define __HAL_RCC_GPIOA_CLK_DISABLE() (RCC->APB2ENR) &= ~ (RCC_APB2ENR_GPIOAEN)

同样,宏定义标识符__HAL_RCC_GPIOA_CLK_DISABLE()的作用是设置 RCC->APB2ENR寄存器的位 2 为 0,也就是禁止 GPIOA 时钟。具体使用方法我们这里就不做过多讲解,我们这里同样列出几个常用的禁止外设时钟的宏定义标识符使用方法:

__HAL_RCC_DMA1_CLK_DISABLE(); /* 禁止 DMA1 时钟 */
__HAL_RCC_USART2_CLK_DISABLE(); /* 禁止串口 2 时钟 */
__HAL_RCC_TIM1_CLK_DISABLE(); /* 禁止 TIM1 时钟 */

  • 2
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F103R6是一款基于ARM Cortex-M3内核的微控制器,它具有丰富的外设和功能。在配置STM32F103R6的时钟时,需要设置系统时钟和外设时钟。 1. 系统时钟配置: - 首先,需要选择系统时钟源。可以选择内部时钟源(HSI)或外部时钟源(HSE)。 - 如果选择内部时钟源,可以使用默认的内部高速时钟(HSI)作为系统时钟源。可以通过设置RCC_CFGR寄存器来配置HSI的分频系数。 - 如果选择外部时钟源,需要将外部时钟源连接到MCU的引脚上,并通过设置RCC_CFGR寄存器来配置外部时钟源的分频系数。 - 然后,需要选择系统时钟的分频系数。可以通过设置RCC_CFGR寄存器来配置分频系数,以得到所需的系统时钟频率。 2. 外设时钟配置: - 对于每个外设,都有一个时钟使能寄存器(RCC_APBxENR或RCC_AHBxENR),用于控制外设时钟的使能和禁用。 - 通过设置相应的时钟使能位,可以使能或禁用特定的外设时钟。 下面是一个示例代码,演示了如何配置STM32F103R6的时钟: ```c #include "stm32f10x.h" void SystemClock_Config(void) { // 选择系统时钟源为外部时钟源(HSE) RCC->CFGR |= RCC_CFGR_SW_HSE; // 配置外部时钟源的分频系数 RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB时钟不分频 RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // APB1时钟分频系数为2 RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; // APB2时钟不分频 // 使能外部时钟源(HSE) RCC->CR |= RCC_CR_HSEON; while(!(RCC->CR & RCC_CR_HSERDY)); // 使能外设时钟 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // 使能TIM2时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 使能GPIOA时钟 } int main(void) { // 配置系统时钟 SystemClock_Config(); // 其他代码... return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值