新到一家公司后,有个项目要用到STM32F207Vx单片机,找到网上的例子照猫画虎的写了几个例子,比如ADC,可是到了ADC多通道转换的时候就有点傻眼了,这里面的时钟跑的到底是多少M呢?单片机外挂的时钟是25M,由于该单片机时钟系统较为复杂,有内部高/低、外部高/低 、PLL锁相环时钟,又有AHB总线时钟、APB1/2时钟,而例子中很少讲到系统时钟的默认配置是怎么配置呢?那么就发点时间研究下这个单片机内部的复杂时钟系统吧。

下图是STM32F2系列的时钟树结构图:

1、内部高速时钟HSI、外部高速时钟HSE和PLL时钟PLLCLK时钟都接到了SW开关处,通过SW选择哪一路作为SYSCLK,SYSCLK经过AHB分频器进行分频得到HCLK,APB1和APB2是挂在总线AHB上的,通过APB1和APB2分频得出fpclk1和fpclk2。

2、PLL输入时钟源主要是靠外部高速时钟和内部高速时钟作为时钟源,通过PLLCFGR寄存器的bit22来选择具体哪一路作为时钟源。选择好了时钟源进入/M分频器,也就是PLLM进行分频,送入VCO,在通过xN,进行倍频,也就是PLLN:(1)通过/P进行分频(PLLP)得到PLLCLK;(2)通过/Q分频(PLLQ),得到PLL48CK。

然后边看代码边对照结构图进行分析,看软件如何给单片机配置系统时钟的。

然后找到启动代码“startup_stmf32xx.s”,该代码是用汇编写的,可以看到,在调用main函数之前,是先调用了SystemInit函数的,该函数是在“system_stm32f2xx.c”中

; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

代码如下,变量直接赋个16进制的数,都不知道是啥意思,目的是干什么的,不知道,所以看下面代码时最好对照STM32F2x用户手册。当然这个只是一个初始化,待会主要看SetSysClock();这个函数,在调用该函数之前,我们知道单片机是先启用了内部高速时钟等一些配置。

void SystemInit(void)
{
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001; //RCC_CR复位值0x0000_xx83,内部高速时钟使能,也就是说上电开始就使用内部高速时钟,16MHZ /* Reset CFGR register */
RCC->CFGR = 0x00000000; //通过开关SW选择内部高速时钟作为系统时钟16MHZ
//AHB prescaler 不分频
//APB Low speed prescaler (APB1) 不分频,fplck1 = 16MHZ
//APB high-speed prescaler (APB2)不分频,fplck2 = 16MHZ
//MCO1和MCO2时钟输出等配置可参考用户手册Page95 /* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF; /* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010; //RCC_CFGR复位值是0x2400_3010 /* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF; //对bit18 HSEBYP 设置为0,外部高速时钟被禁止 /* Disable all interrupts */
RCC->CIR = 0x00000000; //所有时钟中断都被禁止 #ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */ /* Configure the System clock source, PLL Multiplier and Divider factors,
AHB/APBx prescalers and Flash settings ----------------------------------*/
SetSysClock(); /* Configure the Vector Table location add offset address ------------------*/
#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
}

在SystemInit(void)函数中在配置完一些参数后,还调用了SetSysClock()函数,该函数代码如下

static void SetSysClock(void)
{
/******************************************************************************/
/* PLL (clocked by HSE) used as System clock source */
/******************************************************************************/
__IO uint32_t StartUpCounter = , HSEStatus = ; /* Enable HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON); //外部高速时钟使能,25MHZ /* Wait till HSE is ready and if Time out is reached exit */ //外部时钟使能后,得需要一点时间到达各个端口
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY为0,说明外部时钟还没准备好,1说明外部时钟已准备好
StartUpCounter++;//对读的次数进行累加,当累加次数到达1280次时,就意味着启动时间超时
} while((HSEStatus == ) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET)//这里判断上面的do while循环是因哪个条件结束的
{
HSEStatus = (uint32_t)0x01; //说明时钟已准备好了,才结束do whlie循环
}
else
{
HSEStatus = (uint32_t)0x00; //说明是因为超时了而退出do while循环
} if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK / 1*/
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分频,AHB出来后时钟就是sysclk=120M /* PCLK2 = HCLK / 2*/
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分频,fpclk2 = sysclk/2 = 60M /* PCLK1 = HCLK / 4*/
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分频,fplck1 = sysclk/4 = 30M /* Configure the main PLL */ //主要对PLL和PPI2S 进行配置
RCC->PLLCFGR = PLL_M | (PLL_N << ) | (((PLL_P >> ) -) << ) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << );
//配置完后RCC->PLLCFGR = 0x05403c19,然后对照寄存器RCC_PLLCFGR查看哪些位对应哪些功能
//通过bit5~bit0可计算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,对应时钟结构图中的/M
//通过bit14~bit6可计算出倍频因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,对应时钟结构图中的xN
//bit17~bit16可计算出分频因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M
//bit22是选择给PLL输入的时钟源,输入时钟源有外部和内部高速时钟,这里选择的是外部高速时钟即PLL input clock = HSE =25M
//bit27~24可计算出分频因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M /* Enable the main PLL */
RCC->CR |= RCC_CR_PLLON; //使能PLL /* Wait till the main PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == )
{
}
//到这里RCC->CR已配置完,最终值0x03036783
//通过查看用户手册知道,内部高速时钟、外部高速时钟、PLL时钟都已开启 /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS; /* Select the main PLL as system clock source */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL; //到这里RCC->CFGR已配置完,最终值是0x0000_940A
//通过查看用户手册,知道,PLL时钟作为系统时钟即120M
//AHB不分频,即HCLK = 120M
//APB1 4分频,即fpclk1 = 120/4=30M
//APB2 2分频,即fpclk2 = 120/2=60M /* Wait till the main PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_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 */
} }

OK,分析完这段代码后,调用系统固件函数后,现在知道了时钟树结构图中右边出来的时钟是多少MHz了吧。

总结:

1、使用的是外部时钟25MHZ,通过PLL进行分频倍频分频得到PLLCLK 120M,PLLCLK作为系统时钟SYSCLK。

2、APB1出来是30M,也就是FPCLK1。

3、APB2出来是60M,也就是FPCLK2。

STM32F2系列系统时钟默认配置的更多相关文章

  1. CentOS和RedHat等系列系统 yum源配置、时间同步

    一.yum源配置 1,进入yum源配置目录cd /etc/yum.repos.d 2,备份系统自带的yum源mv CentOS-Base.repo CentOS-Base.repo.bk下载163网易 ...

  2. STM32F4_RCC系统时钟配置及描述

    Ⅰ.概述 对于系统时钟应该都知道它的作用,就是驱动整个芯片工作的心脏,如果没有了它,就等于人没有了心跳. 对于使用开发板学习的朋友来说,RCC系统时钟这一块知识估计没怎么去配置过,原因在于开发板提供的 ...

  3. 基于Systick系统时钟延时的LED闪烁灯

    1.回顾我们的51 单片机编程,当我们需要做系统延迟的时候,最常采用的一种方式就是使用for 循环的空语句等待来实现. 当然,在STM32 里面也可以这么实现.但是在STM32 的Cortex 内核里 ...

  4. STM32—时钟树(结合系统时钟函数理解)

    时钟树的概念: 我们可以把MCU的运行比作人体的运行一样,人最重要的是什么?是心跳! 心脏的周期性收缩将血液泵向身体各处.心脏对于人体好比时钟对于MCU,微控制器(MCU)的运行要靠周期性的时钟脉冲来 ...

  5. 痞子衡嵌入式:系统时钟配置不当会导致i.MXRT1xxx系列下OTFAD加密启动失败

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是系统时钟配置不当会导致i.MXRT1xxx系列下OTFAD加密启动失败问题. 我们知道,i.MXRT1xxx家族早期型号(RT1050/ ...

  6. STM32F2系列时钟的配置

    前一节详细介绍了系统默认的时钟配置,及各路时钟输出是多少,这是默认配置的,但实际使用的时钟默认的时钟并不符合要求,所以就得知道如何调用库函数进行配置. 最好的资料就是查阅stm32f2xx_rcc.c ...

  7. STM32入门系列-STM32时钟系统,时钟初始化配置函数

    在前面推文的介绍中,我们知道STM32系统复位后首先进入SystemInit函数进行时钟的设置,然后进入主函数main.那么我们就来看下SystemInit()函数到底做了哪些操作,首先打开我们前面使 ...

  8. STM32入门系列-STM32时钟系统,时钟使能配置函数

    之前的推文中说到,当使用一个外设时,必须先使能它的时钟.怎么通过库函数使能时钟呢?如需了解寄存器配置时钟,可以参考<STM32F10x中文参考手册>"复位和时钟控制(RCC)&q ...

  9. Cotex-M3内核STM32F10XX系列时钟及其配置方法

    一.背景 最近做个项目,需要使用STM32,还是以前一样的观点,时钟就是MCU心脏,供血即时钟频率输出,想要弄明白一个MCU,时钟是一个非常好的切入点.言归正传,网上已经有太多大神详述过STM32的详 ...

随机推荐

  1. Jenkins中关于一些插件的使用

    Jenkins中关于一些插件的使用方法 最近在为公司搭建CI平台过程中,以及在具体项目实施过程中使用过的一些插件的具体用法: 1. ant插件 这个插件可能是我们最为经常使用的,若构建脚本是使用bui ...

  2. MongoDB学习(翻译4)

    接上篇..... EndsWith 此方法用于测试文档对象的字符串类型的字段或者属性是否以某一个特定的字串结束 var query =     from c in collection.AsQuery ...

  3. struts2文件上传大小限制问题

    struts2默认文件上传大小为2M,如需修改默认大小,解决方法如下: <struts> <constant name="struts.multipart.maxSize& ...

  4. 让MyEclipse里的Tomcat自动reloadable

    1  修改server.xml Context path="/***" docBase="XXX" reloadable="true"/&g ...

  5. QzzmServer v2.0正式版发布

                                 V2.1升级程序已发布,具体见下文                                首先,感谢网友的热情的测评及反馈,现Qzzm ...

  6. 实用的android颜色配置表(亮瞎尼的双眼)

    android开发中,常常会用到color.xml颜色配置,好的颜色配置可以让尼的应用让人看起来赏心悦目! 不罗嗦,上图先 该工程已经罗列了常用的颜色配置 附上工程链接:http://download ...

  7. 探讨C++ 变量生命周期、栈分配方式、类内存布局、Debug和Release程序的区别

    探讨C++ 变量生命周期.栈分配方式.类内存布局.Debug和Release程序的区别(一) 今天看博客园的文章,发现博问栏目中有一个网友的问题挺有趣的,就点进去看了下,标题是“C++生存期问题”,给 ...

  8. pipe----管道----fork

    #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h&g ...

  9. Haskell 笔记(四)函数系统

    函数系统 函数式编程当然少不了函数系统啦,在教程最初的时候就有一个最简单的函数,函数系统贯穿在Haskell全部,Haskell的函数有几个重要的性质. 首先声明一下函数的参数和返回值类型 然后有一个 ...

  10. Linux内核 hlist_head/hlist_node结构解析

    内核中的定义: struct hlist_head {    struct hlist_node *first;}; struct hlist_node {    struct hlist_node ...