您的位置:首页 > 编程语言

STM32F10X 时钟相关代码及分析

2011-10-17 09:23 489 查看
今天学习STM32F107VC时钟相关寄存器以及控制,配合源代码,数据手册,写了点学习笔记供以后参考。

很重要的示意图:





代码已加注释,如下:

//引自: system_stm32f10x.c 文件
void SystemInit (void)
{
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
/* Set HSION bit */
//先将HSION 内部高速时钟使能,HSIRDY指示内部8MHz RC振荡器是否就绪
RCC->CR |= (uint32_t)0x00000001;

/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL //如果不是互联网型
//RCC->CFGR &= 0xF8FF0000  意思是清除低16位(SW,HPRE,PPRE1,PPRE2,ADCPRE),第24~26位(MC0)
RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
//STM32F107VC 是互联网型芯片,只是将MC0的位数多加了一位,由之前的第24~26位变成第24~27位。MC0[2:0] -> MC0[3:0]
//所以这里清除第24~27位和低16位
RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */

//这里值得注意,按最初的想法是这里可以合并起来做清除,而不用RCC->CR &= XXXX 然后又 RCC->CR &= XXXX,之所以这样做的原因
//是有些位必须在关闭关闭时钟时才能写入。如:PLLXTPRE PLLMUL PLLSRC USBPRE PLLON等
/* Reset HSEON, CSSON and PLLON bits */
//必须关闭HSEON后才能写HSEBYP位,所以这里将Reset HSEBYP bit单独拿出来放在下面。
RCC->CR &= (uint32_t)0xFEF6FFFF;

/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;

/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
//清除第16~22位 注意这几个寄存器位很关键,必须将对应的时钟关闭后才能配置
//PLLSRC 选择时钟源作为PLL的时钟,PLLXTPRE 当且仅当PLLSRC选择HSE作为时钟时有效,PLLMUL 为倍频系数位,控制在2~16倍,但PLL的输出频率最大不能超过72M
//USBPRE 预分频后频率为48MHz或24MHz

RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifndef STM32F10X_CL
/* Disable all interrupts and clear pending bits  */
//关闭所有时钟中断,并清除所有中断标志位 (CSSC PLL HSER HIS LSE LSI)
RCC->CIR = 0x009F0000;
#else //F107VC指向下面的代码
/* Reset PLL2ON and PLL3ON bits */
//其实就是关闭PLL2和PLL3
RCC->CR &= (uint32_t)0xEBFFFFFF;

/* Disable all interrupts and clear pending bits  */
//将CSSC PLL3 PLL2 HSE HIS LSE LSI的中断标志位都清除,这里和C51有点不同,中断标志位寄存器分离了,
//如: HSI_RDYF  HSIRDYC,前者是只读,由硬件置位;后者只写,软件置位HSIRDYC来控制清除HSI_RDYF位。
RCC->CIR = 0x00FF0000;

/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000; //清空PREDIV PLL2MUL PLL3MUL等
#endif /* STM32F10X_CL */

/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
/* Configure the Flash Latency cycles and enable prefetch buffer */
SetSysClock();

}

//这个函数其实就是根据宏的定义调用对应的时钟设置函数,代码里面定义了SYSCLK_FREQ_72MHz,所以默认是调用SetSysClockTo72()
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif

/* If none of the define above is enabled, the HSI is used as System clock
source (default after reset) */
}

//肯定是执行这个宏里面的代码
#elif defined SYSCLK_FREQ_72MHz
/**
* @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 */
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; //等待HSE进入稳定状态,就绪后会将HSERDY置1
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSEStartUp_TimeOut)); //HSEStartUp_TimeOut 0x0500

//执行到这里有可能是超时,也有可能是HSE稳定了
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01; //这里表明却是稳定了
}
else
{
HSEStatus = (uint32_t)0x00;
}

//注意只有在系统时钟(SYSCLK)小于24MHz并且没有打开AHB的预分频器(即HCLK必须等于
//SYSHCLK)时,才能执行预取缓冲器的打开和关闭操作,所以一般是在初始化时进行操作。
if (HSEStatus == (uint32_t)0x01) //HSE 外部高速时钟晶振已经开启并且稳定
{
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE; //开启 Flash 预取缓冲区

//2等待周期,当 48MHz < SYSCLK ≤ 72MHz
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;

//AHB桥的时钟等于SYSCLK 72MHz
//设置AHB预分频,CFGR[7:4] -> HPRE[3:0] |= 0x00000000,SYSCLK不分频
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

//APB2桥的时钟等于AHB时钟 72MHz
//设置PPRE2预分频,CFGR[13:11] -> PPRE2[2:0] |= 0x00000000,HCLK不分频
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;

//APB1桥的时钟等于AHB时钟的一半 36MHz,低速APB桥
//设置PPRE1预分频,CFGR[10:8] -> PPRE1[2:0] |= 0x00000400,使用HCLK二分频
/* PCLK1 = HCLK/2 */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

#ifdef STM32F10X_CL //STM32F107X
/* 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);

//从数据手册的时钟树图上可以看出,PLL2唯一的时钟源就是PREDIV2预分频后的HSE,这里典型值取5分频,PREDIV2 = 25 / 5 = 5MHz
//PLL2通过PLL2MUL倍频寄存器提高工作频率,典型值为8倍频,PLL2CLK = 40 MHz。
//PLL的时钟源可以由PREDIV1和HSI二分频提供,这里选用PREDIV1。PREDIV1自身的时钟源又可以由HSE直接提供和PLL2提供,这里选用PLL2提供。

//设置PREDIV2、PREDIV1、PREDIV1SRC预分频和PLL2MUL倍频寄存器值
//HSE频率为25MHz,PREDIV2 5分频后传给PLL2的频率为5MHz,然后PLL2MUL 选择为8倍频,PLL2的频率为5 × 8 = 40MHz
//使用PLL2为PREDIV1的时钟源,并且PREDIV1 5分频,PREDIV1的频率为8MHz
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; //开启PLL2时钟
/* 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_PLLXTPRE_PREDIV1 0x00000000 不对输入时钟进行分频
//选择PREDIV1为PLL的时钟源,并且设置PLL倍频为9倍频,所以频率为 72MHz
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9);
#else
/*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
//先清除PLLMUL 倍频系数位,PLLSRC 输入时钟源位,PLLXTPRE HSE分频位
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));

//使用HSE为PLL的输入时钟源,并且不对HSE分频,另外PLLMUL倍频系数选择9倍频,外部高速时钟被限定为8MHz频率
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
//
#endif /* STM32F10X_CL */

/* Enable PLL */
RCC->CR |= RCC_CR_PLLON; //将PLL开启

/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0) //等待PLL就绪
{
}

/* Select PLL as system clock source */
//将PLL配置为系统时钟SYSCLK频率
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 */
//检查SWS位是不是是10,如果是就表明是PLL作为系统时钟
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 */

/* Go to infinite loop */
while (1)
{
}
}
}
#endif


总的来说,控制STM32F107 互联网系列的时钟可以分为下面几步:

第一步、 开启外部高速HSE时钟,需要其等待其稳定 (因为后面PLL2要使用其作为时钟源)

第二步、开启Flash闪存预取缓冲区 (这个东西还没有很了解)

第三步、设置AHB、APB1、APB2的预分频寄存器和PLL相关寄存器 (主要是选定PLL2 、PLL时钟源)

第四步、开启PLL2和PLL并选用PLL作为系统时钟源
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: