第9章 基础重点—时钟系统

  • Post author:
  • Post category:其他




一、关于时钟

如图 9.1.1 所示,

左侧的①HSI(内部高速时钟)、 ②HSE(外部高速时钟)、③LSE(外部低速时钟)、④LSI(内部低速时钟)为时钟源,右侧的各种片上外设。

图中矩形框内用“/”加数字表示分频器,如:
/2
,数字表示几分频;矩形框内用“X”加数字表示的为锁相环,如:
X2
,数字表示几倍频;倒梯形表示选择器,长边表示多个输入,短边表示选择其中一个输出,如:
在这里插入图片描述


⑥系统时钟SYSCLK最高为72MHz,从图中左侧的

选择器SW

可以看到来源有三个,分别是:①内部高速时钟HSI(绿色)、⑤锁相环时钟PLLCLK(紫色)和②外部高速时钟HSE(黄色),而锁相环时钟PLLCLK由内部高速时钟HSI和外部高速时钟HSE,经过分频和PLL锁相环倍频而来。

内部高速时钟HSI可直接经过选择器SW给系统时钟SYSCLK,此时系统时钟SYSCLK为8MHz;内部高速时钟HSI先2分频,再经过选择器PLLSRC进入锁相环PLLMUL,最大倍频为16倍,得到64MHz的锁相环时钟PLLCLK给系统时钟SYSCLK;当外部高速时钟HSE(假设外接晶振为8MHz时)直接给选择器SW,则系统时钟SYSCLK为8MHz;当外部高速时钟HSE(假设外接晶振为8MHz时)直接经过选择器PLLXTPRE给 PLLSRC,再经过PLLMUL 9倍频,得到72MHz的PLLCLK给系统时钟SYSCLK。

⑩RTCCLK(实时时钟)的时钟源也有三个,分别是②外部高速时钟HSE的128倍分频(黄色)、③外 部低速时钟LSE的32.768kHz(蓝色)、④内部低速时钟LSI的40kHz(橙色)。

在这里插入图片描述
IWDGCLK(独立看门狗)的时钟来源于④内部低速时钟LSI的40kHz(橙色)。

理清各个时钟来源后,再来看各总线的时钟。⑦高速接口总线AHB由⑥SYSCLK系统时钟分频得到,最高是系统时钟的72MHz。⑧外设总线APB1和⑨外设总线APB2,来源于⑦高速接口总线AHB,APB1的输出时钟最高是36MHz,APB2的输出时钟最高是72MHz。APB1和APB2下有各种外设,比如GPIO、USART等。

在这里插入图片描述



二、硬件设计

内部时钟HSI不涉及硬件,外部时钟HSE参考最小系统的时钟电路。



三、软件设计



3.1 软件设计思路

实验目的:分别使用内部时钟HSI和外部时钟HSE作为系统时钟。

  1. 使用内部时钟HSI配置系统时钟到最大值64Mhz;
  2. 调用库函数读取系统时钟值以验证;
  3. 使用外部时钟HSE配置系统时钟到最大值72Mhz;
  4. 调用库函数读取系统时钟值以验证;



3.2 软件设计讲解



3.2.1 使用内部时钟源HSI作为PLL的时钟源,然后将PLL作为系统时钟源

HAL库定义了两个结构体,只需要设置这两个结构体成员,就

完成对时钟的设置。

/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow : 
  *            System Clock source            = PLL (HSI)
  *            SYSCLK(Hz)                     = 64000000
  *            HCLK(Hz)                       = 64000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 2
  *            APB2 Prescaler                 = 1
  *            PLLMUL                         = 16
  *            Flash Latency(WS)              = 2
  * @param  None
  * @retval None
  */
void SystemClock_Config(void)
{    
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType    = RCC_OSCILLATORTYPE_HSI; // 使用HSI作为时钟振荡器
    RCC_OscInitStruct.HSEState          = RCC_HSE_OFF; // 关闭HSE
    RCC_OscInitStruct.HSEPredivValue    = RCC_HSE_PREDIV_DIV1; // HSE预分频值为1
    RCC_OscInitStruct.HSIState          = RCC_HSI_ON; // 打开HSI
    RCC_OscInitStruct.PLL.PLLState      = RCC_PLL_ON; // 打开PLL
    RCC_OscInitStruct.PLL.PLLSource     = RCC_PLLSOURCE_HSI_DIV2; // PLL的输入时钟源为HSI的一分之二
    RCC_OscInitStruct.PLL.PLLMUL        = RCC_PLL_MUL16; // PLL倍频系数为16
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        while(1);
    }

    /** Initializes the CPU, AHB and APB busses clocks 
    */
    RCC_ClkInitStruct.ClockType         = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK
                                         | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource      = RCC_SYSCLKSOURCE_PLLCLK; // SYSCLK的来源为PLL的输出
    RCC_ClkInitStruct.AHBCLKDivider     = RCC_SYSCLK_DIV1; // AHB总线时钟分频系数为1
    RCC_ClkInitStruct.APB1CLKDivider    = RCC_HCLK_DIV2; // APB1总线时钟分频系数为2
    RCC_ClkInitStruct.APB2CLKDivider    = RCC_HCLK_DIV1; // APB2总线时钟分频系数为1

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    {
        while(1);
    }
}



3.2.2 获取系统时钟的函数

主函数里调用HAL库提供的“HAL_RCC_GetSysClockFreq()”函数获取系统时钟验证。

// 此处定义全局变量以便在debug的时候可以看到这个变量的值
uint32_t sys_clk = 0;

int main(void)
{    
    
    // 初始化HAL库函数必须要调用此函数
    HAL_Init();

    /*
     * 系统时钟即AHB/APB时钟配置
     * 当使用内部高速时钟HSI(8MHz)配置系统时钟时,使用PLL前会默认先二分频得到4MHz的PLL分频输入频率
     * 然后经过锁相环放大,最大放大倍数为16,即4*16=64MHz是能配置的最大系统频率,F103的最大系统频率为72MHz,64MHz显然是合法的系统频率
    */
    SystemClock_Config();
    
    // 调用库函数来检验自己的配置是否成功配置为系统频率64MHz
    sys_clk = HAL_RCC_GetSysClockFreq();
    while(1);
}



3.2.3 使用外部时钟源HSE作为PLL的时钟源,然后将PLL作为系统时钟源

/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow : 
  *            System Clock source            = PLL (HSE)
  *            SYSCLK(Hz)                     = 72000000
  *            HCLK(Hz)                       = 72000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 2
  *            APB2 Prescaler                 = 1
  *            PLLMUL                         = 9
  *            Flash Latency(WS)              = 2
  * @param  None
  * @retval None
  */
void SystemClock_Config(void)
{    
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType    = RCC_OSCILLATORTYPE_HSE; // 设置时钟振荡器类型为HSE
    RCC_OscInitStruct.HSEState          = RCC_HSE_ON; // 打开HSE
    RCC_OscInitStruct.HSEPredivValue    = RCC_HSE_PREDIV_DIV1; // HSE预分频值为1
    RCC_OscInitStruct.HSIState          = RCC_HSI_OFF; // 关闭HSI
    RCC_OscInitStruct.PLL.PLLState      = RCC_PLL_ON; // 打开PLL
    RCC_OscInitStruct.PLL.PLLSource     = RCC_PLLSOURCE_HSE; // PLL的输入时钟源为HSE
    RCC_OscInitStruct.PLL.PLLMUL        = RCC_PLL_MUL9; // PLL倍频系数为9
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        while(1);
    }

    /** Initializes the CPU, AHB and APB busses clocks 
    */
    RCC_ClkInitStruct.ClockType         = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK
                                         | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource      = RCC_SYSCLKSOURCE_PLLCLK; // SYSCLK的来源为PLL的输出
    RCC_ClkInitStruct.AHBCLKDivider     = RCC_SYSCLK_DIV1; // AHB总线时钟分频系数为1
    RCC_ClkInitStruct.APB1CLKDivider    = RCC_HCLK_DIV2; // APB1总线时钟分频系数为2
    RCC_ClkInitStruct.APB2CLKDivider    = RCC_HCLK_DIV1; // APB2总线时钟分频系数为1

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    {
        while(1);
    }
}



3.2.4 获取系统时钟的函数

与前面类似,主函数里调用HAL库提供的“HAL_RCC_GetSysClockFreq()”函数获取系统时钟验证。

uint32_t sys_clk = 0;

int main(void)
{    
    // 初始化HAL库函数必须要调用此函数
    HAL_Init();

    /*
     * 系统时钟即AHB/APB时钟配置
     * 当使用外部高速时钟HSE(8MHz)配置系统时钟时,使用PLL前可以选择不分频或者二分频,我们要配置到最大72MHz的系统频率此处当然是不分频
     * 然后经过锁相环放大,最大放大倍数为16,我们要想得到72MHz,此处选择9倍放大系数,即8*9=72MHz即可达到目标
    */
    SystemClock_Config();
    
    // 调用库函数来检验自己的配置是否成功配置为系统频率64MHz
    sys_clk = HAL_RCC_GetSysClockFreq();
    while(1);
}



四、代码工程

工程链接:

基础重点-时钟系统



版权声明:本文为qq_37391577原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。