花了一天的时间总骗了解了SystemInit()函数实现了哪些功能,初学STM32,现记录如下(有侠理解错误的地方还请大意
的库):使用的是3.5,用的是STM32F107VC ,开发环境RVMDK4.23
我已经定义了STM32F10X_CL,SYSCLK_FREQ_72MHz
函数调用顺序:
startup_stm32f10x_cl.s(启动文件→SystemInit()→SetSysClock()→SetSysClockTo72()
初始化的
RCC0寄存器值:RCC_CR = 0x00 ; RCC_CFGR = 0x0000 0000;RCC_CIR = 0x0000 0000;RCC_CFGR2 = 0x0000 0000;
SystemInit()
调用SetSysClock()之前的RCC寄存器的值如下(一些与操作,或在操作,在此赘述了):
RCC->CR = 0x0000 0083; RCC->CIR = 0x00FF0000;RCC->CFGR2 = 0x00000000;至于这些寄存器代表着什么英文,详见芯片数据RCC寄存器,该文重点不在此处;
SetSysClock()函数如下:
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif 定义了 SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif 定义了 SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif 定义了 SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif 定义了 SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz //我的定义是SYSCLK_FREQ_72MHz,所以调用SetSysClockTo72()
SetSysClockTo72();
#endif
}
SetSysClockTo72()函数如下:
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/ * SYSCLK,HCLK,PCLK2和PCLK1配置---------------------------
/
/启用HSE * /
RCC-> CR | =( (uint32_t)RCC_CR_HSEON);
/* 等到 HSE 准备好,如果超时退出 */
做
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
启动计数器++;
} 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)
{
/* 启用预取缓冲区 */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2 等待状态 */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 */RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; #ifdef STM32F10X_CL
/* 配置 PLL ------------------------------------------- -----------
/
/PLL2配置:PLL2CLK =(HSE / 5)* 8 = 40兆赫
/
/PREDIV1配置:PREDIV1CLK = PLL2 / 5 = 8兆赫* /
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); /* 启用 PLL2 */RCC->CR |= RCC_CR_PLL2ON;/* 等到 PLL2 准备好 */while((RCC->CR & RCC_CR_PLL2RDY) == 0){} /* PLL 配置: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_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL) #else
/* PLL 配置:PLLCLK = HSE * 9 = 72 MHz
/
RCC->CFGR &
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /STM32F10X_CL */
/* 启用 PLL */RCC->CR |= RCC_CR_PLLON;/* 等待 PLL 准备就绪 */while((RCC->CR & RCC_CR_PLLRDY) == 0){}/* 选择 PLL 作为系统时钟源 */RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; /* 等待 PLL 作为系统时钟源 */while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08){} }
else
{ /* 如果 HSE 启动失败,应用程序会有错误的时钟
配置。用户可以在这里添加一些代码来处理这个错误 */
}
}
1:AHB, APB1,APB2 注册确定
//HCLK = SYSCLK ,从下面的分析可以得出SYSCLK是使用PLLCLK时钟的,也就是72MHZ(至于72MHZ如何得来,请看下面分析)
//那么就是HCLK(AHB时钟)=PLLCLK = 72MHZ
/ /AHB总线也就是时钟系统logsysclk = HCLK = SYSCLK = 72MHZ
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
//PLCK2等于HCLK一分频,所以PCLK2 = HCLK,HCLK = 2MHZ,那么PLCK2(APB2总线时钟) = 72MHZ
//APB2总线时钟HCLK的一分频,也就是不分频;APB2 = HCLK = SYSCLK = 72MHZ
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
//PCLK1 = HCLK / 2;CLK1 等于HCLKP的二分频,那么PCLK1(APB1) = 72MHZ / 2 = 36MHZ //APB1 类别属于HCLK的二分频,也就是APB1= HCLK / 2 = 36MHZ /* PCLK1 = HCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; 2:如何得出SYSCLK(系统时钟)为72MHZ(外部晶振25MHZ)
//记得参考英文芯片数据的时钟树P115页和RCC时钟寄存器进行理解
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
RCC_CFGR2_PREDIV2_DIV5: PREDIV2 = 5; 5分频
也就是PREDIV2对输入的外部时钟5分频,则PLL2和PLL3无倍频前是25 /5 = 5MHZ
RCC_CFGR2_PLL2MUL8 : PLL2MUL = 8; 8倍频
8频,后PLL2频率 = 5 * 8 = 40MHZ;因此PLL2CLK = 40MHZ
RCC_CFGR2_PREDIV1SRC_PLL2 : RCC_CFGR2的第16位为1, 选择PLL2CLK PREDIV1的时钟源
RCC_CFGR2_PREDIV1_DIV5:PREDIV1 = 5;PREDIV1对输入频率= 5;PREDIV1对输入频率= 5分
频进行配置
PREDIV1MHRCCCF = 0 >CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9);
RCC_CFGR_PLLXTPRE_PREDV1 :操作是RCC_CFGR的第17位PLLXTPRE,操作RCC_CFGR2的位[3:0]中位是相同的效果
RCC_CFGR_PLLSRC_PREDV1 :选择PLL输入PREDIV1输出作为PLL输入;PREDIV1CLK = 8MHZ,所以给PLL倍频的输入源是8MHZ
RCC_CFGR_PLLMULL9 :PLLMUL = 9;PLL倍频是为9,也就是对PLLCLK = PREDIV1CLK * 8 = 72
以上对RCC_CFGR进行的配置
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; //选择PLLCLK作为系统源
完成至基本已经,配置的此配置如下所述:
SYSCLK(系统时钟) = 72Z
AHB时钟= 72MHZ
APB1 总线 = 36MHZ
APB2 时钟 = 72MHZ
PLL = 72MHZ
PLL2时钟= 40MHZ
STM32的电话树
对于那些初次接触STM32的朋友(甚至是首次接触了机械臂的用户朋友),说到了开发环境的使用之后,总会在同一问题上“陷入困境”。个关键字叫:钟树。
外接,微控制器(处理器)的必须要依赖外部的一个调用来运行驱动——往往用于提供输入输入起始,最终转换为多个外部设备的辅助为末,荷兰“能量”流动的人口流动的,就像大树的分流通过主干流向各个路径,从而带来了常情的“情感树”。自己也有自己的一个家庭树系统,但其中的汽车是不受控制的,也就是在拥有自己的电后,汽车树就固定在某个不可改变的状态(假设即将发生的正常工作的状态) )。比如51单片机使用典型的12MHz的晶振作为时钟源,则外设如IO口,定时器,串口等设备的驱动时钟速率便已经是固定的,用户无法将此时钟速率更改,除非更换晶振。
而STM32微控制器的时钟树则是可配置的,外设的输入源与最终达到处的时钟速率不再有固定的关系,以后会详细解析STM32微控制器的时钟树图1是STM32微。控制器的时钟树,1是内部各个标号所表示的
小区。图1标号标释义
1 低
外部速速(LSI,40Khz)2 低外部内部速速(LSE,32.768Khz)
3 外部高速下载( HSE,3-25MHz)
4 内部高速私有(HIS,8MHz)
5 PLL 输入选择位
6 RTC输入选择位
7 PLL1 分频数参数
8 PLL1 倍频频率
9 系统选择位
频频10 USB 分频
11 AHB 分频频滤波器
12 APB1分频滤波器
13 AHB总线
14 APB1外设总线
15 22分频滤波器
16 APB2外设总线
17 ADC预分频滤波器
18 ADC
19 PLL2分频数滤波器
20 PLL2倍频
频率滤波器21 PLL选择源选择
开关设备22 独立看门狗设备
23 RTC设备
图1 STM32的电话树
在认识这颗心树之前,首先要明确“主干”和最终的“分支”。这个假设使用外部8MHz晶振作为STM32的时钟输入源(最常见的这步),则8MHz就是“主干”,而“分支”很可能是最终的外部设备,比如通用输入设备(GPIO)。这样输出可以轻松找出第一条的“脉络”:
3——5——7——21—— 8——9——11——13条对于
此条时钟路径做如下解析:
3,首先是外部的3-25MHz(前文已假为8MHz)输入;
对于5,PLL通过选择位犯罪选择题PLL分支的输入外部(外部选择外部晶振);
对于7,设置晶振的分频数(假1分频);
对于21,选择倍频频的时钟源(选择分频后的外部晶振);
对于8,设置PLL倍频数(假9倍频);
9,选择系统光源(对于假选择PLL倍频所的输出);
11,设置AHB总线分频数(假1分频);
对于13,总部到达AHB;
在上一章节中所介绍的GPIO外设属于APB2设备,即GPIO的调用来自APB2总线,同样在图1中也可以获取GPIO外设的主机寻:
3——5——7—— 21——9——11——115——16
对于3,首先是外部的3-25MHz(前文已假为8MHz)输入;
对于5,通过PLL选择位选择任务PLL分支的输入名称(假设选择外部晶振);
对于7,设置外部晶振的分频数(假1分频);
21,选择PLL倍频的时钟源(假设选择分频后的外部晶振);
对于8,设置PLL倍频数(假9频);
对于9,选择系统源(假选择通过PLL倍频所输出的频率);
对于11,设置AHB总线分频数(假1分频);
对于15,设置APB2总线分频数(假1分频);
对于16条,每条到达APB2总线;
现在来计算一下GPIO设备的最大驱动速度(各种条件已在上述要点中假设):
- 由3知此晶振输入为8MHz,由5——21PLL的时钟源为经过分频后的外部晶振,并且分频数为1分频,因此首先得出PLL的时钟源为:8MHz / 1 = 8MHz。
- 由8、9知倍频数为9且将PLL倍频后的时钟输出选择为系统时钟,则得出系统时钟为8MHz * 9 = 72MHz。
- 时钟达到AHB预分频器,由11个知觉通过AHB预分频器之后的速率仍为72MHz。
- 转子达到APB2预分频器,由15通过APB2预分频器后速率仍为72MHz。
- 时钟到达APB2总线外设。
因此STM32的APB2总线外设,所能达到的最大速率为72MHz。依据以上方法读者可以搜寻出APB1总线外设时钟、RTC外设时钟、独立看门狗等外设时钟的来龙去脉。接下来从程序的角度分析时钟树的设置,程序清单如下:
void RCC_Configuration(void)
{
ErrorStatus HSEStartUpStatus; (1)
RCC_DeInit(); (2)
RCC_HSEConfig(RCC_HSE_ON); (3)
HSEStartUpStatus = RCC_WaitForHSEStartUp(); (4)
if(HSEStartUpStatus == SUCCESS) (5)
{
RCC_HCLKConfig(RCC_SYSCLK_Div1); (6)
RCC_PCLK2Config(RCC_HCLK_Div1); (7)
RCC_PCLK1Config(RCC_HCLK_Div2); (8)
FLASH_SetLatency(FLASH_Latency_2); (9)
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); (10)
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); (11)
RCC_PLLCmd(ENABLE); (12)
而(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==重置);(13)
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); (14)
while(RCC_GetSYSCLKSource() != 0x08); (15)
}
}