2讲 RCC 时钟树讲解、系统时钟配置函数(寄存器配置)、使用HSE配置系统时钟(固件库配置)、HSI配置
内容纲要

2讲 RCC 时钟树讲解、系统时钟配置函数、使用HSE配置系统时钟

AHB 高速总线

APB 低速外设总线

file

AHB、APB参考网址

file

file

AHB----168M

APB1----42M 四分频

做项目的时候通常使用HSE时钟提供频率

使用有源晶振 HSI精度不高

file

file

file

(PPT错误,Low应该是High)

file

Q:为什么不在外部只用高频率的晶振?而是用锁相环来修改晶振频率?

S:外部晶振频率越高对器件工艺要求会更高,锁相环经过分频因子M将频率分成1MHZ 然后倍频因子N 倍频为336Mhz,流向AHB经过分频为2 修改时钟为168Mhz

file

  • 图上经过/M分频得到1Mhz 倍频xN 倍,得到VCO时钟 然后分频P 得到168Mhz
  • 经过/Q 的时候是将336分成48MHz 用处如下

file

系统时钟

来源:1. HSI 最高16M 2. HSE最高4-26M 3. 来源于PLLCLK 最高168Mhz ( 对于该芯片来说 )

file

file

file

  • HCLK时钟

file

file

  • PCLK1时钟

    file

  • PCLK2时钟

    file

  • RTC时钟

    file

一般使用LSE 外部时钟,只要是STM32芯片,LSE外部频率始终是32.768KHZ。LSI不稳定所以用的少。

file

  • MCO时钟

    file

  • I2C时钟

    file

    file

file


3讲 时钟配置SetSysClock() 自带的时钟函数学习

  • 先理清流程。

file

#include "bsp_clkconfig.h"
#include "stm32f4xx.h"

 #define PLL_M      25

/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
#define PLL_Q      7

#define PLL_N      336
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P      2

//////////////////////////这个函数是直接从复制的system_stm32f4xx.c文件的 void SystemInit(void); 函数/////////////////////////////

/**
  * @brief  Configures the System clock source, PLL Multiplier and Divider factors, 
  *         AHB/APBx prescalers and Flash settings
  * @Note   This function should be called only once the RCC clock configuration  
  *         is reset to the default reset state (done in SystemInit() function).   
  * @param  None
  * @retval None
  */
void UserSetSysClock(void)
{

    RCC_DeInit();   //这里调用的是stm32f4xx_rcc.c中的函数进行复位

/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;

  /* Enable HSE */
    /* 使能HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);

  /* Wait till HSE is ready and if Time out is reached exit */
    /* 等待HSE(高速外部时钟) 启动稳定,如果超时则退出 */
  do
  {

        /* 1字节=8Byte */
         //也就是Linux32位下int 大小4字节占用 32bit  16进制每2位代表8bit(1字节)
    StartUpCounter++;

        //下方代码,  RCC_CR_HSERDY 这个标志位是只读标志位 RCC->CR 代表调用这一块寄存器,将RCC_CR_HSERDY开启,
    HSEStatus = RCC->CR & RCC_CR_HSERDY;   //#define  RCC_CR_HSERDY   ((uint32_t)0x00020000)     10 0000 0000 0000 0000
        //
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));  //当 启动失败  或者  定时器没有超时就继续向上循环。
    /*  反过来启动成功  或者 定时器超时 就跳出循环)   */

    //RESET的定义  typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
  if ((RCC->CR & RCC_CR_HSERDY) != RESET)  //这部分代码就是等待HSE 启动稳定,如果超时就退出
  {
    HSEStatus = (uint32_t)0x01;  //单纯将状态记录下来,在下方代码会有体现对状态的修改。
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }

    // 如果HSE 启动稳定,
  if (HSEStatus == (uint32_t)0x01)
  {

        /************************* 与电源有关的外设  ************************/
    /* Select regulator voltage output Scale 1 mode */
        /*  选择电压调节器的模式为1 */
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;   //将外设时钟寄使能寄存器置1
    PWR->CR |= PWR_CR_VOS;

    /* HCLK = SYSCLK / 1*/
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;    //配置 AHB预分频器  (AHB prescaler ) 让寄存器知道它分频了多少     单词prescaler(预分频器)
        /*!< RCC clock configuration register, */

    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;    //divice 分,除法。

    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;   //配置 APB1  低速预分频器 (APB Low Speed prescaler)

    /* Configure the main PLL */
        /******  配置锁相环PLL 的 PQR M  分频因子,配置 N倍频因子 *****/

        //这里的晶振采用的 25MHz,如果采用别的晶振,这里的PLL_M 可以改为别的,比如16MHz 就改为16 
        //分频因子M必须得到1Mhz 的时钟
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);

    /******  配置锁相环PLL 的 PQR M  分频因子,配置 N倍频因子 *****/

    /* Enable the main PLL */
        /*使能主PLL*/
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till the main PLL is ready */
        /*  等待主PLL稳定**/
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }

    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
        /* 配置FLSH预取址,指令缓存,数据缓存,等待周期 */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;

    /* Select the main PLL as system clock source */
        /* 选择主PLL时钟作为系统时钟 */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;

    /* Wait till the main PLL is used as system clock source */
        /* 确保主PLL 选为系统时钟 */
    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 */
        /* HSE启动失败,则在这里添加启动失败的处理代码 */   
  }
}

Summary:

在系统默认初始化RCC 时钟之后,如果你想使用自己的时钟,你需要复位时钟,然后再调用你的时钟。

file

file


4讲 使用HSE配置系统时钟并使用MCO输出监控系统时钟

  • 使用HSE配置系统时钟

值得一看的文章

/********************************* HSE操作固件库进行编程 ***********************************************/
void HSE_SetSysClock(uint32_t PLLM, uint32_t PLLN, uint32_t PLLP, uint32_t PLLQ)
{

    ErrorStatus HSE_ConfigStatus = ERROR; 
    RCC_DeInit();

  /* Enable HSE */
    /* 使能HSE */
  //RCC->CR |= ((uint32_t)RCC_CR_HSEON);
    //1.调用函数库实现HSE使能
    RCC_HSEConfig(RCC_HSE_ON);

    //2.等待时钟初始化完毕
    HSE_ConfigStatus = RCC_WaitForHSEStartUp();   //这个函数执行的时候会自动等待

    if( HSE_ConfigStatus == SUCCESS)
    {
    //3.开启APB1 的电源使能  //ask? 为什么不开启AHB1的呢?  不过AHB1在函数调用的时候会开。
        RCC->APB1ENR |= RCC_APB1ENR_PWREN;
        PWR->CR |= PWR_CR_VOS;     //VOS[1:0] bits (Regulator voltage scaling output selection)

    //4.配置预分频器
        RCC_HCLKConfig(RCC_SYSCLK_Div1);  //AHB高速总线直接全频
        RCC_PCLK1Config(RCC_HCLK_Div4);  //APB1四分频
        RCC_PCLK2Config(RCC_HCLK_Div2);   //APB2二分频

    //5.配置锁相环
        RCC_PLLConfig(RCC_PLLSource_HSE,PLLM,PLLN, PLLP,PLLQ);

        //6.使能主PLL 
        RCC_PLLCmd(ENABLE);

        //7.等待主PLL 稳定

        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }

        //  /* 配置FLSH预取址,指令缓存,数据缓存,等待周期 */ 
         FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;

        //8.配置PLL 为系统时钟
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

        //8.验证时钟是否为系统时钟。
        while( RCC_GetSYSCLKSource() != 0x08  )
        {
        /* 循环体里面表示函数没配置成功 */
        }

    }else
    {
    //如果HSE启动失败,
    }
}
/********************************* HSE操作固件库进行编程 END ***********************************************/
  • 用MCO输出监控系统时钟
/********************************- 用MCO输出监控系统时钟 ****************************************************/
void OutPutMCO1()
{
    //假定使用的是HSE 时钟来提供频率
    //1.配置GPIOA  的pin8引脚,  MCO1引脚 PA8  MCO 输出时钟不超过100MHz 最大I/O速度

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef initGPIO;
    initGPIO.GPIO_Pin = GPIO_Pin_8;
    initGPIO.GPIO_Mode = GPIO_Mode_AF;  //这里选择的是复用模式。
    initGPIO.GPIO_Speed = GPIO_High_Speed;
    initGPIO.GPIO_OType = GPIO_OType_PP;
    initGPIO.GPIO_PuPd = GPIO_PuPd_NOPULL; //输出端口既不上拉也不下拉

    GPIO_TypeDef typeGPIOA;
    typeGPIOA.ODR = GPIO_Pin_8;  
    GPIO_Init(GPIOA, &initGPIO);  

    //配置MCO 输出  选择MCO 输出的源端口信号。
    RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_1);

}

void OutPutMCO2()
{
    //假定使用的是HSE 时钟来提供频率
    //1.配置GPIOA  的pin9引脚,  MCO2引脚 PC9  MCO 输出时钟不超过100MHz 最大I/O速度

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

    GPIO_InitTypeDef initGPIO;
    initGPIO.GPIO_Pin = GPIO_Pin_9;
    initGPIO.GPIO_Mode = GPIO_Mode_AF;  //这里选择的是复用模式。
    initGPIO.GPIO_Speed = GPIO_High_Speed;
    initGPIO.GPIO_OType = GPIO_OType_PP;
    initGPIO.GPIO_PuPd = GPIO_PuPd_NOPULL; //输出端口既不上拉也不下拉

    GPIO_TypeDef typeGPIOC;
    typeGPIOC.ODR = GPIO_Pin_9;  
    GPIO_Init(GPIOC, &initGPIO);  

    //配置MCO 输出  选择MCO 输出的源端口信号。
    RCC_MCO2Config(RCC_MCO2Source_SYSCLK, RCC_MCO2Div_1);  //MCO2与MCO1有一个区别在于这里可以输出系统时钟,而MCO1没有这个选项

}

/********************************- 用MCO输出监控系统时钟 END ****************************************************/

5讲 使用HSE配置系统时钟

void HSI_SetSysClock(void)
{

    volatile ErrorStatus HSI_ConfigStatus = ERROR; 
    RCC_DeInit();
    //uint32_t timeOutCount = 0;

    //1.调用函数库实现HSI使能
    //RCC_HSIConfig(RCC_HSI_ON);
    RCC_HSICmd(ENABLE);

    //2.等待时钟初始化完毕
    /*由于HSI 没有等待函数,所以需要自己写一个*/

        //timeOutCount++;
     HSI_ConfigStatus = RCC->CR & 0x02;  //RCC->CR 和0x02相与,如果CR的 HSIRDY(默认0) 位为1就代表HSI启动成功

    if( HSI_ConfigStatus != ERROR)
    {
    //3.开启APB1 的电源使能  //ask? 为什么不开启AHB1的呢?  不过AHB1在函数调用的时候会开。
        RCC->APB1ENR |= RCC_APB1ENR_PWREN;
        PWR->CR |= PWR_CR_VOS;     //VOS[1:0] bits (Regulator voltage scaling output selection)

    //4.配置预分频器
        RCC_HCLKConfig(RCC_SYSCLK_Div1);  //AHB高速总线直接全频
        RCC_PCLK1Config(RCC_HCLK_Div4);  //APB1四分频
        RCC_PCLK2Config(RCC_HCLK_Div2);   //APB2二分频

    //5.配置锁相环
        RCC_PLLConfig(RCC_PLLSource_HSI,16,336, 2,7);

        //6.使能主PLL 
        RCC_PLLCmd(ENABLE);

        //7.等待主PLL 稳定

        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }

        //  /* 配置FLSH预取址,指令缓存,数据缓存,等待周期 */ 
         FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;

        //8.配置PLL 为系统时钟
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

        //8.验证时钟是否为系统时钟。
        while( RCC_GetSYSCLKSource() != 0x08  )
        {
        /* 循环体里面表示函数没配置成功 */
        }

    }else
    {
    //如果HSI启动失败,
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇