刚看到system_stm32f10x.c文件时,一堆的字母,看起来感觉头都大了,仔细读下来,其实包含的内容并不多,各位看官且看我一一道来。
首先,我们把注释去掉,函数收缩起来,就可以得到以下的视图:
1. 一堆的#define,这是给我们做选择的,用哪个,就把注释取消,不用的注释掉就好了
2.这个是根据上面定义的内容,进行的选择分支,我们一般是不动这里的
3.这个更好理解了,实现以上定义的功能函数。这样看就很清晰了,开机:1. 运行SystemInit();2. 如果有用外部SRAM的话,运行SystemInit_ExtMenCtl(),没有的话跳过;3. 再根据选择的频率运行对应的频率设置函数SetSysClockto**()。
注释内容翻译:
* 1. This file provides two functions and one global variable to be called from
* user application:
(1.这个文件提供了两个函数和一个全局变量给用户应用调用)
* - SystemInit(): Setups the system clock (System clock source, PLL Multiplier
* factors, AHB/APBx prescalers and Flash settings).
* This function is called at startup just after reset and
* before branch to main program. This call is made inside
* the "startup_stm32f10x_xx.s" file.
(SystemInit():设置系统时钟(系统时钟源,PLL倍频系数,AHB/APBx预分频器和flash时钟设定)
这个函数在复位后,进入main程序之前,就会被启动文件调用,调用命令在“startup_stm32f10x_xx.s”这个文件中)
*
* - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
* by the user application to setup the SysTick
* timer or configure other parameters.
(SystemCoreClock变量):包含内核时钟(HCLK),可以用于设置SysTick定时器,或者配置其他参数。
(如果没有用这个变量来计算的值的话,这个可以不管了,可以自己直接定义一个新的时钟变量来用,这样可能产生的问题就是系统时钟突然变了,所有基于这个值的计算都会出错。
如果要用这个时钟变量的话,最好就是用之前调用一下SystemCoreClockUpdate()函数。)
* - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
* be called whenever the core clock is changed
* during program execution.
(SystemCoreClockUpdate():更新SystemCoreClock变量,在程序执行过程中,无论什么时候改变内核时钟,都要调用该函数)
*
* 2. After each device reset the HSI (8 MHz) is used as system clock source.
* Then SystemInit() function is called, in "startup_stm32f10x_xx.s" file, to
* configure the system clock before to branch to main program.
(2.在每一个设备复位之后,HSI(内部RC时钟源8MHz)就会用作系统时钟源)
(然后在"startup_stm32f10x_xx.s"文件中调用SystemInit()函数,在进入main程序前,配置系统时钟)
*
* 3. If the system clock source selected by user fails to startup, the SystemInit()
* function will do nothing and HSI still used as system clock source. User can
* add some code to deal with this issue inside the SetSysClock() function.
(3.如果用户选择的系统时钟源启动失败,SystemInit()函数会什么都不做,然后HSI仍然用作系统时钟源。用户可以在SetSysClock()函数中添加一些代码,用来处理这件事。)
意思就是:假如想用外部8MHz晶振来做时钟源,但是这个外部晶振坏了,或者没焊好,这个SystemInit()函数就不会切换到外部晶振了,仍然用内部时钟源。
*
* 4. The default value of HSE crystal is set to 8 MHz (or 25 MHz, depedning on
* the product used), refer to "HSE_VALUE" define in "stm32f10x.h" file.
* When HSE is used as system clock source, directly or through PLL, and you
* are using different crystal you have to adapt the HSE value to your own
* configuration.
(4.默认的HSE(外部时钟源)设为8MHz(或者25MHz,取决于产品的使用),参考"stm32f10x.h" 文件中的"HSE_VALUE"定义。当使用的外部晶振频率与默认的8MHz不同时,那必须设置"HSE_VALUE"值和所用的晶振频率一致。(这句不太好翻译,不知道是不是漏字了,我英文水平有限,只能瞎编了))
* @brief Setup the microcontroller system
* Initialize the Embedded Flash Interface, the PLL and update the
* SystemCoreClock variable.
(这里是函数的简介:设置微处理器系统,初始化内置flash接口,PLL和更新SystemCoreClock变量)
* @note This function should be used only after reset.
(这个函数应当仅用于复位后)
* @param None
* @retval None
*/
void SystemInit (void)
{
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
(复位RCC时钟配置到默认复位状态(用于调试))
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
(设置HSION位,由寄存器描述可知,这里置1是HSI开启)
/* 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 */
(复位RCC_CFGR寄存器,CL型和非CL型产品分开,是因为CL型的MCO是4bit的,其他的是3bit的。
复位的值对照寄存器可以看到:
1.MCO没有时钟输出;2. ADCPRE:ADC预分频2分频;3.PPRE2:APB2不分频;4.PPRE1:APB1不分频;5.HPRE:AHB不分频;6:SW:选择HSI为系统时钟)
(以上寄存器的内容,请自行参考《STM32中文参考手册_V10》第61,62页和87,88页)
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
(PLL关闭,时钟监测器关闭,外部晶振关闭。)
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
(不避开外部晶振(英文Bypass,这个可能是指先选上备用的意思吧,不确定。反正Bypass就是不能用外部晶振)。)
(经大神指导,见评论,这个Bypass指的是旁路时钟源(BYPASS Clock Source),直接从SOC_IN导入外部时钟信号。感谢!——修改于2024.09.26)
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
RCC->CFGR &= (uint32_t)0xFF80FFFF;
(USB、OTG预分频1.5分频,PLL倍频2倍,HSE不分频,HSI 2分频。)
#ifdef STM32F10X_CL
/* Reset PLL2ON and PLL3ON bits */
RCC->CR &= (uint32_t)0xEBFFFFFF;
(关闭PLL2、PLL3。)
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x00FF0000;
(关闭所有中断和清除中断标志位。)
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
(系统时钟作为I2S时钟,PLL不倍频,预分频器不分频。)
(下面几句也是一样的,只是不同类型产品,寄存器有一些差别而已)
#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(如果用外部SRAM,调用SystemInit_ExtMemCtl()函数)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
(配置系统时钟频率,HCLK,PCLK2和PCLK1预分频系数)
/* Configure the Flash Latency cycles and enable prefetch buffer */
(配置flash的延迟周期和预取缓冲器)
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
}
(配置系统时钟频率的函数基本都一样的,这里就只拿72MHz的来说吧)
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
(1. 使能外部晶振HSE)
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
(读取HSE的状态标志,以判断HSE是否已准备好)
StartUpCounter++;
(同时进行计数)
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
(当HSE没有准备好,并且计数超时,就一直循环,有一个条件不成立就退出循环,往下执行)
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
(这里是判断HSE已经准备好了)
{
HSEStatus = (uint32_t)0x01;(准备好了,状态位位0x01)
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
(判断状态位,如果是准备好了,往下执行代码,不然的话,就会跑到最底部else上了,那里留了一句话,HSE启动失败,你可以在这里添加失败后的系统处理代码)
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
(2.使能flash预存取缓冲区)
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
(flash读写延迟周期设置。这里有个规则,系统时钟频率0~24MHz,延时0周期,24~48MHz,1周期,48~72MHz,2周期,这个函数是设置成72的,所以这里是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 */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
(3.设置AHB、APB1、APB2分频。这里可以看到,AHB、APB2不分频,APB1为2分频,是因为AHB、APB2最大频率可达72MHz,APB1最大只能到36MHz,如果系统时钟设置超过36MHz的话,APB1就必须2分频,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);
(4.配置PLL2倍率)
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
}
(5.使能PLL2)
/* 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);
(6.配置PLL倍率)
#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);
(#else这里,有些产品是没有PLL2的,只需设置PLL倍率就行了)
#endif /* STM32F10X_CL */
/* 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)
{
}
(8.系统时钟选择PLL时钟源,等待就绪。这就完成配置了。)
}
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 */
}
}