开发板的各个模块工作起来的首要条件是有时钟信号来同步。所以在使用STM32各模块前一定要进行时钟的初始化( SystemInit() )并开启此模块的时钟。SystemInit()函数配置了SYSCLK时钟来源及SYSCLK去向问题,还配置了预取指模块。
void SystemInit (void) |
配置微控芯片系统。初始化内嵌闪存接口、PLL及更新系统时钟频率。
1. RCC->CR |= (uint32_t)0x00000001; 2. 3. #ifndef STM32F10X_CL 4. RCC->CFGR &= (uint32_t)0xF8FF0000; 5. #else 6. RCC->CFGR &= (uint32_t)0xF0FF0000; 7. #endif 8. 9. RCC->CR &= (uint32_t)0xFEF6FFFF; 10. RCC->CR &= (uint32_t)0xFFFBFFFF; 11. RCC->CFGR &= (uint32_t)0xFF80FFFF; 12. 13. #ifndef STM32F10X_CL 14. RCC->CIR = 0x009F0000; 15. #else 16. RCC->CR &= (uint32_t)0xEBFFFFFF; 17. RCC->CIR = 0x00FF0000; 18. RCC->CFGR2 = 0x00000000; 19. #endif |
STM32F10X_CL表示互联互联系列处理器(STM32F105(7)XX),小老百姓暂时使用的是103的芯片,故而源码中STM32F10X_CL模块自然被忽略。
1. 选择HSI作为SYSCLK来源。
4. 复位CFGR的bit[26:24, 15:0] (MCO,时钟输出;ADCPRE,ADC预分频;PPRE2,高速APB预分频(APB2);PPRE1,低速APB预分频(APB1);HPRE,AHB预分频;SWS,系统时钟切换状态;SW系统时钟切换)位,对应的含义分别为“微控器无时钟输出”、“PCLK2 2分频后作为ADC时钟”、“HCLK不分频(PCLK2的预分频系数)”、“HCLK不分频(PCLK1的预分频系数)”、“SYSCLK不分频(AHB预分频系数为1)”、“目前是HSI作为系统时钟的状态”、“用HSI作为系统时钟”。
9~10. 复位CFGR的bit[24,19,17,16]( PLLON,PLL使能;CSSON,时钟系统安全使能;HSERDY,外部高速时钟就绪标志;HSEON,外部高速时钟使能),即PLL关闭,时钟监测器关闭,HSE振荡器无旁路,HSE振荡器关闭。
11. 复位CFGR的bit[22:16]位,即PLL时钟1.5倍分频作为USB时钟,PLL2倍频输出,HSE不分频,HSI振荡器时钟经2分频后作为PLL输入时钟。
14. 配置CIR的bit[23,20,19:16]位为1,清除CSSF安全系统、PLL就绪、HSE就绪、HSI就绪、LSE就绪、LSI就绪中断标志位。
1. static void SetSysClock(void) 2. { 3. #ifdef SYSCLK_FREQ_HSE 4. SetSysClockToHSE(); 5. #elif defined SYSCLK_FREQ_24MHz 6. SetSysClockTo24(); 7. #elif defined SYSCLK_FREQ_36MHz 8. SetSysClockTo36(); 9. #elif defined SYSCLK_FREQ_48MHz 10. SetSysClockTo48(); 11. #elif defined SYSCLK_FREQ_56MHz 12. SetSysClockTo56(); 13. #elif defined SYSCLK_FREQ_72MHz 14. SetSysClockTo72(); 15. #endif 16. } |
如果用户定义了SYSCLK_FREQ_HSE宏则就调用”SetSysClockToHSE();”函数来确定SYSCLK的时钟来源为HSE,如果用户定义了SYSCLK_FREQ_24MHz则调用”SetSysClockTo24();”函数来确定SYSCLK的时钟频率为24MHz……如果用户定义了SYSCLK_FREQ_72MHz宏则调用” SetSysClockTo72();”函数来确定SYSCLK的时钟频率为72MHz,如果这些宏都未定义,则在系统复位后HSI默认为SYSCLK的时钟源。以配置SYSCLK时钟频率为72MHz源码来笔记。”当需要配置SYSCLK时钟频率为72MHz时SYSCLK的时钟信号由PLL输出,PLL的时钟来源为HSE(HSE比HSI稳定)。非互联系列的芯片只管非STM32F10X_CL模块下的代码.
1. RCC->CR |= ((uint32_t)RCC_CR_HSEON); 2. do{ 3. HSEStatus = RCC->CR & RCC_CR_HSERDY; 4. StartUpCounter++; 5. } while((HSEStatus == 0)&& (StartUpCounter != HSEStartUp_TimeOut)); 6. 7. if ((RCC->CR &RCC_CR_HSERDY) != RESET){ 8. HSEStatus = (uint32_t)0x01; 9. }else{ 10. HSEStatus = (uint32_t)0x00; 11. } |
1. RCC_CR_HSEON的值为((uint32_t)0x00010000),配置CR寄存器bit[16](HSEON,外部高速时钟使能)为1,开启外部高速时钟。
2~5. 在开启任何一个时钟都需要等待此时钟的稳定。RCC_CR_HSERDY的值为((uint32_t)0x00020000),CR的bit[17](HSERDY,外部高速时钟就绪标志),此位由硬件置位,当此位为1时表明HSE时钟已经稳定就绪。此段代码一直用循环等待直到CR的bit[17]为1或者StartUpCounter的值为HSEStartUp_TimeOut((uint16_t)0x0500,如果StartUpCounter计数到这个值表示HSE时钟就绪超时)。
7~11. RESET的值为0。如果CR的bit[17]为1,则HSEStatus为1表示HSE时钟就绪成功,否则HSEStatus为0,表示HSE时钟就绪失败。预取模块是闪存部分内容,得参见《STM32F10XXX,PM0042 闪存编程手册》。Cortex-M3在访问FLASH闪存时,在I-Code总线上取指令,在D-Code上取数据。预取模块是用于通过ICode总线读取指令的。预取指模块可以有效地提高对I-Code总线访问的效率。
预取指模块包含两部分,预取缓冲器和预取控制器。预取缓冲器可使CPU更快的执行,CPU读取一个字的同时下一个字已经在预取缓冲器中等候,当代码跳转的边界为8字节的倍数时,闪存的加速比例为2。预取控制器根据预取缓冲器中可用的空间决定是否访问闪存,预取缓冲器中至少有一块空余的空间时,预取控制器则启动一次读FLASH操作。
当AHB时钟的预分频系数不为1时,必须打开预取缓冲器。为了维持读闪存的控制信号,预取控制器的时钟周期与闪存访问时间的比例由闪存访问控制器控制;这个值给出了能够正确地读取数据时,闪存控制信号所需的时钟周期数目;复位后,该值为1,闪存访问为两个时钟周期(FLASH_ACR的复位值为01,长度=1)。
1. if (HSEStatus == (uint32_t)0x01){ 2. FLASH->ACR |= FLASH_ACR_PRFTBE; 3. FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); 4. FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; 5. } |
1. 如果HSE时钟就绪成功。
2. FLASH_ACR_PRFTBE的值为((uint8_t)0x10),配置ACR的bit[4](PRFTBE,预取缓冲区为使能)为1启用预取缓冲区(AHB预分频器的系数会被配置为1)。
3~4. FLASH_ACR_LATENCY的值为((uint8_t)0x03),3语句将ACR的bit[1:0]配置为00供4语句配置bit[1:0]为10,让SYSCLK周期与闪存访问时间的比例为2。FLASH_ACR_LATENCY_2的值为((uint8_t)0x02)。
关于ACR寄存器bit[2:0]位表示SYSCLK(系统时钟)周期与闪存访问时间的比例
000:零等待状态,当 0
001:一个等待状态,当 24MHz
虽然SYSCLK还未配置好,但这些作为SYSCLK时钟去向的时钟已经可以提前做好准备了。
1. if (HSEStatus == (uint32_t)0x01){ 2. RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; 3. RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; 4. RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; 5. } |
1. 在HSE时钟成功就绪的情况下,进行AHB,APB2,APB1预分频。
2. SYSCLK分频配置(HCLK由AHB预分频器分频后输出)。RCC_CFGR_HPRE_DIV1的值为((uint32_t)0x00000000),CFGR的bit[7:4]( HPRE,AHB预分频)为0000,表示SYSCLK不分频。
3. HCLK分频设置(PLCKx由HCLK经APBx预分频器分频后输出)。RCC_CFGR_PPRE2_DIV1 的值为((uint32_t)0x00000000),CFGR的bit[13:11](PPRE2,高速APB预分频)配置为000,表示HCLK不分频。
4. HCLK分频设置。RCC_CFGR_PPRE1_DIV2的值为((uint32_t)0x00000400),CFGR的bit[10:8](PPRE1,低速APB预分频)配置为100,表示HCLK经APB2预分频系数为2(因为此时HCLK=SYSLCK=72MHz,PLCK1最大只能为36MHz)。 1. if (HSEStatus == (uint32_t)0x01){ 2. RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |RCC_CFGR_PLLMULL)); 3. RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); 4. 5. RCC->CR |= RCC_CR_PLLON; 6. while((RCC->CR & RCC_CR_PLLRDY) == 0){ 7. } 8. 9. RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); 10. RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; 11. 12. while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08){ 13. } 14. } |
1. 在HSE就绪成功的情况下配置SYSCLK时钟来源。
2~3. 配置PLL的时钟来源,PLL时钟来源分频器系数,PLL输出倍频器系数得到SYSCLK=72MHz。RCC_CFGR_PLLSRC 的值为((uint32_t)0x00010000),RCC_CFGR_PLLXTPRE 的值为((uint32_t)0x00020000),RCC_CFGR_PLLMULL的值为((uint32_t)0x003C0000),2语句主要是将CFGR的bit[21:18,17,16,]配置为0,即PLL2倍频输出,HSE不分频,HSI振荡器时钟经2分频后作为PLL输入时钟,为复位状态,为此后的配置做好铺垫。RCC_CFGR_PLLSRC_HSE的值为((uint32_t)0x00010000),RCC_CFGR_PLLMULL9为((uint32_t)0x001C0000),分别配置了CFGR的bit[16](PLLSRC,PLL输入时时钟源)、bit[21:18](PLLMUL,PLL倍频系数)为1,0111,对应着PLL的时钟源为HSE,PLL输出倍频为9。所以,如果设置PLL为SYSCLK时钟源,则SYSCLK=8MHz*9=72Mhz(对于非互联系列的处理器,HSE=8MHz)。
5~7. 开启PLL并等待PLL时钟就绪。RCC_CR_PLLON 的值为((uint32_t)0x01000000),将CR的bit[24](PLLON,PLL使能位)配置为1即PLL使能。RCC_CR_PLLRDY的值为((uint32_t)0x02000000),判断CR 的bit[25](PLLRDY,PLL时钟就绪标志位)是否为1,为1则表示PLL时钟就绪。
9~10. 配置PLL成为SYSCLK时钟来源。RCC_CFGR_SW 的值为((uint32_t)0x00000003),9语句先清除CFGR 的bit[1:0](SW,SYSCLK时钟源选择位)方便后续配置该位。RCC_CFGR_SW_PLL的值为((uint32_t)0x00000002),10语句就将SW配置为10表示PLL作为SYSCLK来源。
12~13. 等待系统切换PLL为SYSCLK时钟来源。RCC_CFGR_SWS的值为((uint32_t)0x0000000C),while内的语句判断CFGR的bit[3:2]位是否为10(SYSCLK来源为PLL),直到SYSCLK被切换为PLL为止。
1. if (HSEStatus == (uint32_t)0x01){ 2. // ...... 3. } else{ 4. //If HSE fails to start-up, the application will have wrongclock configuration. User can add here some code to deal with this error 5. //Goto infinite loop 6. while(1){ 7. } 8. } |
若HSE时钟就绪超时时,则可以添加处理这个错误的代码,如就直接无限循环等待人工重启开发板。
Learning Note Over.