【转】stm32之RCC

转自:http://bbs.ednchina.com/BLOG_ARTICLE_1793931.HTM

系统的时钟可以有3个来源:内部时钟HSI,外部时钟HSE,或者PLL(锁相环模块)的输出。它们由RCC_CFGR寄存器中的SW来选择。

 

SW(1:0):系统时钟切换 
       由软件置’1’或清’0’来选择系统时钟源。        在从停止或待机模式中返回时或直接或间接作为系统时钟的HSE出现故障时,由硬件强制选择HSI作为系统时钟(如果时钟安全系统已经启动) 
00:HSI作为系统时钟; 
01:HSE作为系统时钟; 
10:PLL输出作为系统时钟; 
11:不可用。

 

////////////////////////////////////////////////////////////////////

 

PLL的输出直接送到USB模块,经过适当的分频后得到48M的频率供USB模块使用。

 

系统时钟的一路被直接送到I2S模块;另一路经过AHB分频后送出,送往各个系统,其中直接送往SDI,FMSC,AHB总线;8分频后作为系统定时器时钟;经过APB1分频分别控制PLK1、定时器TIM2~TIM7;经过APB2分频分别控制PLK2、定时器TIM1~TIM8、再经分频控制ADC;

 

由此可知,STM32F10x芯片的时钟比之于51、AVR、PIC等8位机要复杂复多,因此,我们立足于对着芯片手册来解读程序,力求知道这些程序代码如何使用,为何这么样使用,如果自己要改,可以修改哪些部分,以便自己使用时可以得心应手。

 

单步执行,看一看哪些代码被执行了。

 

 /* Reset the RCC clock configuration to the default reset state(for debug purpose) */

 

  /* Set HSION bit */

 

 RCC->CR |= (uint32_t)0x00000001;

 

 

 

 

 

 

 

这是RCC_CR寄存器,由图可见,HSION是其bit 0位。

 

       HSION:内部高速时钟使能

 

       由软件置’1’或清零。

 

       当从待机和停止模式返回或用作系统时钟的外部4-25MHz时钟发生故障时,该位由硬件置’1’来启动内部8MHz的RC振荡器。当内部8MHz时钟被直接或间接地用作或被选择将要作为系统时钟时,该位不能被清零。

 

       0:内部8MHz时钟关闭;

 

       1:内部8MHz时钟开启。

 

///////////////////////////////////////////////////////////////////////

 

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */

 

#ifndef STM32F10X_CL

 

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

 

 

 

 

 

这是RCC_CFGR寄存器

 

该行程序清零了MC0[2:0]这三位,和ADCPRE[1:0],ppre2[2:0],PPRE1[2:0],HPRE[3:0],SWS[1:0]和SW[1:0]这16位。

 

/*

 

MCO: 微控制器时钟输出,由软件置’1’或清零。

 

0xx:没有时钟输出;

 

100:系统时钟(SYSCLK)输出;

 

101:内部8MHz的RC振荡器时钟输出;

 

110:外部4-25MHz振荡器时钟输出;

 

111:PLL时钟2分频后输出。

 

*/

 

/* Reset HSEON, CSSON and PLLON bits */

 

  RCC->CR &= (uint32_t)0xFEF6FFFF;

 

清零了PLLON,HSEBYP,HSERDY这3位。

 

 /* Reset HSEBYP bit */

 

  RCC->CR &= (uint32_t)0xFFFBFFFF;

 

清零了HSEBYP位 ///???为什么不一次写??

 

       HSEBYP:外部高速时钟旁路,在调试模式下由软件置’1’或清零来旁路外部晶体振荡器。只有在外部4-25MHz振荡器关闭的情况下,才能写入该位。

 

0:外部4-25MHz振荡器没有旁路;

 

1:外部4-25MHz外部晶体振荡器被旁路。

 

所以要先清HSEON位,再清该位。

 

 

 

/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */

 

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

 

清零了:USBPRE,PLLMUL,PLLXTPR,PLLSRC共7位

 

 /* Disable all interrupts and clear pending bits  */

 

  RCC->CIR = 0x009F0000;

 

////这个暂不解读

 

SetSysClock();

 

跟踪进入该函数,可见一连串的条件编译:

 

 

 

 

 

单步运行,执行的是:

 

#elif defined SYSCLK_FREQ_72MHz

 

  SetSysClockTo72();

 

为何执行该行呢,找到SYSCLK_PREQ_**的相关定义,如下图所示。

 

 

 

 

 

       这样就得到了我们所要的一个结论:如果要更改系统工作频率,只需要在这里更改就可以了。

 

    可以继续跟踪进入这个函数来观察如何将工作频率设定为72MHz的。

 

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);

 

 //开启HSE

 

  /* Wait till HSE is ready and if Time out is reached exit */

 

  do

 

  {

 

    HSEStatus = RCC->CR & RCC_CR_HSERDY;

 

    StartUpCounter++; 

 

  } while((HSEStatus == 0) && (StartUpCounter != HSEStartUp_TimeOut));

 

//等待HSE确实可用,这有个标志,即RCC_CR寄存器中的HSERDY位(bit 17),这个等待不会无限长,有个超时策略,即每循环一次计数器加1,如果计数的次数超过HSEStartUp_TimeOut,就退出循环,而这个HSEStartUp_TimeOut在stm32f10x.h中定义,

 

#define HSEStartUp_TimeOut   ((uint16_t)0x0500) /*!< Time out for HSE start up */

 

///////////////////////////////////////////////////////////////////////////////////////////////

 

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)

 

  {

 

    HSEStatus = (uint32_t)0x01;

 

  }

 

  else

 

  {

 

    HSEStatus = (uint32_t)0x00;

 

  } 

 

///再次判断HSERDY标志位,并据此给HSEStatus变量赋值。

 

  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;   

 

 

 

 

 

    /* HCLK = SYSCLK */

 

    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

 

   //找到定义: #define  RCC_CFGR_HPRE_DIV1                  ((uint32_t)0x00000000)        /*!< SYSCLK not divided */ 

 

 

 

    /* PCLK2 = HCLK */

 

    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;

 

  //找到定义:#define  RCC_CFGR_PPRE2_DIV1                 ((uint32_t)0x00000000)        /*!< HCLK not divided */

 

 

 

    /* PCLK1 = HCLK */

 

    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

 

//找到定义:#define  RCC_CFGR_PPRE1_DIV2                 ((uint32_t)0x00000400)        /*!< HCLK divided by 2 */

 

 

 

#ifdef STM32F10X_CL

 

    ……

 

  #else   

 

    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */

 

    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |

 

                                        RCC_CFGR_PLLMULL));

 

    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);

 

#endif /* STM32F10X_CL */

 

//以上是设定PLL的倍频系数为9,也就是说,这个72M是在外部晶振为8M时得到的。

 

    /* Enable PLL */

 

    RCC->CR |= RCC_CR_PLLON;

 

 

 

    /* Wait till PLL is ready */

 

    while((RCC->CR & RCC_CR_PLLRDY) == 0)

 

    {

 

    }

 

        /* Select PLL as system clock source */

 

    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 */

 

    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)

 

    {

 

    }

 

  }

 

}

 

至此,我们可以归纳几条:

 

(1)       时钟源有3个

 

(2)       开机时默认是HSI起作用,可以配置为所要求的任意一个时钟

 

(3)       配置时必须按一定的顺序来打开或都关闭一些位,并且各时钟起作用有一定的时间,因此要利用芯片内部的标志位来判断是否可以执行下一步。

 

(4)       如果外部时钟、PLL输出失效,系统可以自动回复到HSI(开启时钟安全系统)

 

(5)       HSI的频率准确度可以达到+/- 1%,如果有必要时,还可以用程序来调整这个频率,可调的范围大致在200KHz左右。

 

最后让我们来感受一下劳动的果实吧--试着改改频率看有何反应。

 

为查看更改后的效果,先记录更改前的数据。将调试切换到仿真,在第一条:

 

Delay(0xAFFFF);

 

指令执行前后,分别记录下Status和Sec

 

Status:2507      3606995

 

Sec:0.00022749   0.05028982

 

将振荡频率更改为36MHz,即

 

...

 

 #define SYSCLK_FREQ_36MHz  36000000   //去掉该行的注释
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
/*#define SYSCLK_FREQ_72MHz  72000000*/  //将该行加上注释

 

再次运行,结果如下:

 

Status:2506      3606994

 

Sec:0.00008478   0.10036276

 

    基本上是延时时间长了一倍。改成硬件仿真,将代码写入板子,可以看到LED闪烁的频率明显变慢了。

转载于:https://www.cnblogs.com/clarissyan/archive/2012/01/13/2321870.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值