【正点原子STM32连载】第八章 APM32E103时钟系统介绍摘自【正点原子】APM32E103最小系统板使用指南

1)实验平台:正点原子APM32E103最小系统板
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第八章 APM32E103时钟系统介绍

MCU都是基于时序控制的系统,本章将为APM32E103的时钟系统作一个简单的介绍,帮助读者更全面、系统地认识APM32E103的时钟系统结构,并掌握APM32E103的时钟配置。
本章分为如下几个小节:
8.1 认识时钟树
8.2 配置系统主频
8.3 开启和关闭外设时钟

8.1 认识时钟树

从数字电路的知识可以知道:任意复杂的电路控制系统都可以经由门电路组成的组合电路实现。APM32内部也是由多种多样的电路模块组合在一起实现的。若一个电路越复杂,在达到正确的输出结果前,它可能因为延时会有一些短暂的中间状态,而这些中间状态有时会导致输出结果会有一个短暂的错误,这叫做电路中的“毛刺现象”,如果电路需要运行得足够快,那么这些错误状态会被其它电路视为输入采样,最终形成一系列的系统错误。为了解决这个问题,在单片机系统中,设计时以时序电路控制替代纯粹的组合电路,在每一级输出结果前对各个信号进行采样,从而使得电路中某些信号即使出现延时也可以保证各个信号的同步,可以避免电路中发生的“毛刺现象”,达到精确控制输出的效果。
由于时序电路的重要性,因此在MCU设计时就设计了专门用于控制时序的电路,在芯片设计中称为时钟树设计。由此设计出来的时钟,可以精确控制单片机系统,这也是本小节要展开分析的。为什么是时钟树而不是时钟呢?一个MCU越复杂,时钟系统也会相应的变得复杂,如APM32E103的时钟系统就相对复杂,不像简单的51单片机,一个系统时钟就可以解决一切。对于APM32E103系列的芯片,正常工作的主频可以达到120MHz,但并不是所有外设都需要系统时钟这么高的频率,比如独立看门狗和RTC只需要40KHz的时钟频率以及32.768KHz的时钟频率即可工作。同一个电路,时钟频率越快功耗就越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的MCU一般都是采取多时钟的方法来解决这些问题。
APM32本身相对复杂,外设资源非常丰富,为了保证低功耗,APM32上电或复位后默认不开启这些外设功能,即不为这些外设提供时钟。用户可以根据自己的需要决定APM32芯片要使用的功能,即为这些外设提供时钟,同时也要配置时钟源和时钟的频率。
【正点原子STM32连载】第八章 APM32E103时钟系统介绍摘自【正点原子】APM32E103最小系统板使用指南_第1张图片

图8.1.1 APM32E103时钟树
上图就是APM32E103的时钟树框图
①:该部分是四个时钟源,分别为:LSI(内部低速时钟)、LSE(外部低速时钟)、HSI(内部高速时钟)、HSE(外部高速时钟),这四个时钟源是整个时钟树的“源头”。
②:该部分为PLL(锁相环),这一部分内部是较为复杂的模拟电路,主要用于对输入的时钟信号倍频后输出,输入的时钟源可以来自HSI/2、HSE或者HSE/2。
③:该部分主要用于选择系统时钟的时钟源,从图中可以看出,该选择器的输入有HSE、HSI和PLL1,输出作用于SYSCLK,而SYSCLK通过AHB Prescaler分频后输出,该输出将直接或间接地作为AHB、APB1和APB2总线上外设的时钟源,其中FCLK就是MCU内核的时钟,这部分对整个系统的运行起着至关重要的作用。
④:AHB Prescaler分频后的时钟再通过APB1 Prescaler和APB2 Prescaler分频后分别作为APB1和APB2总线上外设的时钟。
8.1.1 时钟源
APM32E103有四个时钟源,分别为:LSI(内部低速时钟)、LSE(外部低速时钟)、HSI(内部高速时钟)、HSE(外部高速时钟)。按低速时钟源和高速时钟源可分为LSI、LSE和HSI、HSE,按内部时钟源和外部时钟源可分为LSI、HSI和LSE、HSE,其中内部时钟源LSI和HSI是MCU芯片内部的时钟源,芯片上电后即可输出时钟,无需借助外部电路。
①:外部高速时钟HSE(High Speed External Clock)
该时钟信号由外部晶体、陶瓷谐振振荡器或外部时钟源产生,频率范围为4MHz~16MHz,正点原子APM32E103最小系统板使用频率为8MHz的晶体振荡器产生该时钟信号。
②:外部低速时钟LSE(Low Speed External Clock)
该时钟信号由外部晶体、陶瓷谐振振荡器或外部时钟源产生,频率为32.768KHz,主要作为RTC的时钟源,正点原子APM32E103最小系统板使用频率为32.768KHz的晶体振荡器产生该时钟信号。
③:内部高速时钟HSI(High Speed Internal Clock)
该时钟信号由内部RC振荡器产生,频率为8MHz。
④:内部低速时钟LSI(Low Speed Internal Clock)
该时钟信号由内部RC振荡器产生,频率为36KHz~51KHz(受温度、电压影响),主要作为独立看门狗或RTC的时钟源。
MCU上电时默认会使用HSI作为系统时钟启动,只有完成了相关的时钟配置,MCU才会根据配置切换到对应的时钟源,因此同时了解这几个时钟源是很有必要的,下文将会有提到时钟配置方法的章节。
8.1.2 锁相环
锁相环(PLL)是自动控制系统中常用的一个反馈电路,在APM32主控中,锁相环主要有对输入时钟进行净化和倍频这两个作用,前者是利用锁相环电路的反馈机制实现的,后者是用于使APM32主控稳定地工作于更高的时钟频率。
在APM32中,锁相环的输出时钟可以配置为系统时钟的时钟源,如下图所示:
【正点原子STM32连载】第八章 APM32E103时钟系统介绍摘自【正点原子】APM32E103最小系统板使用指南_第2张图片

图8.1.2.1 PLL输出时钟作为系统时钟示例
上图说明了APM32锁相环的输出时钟配置为系统时钟的时钟源的一个过程。首先,从最左侧的OSC_OUT和OSC_IN开始,这两个引脚分别连接到外部晶振的两端(就是HSE的时钟信号)。其次,紧跟着箭头方向遇到了第一个分频器PLLHSEPSC。在这个分频器中,我们可以选择设置不分频(或者叫1分频)或选择2分频(/2),这里我们选择不分频。然后,跟着箭头往下走遇到一个开关PLLSEL,这个开关可以选择HSE或者HIS作为其时钟输出,这里我们选择HSE作为时钟输出,紧接着我们便遇到了锁相环PLL(也叫倍频器),其倍频系数可以选择2到16,另外根据APM32数据手册我们可以知道HSECLK振荡器频率的典型值为8MHz,因此得到设置的倍频因子为15,也就是说时钟频率为原来的15倍,经过倍频后的时钟为PLLCLK,也就是120MHz。
我们通过上面这一过程较为直观的了解到了APM32锁相环输出时钟配置为系统时钟的过程,下面我们将通过配置寄存器的方式来加深我们对这一过程的理解,如下表所示:
17 PLLHSEPSC R/W 配置作为 PLL 时钟源的 HSECLK 分频(HSECLK Prescaler Factor for PLL Clock Source)分频 HSECLK 后作为 PLL 输入时钟。
0:HSECLK 不分频
1:HSECLK2 分频
注:只能在关闭 PLL 时才能写入此位。
表8.1.2.1 PLL配置寄存器PLLHSEPSC位
上面过程中基本可以通过设置时钟配置寄存器(RCM_CFG)来完成。当该位写入0时,则选择设置不分频;该位写入1时,则选择设置2分频。
16 PLLSRCSEL R/W 选择 PLL 时钟源(PLL Clock Source Select)选择 PLL 输入时钟源。
0:HSICLK RC 振荡器时钟经 2 分频后作为 PLL 输入时钟
1:HSECLK 作为 PLL 输入时钟
注:只能在关闭 PLL 时才能写入此位。
表8.1.2.2 PLL配置寄存器PLLSRCSEL位
上面这一过程同样可以通过设置时钟配置寄存器(RCM_CFG)来完成。当该位写入0时,则选择HSICLK(内部高速时钟)经过2分频后作为时钟源;该位写入1时,则选择HSECLK(外部高速时钟)作为时钟源。
1:0 SCLKSEL R/W 选择系统时钟时钟源(System Clock Source Select)选择系统时钟源。
在从停止或待机模式中返回时或直接或间接作为系统时钟的 HSE 出现故障时,由硬件强制选择 HSICLK 作为系统时钟(如果时钟安全系统已经启动)
00:HSICLK 作为系统时钟
01:HSECLK 作为系统时钟
10:PLLCLK 作为系统时钟
11:不可用
表8.1.2.3 时钟配置寄存器SCLKSEL位
SCLKSEL位用于选择SYSCLK的时钟源,可配置HSICLK、HSECLK或PLLCLK作为SYSCLK的时钟源。对于示例,将该位写入2,即可选择频率为120MHz的PLLCLK作为SYSCLK时钟源。
8.1.3 系统时钟
APM32的系统时钟(SYSCLK)为整个芯片的绝大多数外设和内核提供时钟信号,对于相同稳定运行的系统,时钟信号的频率越高,系统运行的速度也就越快,单位时间能够处理的事务也就越多。在上一小节中,将频率为8MHz的HSE时钟信号通过PLL倍频为120MHz的时钟信号作为SYSCLK的时钟源。SYSCLK时钟信号可直接或间接地作为AHB总线、APB1总线、APB2总线和内核的时钟源。
【正点原子STM32连载】第八章 APM32E103时钟系统介绍摘自【正点原子】APM32E103最小系统板使用指南_第3张图片

图8.1.3.1 各总线和内核时钟配置示例
①:USBDCLK:即USB时钟。USB时钟是由PLLCLK经过USB预分频器得到,其时钟频率通常为48MHZ,由于其对时钟要求较高,所以USB时钟只能是由HSE倍频得到,不能使用HSI倍频。
②:Cortex System Clock:即Cortex系统时钟。Cortex 系统时钟由 HCLK 8 分频得到,等于15MHz,Cortex 系统时钟用来驱动内核的系统定时器 SysTick,SysTick一般用于操作系统的时钟节拍,也可以用做普通的定时。
③:HCLK:即AHB时钟。AHB时钟的全称是Advanced High-Performance Bus,即高性能时钟总线,其时钟频率最大为120MHz,主要用于高性能模块(如CPU、DMA、DSP)之间的链接,即当调用这项模块时需要开启AHB时钟。
④:APB1总线时钟:低速总线时钟,时钟信号来源于输入的AHB时钟信号再经APB1预分频后输出,APB1的分频系数可选1,2,4,8,16 ,最大时钟频率为60MHz,在APM32E103中通常用于驱动DAC、CAN、USB、I2C、USART2/3(UART)、SP12/SP13、基本定时器和通用定时器等系统外设。
⑤:APB2总线时钟:高速总线时钟,与APB1总线时钟工作原理类似,时钟信号来源于输入的AHB时钟信号再经APB2预分频后输出,APB2的分频系数可选1,2,4,8,16,最大时钟频率为120MHz,该时钟信号通常用于驱动ADC、USART1、SPI1、高级定时器、GPIO、EXTI、AFIO等系统外设。
8.2 配置系统主频
APM32E103在默认情况下,使用频率为8MHz的HSI作为系统时钟的时钟源,因此无需外部晶振也能够正常烧录和运行程序。
在上一小节中,介绍了如何配置时钟树,以让内核在120MHz的主频下运行,120MHz是Geehy官方针对APM32E103推荐的最大工作频率。
本节将介绍如何在代码中具体的对APM32E103的时钟进行配置。本书配置实验例程都是在main()函数中调用sys_apm32_clock_init()对APM32E103的时钟进行配置的,如下所示:

int main(void)
{
    /* 省略其它无关的代码 */
    
    sys_apm32_clock_init(15);   /* 配置系统时钟 */
    
    /* 省略其它无关的代码 */
    
}

相信读者对sys_apm32_clock_init()函数传入的参数并不陌生,这就是在上一小节示例中配置相关寄存器中PLL的倍频因子(倍频系数)15。
sys_apm32_clock_init()函数的代码比较多,请读者在实验例程中查看本函数的具体实现,本小节中列出该函数的关键代码,如下所示:

uint8_t sys_apm32_clock_init(uint32_t plln)
{
    RCM_Reset();                            /* 复位RCM */
    RCM_ConfigHSE(RCM_HSE_OPEN);            /* 使能外部高速时钟 */
    
    if (RCM_WaitHSEReady() != SUCCESS)      /* 等待HSECLK稳定 */
    {
        return 1;
    }
    
    FMC_EnablePrefetchBuffer();             /* 使能Flash预取缓冲区 */
    FMC->CTRL1_B.WS = 4;                    /* 设置Flash访问等待周期个数为4个 */
    
    RCM_ConfigAHB(RCM_AHB_DIV_1);           /* 设置AHB时钟的预分频系数为1 */
    RCM_ConfigAPB2(RCM_APB_DIV_1);          /* 设置APB2时钟的预分频系数为1 */
    RCM_ConfigAPB1(RCM_APB_DIV_2);          /* 设置APB1时钟的预分频系数为2 */
    
    RCM_ConfigPLL(RCM_PLLSEL_HSE, (RCM_PLLMF_T)(plln - 2)); /* 配置PLL */
    RCM_EnablePLL();                                        /* 使能PLL */
    while (RCM_ReadStatusFlag(RCM_FLAG_PLLRDY) == RESET);   /* 等待PLL时钟就绪 */
    
    RCM_ConfigSYSCLK(RCM_SYSCLK_SEL_PLL);            /* 选择PLL1CLK作为系统时钟 */
    while (RCM_ReadSYSCLKSource() != RCM_SYSCLK_SEL_PLL);/* 等待系统时钟配置成功 */
    
    return 0;
}

跟上一小节介绍的一致,AHB、APB2和APB1总线的预分频器分别配置为不分频、不分频和2分频;根据函数的传入参数配置PLL配置寄存器中PLL的值,并将PLL的时钟源配置为HSE;将PLL1CLK配置为SYSCLK的时钟源。上面列出的代码仅是一些关键代码,除了列出代码的操作外还需要一些额外的操作,例如使能HSE并等待HSE稳定等,建议读者打开本书配套的任意实验例程,在sys.c文件中详细地查看本函数的具体实现。
通过sys_apm32_clock_init()函数就能够非常方便地配置系统主频了,例如,在考虑对MCU的性能需求和功耗的场景中,可以适当降低MCU的主频,来达到性能和功耗的平衡。
8.3 开启和关闭外设时钟
在前面的章节中,分析了APM32E103的时钟系统和相关的配置步骤,但是在使用某些外设之前,还需要手动开启相应外设的时钟,这是因为MCU在上电或复位后会默认关闭大部分外设的时钟,以降低功耗,因此,在程序开发中,对于某些开启的时钟但不再使用的外设,也应将其时钟关闭。
APM32E103外设的时钟由RCM(Reset and Clock Management,复位与时钟管理)控制,RCM中控制外设时钟的寄存器被按总线进行分类,涉及了三个寄存器,如下表所示:
RCM_AHBCLKEN AHB 外设时钟使能寄存器
RCM_APB2CLKEN APB2 外设时钟使能寄存器
RCM_APB1CLKEN APB1 外设时钟使能寄存器
表8.3.1 RCM中控制外设时钟的寄存器
其中RCM_AHBCLKEN是用于控制AHB总线上外设的时钟的;RCM_APB1CLKEN用于控制APB1总线上外设的时钟;RCM_APB2CLKEN用于控制APB2总线上外设的时钟。
下面以GPIOA为例,介绍如何配置RCM中相应的寄存器来开启或关闭GPIOA的时钟。首先要确定GPIOA是在AHB总线、APB1总线还是APB2总线上,这里可以借助APM32E103的地址映射来判断,APM32E103的地址映射图,如下图所示:
【正点原子STM32连载】第八章 APM32E103时钟系统介绍摘自【正点原子】APM32E103最小系统板使用指南_第4张图片

图8.3.1 APM32E103地址映射图

从上图中可以看出,Port A~Port G都是APB2总线上的外设,那么就能够确定了GPIOA是位于APB2总线上的外设。
因为在APM32E103中RCM控制外设时钟的寄存器只有三个,所以也很容易确定RCM_APB2CLKEN寄存器就是控制GPIOA时钟的寄存器。在RCM_APB2CLKEN寄存器中可以找到PAEN位,该位就是用于控制GPIOA外设时钟使能和禁止的,如下表所示:
2 PAEN R/W 使能 IO 端口 A 时钟(I/O Port A Clock Enable)
0:禁止
1:使能
表8.3.2 RCM_APB2CLKEN寄存器PAEN位
由上图可知,往RCM_APB2CLKEN寄存器的PAEN位写入1后,就能配置使能GPIOA外设的时钟,往RCM_APB2CLKEN寄存器的PAEN位写入0后,就能配置禁止GPIOA外设的时钟。
在Geehy的标准库中提供了开启和关闭总线外设时钟的函数(函数的实现在apm32e10x_rcm.c文件中,调用时需包含apm32e10x_rcm.h的头文件),如下所示:

void RCM_EnableAHBPeriphClock(uint32_t AHBPeriph);
void RCM_DisableAHBPeriphClock(uint32_t AHBPeriph);
void RCM_EnableAPB2PeriphClock(uint32_t APB2Periph);
void RCM_DisableAPB2PeriphClock(uint32_t APB2Periph);
void RCM_EnableAPB1PeriphClock(uint32_t APB1Periph);
void RCM_DisableAPB1PeriphClock(uint32_t APB1Periph);

那这些函数需要传入那些参数呢?
在apm32e10x_rcm.h文件中,通过枚举列出了大部分的外设,如下所示:

/**
 * @brief   AHB2 peripheral
 */
typedef enum
{
    RCM_APB2_PERIPH_AFIO    = BIT0,
    RCM_APB2_PERIPH_GPIOA   = BIT2,
    RCM_APB2_PERIPH_GPIOB   = BIT3,
    RCM_APB2_PERIPH_GPIOC   = BIT4,
    RCM_APB2_PERIPH_GPIOD   = BIT5,
    RCM_APB2_PERIPH_GPIOE   = BIT6,
    RCM_APB2_PERIPH_GPIOF   = BIT7,
    RCM_APB2_PERIPH_GPIOG   = BIT8,
    RCM_APB2_PERIPH_ADC1    = BIT9,
    RCM_APB2_PERIPH_ADC2    = BIT10,
    RCM_APB2_PERIPH_TMR1    = BIT11,
    RCM_APB2_PERIPH_SPI1    = BIT12,
    RCM_APB2_PERIPH_TMR8    = BIT13,
    RCM_APB2_PERIPH_USART1  = BIT14,
    RCM_APB2_PERIPH_ADC3    = BIT15
} RCM_APB2_PERIPH_T;

因为GPIOA的外设时钟由RCM_APB2CLKEN寄存器中的PAEN位控制,因此调用函数RCM_EnableAPB2PeriphClock ()并传入参数RCM_APB2_PERIPH_GPIOA就能够开启GPIOA的外设时钟了,同样的若要关闭GPIOA的外设时钟,则调用函数RCM_DisableAPB2PeriphClock()并传入参数RCM_APB2_PERIPH_GPIOA即可。

你可能感兴趣的:(stm32,嵌入式硬件,单片机)