购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.2 STM32基础知识储备

第1章已经讲解了STM32芯片的系统架构和主要总线,但是当时主要是为介绍STM32寄存器做铺垫,如果开发者需要更加全面地了解这部分知识,可以查阅《STM32F10x中文参考手册》。本节将会在STM32系统架构的基础上,进一步讲解STM32的端口复用和重映射、嵌套向量中断控制器和时钟系统等相关知识,为后面的嵌入式系统设计做准备。

4.2.1 端口复用和重映射功能

1. 端口复用功能

STM32有很多内置外设,这些外设的外部功能引脚是与GPIO端口进行复用的,当一个GPIO端口作为内置外设的功能引脚使用时,称为复用。关于STM32端口复用的知识在《STM32F10x中文参考手册》中的复用功能I/O和调试配置部分有详细讲解,对于STM32F103ZET6具体哪个GPIO端口可以复用为哪些内置外设的功能引脚,在本书的附录A中有明确的标注。

在STM32中端口复用的案例众多,这里不再详述,仅以STM32中的USART串口为例,对这部分知识进行讲解。对于STM32F103ZET6单片机而言,通过查阅《STM32F10x中文参考手册》可以知道,USART1的引脚所对应的GPIO端口为PA9和PA10(见表4.3)。PA9和PA10的默认功能是通用I/O端口,所以当PA9和PA10引脚作为USART1的TX和RX功能引脚使用时,就是端口复用。

表4.3 USART1复用功能

对于USART1而言,复用端口初始化主要有以下几个步骤。

(1)GPIO端口时钟使能。使用端口复用自然要使能相应GPIO端口的时钟,即

(2)复用的外设时钟使能。如果需要将引脚PA9和PA10复用为串口,则需要使能串口时钟,即

(3)GPIO端口模式配置。当I/O端口复用为内置外设功能引脚时,必须设置GPIO端口模式。GPIO端口模式的相关知识将会在第5章进行详细讲解。在复用功能下GPIO端口模式应该如何对应,可以通过查阅《STM32F10x中文参考手册》获得。USART1复用端口的模式配置如表4.4所示,表中的全双工模式与半双工同步模式是指两种不同的通信模式,该部分知识将在第7章进行详细讲解。

表4.4 USART复用端口的模式配置

从表4.4可以看出,如果要配置全双工模式的USART1,那么TX引脚,即PA9引脚需要配置为推挽复用输出模式,而RX引脚,即PA10引脚需要配置为浮空输入或带上拉输入模式。具体内容如下。

上述代码实现了PA9引脚为推挽复用输出模式、PA10引脚为浮空输入模式的配置,这部分知识将在第5章进行详细讲解。

经过前面三个步骤即可完成全双工模式的USART1的初始化过程,其他端口复用的初始化过程与之类似,这里不再详述。在使用复用功能时,至少需要使能两个时钟,即GPIO端口时钟和复用外设时钟,还要初始化GPIO端口和复用外设功能。

2. 端口重映射功能

STM32有很多内置外设,每个内置外设都有若干个引脚,一般这些引脚的输出端口是固定不变的。但为了让开发者可以更好地安排引脚的走向和功能,在STM32中引入了端口重映射(Remap)概念,即一个外设的引脚除了具有默认的端口引脚,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其他端口引脚上。简单来讲,端口重映射就是把某个引脚的外设功能映射到另一个端口引脚上,但这并不是随便映射的,具体对应关系在《STM32F10x中文参考手册》中的复用功能I/O和调试配置部分有详细讲解,下面同样以USART1为例进行说明。

表4.5所示是USART1复用功能重映射表,从表中可以看出,在默认情况下,USART1复用的时候,其引脚位对应为PA9和PA10,也可以将TX和RX重新映射到引脚PB6和PB7上。

表4.5 USART1复用功能重映射表

在进行端口重映射时,不仅要使能在复用功能中所介绍的两个时钟,还要使能AFIO时钟,之后还需要调用重映射函数。具体步骤如下。

(1)使能GPIOB时钟。

(2)使能USART1时钟。

(3)使能AFIO时钟。

(4)开启重映射。

这样就将USART1的TX和RX功能引脚重映射到引脚PB6和PB7上面了。至于有哪些功能可以重映射,除了查阅《STM32F10x中文参考手册》,还可以通过GPIO_PinRemapConfig函数查看第一个属性参数的取值范围来获得。重映射标识符取值的宏定义放在stm32f10x_gpio.h文件中,从这些宏定义中,也可以获取关于片内外设功能引脚的端口重映射信息,具体内容如下。

从上述程序代码可以看出,许多片内外设和USART1一样,都只有一种重映射,但也有许多片内外设存在部分重映射和完全重映射两种,如USART3、TIM1、TIM2等。部分重映射是指外设的部分功能引脚和默认的一样,另一部分功能引脚则可以重新映射到其他端口引脚上。完全重映射是指所有功能引脚都能够重新映射到其他端口引脚上。以USART3为例,通过查阅《STM32F10x中文参考手册》可以获得USART3复用功能重映射,如表4.6所示。

表4.6 USART3复用功能重映射

当USART3采用部分重映射时,将PB10、PB11和PB12引脚所对应的TX、RX和CK功能引脚重映射到PC10、PC11和PC12引脚上。而PB13与PB14引脚所对应的CTS与RTS功能引脚和没有重映射情况是一样的。当USART3采用完全重映射时,TX、RX和CK功能引脚会被重映射到PD8、PD9和PD10引脚上,并且CTS和RTS功能引脚也会重映射到PD11和PD12引脚上。如果需要使用USART3的部分重映射,则调用重映射函数的方法如下。

同样,如果需要使用USART3的全部重映射,则调用重映射函数的方法如下。

这里只简要介绍了STM32端口复用和重映射的相关知识,以方便开发者了解这部分知识,对于其他众多的端口复用与重映射案例,在以后的嵌入式系统设计中用到时再进行讲解。

4.2.2 嵌套向量中断控制器简介

中断是指内部或外部事件使CPU暂停当前程序,转去执行中断服务程序的一种工作机制,由中断源、中断控制和中断响应等几部分组成。

(1)中断源:中断请求的来源;

(2)中断控制:中断的允许/禁止,中断优先级与优先级嵌套;

(3)中断响应:保护断点,转去执行中断服务程序。

Cortex-M3内核可以支持256个中断,其中包括16个内核中断和240个外部中断(也称可屏蔽中断),并能够设置256级可编程的中断优先级。STM32采用Cortex-M3内核,但没有使用Cortex-M3内核的全部中断功能,只用了其中一部分。

STM32最多能够支持84个中断,包括16个内核中断和68个外部中断,并且具有16级可编程的中断优先级,其中68个外部中断是在嵌入式系统设计中经常使用的。但并不是所有的STM32都具备全部的68个外部中断,如STM32F103系列只包含60个中断;STM32F107系列包含68个中断。详细的内部中断和外部中断清单可以通过查阅《STM32F10x中文参考手册》获得,也可以在STM32标准函数库文件里的stm32f10x.h头文件中查到。

STM32中断系统包括中断源、中断通道、中断屏蔽、中断优先级、嵌套向量中断控制器和中断服务程序等。在学习STM32中断系统之前,必须先明确嵌套向量中断控制器(Nested Vectored Interrupt Controller,NVIC)的概念,NVIC控制着整个芯片的中断相关功能,是Cortex-M3内核中的外设之一,并与Cortex-M3内核紧密耦合。

说明

不同的芯片厂商在设计芯片时,会对Cortex-M3内核中的NVIC进行裁剪,把不需要的部分去掉,所以STM32中的NVIC只是Cortex-M3内核中的NVIC的一个子集,这也是STM32没能完全使用Cortex-M3内核中断功能的原因。

1. NVIC寄存器简介

STM32标准函数库中的core_cm3.h头文件对NVIC结构体的定义如下。

由上述程序代码不难发现,NVIC结构体为一些寄存器预留了很多位,这是为了方便在以后的系统设计过程中对其功能进行扩展。STM32的中断就是在这些NVIC寄存器的控制和管理下有序执行的,只有了解这些中断寄存器,才能方便地使用STM32的中断。下面简要介绍几个主要的寄存器。

(1)ISER[8]。ISER的全称为Interrupt Set Enable Registers,即中断使能寄存器组。Cortex-M3内核所支持的256个中断就是用这里的8个32位寄存器来控制的,寄存器中的每一位都可以使能一个中断。但STM32F103系列的外部中断只有60个,所以STM32F103系列的嵌入式系统设计只能用到ISER[0]和ISER[1]中的前60位。ISER[0]中的bit0~31分别对应中断0~31,ISER[1]中的bit0~27对应中断32~59,需要使能哪个中断,就将相应的ISER寄存器位置1。

(2)ICER[8]。ICER的全称为Interrupt Clear Enable Registers,即中断清除寄存器组。ICER与ISER的功能恰好相反,可以用来清除某个中断的使能。ICER寄存器位与每个中断的对应关系和ISER寄存器位是一样的。这里专门设置一个ICER来清除中断位,而不是向ISER写0来清除,因为NVIC寄存器都是写1有效、写0无效的,具体原理可参阅《Cortex-M3权威指南》中的NVIC中断控制部分。

(3)ISPR[8]。ISPR的全称为Interrupt Set Pending Registers,即中断使能挂起寄存器组。ISPR寄存器位与每个中断的对应关系和ISER寄存器位一样,通过对相应的寄存器位置1,可以将正在进行的中断挂起,进而执行同级或更高级别的中断。同样,对其写0是无效的。

(4)ICPR[8]。ICPR的全称为Interrupt Clear Pending Registers,即中断清除挂起寄存器组。ICPR寄存器位与每个中断的对应关系和ISER寄存器位一样,通过对相应的寄存器位置1,使挂起的中断重新挂接,写0无效。

(5)IABR[8]。IABR的全称为Interrupt Active Bit Registers,即中断有效位寄存器组。IABR寄存器位与每个中断的对应关系同样和ISER寄存器位是一样的,如果对应的寄存器位置1,则表示该位所对应的中断正在被执行。IABR是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完毕之后由硬件自动清零。

(6)IP[240]。IP的全称为Interrupt Priority Registers,即中断优先级寄存器组。STM32的中断分组与IP寄存器组密切相关,IP寄存器组由240个8位寄存器组成,每个外部中断占用8位,共可以表示240个外部中断。而STM32F103系列单片机只用到了前60位,即IP[59]~IP[0]分别对应外部中断59~0。当然,每个外部中断所占用的8位也没有全部使用,STM32具有16级可编程的中断优先级,因此在设置中断优先级时,只需要使用4位就足够了。这里使用的是寄存器的高4位,这4位又分为抢占优先级和响应优先级(也称子优先级),抢占优先级在前,响应优先级在后,并且这两个优先级各占几位还需要根据SCB->AIRCR中的中断分组设置来决定。

下面简要介绍STM32的中断分组。STM32将中断分为5组,即组0~4,该分组的设置是由SCB->AIRCR寄存器的bit10~8定义的。AIRCR中断分组设置如表4.7所示。

表4.7 AIRCR中断分组设置

通过表4.7可以清楚地看到组0~4对应的配置关系。

(1)第0组。所有4位都用来配置响应优先级,即不同的中断有16种不同的响应优先级。

(2)第1组。高1位用来配置抢占优先级,低3位用来配置响应优先级,即有2种不同的抢占优先级(0~1级),有8种不同的响应优先级(0~7级)。

(3)第2组。高2位用来配置抢占优先级,低2位用来配置响应优先级,即有4种不同的抢占优先级(0~3级),有4种不同的响应优先级(0~3级)。

(4)第3组。高3位用来配置抢占优先级,低1位用来配置响应优先级,即有8种不同的抢占优先级(0~7级),有2种不同的响应优先级(0~1级)。

(5)第4组。所有4位都用来配置抢占优先级,即不同的中断有16种不同的抢占优先级,没有响应优先级属性。

以组3为例,将中断优先级组设置到第3组时,60个外部中断的中断优先级寄存器高4位中的高3位均为抢占优先级,而低1位为响应优先级。每个中断都可以设置抢占优先级为0~7,响应优先级为1或0。抢占优先级的级别高于响应优先级的级别,二者的数值越小,其优先级越高。

当两个中断的抢占优先级和响应优先级一样时,根据中断先发生的顺序执行;当抢占优先级不同时,高抢占优先级的中断可以打断正在执行的低抢占优先级的中断;而当抢占优先级相同,而响应优先级不同时,高响应优先级的中断却不可以打断低响应优先级的中断。例如,假设设置中断优先级组为2,设置中断3(RTC中断)的抢占优先级为2,响应优先级为1;设置中断6(外部中断0)的抢占优先级为3,响应优先级为0;设置中断7(外部中断1)的抢占优先级为2,响应优先级为0。那么这三个中断的优先级顺序为中断7>中断3>中断6,并且中断3和中断7都可以打断中断6,但中断7和中断3不可以相互打断。

2. NVIC库函数概述

通过前面的介绍,我们已经大致了解了STM32中断设置的过程。在实现对中断配置的管理之前,还需要对NVIC库函数有一个基本的认识。常用的NVIC库函数如表4.8所示。

表4.8 常用的NVIC库函数

下面简要介绍一些常用的NVIC库函数。

(1)NVIC初始化设置类函数。

① NVIC_DeInit函数:该函数将外设NVIC寄存器重设为默认值。

例如,重置NVIC寄存器为默认的复位值:

② NVIC_SCBDeInit函数:该函数将外设SCB寄存器重设为默认值。

例如,重置SCB寄存器为默认的复位值:

③ NVIC_PriorityGroupConfig函数:该函数将优先级分组设置为抢占优先级和响应优先级。

表4.9 NVIC_PriorityGroup取值定义

例如,定义抢占优先级1位,响应优先级3位:

④ NVIC_Init函数:该函数根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器。

NVIC_InitTypeDef结构体定义在STM32标准函数库文件中的misc.h头文件下,具体定义如下。

每个NVIC_InitTypeDef结构体成员的功能和相应的取值如下。

· NVIC_IRQChannel。该成员用来使能或失能指定的IRQ通道。不同的中断对应不同的IRQ通道,在配置过程中一定不可写错,即使程序写错了也不会报错,只会导致不响应中断。NVIC_IRQChannel的取值可以在stm32f10x.h头文件中的IRQn_Type结构体中找到。NVIC_IRQChannel取值定义如表4.10所示。

表4.10 NVIC_IRQChannel取值定义

· NVIC_IRQChannelPreemptionPriority。该成员用来定义中断的抢占优先级,需要根据优先级分组来确定,不同分组对应的抢占优先级的取值范围在前面已经介绍过了,这里不再详述。

· NVIC_IRQChannelSubPriority。该成员用来定义中断的响应优先级,也需要根据优先级分组来确定。同样,不同分组对应的响应优先级的取值范围在前面也已经介绍过了。

· NVIC_IRQChannelCmd。该成员指定了成员NVIC_IRQChannel中定义的IRQ通道是使能(ENABLE)还是失能(DISABLE),操作的是ISER寄存器和ICER寄存器。

例如,设置优先级分组为第1组,并定义USART1串口中断的抢占优先级为1,响应优先级为5:

⑤ NVIC_StructInit函数:该函数把NVIC_InitStruct中的每个参数按默认值填入。

表4.11 NVIC_InitStruct默认值

⑥ NVIC_SetIRQChannelPendingBit函数:该函数设置指定的IRQ通道待处理位。

例如,设置SPI1的IRQ通道待处理位:

(2)NVIC使能类函数。

① NVIC_SETPRIMASK函数:该函数用于使能PRIMASK优先级。

例如,使能PRIMASK优先级:

② NVIC_RESETPRIMASK函数:该函数用于失能PRIMASK优先级。

例如,失能PRIMASK优先级:

③ NVIC_SETFAULTMASK函数:该函数用于使能FAULTMASK优先级。

④ NVIC_RESETFAULTMASK函数:该函数用于失能FAULTMASK优先级。

⑤ NVIC_SystemHandlerConfig函数:该函数用于使能或失能指定的系统Handler。

表4.12 SystemHandler取值定义

例如,使能存储器管理Handler:

(3)NVIC检查选择类函数。

① NVIC_ClearIRQChannelPendingBit函数:该函数用于清除指定的IRQ通道待处理位。

例如,清除ADC的IRQ通道待处理位:

② NVIC_GenerateSystemReset函数:该函数用于产生一个系统复位。

例如,使能PRIMASK优先级:

③ NVIC_SystemLPConfig函数:该函数用于选择系统进入低功耗模式的条件。

表4.13 LowPowerMode取值定义

例如,选择从中断中唤醒系统:

④ NVIC_GetSystemHandlerActiveBitStatus函数:该函数用于检查指定的系统Handler活动位设置与否。

表4.14 SystemHandler取值定义

例如,检查系统Handler的总线错误活动位设置与否:

通过讲解上述几种常用的NVIC库函数,基本上就可以使用库函数编程实现中断配置的管理了。在一般的编程配置过程中,并不需要用到上述所有函数,在实际操作中需要注意以下三个编程要点。

· 使能外设某个中断,由具体外设的中断使能相关位进行控制,如串口有发送完成中断和接收完成中断,这两个中断的使能都由串口控制寄存器的中断使能位控制。

· 初始化NVIC_InitTypeDef结构体,配置中断优先级分组,设置抢占优先级和响应优先级、使能中断请求等。这部分工作在介绍NVIC_Init中断初始化函数的时候已经介绍过了,具体内容可以参照前面配置USART1串口中断的例程。

· 编写中断服务函数,在启动startup_stm32f10x_hd.s文件时,已经预先为每个中断编写了一个中断服务函数,只是这些中断函数都为空,目的是初始化中断向量列表。这些服务函数在实际应用中需要重新编写,为了方便管理把中断服务函数统一写在stm32f10xit.c库文件中。

注意

所编写的中断服务函数的函数名必须与启动文件中预先设置的一样,如果写错,则会在执行过程中找不到中断服务函数的入口,从而直接跳转到启动文件中的预先写好的空函数中,并且进行无限循环,无法实现中断。

以上简要介绍了NVIC中断管理与配置的相关概念与方法,如中断的初始化、分组设置、优先级管理、清除中断和查看中断状态等,并在介绍寄存器的基础上简要讲解了常用的NVIC库函数,这些知识在以后的嵌入式系统设计实践中会经常用到。

4.2.3 时钟系统与RCC控制器

1. STM32的时钟系统

对于一个微控制器而言,时钟系统就像人的心跳一样重要。STM32不像51单片机那样只需要一个系统时钟,它的时钟系统十分复杂。STM32的时钟系统知识在《STM32F10x中文参考手册》中的系统时钟部分有非常详细的讲解,这里只进行简要的总结介绍。

STM32的时钟系统之所以设计得这么复杂,是因为STM32自身就非常复杂,它有非常多的外设,而且并不是所有外设都需要高的时钟频率,如看门狗和RTC只需要几十千赫兹的时钟频率。由于在同一个电路中,时钟频率越高,其功耗就越大,抗电磁干扰的能力也会减弱,所以较复杂的单片机一般采取多时钟源的方法来解决这些问题。STM32的时钟系统图如图4.1所示。

图4.1 STM32的时钟系统图

从图4.1可以看出,STM32共有5个时钟源,即HSI、HSE、LSI、LSE和PLL。根据时钟频率高低,可以将时钟源分为高速时钟源和低速时钟源,其中HSI、HSE和PLL是高速时钟,LSI和LSE是低速时钟。根据时钟来源不同,可以将时钟源分为外部时钟源和内部时钟源。外部时钟源需要通过外接晶振的方式获取时钟源,HSE和LSE是外部时钟源;其他的是内部时钟源,不需要外接晶振。

下面逐一介绍STM32的5个时钟源。

(1)高速内部时钟源HSI,使用RC振荡器,频率为8MHz。

(2)高速外部时钟源HSE,可以外接石英/陶瓷谐振器或接外部时钟源,频率范围为4~16 MHz。

(3)低速内部时钟源LSI,使用RC振荡器,频率为40kHz。独立看门狗(IWDG)的时钟源只能采用LSI,LSI还可以作为实时时钟(Real Time Clock,RTC)的时钟源。

(4)低速外部时钟源LSE,可以外接频率为32.768kHz的石英晶体,它主要作为RTC的时钟源。

(5)锁相环PLL时钟源,其时钟输入源可选择为HSI/2、HSE或HSE/2,通过设置PLL的倍频因子,可以对PLL的时钟输入源进行倍频,倍频可选择为2~16倍,但是最大输出频率不要超过72MHz。

前面简要概括了STM32的5个时钟源,它们是如何给各个外设和系统提供时钟的呢?在控制STM32系统时钟时,需要配置时钟控制器RCC的相关寄存器。时钟控制器RCC的相关知识将在之后进行详细的介绍。下面逐一讲解如何设置时钟。

(1)系统时钟。

① 锁相环时钟PLLCLK。前面已经提到了PLL的时钟输入源可选择为HSI/2、HSE或HSE/2,而具体使用哪个输入源由时钟配置寄存器RCC_CFGR的bit16,即PLLSRC决定,倍频因子由RCC_CFGR的bit21~18,即PLLMUL[3:0]进行设置。通常PLL的时钟来源为HSE=8MHz,并将倍频因子设置为9,此时PLL时钟频率PLLCLK应为8×9=72MHz,72MHz也是ST官方推荐的稳定运行时钟频率。

② 系统时钟SYSCLK。系统时钟的来源可以是HSI、PLLCLK或HSE,具体使用哪个输入源可以由时钟配置寄存器RCC_CFGR的bit1~0,即SW[1:0]设置,一般会选取PLLCLK作为系统时钟的来源,即通常取SYSCLK=PLLCLK=72MHz。72MHz是系统时钟的最大频率,当然也可以超频使用。但在一般情况下,为了保证系统稳定性,建议不要冒风险使用超频。

③ AHB总线时钟HCLK。系统时钟SYSCLK经过AHB预分频器分频之后得到的时钟称为AHB总线时钟,即HCLK。分频因子可以是[1, 2, 4, 8, 16, 64, 128, 256, 512],由时钟配置寄存器RCC_CFGR的bit7~4,即HPRE[3:0]进行设置,通常设置为1分频,即HCLK=SYSCLK=72MHz。大部分片上外设的时钟是经过HCLK分频得到的,AHB总线上外设的时钟设置为多少,需要等到使用该外设时再设置。

④ APB1总线时钟PCLK1。APB1总线时钟PCLK1由HCLK经过低速APB1预分频器得到,分频因子可以是[1, 2, 4, 8, 16],由时钟配置寄存器RCC_CFGR的bit10~8,即PRRE1[2:0]进行设置,通常设置为2分频,即PCLK1=HCLK/2=36MHz。APB1为低速外设总线,其总线时钟PCLK1的最高频率为36MHz,在该总线上挂载着不同的片上低速外设,如电源接口、备份接口、CAN、USB、I 2 C1、I 2 C2、UART2和UART3等。外设的时钟设置为多少,需要等到使用该外设时再设置。

⑤ APB2总线时钟PCLK2。APB2总线时钟PCLK2由HCLK经过高速APB2预分频器得到,分频因子可以是[1, 2, 4, 8, 16],由时钟配置寄存器RCC_CFGR的bit13~11,即PPRE2[2:0]进行设置,通常设置为1分频,即PCLK2=HCLK=72MHz。APB2为高速外设总线,其总线时钟PCLK2的最高频率为72MHz,该总线上挂载着不同的片上高速外设,如USART1、SPI1、Timer1、ADC1、ADC2和全部GPIO等。外设的时钟设置为多少,需要等到使用该外设时再设置。

(2)其他时钟。

① USB时钟。USB时钟由PLLCLK经过USB预分频器得到,分频因子可以是1或1.5,由时钟配置寄存器RCC_CFGR的bit22,即USBPRE进行设置。USB时钟的最高频率为48MHz,根据分频因子推算,PLLCLK只能是48MHz或72MHz。通常设置PLLCLK=72MHz,USBCLK=48MHz,即采用1.5分频。USB对时钟要求比较高,所以PLLCLK只能由HSE倍频得到,而不能使用HSI倍频。

② Cortex内核时钟。Cortex内核时钟由HCLK进行8分频得到,为9MHz。Cortex内核时钟用来驱动内核的系统定时器SysTick,SysTick一般用于操作系统的时钟节拍,也可以用于普通定时。

③ ADC时钟。ADC时钟由PCLK2经过ADC预分频器得到,分频因子可以是[2, 4, 6, 8],由时钟配置寄存器RCC_CFGR的bit15~14,即ADCPRE[1:0]进行设置。注意,这里没有1分频,并且ADC时钟频率最高为14MHz。如果将ADC时钟设置为14MHz,并将采样周期设置成最短(1.5个周期),则ADC的转换时间将会达到最短,即1μs,此时PCLK2的时钟频率只能是28MHz、56MHz、84MHz或112MHz。由于PCLK2最高频率为72MHz,所以为了使ADC的转换时间达到最短,PCLK2时钟频率只能取28MHz或56MHz。

④ RTC时钟。从STM32的时钟系统图可以看出,RTC时钟可由HSE/128分频得到,也可由低速外部时钟信号LSE提供,其频率为32.768kHz,还可由低速内部时钟信号LSI提供。选用哪个时钟由备份域控制寄存器RCC_BDCR的bit9~8,即RTCSEL[1:0]进行设置。

⑤ 独立看门狗时钟。独立看门狗时钟由LSI提供,且只能由LSI提供。LSI提供低速的内部时钟信号,频率为30~60kHz,通常取40kHz。

⑥ MCO时钟输出。在STM32F1系列中,由PA8引脚复用得到的微控制器时钟输出(Microcontroller Clock Output,MCO)引脚的主要作用是对外提供时钟,相当于一个有源晶振。MCO的时钟来源可以是PLLCLK/2、HSI、HSE或SYSCLK,具体选哪个由时钟配置寄存器RCC_CFGR的bit26~24,即MC0[2:0]进行设置。该时钟输出引脚除了对外提供时钟,还可以通过未接示波器监控MCO引脚的时钟输出来验证系统时钟配置是否正确。

通过前面对STM32系统时钟与其他时钟的简要介绍,可以明确各个时钟源与时钟之间的对应关系。STM32时钟系统的初始化是在system_stm32f10x.c文件的SystemInit函数中进行的,在SystemInit函数中默认设置的系统时钟大小如下。

① 锁相环时钟PLLCLK=72MHz;

② 系统时钟SYSCLK=72MHz;

③ AHB总线时钟HCLK(使用SYSCLK)=72MHz;

④ APB1总线时钟PCLK1=36MHz;

⑤ APB2总线时钟PCLK2=72MHz。

在设置时钟时,一定要仔细参考STM32的时钟系统图,以避免出现不必要的错误。这里需要说明一下,STM32的系统时钟默认是在SystemInit函数的SetSysClock函数中进行判断的,而其设置是通过宏定义进行的。SetSysClock函数如下。

上述代码非常简单,先判断宏定义的系统时钟频率为多少,再设置为相应的值。例如,默认的系统时钟频率为72MHz,则设置如下。

如果需要设置为36MHz,则只需要注释掉上面的代码,然后加入以下代码。

当设置好系统时钟后,可以通过变量SystemCoreClock获取系统时钟值。在system_stm32f10x.c文件中设置的代码如下。

另外,前面介绍的时钟很多是带使能控制的,如AHB总线时钟、Cortex内核时钟、各种APB1外设和APB2外设等。当需要使用某个模块时,需要先使能对应的时钟,这些内容在以后的嵌入式系统设计实例中再进一步讲解。

STM32时钟系统除了初始化在system_stm32f10x.c文件中的SystemInit函数中进行配置,其他配置主要通过stm32f10x_rcc.c库文件来实现,打开这个文件就会发现很多RCC时钟配置函数。

2. RCC时钟控制器

在前面讲解STM32时钟系统时,已经涉及了对RCC寄存器的相关配置。在STM32标准函数库中,RCC也作为一个结构体,用于实现STM32时钟系统的相关配置。

RCC寄存器说明如表4.15所示。

表4.15 RCC寄存器说明

每个寄存器上的不同位被定义为不同的功能。例如,时钟配置寄存器RCC_CFGR不同位的定义如下。

(1)bit1~0定义为SW[1:0],由软件置1或清0来选择系统的时钟源(SYSCLK)。

(2)bit3~2定义为SWS[1:0],由硬件置1或清0来指示哪一个时钟源被作为系统时钟。

(3)bit7~4定义为HPRE[3:0],由软件置1或清0来控制AHB时钟的预分频系数。

(4)bit10~8定义为PPRE1[2:0],由软件置1或清0来控制低速APB1时钟(PCLK1)的预分频系数。

(5)bit13~11定义为PPRE2[2:0],由软件置1或清0来控制高速APB2时钟(PCLK2)的预分频系数。

(6)bit15~14定义为ADCPRE[1:0],由软件置1或清0来确定ADC的时钟频率。

(7)bit16定义为PLLSRC,由软件置1或清0来选择PLL的输入时钟源,只有在关闭PLL时才能写入此位。

(8)bit17定义为PLLXTPRE,由软件置1或清0来选择PREDIV1分频因子的最低位。

(9)bit21~18定义为PLLMUL[3:0],由软件设置来确定PLL的倍频系数,只有在PLL关闭的情况下才可被写入。

(10)bit22定义为OTGFSPRE,由软件置1或清0来产生48MHz的全速USB OTG时钟。在RCC_APB1ENR寄存器使能全速OTG时钟之前,必须保证该位已经有效。如果全速OTG时钟被使能,则该位不能被清0。

(11)bit27~24定义为MCO[3:0],由软件置1或清0来设置微控制器的时钟输出。

(12)bit31~28为保留位,始终读为0。

RCC寄存器组中各个寄存器的功能与配置方法的详细描述可以参考《STM32F10x中文参考手册》中的RCC寄存器部分,这里不再详述。

在实际的嵌入式系统设计过程中,主要采用RCC库函数完成相关寄存器的配置工作,进而配置STM32的时钟系统,如时钟设置、外设复位和时钟管理等。RCC相关库函数放在stm32f10x_rcc.c库文件中,常用的RCC库函数有32个,如表4.16所示。

表4.16 常用的RCC库函数

下面简要介绍主要的RCC库函数。

(1)RCC初始化相关函数。

① RCC_DeInit函数:该函数将外设RCC寄存器重设为默认值。

例如,将RCC寄存器配置为默认值:

② RCC_ClearFlag函数:该函数用于清除RCC的复位标志位。

例如,清除复位标志位:

③ RCC_GetFlagStatus函数:该函数用于检查指定的RCC标志位设置与否。

表4.17 RCC_FLAG取值定义

例如,检查PLL时钟是否准备就绪:

(2)配置高速时钟源相关函数。

① RCC_HSEConfig函数:该函数用于设置外部高速晶振(HSE)。

表4.18 RCC_HSE状态定义

例如,打开外部高速晶振(HSE):

② RCC_WaitForHSEStartUp函数:该函数用于等待HSE起振。

RCC_WaitForHSEStartUp的使用步骤如下。

③ RCC_HSICmd函数:该函数用于使能或失能内部高速晶振(HSI)。

例如,使能内部高速晶振(HSI):

(3)设置PLL时钟源和倍频系数。

① RCC_PLLConfig函数:该函数用于设置PLL时钟源和倍频系数。

表4.19 RCC_PLLSource取值定义

表4.20 RCC_PLLMul取值定义

注意

这里必须正确使用设置,使PLL输出时钟频率不得超过72MHz。

例如,设置PLL时钟源为外部高速晶振HSE(8MHz),并设置PLL时钟输出为72MHz:

② RCC_PLLCmd函数:该函数用于使能或失能PLL。

例如,使能PLL时钟:

(4)设置系统时钟相关函数。

① RCC_SYSCLKConfig函数:该函数用于设置系统时钟(SYSCLK)。

表4.21 RCC_SYSCLKSource取值定义

例如,选择PLL作为系统时钟的时钟源:

② RCC_GetSYSCLKSource函数:该函数用于返回用作系统时钟的时钟源。

例如,检测外部高速时钟HSE是否作为系统时钟源:

(5)设置AHB时钟相关函数。

① RCC_HCLKConfig函数:该函数用于设置AHB时钟(HCLK)。

表4.22 RCC_HCLK取值定义

例如,设置AHB时钟为系统时钟:

② RCC_PCLK1Config函数:该函数用于设置低速AHB时钟(APB1总线时钟PCLK1)。

表4.23 RCC_PCLK1取值定义

例如,设置PCLK1时钟为系统时钟的1/2:

③ RCC_PCLK2Config函数:该函数用于设置高速AHB时钟(APB2总线时钟PCLK2)。

表4.24 RCC_PCLK2取值定义

例如,设置PCLK2时钟为系统时钟:

④ RCC_AHBPeriphClockCmd函数:该函数用于使能或失能AHB外设时钟。

表4.25 RCC_AHBPeriph取值定义

例如,使能DMA时钟源:

⑤ RCC_APB1PeriphClockCmd函数:该函数用于使能或失能APB1外设时钟。

表4.26 RCC_APB1Periph取值定义

例如,使能BKP和PWR时钟源:

⑥ RCC_APB2PeriphClockCmd函数:该函数用于使能或失能APB2外设时钟。

表4.27 RCC_APB2Periph取值定义

例如,使能GPIOA和SPI1时钟源:

(6)设置USB/ADC时钟相关函数。

① RCC_USBCLKConfig函数:该函数用于设置USB时钟(USBCLK)。

表4.28 RCC_USBCLKSource取值定义

例如,设置USB时钟为PLL时钟的1/1.5:

② RCC_ADCCLKConfig函数:该函数用于设置ADC时钟(ADCCLK)。

表4.29 RCC_ADCCLKSource取值定义

例如,设置ADC时钟为PCLK2时钟的1/2:

(7)配置低速时钟源相关函数。

① RCC_LSEConfig函数:该函数用于设置外部低速晶振(LSE)。

表4.30 RCC_LSE状态定义

例如,打开外部低速晶振LSE:

② RCC_LSICmd函数:该函数用于使能或失能内部低速晶振(LSI)。

例如,使能内部低速晶振LSI:

(8)设置RTC时钟相关函数。

① RCC_RTCCLKConfig函数:该函数用于设置RTC时钟(RTCCLK)。

表4.31 RCC_RTCCLKSource取值定义

例如,选择LSE作为RTC时钟源:

② RCC_RTCCLKCmd函数:该函数用于使能或失能RTC时钟。

例如,使能RTC时钟源:

(9)RCC中断相关函数。

① RCC_ITConfig函数:该函数用于使能或失能指定的RCC中断。

表4.32 RCC_IT取值定义1

例如,使能PLL准备中断:

② RCC_GetITStatus函数:该函数用于检查指定的RCC中断发生与否。

表4.33 RCC_IT取值定义2

例如,检查PLL中断是否发生:

③ RCC_ClearITPendingBit函数:该函数用于清除RCC的中断待处理位。

例如,清除PLL中断位:

上述就是主要的RCC库函数,在以后的嵌入式系统设计过程中会经常用到。通过对时钟系统和时钟控制器RCC的讲解,了解了STM32时钟源和各个时钟的特性功能及其配置方法。时钟系统及其相关配置对于STM32嵌入式系统设计是至关重要,在嵌入式系统设计实践前,一定要熟练掌握。

在熟悉了C语言的相关操作和STM32基本知识后,就能够进行简单的STM32实例程序设计了。下一章将正式开始讲解嵌入式系统设计实践。 POjvwAPAxnH4To1tFu4a7dh0WfqUWHggutyGrUs91/Q+Kvht53wWh8YkYO3D78u+

点击中间区域
呼出菜单
上一章
目录
下一章
×