STM32之时钟

1、时钟源

三个不同的时钟源可做系统时钟(SYSCLK):

HSI 振荡器时钟 :高速内部时钟
HSE 振荡器时钟:高速外部时钟
PLL 时钟

两个二级时钟

LSI(低速内部时钟) :32kHz 低速内部 RC (LSI RC) 独立看门狗时钟源,RTC 可用于自动唤醒停止/待机模式。
LSE(低速外部时钟):32.768kHz 低速外部晶振 (LSE crystal) ,可选择作为 RTC(RTCCLK) 时钟源

每一个时钟源不需要时都可以被选择性关闭,用来节省系统电源消耗

预分频器被用来设置 AHB, APB (APB2) 和 APB (APB1) 频率。 AHB域最大频率可设置为168 MHz。APB2 域最高频率可达 84 MHz。APB1 域最高频率可达 42 MHz。

AHB:主要用于控制USB,网卡,IO口,DMA等设备
APB:主要用于控制串口,AD,I2C等设备
以上总线时钟分频系数在RCC_CFGR 寄存器的10到15位设置

2、时钟输出

  • 一共有两个微控制器时钟输出脚
    1. MCO1:配置预分频器可以选择四种时钟源输出到MCO1引脚
      (PA8)HSI LSE HSE PLL
      配置 MCO1PRE[2:0] 和 MCO1[1:0] 位来选择上面给出时钟源
    2. MCO2:配置预分频器可以选择四种时钟源输出到MCO2引脚(PC9)
      HSE PLL SYSCLK PLLI2S
      配置 MCO2PRE[2:0] 和 MCO2 位来选择上面给出的时钟源

另:输出时钟的最高频率不得超过100MHz,因为引脚的最高速不超过100MHz

3、时钟测量

STM32F407ZG允许用TIM5 channel 4 和 TIM11 channel 1捕获时钟源,以此间接的测量所有片上时钟源的频率

  • TIM5 channel 4

    TIM5有一个复用控制器可用来选择输入捕获由I/O触发还是由内部时钟触发。可通过设置 TIM5_OR 的 TI4_RMP [1:0] 位来选择。
    测量LSI时钟频率的设置步骤:

    1. 使能 TIM5 时钟,并配置 channel 4 为输入捕获模式
    2. 设置 TIM5_OR 寄存器的 TI4_RMP 位为 0x01 来连接 LSI 时钟
    3. 用TIM5 4个事件或者中断来捕获/比较测量内部时钟
      4.用测量到的 LSI 频率来更新 RTC 的预分频对看门狗时钟输出编程
  • TIM11 channel 1

    TIM11有一个复用控制器可用来选择输入捕获由I/O触发还是由内部时钟触发。可通过设置 TIM11_OR 的 TI1_RMP [1:0] 位来选择。
    设置步骤与TIM5 channel 4 类似

4、PLL的数值设置

f(VCO clock) = f(PLL clock input) × (PLLN / PLLM)
f(PLL general clock output) = f(VCO clock) / PLLP
f(USB OTG FS, SDIO, RNG clock output) = f(VCO clock) / PLLQ
标号 意义
f(PLL clock input) 也就是输入到PLL锁相环的时钟,输入源可选择,系统刚刚上电的时候是HSI源,参见下面的:系统复位时钟设置
f(VCO clock) 暂且理解为经倍频之后待输出的频率,之后要进行分频设置,频率大小与芯片硬件相关
f(PLL general clock output) PLL实际输出的时钟频率,参照STM32时钟树图的中间部分SYSCLK 168MHz max处,这个点的时钟频率就是f(PLL general clock output),之后又会经过分频以供不同的设备使用
f(USB OTG FS, SDIO, RNG clock output) 也就是USB SDIO等等的频率,通常48MHz

5、时钟树图

对比S3C2440与该STM32也就是Cortex M4的时钟图(S3C2440画的更清晰明了,更详细一点)

STM32的时钟树图
STM32之时钟_第1张图片

S3C2440的时钟树图
STM32之时钟_第2张图片

ARM的时钟基本就是采用PLL锁相环结构,之后分出来FCLK,PCLK与HCLK分别给系统内核,片内外设,以及硬件提供时钟。

6、系统复位时时钟设置

寄存器 复位值 意义
RCC_CR 0x00000083 开启HSI,系统运行刚上电就是以HSI为时钟源运行的
RCC_PLLCFGR 0x24003010 PLLQ = 4, HSI为PLL源,PLLP = 2,PLLM = 16,PLLN = 192
RCC_CFGR 0x00000000 系统时钟不分频,系统时钟源为HSI

STM32F407ZG寄存器地址映射
STM32之时钟_第3张图片

7、时钟初始化最简编程实例

#define BYM_RCC_struct \
    ((RCC_TypeDef *)(AHB1PERIPH_BASE + 0x3800))

#define BYM_FLASH_struct \
    ((FLASH_TypeDef *)(AHB1PERIPH_BASE + 0x3C00))

#define F_VCO_CLK_336   ((336 << 6) | (8 << 0))
#define PLL_OUT_CLK_168 (0 << 16)
#define USB_OUT_CLK_48  (7 << 24)

/* * Initialize system clock * f(VCO clock) = f(PLL clock input) * (PLLN / PLLM) * f(PLL general clock output) = f(VCO clock) / PLLP * f(USB OTG FS, SDIO, RNG clock output) = f(VCO clock) / PLLQ * * PLLP = 2; PLLQ = 7; PLLN / PLLM = 42; * f(PLL clock input) = 8M; f(VCO clock) = 336M; * f(PLL general clock output) = 168M; f(USB OTG FS, SDIO, RNG clock output) = 48M */
void sys_clk_init(void)
{   
    /* 开启HSE高速外部时钟 */
    BYM_RCC_struct->CR &= ~(1 << 16);      //Disable HSE, be required by bit18 if you want to write bit18
    BYM_RCC_struct->CR &= ~(1 << 18);      //HSE not bypassed
    BYM_RCC_struct->CR |= (1 << 16);        //Enable HSE
    while((BYM_RCC_struct->CR & (1 << 17)) == 0);   //Wait for HSE ready

    /* 设置倍频系数确定时钟频率(PLL模式) */
    BYM_RCC_struct->PLLCFGR &= ~(0xf << 24);   //Clear old data
    BYM_RCC_struct->PLLCFGR |= USB_OUT_CLK_48;  //Flush new data
    BYM_RCC_struct->PLLCFGR &= ~(0x3 << 16);   //Clear old data
    BYM_RCC_struct->PLLCFGR |= PLL_OUT_CLK_168; //Flush new data
    BYM_RCC_struct->PLLCFGR &= ~(0x7fff << 0); //Clear old data
    BYM_RCC_struct->PLLCFGR |= F_VCO_CLK_336;   //Flush new data

    /* 选择PLL源为HSE,开启PLL */
    BYM_RCC_struct->PLLCFGR |= (1 << 22);       //HSE oscillator clock selected as PLL and PLLI2S clock entry

    BYM_RCC_struct->CR &= ~(1 << 26);          //Close PLLI2S
    BYM_RCC_struct->CR |= (1 << 24);            //Enable PLL
    while((BYM_RCC_struct->CR & (1 << 25)) == 0);

    /* 使能指令预取与指令数据Cache(调试过程发现此步是必须的,否则程序没法运行) */
    BYM_FLASH_struct->ACR   |= (1 << 8);        //
    BYM_FLASH_struct->ACR   |= (1 << 9);        //Enable ICache
    BYM_FLASH_struct->ACR   |= (1 << 10);       //Enable DCache
    BYM_FLASH_struct->ACR   |= (5 << 0);        //Wait

    /* 配置预分频器来对时钟进行分频 */
    BYM_RCC_struct->CFGR    &= ~(0x7 << 13);
    BYM_RCC_struct->CFGR    |= (4 << 13);       //APB2 max value = 84M, 2 devided as system clock = 168M
    BYM_RCC_struct->CFGR    &= ~(0x7 << 10);
    BYM_RCC_struct->CFGR    |= (5 << 10);       //APB1 max value = 42M, 4 devided as system clock = 168M
    BYM_RCC_struct->CFGR    &= ~(0xf << 4);        //system clock not devided

    /* 选择系统时钟来源 */
    BYM_RCC_struct->CFGR    &= ~(0x3 << 0);
    BYM_RCC_struct->CFGR    |= (2 << 0);        //PLL used as system clock
    while((BYM_RCC_struct->CFGR & (2 << 2)) == 0);

    BYM_RCC_struct->CR &= ~(1 << 0);       //Close HSI
    while((BYM_RCC_struct->CR & (1 << 1)));
}

8、初始化时钟的步骤,PLL模式

  • 开启HSE高速外部时钟
  • 设置倍频系数确定时钟频率(PLL模式)
  • 配置预分频器来对时钟进行分频
  • 选择PLL源为HSE,开启PLL
  • 使能指令预取与指令数据Cache(调试过程发现此步是必须的,否则程序没法运行)
  • 选择系统时钟来源

由上面的程序可知,某些步骤可以被打乱,但是开启PLL之前必须选择好PLL源以及设置好倍频系数。设置系统时钟来源之前必须先启动ICache与DCache。要关闭HSI的话要在上述步骤设置完之后才能够关闭,因为系统上电之后默认的是HSI为系统时钟源并且使用PLL,所以在HSI还在被使用的时候是不可能被关闭的,只有系统时钟源改变之后才能够被关闭,关闭HSI以节省电量

你可能感兴趣的:(单片机,时钟,stm32)