本次主要讲了从寄存器的思路讲了两个函数的作用,分别是SystemInit()和SetSysClockTo72()。要学会这两个函数首先要完全理解时钟框图[7]STM32基础知识-时钟系统,我在这篇博客里面有比较详细的讲解。
库函数都是通过底层控制寄存器实现对硬件的控制,所以等会会一直对应着《STM32中文参考手册V10》的6.3小节RCC寄存器描述来对应的讲他究竟调用了哪些寄存器,要实现的功能是什么。我个人觉得通过文字还是比较难讲清楚的,我也不可能像原子哥那样一行一行的讲代码(毕竟写博客的初衷是给我复习用的)所以如果有不懂的可以移步原子哥的视频【正点原子】STM32开发板实验教程(F103)第20讲。
SystemInit(),顾名思义就是一个初始化函数,值得一提的是,我们在main函数中确从来没有主动调用过这个函数,其原因是在执行main函数的时候,他会默认先执行SystemInit函数,所以就无需去调用这个函数了。
这个函数的功能是怎么实现的,我直接先贴源码,然后把有的部分着重说明一下,其实STM32官方库中已经有英文注释了,英语好的人直接就能看懂。
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 //我们的芯片是HD大容量芯片,通过判定下面一条语句无效,执行else
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;
#ifdef STM32F10X_CL
/* Reset PLL2ON and PLL3ON bits */
RCC->CR &= (uint32_t)0xEBFFFFFF;
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x00FF0000;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
#else
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
/* Configure the Flash Latency cycles and enable prefetch buffer */
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
}
SystemInit()的第一条语句是RCC->CR |= (uint32_t)0x00000001;
在之前用寄存器点亮LED灯时,也用过类似的语句,它的目的是把RCC的CR寄存器的第0位配置成1,具体实现的作用要见STM32中文参考手册,如下图所示:
通过图片可以看出第一条语句的作用是打开HSI时钟源。
紧接着下一个语句是RCC->CFGR &= (uint32_t)0xF0FF0000;
通过&=运算,对寄存器的0-15位和24-27位置0,同样是通过对比STM32中文参考手册的CR寄存器配置,其目的就是上面的注释, Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits,如下图所示:
总之SystemInit()就是一个初始化函数,具体怎么实现的功能,可以通过STM32中文参考手册了解。
这个函数也是,英文学得好话直接就知道,这个函数的目的是使系统时钟设为72MHz,除了72MHz,系统时钟还可以设置为其他很多频率,如下面的代码。
#ifdef SYSCLK_FREQ_HSE
static void SetSysClockToHSE(void); //系统时钟直接采用HSE时钟源,8MHz
#elif defined SYSCLK_FREQ_24MHz//各种系统时钟频率的设置函数
static void SetSysClockTo24(void);
#elif defined SYSCLK_FREQ_36MHz
static void SetSysClockTo36(void);
#elif defined SYSCLK_FREQ_48MHz
static void SetSysClockTo48(void);
#elif defined SYSCLK_FREQ_56MHz
static void SetSysClockTo56(void);
#elif defined SYSCLK_FREQ_72MHz
static void SetSysClockTo72(void);
#endif
SetSysClockTo72()的代码太长了我就不直接贴了,大致思路和 SystemInit()函数一样,也是通过对比STM32中文参考手册来了解它具体实现的功能。
首先定义了两个变量,用于等会if语句判定用的
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
其次对CR寄存器进行配置,目的是开启HSE时钟
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
这里对RCC_CR_HSEON进行了宏定义,但是通过对比STM32中文参考手册发现功能确实是开启HSE时钟
#define RCC_CR_HSEON ((uint32_t)0x00010000)
其次是判定HSE时钟是否准备就绪, RCC_CR_HSERDY仍然是一个宏定义值,同样是通过配置CR寄存器的位17来判定HSE是否准备就绪。
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
如果准备就绪了,就对之前设定的判定变量赋值,然后进行下一条语句
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if语句判定,继续执行,这里的flash指的是闪存时间,具体可参考《STM32F10xxx闪存编程参考手册》,这里不过多描述(其实也不重要)。后面三条语句和前面一样是通过寄存器配置,实现的功能见注释。
if (HSEStatus == (uint32_t)0x01)
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK/2 */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
这里上一张图,三条语句要实现的三个功能分别是HCLK = SYSCLK, PCLK2 = HCLK,PCLK1 = HCLK/2,所以就要AHB和APB1不分频,APB2二分频,而CFGR这个寄存器就是设定分频系数的。
这段代码同样是配置寄存器,但是要注意的是,他用了一个或运算,将很多步骤同步完成了,实现的目的是: PLL configuration: PLLCLK = HSE * 9 = 72 MHz
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
《STM32开发指南-库函数版本》4.3小姐
《STM32中文参考手册V10》-第六章