首先看到的是5个时钟源,其中有三个高速时钟源(频率产生的来源)
HSI时钟:高速内部时钟,由于是RC振荡器故频率不够稳定,大约是8MHz,可两分频后做选择器2的输入
HSE时钟:高速外部时钟,接外部晶振(4~16MHz),可以直接做选择器1的输入,另外两分频后做选择器1的输入,选择器1又是选择器2的输入,选择器2作为PLL(锁相环倍频输出)的时钟输入,PLL输出的频率为72MHz,通过系统时钟的选择器后可做系统时钟的选择也可直接做系统时钟的时钟源
以上,系统时钟共有三个来源HSI、PLLCLK、HSE
LSE时钟:低速外部时钟,接32.768kHz石英晶体,主要是RTC的时钟源
LSI时钟:低俗内部时钟,内部RC振荡器产生,约为40kHz独立看门狗的时钟来源只能是LSI 也可做RTC时钟源
PA8引脚可在MCO输出内部时钟,其来源系统时钟、HSI、HSE、PLL/2.
typedef struct
{
__IO uint32_t CR; //HSI,HSE,CSS,PLL等使能和就绪标志位
__IO uint32_t CFGR; //PLL等的时钟源选择,分频系数设定;控制选择器选择时钟源的输出,另外还用来配置PLL倍频系数和AHB分频因子
__IO uint32_t CIR; //清除/使能始终就绪终端(不常用
__IO uint32_t APB2RSTR; //APB2线上外设复位寄存器(不常用
__IO uint32_t APB1RSTR; //APB1线上外设复位寄存器
__IO uint32_t AHBENR; //DMA,SDIO等时钟使能
__IO uint32_t APB2ENR; //APB2线上外设时钟使能
__IO uint32_t APB1ENR; //APB1线上外设时钟使能
__IO uint32_t BDCR; //备份域控制寄存器(不常用
__IO uint32_t CSR; //控制状态寄存器(不常用
}RCC_TypeDef
/***时钟使能配置***/
RCC_LSEConfig()
RCC_HSEConfig()
RCC_HSICmd()
RCC_LSICmd()
RCC_PLLCmd()
/***时钟源相关配置***/
RCC_PLLConfig()
RCC_SYSCLKConfig()
RCC_RTCCLKConfig()
/***分频系数相关配置***/
RCC_HCLKConfig()
RCC_PCLK1Config()
RCC_PCLK2Config()
/***外设时钟使能***/
RCC_APB1PeriphClockCmd(); //APB1线上外设时钟使能
RCC_APB2PeriphClockCmd(); //APB2线上外设时钟使能
RCC_AHBPeriphClockCmd(); //AHB线上外设时钟使能
/***其他外设时钟使能***/
RCC_ADCCLKConfig()
RCC_RTCCLKConfig()
/***状态参数获取参数***/
RCC_GetClocksFreq();
RCC_GetSYSCLKSource();
RCC_GetFlagStatus();
/***RCC中断相关函数***/
RCC_ITConfig()
RCC_GetITStatus()
RCC_ClearITPendingBit()
SystemInit(); //设置系统复位后状态的初始配置
void SystemInit (void)
{
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
/* Set HSION bit 打开HSICLK*/
RCC->CR |= (uint32_t)0x00000001; //CR寄存器最后一位设置为1即可打开HSICLK
/* 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 HSEON, CSSON and PLLON bits Reset即把这些位设置为0*/
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
/*****************************
在这中间系统是通过宏定义来判断系统的频率是多少
如果系统定义了72MHz
也就是
SystemCoreClock = SYSYCLK_FREQ_72MHz = 72000000
则在系统文件中会判断出来以后的函数入口
也就是 SetSysClockTo72()
下面就是这个函数的讲解
****************************/
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE */ //使能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; //RCC_CR_HSERDY为1,即外部高速时钟已就绪
StartUpCounter++; //即将跳出循环
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); //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;
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/*****************************
由于,CPU的处理速度是要比闪存要快的,
所以需要进行等待,系统时钟为72MHz
需要两个等待状态
*****************************/
/* HCLK = SYSCLK */ //AHB预分频器设置为1分频
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;
#ifdef ......
#else
/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |RCC_CFGR_PLLMULL)); //通过设置CFGR寄存器来设置PLL倍频系数和时钟来源
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /* STM32F10X_CL */
/* Enable PLL */ //使能PLL
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
//这是在跑空循环等待PLL就绪
}
/* Select PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
//把PLL时钟切换到SYSCLK上
/* 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
}
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit //可以看出系统实现执行系统初始化函数,再来执行main函数的
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
4个Systick定时器
固件中的相关库函数
SysTick_CLKSourceConfig() //Systick时钟源选择
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
/* Check the parameters */
assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource)); //这里面SysTick_CLKSource只能选择两种,一分频(由最开始的图可以知道这选定的是内部时钟)和把分频(由上面的图可得知这选定的是AHB分频后的9MHz)
if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
{
SysTick->CTRL |= SysTick_CLKSource_HCLK; //SysTick的定义也是结构体指针,地址映射到Systick_BASE
}
else
{
SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
}
}
SysTick_Config(uint32_t ticks) //初始化SysTick,时钟为HCLK,并开启中断
static __INLINE uint32_t SysTick_Config(uint32_t ticks) //Tick就是在两次中断之间的时钟周期
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible;对有效性的判断 */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register;类似于溢出,装载那一次也算一个时钟周期 */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
Systick中断服务函数
这里以us为例
#endif
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8
fac_us=SystemCoreClock/8000000;
/********为系统时钟的1/8,查询定义可以知道SystemCoreClock为72000000,故系统8分频后需要跑9次(ms就是9000次)才能跑满1us(10^(-6)) ****************/
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载,赋值给LOAD加载,注意不能超过24位
SysTick->VAL=0x00; //清空当前计数器准备开始倒数
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数,使能CTRL。
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达,第一个条件是CTRL被使能,第二个条件是CTRL标志位不为1(如果为1就跳出循环了)
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器(按位取反后由于SysTick_CTRL_ENABLE_Msk第一位是0,CTRL按位与后关闭)
SysTick->VAL =0X00; //清空计数器
}
SysTick_CTRL_ENABLE_Msk ; //开始倒数,使能CTRL。
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达,第一个条件是CTRL被使能,第二个条件是CTRL标志位不为1(如果为1就跳出循环了)
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器(按位取反后由于SysTick_CTRL_ENABLE_Msk第一位是0,CTRL按位与后关闭)
SysTick->VAL =0X00; //清空计数器
}