技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152
systeminit这个函数对,时钟系统有配置,是通过配置寄存器实现的.
需要看上一讲之后再看这一讲
复习一下.
这里CR寄存器,CFGR寄存器,AHBENR寄存器,APB2ENR寄存器,APB1ENR寄存器,对应的寄存器
用法可以在上讲或者文档中找到.
然后打开,之前的跑马灯实验,库函数版,来看一下代码
找到系统初始化函数
看之前先要准备,时钟框图和寄存器文档.
可以看到,这里
RCC->CR|=(uint32_t)0x00000001
可以看到,这里首先把时钟寄存器的CR寄存器的,最低位设置为1,其他位不变.
可以看到他的注释是SET HSION BIT,也就是打开内部高速时钟寄存器,
去看一下文档,是不是说,这么配置的
可以去看一下,这里HSION,这里,只要是把位0设置为1,就是把内部8MHZ的振荡器打开.
HSI RC打开.
再看一下,这里,#ifndef STM32F10X_CL
因为咱们这里,设置的是大容量的HD
所以,这里走的时候,因为设置了大容量STM32F10X_HD
所以这里会走下面的分支
/* 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 SW...这些设置一个初始值,设置一个默认值.
注意这里的这个STM32F10X_CL这里的这个意思是,类似于系统定义的一个宏定义,这个每个系列的芯片
这个值也不一样.具体对于STM32F10X_CL的讲解,有个博文对他说了一下.
这部分也是把相应的位设置为0
这些都是把对应的位设置为0,也就是可以看到8的地方,只要不是F的地方,就有0的位置,也就是说,会把
对应0的位置,进行与运算从而设置成了0.
&=这个与运算
这里可以对照看看PLLON这个寄存器,就是把位24设置为了0
这里的判断跟上面的判断是一样的,然后,这里的意思是初始化的时候把所有的
中断和相应的位都关掉设置为0
这部分代码也是上面这个意思,做初始化用的.
这里的SetSysClock,这个是很重要的.
这次说的重点.
去看看定义的位置
系统时钟结构体定义了不同频率的时钟.
去看看.c文件中,可以看到下面的文件中,开始的时候
可以看到定义初始化的频率了已经定义了72mhz的.
因为这里设置的是72mhz的,所以上面的代码会调用
SetSysClockTo72(void);
这个代码会重点看一下.一行一行的看
去找到RCC_CR_HSEON这个定义.
可以看到,这里,有个第
#define RCC_CR_HSEON ((uint32_t)0x00010000) /*!< External High Speed clock enable */
4*4=16,也就是这里的1是第16位.
可以看到使能的是外部高速时钟使能.打开外部高速时钟.
然后上面的部分是,等待高速外部时钟稳定.
打开时钟源以后,都需要等待他稳定以后,再去使用才行.
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
//这里这个do循环是用来等待时钟稳定,这里有个标志位可以判断时钟是否稳定
//这里可以看到通过获取RCC->CR 的RCC_CR_HSERDY这个ready位,来判断
//这个时钟是否已经稳定的.
去这个rdy的定义可以看到
#define RCC_CR_HSERDY ((uint32_t)0x00020000) /*!< External High Speed clock ready flag */.
这个第17位是CR时钟的就绪位,
如果这位是1的话,那么就代表外部的时钟已经就绪了.
接下来再看:
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
//这里如果就绪了就把HSEStatus设置为就绪0x01
//如果没有就绪就设置为0x00
接下来看,如果就绪了.
就:
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;
//看这几个对flash的ACR寄存器操作的代码.
//关于这个flash的配置,可以参考下面的文档.
去看
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;
//这部分代码其实就是
//因为内部CPU的速度要比这个flash的速度快很多
//所以这里要等待flash时钟.
可以看弹道当时钟达到48mhz,和72mhz的频率的时候,时钟有两个等待状态.
可以看到这里:
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
有两个等待状态.
接下来:
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
//这个HCLK =SYSCLK可以看到
HCLK=SYSCLK这个意思是,把这个AHB 这个分频器设置为1
之前已经说了这个:
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
//这个CFGR这寄存器,实际上是可以用来配置,选择哪个时钟源
//以及给时钟源设置分频系数
去看一下CFGR寄存器
这里可以看到,这个不分频,要设置位0XXX;不分频
去看一下代码
然后去看看这个:
RCC_CFGR_HPRE_DIV1
定义
可以看到设置的就是不分频.也就是上面文档中标注的.
这里可以看到这里设置PCLK2=HCLK可以看到,
意思就是下面图中的情况,当APB2的,分频系数设置为1的时候
这个时候PCLK2=HCLK
去看文档的话,
可以看到APB2分频器,也需要设置为HCLK的时候,不分频的时候,也是设置为0xxx
去看一下.
看到定义的地方,可以看到
对应的,寄存器,确实也设置为了0
再看下一句:
PCLK1=HCLK
这里的
PCLK1=HCLK
这里是备注,写错了
实际上应该是两分频
去具体看一下代码就知道.
也可以看到这里的DIV2这个就是两分频的意思.
去看一下代码,这里是0X00000400
第10位
从第0位开始数,就是0100 0000 0000
1的位置是第10位.
也可以看到设置为100也就是4的时候,其实是HCLK2分频,可以看到
这个是官方提供的库的备注的一个错误.
HCLK=SYSCLK=PLK2=72mhz的话
PCLK1=36mhz
这个部分是设置时钟的初始化状态.
再往下看:
#ifdef STM32F10X_CL
/* Configure PLLs ------------------------------------------------------*/
/* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
/* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */
RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
}
/* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9);
#else
/* 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);
#endif /* STM32F10X_CL */
//1.STM32F10X_CL 这一部分,因为这里我们用的是HD,大容量,其实这个地方的
//STM32F10X_CL是对应的某个系列的,而这里咱们STM32F103
//这个系列的是走的else
//这里else可以看到
//RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
//比如这个HSE,外部晶振产生8MHZ的频率,这个*9 这样就达到72MHZ的一个频率
//
这个图中也能看到,这里HSE产生一个频率他可以产生4-16mhz的频率,
然后PLL经过9倍,倍频,会给SYSCLK这个系统时钟一个72MHZ的时钟频率,
然后然后72mhz的时钟频率,经过SYSCLK系统时钟,会产生一个HCLK 72MHZ的时钟
然后经过AHB 1倍频的,频率,再经过APB1产生一个36mhz的频率,
然后经过AHB 1倍频的时钟频率,再经过APB2的 1 倍频产生一个 72mhz的PCLK2 的频率
这个就是上面那段代码的意思.
在上面的代码,做完,对PLL的时钟选择,对系统时钟的时钟源选择,对分频,频率都设置好以后.
就可以打开PLL时钟
/* Enable PLL */
RCC->CR |= RCC_CR_PLLON;
//就是操作CR寄存器.
而且文档中可以看到,打开PLL时钟,要使能CR时钟的位24
也可以看定义
这里可以看到是第24位
使能PLL以后,再等待PLL时钟就绪.
可以看到时钟就绪,这个是第25位.
然后再把PLL作为一个系统时钟的来源.
系统时钟的选择,这里也有配置.
去看看定义这里:
这里2就代表10
上面文档中的,选择PLL输出作为系统时钟.
这个函数的代码,就已经都说了
再从头到尾的看一下这个函数:
/**
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2
* and PCLK1 prescalers.
* @note This function should be used only after reset.
* @param None
* @retval None
*/
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */
//1.首先打开HSE, 把外部高速时钟打开.
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
//2.然后等待HSE时钟就绪
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 (HSEStatus == (uint32_t)0x01)
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
//3.然后设置flash
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
//4.然后设置三个时钟.
//设置分频系数,确定各个时钟HCLK PCLK1 PCLK2 跟系统SYSCLK 时钟的关系
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
#ifdef STM32F10X_CL
/* Configure PLLs ------------------------------------------------------*/
/* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
/* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */
RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
}
/* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9);
#else
//5.然后把PLL的倍频系数和时钟来源设置好.
/* 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);
#endif /* STM32F10X_CL */
//6.然后先把PLL时钟打开
/* Enable PLL */
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
//7.然后把系统时钟切换到PLL上面,就是这样的过程
/* Select PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
#endif
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
这里再去搜索SystemCoreClock
可以看到这里,确定了系统时钟为多少了以后,把确定的系统时钟就
设置给SystemCoreClock这个变量了.
也就是说,后面咱们可以通过这个变量来获取到系统时钟的频率是多少的
SystemCoreClock
这样的话,就可以判断系统时钟的大小了.
这里面还有个问题就是
这个每个库函数的实验,这里main函数里,都没有调用systeminit这个函数.
那么他是怎么实现执行main函数之前的时候,会自动去执行systeminit函数的呢
这段代码不知道具体什么原理,但是大体意思是
当系统复位的时候,先执行Systeminit,再去执行main函数.
这里如果你想写自己的系统初始化函数,是可以这样的,把自己写的系统初始化函数的名字写在这里就可以了.
替换掉Systeminit