专题三stm32时钟树及相关库函数

专题三stm32时钟树及相关库函数
专题三stm32时钟树及相关库函数

专题三STM32时钟及相关库函数

一、STM32 MCU 时钟树

STM32 MCU系统时钟树由系统时钟源、系统时钟SYSCLK和设备时钟等部分组成。

图1 STM32芯片时钟树

从图1可看到,芯片时钟源有4个:

1.高速外部时钟HSE(4-16MHz);

2.高速内部时钟HSI(8MHz);

3.低速外部时钟LSE(32.768kHz);

4.低速内部时钟LSI(40kHz)。

从图1还可看到,芯片内部系统时钟SYSCLK的来源有3个。

1.高速外部时钟HSE(4-16MHz);

2.高速内部时钟HSI(8MHz);

3.HSE或HSI通过锁相环2-16倍频后的锁相环时钟PLLCLK。

上述的系统时钟SYSCLK经AHB预分频器后得到AHB总线时钟HCLK,继而得到APB1和APB2总线时钟。要注意的是:APB1挂接的外设有:DAC、UART2-5,TIM2-7、USB、I2C1-2等;APB2挂接的外设有:ADC1-3,GPIOA-G,TIM1,TIM8等。每次要使用STM32的相关外设,都要先使能对应的时钟。

二、STM32的启动

2.1 STM32的启动模式

STM32处理器支持3种系统启动模式,且对应的存储介质均是芯片内置的。在每个STM32的芯片上都有两个引脚BOOT0和BOOT1,这两个引脚在芯片复位时的电平状态决定了芯片复位后从哪个区域开始执行程序。

表1. STM32xx系列处理器的启动模式

第1种启动方式是最常用的用户FLASH启动,正常工作就在这种模式下,STM32的FLASH可以擦除10万次。

第2种方式是系统存储器启动方式,即我们常说的串口下载方式(ISP)。STM32中自带的BootLoader就是在这种启动方式中,如果出现程序硬件错误的话,可以切换到该模式下重新烧写Flash即可恢复正常。

第3种启动方式是STM32内嵌的SRAM启动,该模式用于调试。

上述三种模式,各有作用。如果要进行程序下载,就必须将启动模式调整到第二种方式,程序下载完毕后,再回复到第一种方式,就可以正常工作了。

2.2 STM32的启动文件

启动文件:建立工程文件时,keil MDK会提示“复制启动文件STM32F10x.s 到项目文件夹下”。但是MDK提示的这个启动文件只定义了3个串口,4个定时器,具体到某个型号,每种STM32芯片都不一样。比如有的芯片器件是有5个串口,6个定时器的,如果用MDK提供的启动文件,就有2个串口,2个定时器找不到定义导致不能使用。在3.5版本的固件库下,有加了后缀的启动文件:Startup_stm32f10x_ld.s

Startup_stm32f10x_md.s

Startup_stm32f10x_hd.s

上面,有后缀ld,md,hd,这三个后缀是什么意思呢?原来是这样的:ld Low-density 小容量 16-32K

md Medium-density 中容量 64-128K

hd High-density 大容量 256-512K

xl 超大容量512-1024K

vl value line devices 超值型

cl Connectivity line devices 互联型

小容量:flash<=32K

中容量:64K<=flash <=128K

大容量:256K<=flash

根据你使用的芯片型号,就可以确定要使用哪种启动文件了。比如,我们使用STM32F103RB,这款芯片是128K的容量,那么就应该选启动文件Startup_stm32f10x_md.s。

启动文件主要做了3个工作:

1.定义和初始化堆栈;

2.定义复位向量并初始化;

3.定义中断向量表及其相应的异常处理程序。

4.最后,开始main()函数。

注意,在3.5版固件库中,升级后的启动文件还调用了system_stm32f10x.c 文件中的SystemInit()函数,配置系统时钟到PLL倍频下的72MHz。因此,如果使用新版的启动文件,main()函数就无需再调用SystemInit()函数了。而如果工程文件中包含的是旧版的STM32F10x.s文件,该文件是没有系统时钟配置语句的,因此,用户进入main()函数需要自己调用SystemInit()函数。

三、STM32芯片的时钟设置

从前面的图1可以看到,要设置STM32芯片的时钟,有以下几个方面要考虑:

1.芯片的时钟源选择HSE,HIS还是其他?

2.SYSCLK系统时钟应该选择哪个?

3.各总线的分频系数?

4.PLL锁相环的倍频系数?

5.各个外设对应的时钟是开启呢,还是禁止?

上面5个问题,STM32统一用RCC来解决。我们首先来看下与RCC相关的寄存器有哪些。

typedef struct

{

__IO uint32_t CR;

__IO uint32_t CFGR;

__IO uint32_t CIR;

__IO uint32_t APB2RSTR;

__IO uint32_t APB1RSTR;

__IO uint32_t AHBENR;

__IO uint32_t APB2ENR;

__IO uint32_t APB1ENR;

__IO uint32_t BDCR;

__IO uint32_t CSR;

} RCC_TypeDef;

上面这个结构体类型的定义有这么多寄存器,每个寄存器简要说明如下:1)CR:时钟控制寄存器,选择stm32芯片时钟源,在HSE,HSI,PLL之间进

行选择。

2)CFGR:时钟配置寄存器,选择SYSCLK时钟源,以及各总线时钟的分频系数,

以及PLL的倍频系数

3)CIR:时钟中断寄存器,进行时钟中断的使能和清除。

4)APB2RSTR:APB2外设复位寄存器,进行APB2外设的复位,与APB2连接

的外设有USART1,SPI1,TIM1,ADC2,ADC1,等等。

5)APB1RSTR:APB1外设复位寄存器,进行APB1外设的复位,有DAC,PWR,

CAN2,I2C等。

6)AHBENR:AHB外设使能寄存器。包括以太网MAC,全速USB OTG时钟使能,

SRAM时钟使能,DMA2,DMA1时钟使能等。

7)APB2ENR:APB2外设时钟使能寄存器,APB2连接外设的使能,有USART1,

SPI1,TIM1,ADC2,ADC1,IOPE(GPIOE),IOPD(GPIOD),IOPC(GPIOC),IOPB(GPIOB),IOPA(GPIOA)等。

8)APB1ENR:APB1外设时钟使能寄存器,APB1连接的外设有DAC,CAN2,

CAN1,I2C2,I2C1,UART5,UART4,USART3,USART2,SPI3,SPI2,TIM7-2等。

9)初始阶段我们比较关注的有CR,CFGR,

10)BDCR:备份域控制寄存器

11)CSR:控制/状态寄存器,对看门狗、软件复位等的状态进行标识。

如果要进行时钟设置,就是对上面的寄存器进行操作。有两种方法,一种是直接进行寄存器的赋值,还有一种方法是调用ST公司提供的库函数,用户无需面对寄存器。

以与GPIO外设有关的寄存器为例,APB2ENR,它将使能GPIO的时钟。在程序中,如果要使能GPIO时钟,比如使能GPIOA,已知APB2ENR的bit2位是使能GPIOA,那么寄存器直接操作的方法是:

APB2ENR|=1<<2; //或运算,1有效。

第二种方法就是调用库函数。

四、介绍两种库函数

4.1 外设时钟使能函数

与rcc有关寄存器设置的库函数都在stm32f10x_rcc.c以及stm32f10x_rcc.h 文档中。上面说要使能GPIO时钟,因为GPIO是APB2的外设,因此就是使能APB2相应外设时钟。库函数是:

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)

函数名:RCC_APB2PeriphClockCmd()

功能描述:使能或禁止高速APB2外设时钟。

输入参数1:RCC_APB2Periph,定义了APB2外设在RCC_APB2ENR中的相应位地址。这个参数可以是下列值:

RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB, RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,等等,具体可以在stm32f10x_rcc.c文件中查看函数说明。

输入参数2:NewState,外设时钟的新状态,可以是DISABLE,ENABLE两种。

返回值:无

调用示范:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB时钟

函数的具体定义引用如下:

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)

{

/* Check the parameters */

assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));

assert_param(IS_FUNCTIONAL_STATE(NewState));

if (NewState != DISABLE)

{

RCC->APB2ENR |= RCC_APB2Periph;

}

else

{

RCC->APB2ENR &= ~RCC_APB2Periph;

}

}

4.2 与SYSCLK时钟设置有关的函数void SystemInit(void)

在3.5版本的库里,上面第节介绍的路径下,有个system_stm32f10x.c文件,在这个文件中,定义了一个函数void SystemInit(void)。这个函数将stm32芯片的时钟源设置成了PLL倍频模式,SYSCLK直接被设置为了72MHz。

我们看这个函数的定义:

void SystemInit (void)

{

/* Reset the RCC clock configuration to the default reset state(for debug purpose) */

/* Set HSION bit */

RCC->CR |= (uint32_t)0x00000001;

/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */

#ifndef STM32F10X_CL

RCC->CFGR &= (uint32_t)0xF8FF0000;

#else

RCC->CFGR &= (uint32_t)0xF0FF0000;

#endif /* STM32F10X_CL */

/* Reset HSEON, CSSON and PLLON bits */

RCC->CR &= (uint32_t)0xFEF6FFFF;

/* Reset HSEBYP bit */

RCC->CR &= (uint32_t)0xFFFBFFFF;

/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */

RCC->CFGR &= (uint32_t)0xFF80FFFF;

SetSysClock();

#ifdef VECT_TAB_SRAM

SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */

#else

SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */

#endif

}

红色第1行:RCC->CR |= (uint32_t)0x00000001; 设置时钟和复位寄存器,选择内部8MHz高速RC振荡器作为时钟源。

红色第3行:SetSysClock(); 设置系统时钟到72MHz. 也就是SYSCLK是72MHz。

红色第2行:RCC->CFGR &= (uint32_t)0xF8FF0000; CFGR的7:4是0,AHB 不分频。

在上述设置下,SYSCLK是72MHz。在2.2节启动文件中,我们讲到,如果用3.5版固件库中的Startup_stm32f10x_md.s,那么该文件已经调用了SystemInit()函数,让SYSCLK设置成72MHz,因此main()函数中就不用再调用它进行时钟设置了。

相关文档
最新文档