查看: 1235|回复: 0
收起左侧

第一百零三章:CH32V103应用教程——RCC—HSE/HSI时钟配置

[复制链接]

  离线 

  • TA的每日心情
    慵懒
    2021-7-23 17:16
  • 签到天数: 17 天

    [LV.4]

    发表于 2021-7-31 19:17:40 | 显示全部楼层 |阅读模式

    有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 草帽王子 于 2021-7-31 19:17 编辑

    本章教程主要讲述如何使用HSE或HSI配置系统时钟,可配合CH32V103应用手册第三章学习本教程。

    1、HSI、HSE简介

    HSI是系统内部 8MHz 的 RC 振荡器产生的高速时钟信号。HSI RC 振荡器能够在不需要任何外部器件的条件下提供系统时钟。它的启动时间很短但时钟频率精度较差。HSI 通过设置 RCC_CTLR 寄存器中的 HSION 位被启动和关闭,HSIRDY 位指示 HSI RC 振荡器是否稳定。系统默认 HSION 和 HSIRDY 置 1(建议不要关闭)。如果设置了 RCC_INTR 寄存器的 HSIRDYIE 位,将产生相应中断。

      出厂校准:制造工艺的差异会导致每个芯片的 RC 振荡频率不同,所以在芯片出厂前,会为每颗芯片进行 HSI 校准。系统复位后,工厂校准值被装载到 RCC_CTLR 寄存器的 HSICAL[7:0]中。

      用户调整:基于不同的电压或环境温度,应用程序可以通过 RCC_CTLR 寄存器里的 HSITRIM[4:0]位来调整 HSI 频率。

    注:如果 HSE 晶体振荡器失效,HSI 时钟会被作为备用时钟源(时钟安全系统)。

    HSE 是外部的高速时钟信号,包括外部晶体/陶瓷谐振器产生或者外部高速时钟送入。

    外部晶体/陶瓷谐振器(HSE 晶体):外接 4-16MHz 外部振荡器为系统提供更为精确的时钟源。进一步信息可参考数据手册的电气特性部分。HSE 晶体可以通过设置 RCC_CTLR 寄存器中的 HSEON位被启动和关闭,HSERDY 位指示 HSE 晶体振荡是否稳定,硬件在 HSERDY 位置 1 后才将时钟送入系统。如果设置了 RCC_INTR 寄存器的 HSERDYIE 位,将产生相应中断。
    CH32V CH573单片机芯片-第一百零三章:CH32V103应用教程——RCC—HSE/HSI时钟配置risc-v单片机中文社区(1)

    注:负载电容需要尽可能地靠近振荡器引脚,并根据晶体厂家参数选择容值。

    外部高速时钟源(HSE 旁路):此模式从外部直接送入时钟源到 OSC_IN 引脚,OSC_OUT 引脚悬空。最高支持 25MHz 频率。应用程序需在 HSEON 位为 0 情况下,置位 HSEBYP 位,打开 HSE 旁路功能,然后再置
    位 HSEON 位。
    CH32V CH573单片机芯片-第一百零三章:CH32V103应用教程——RCC—HSE/HSI时钟配置risc-v单片机中文社区(2)
    MCO是microcontroller clock output的缩写,是微控制器时钟输出引脚,在 CH32V103系列中由PA8复用所得,主要作用是可以对外提供时钟,相当于一个有源晶振。MCO的时钟来源可以是:PLLCLK/2、HSI、HSE、SYSCLK,具体选哪个由时钟配置寄存器0的位 26-24:MCO[2:0]决定。除了对外提供时钟这个作用之外,我们还可以通过示波器监控MCO引脚的时钟输出来验证我们的系统时钟配置是否正确。

    通常在程序中,我们都是使用HSE经过PLL倍频之后作为系统时钟。HSE作为外部晶振大小通常为8M,PLL的倍频因子设置为9,因此系统时钟大小为72M。CH32V103系统时钟最高是80M。在CH32V103标准库函数中,可以在启动文件startup_ch32v10x.S中看到,在跳转到main函数之前,会先调用SystemInit()函数把系统始终初始化成72MHz,如下图。关于SystemInit()函数具体配置,可在system_ch32v10x.c文件中查看。

    CH32V CH573单片机芯片-第一百零三章:CH32V103应用教程——RCC—HSE/HSI时钟配置risc-v单片机中文社区(3)

    当HSE出现故障不能使用的时候,如果开启了时钟安全系统,将切换到HSI时钟下。时钟安全系统是控制器的一种运行保护机制,它可以在 HSE 时钟发送故障的情况下,切换到 HSI时钟下,并产生中断通知,允许应用程序软件完成营救操作。

    通过设置 RCC_CTLR 寄存器的 CSSON 位置 1,激活时钟安全系统。此时,时钟监测器将在 HSE 振荡器启动(HSERDY=1)延迟后被使能,并在 HSE 时钟关闭后关闭。一旦系统运行过程中 HSE 时钟发生故障,HSE 振荡器将被关闭,时钟失效事件将被送到高级定时器(TIM1 和 TIM8)的刹车输入端,并产生时钟安全中断,CSSF 位置 1,并且应用程序进入 NMI 不可屏蔽中断,通过置位 CSSC 位,可以清除CSSF 位标志,可撤销 NMI 中断挂起位。

    如果当前 HSE 作为系统时钟,或者当前 HSE 作为 PLL 输入时钟,PLL 作为系统时钟,时钟安全系统将在 HSE 故障时自动将系统时钟切换到 HSI 振荡器,并关闭 HSE 振荡器和 PLL。

    在此处需要注意的是:CH32与STM32不同,STM32F103内部8M时钟输入PLL需要2分频,CH32内部8M经过增强配置,可不分频输入PLL。芯片可以在使用HSI时工作于72MHz主频。为了兼容STM32标准库,同时又为了保证CH32内部8M可不分频输入PLL,CH32提供了一个配置扩展寄存器,通过配置扩展寄存器(EXTEND_CTR)位4,可设置HSI时钟是否分频,具体如下图所示:

    CH32V CH573单片机芯片-第一百零三章:CH32V103应用教程——RCC—HSE/HSI时钟配置risc-v单片机中文社区(4)

    关于扩展配置寄存器,其功能描述具体可参考CH32V103应用手册。

    关于CH32V103 RCC具体信息,可参考CH32V103应用手册。

    2、硬件设计

    RCC为单片机内部资源,无需进行硬件连接。但本章教程需要通过LED闪烁快慢现象观察系统主频高低,因此需要将PA0和PA1与开发板LED灯引脚连接起来。

    3、软件设计

    本章教程主要讲述如何使用HSE和HSI配置系统时钟,具体程序如下:
    clk.h文件
    1. #ifndef __CLK_H

    2. #define __CLK_H



    3. #include  "ch32v10x.h"



    4. void HSE_SetSysClock(uint32_t pllmul);

    5. void HSI_SetSysClock(uint32_t pllmul);



    6. #endif
    复制代码

    clk.h文件主要进行函数声明;
    clk.c文件


    1. #include "clk.h"



    2. /*

    3. * 使用HSE时,设置系统时钟的步骤

    4. * 1、开启HSE ,并等待 HSE 稳定

    5. * 2、设置 AHB、APB2、APB1的预分频因子

    6. * 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置

    7. * 4、开启PLL,并等待PLL稳定

    8. * 5、把PLLCK切换为系统时钟SYSCLK

    9. * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟

    10. */



    11. /* 设置 系统时钟:SYSCLK, AHB总线时钟:HCLK, APB2总线时钟:PCLK2, APB1总线时钟:PCLK1

    12. * PCLK2 = HCLK = SYSCLK

    13. * PCLK1 = HCLK

    14. * 参数说明:pllmul是PLL的倍频因子,在调用的时候可以是:RCC_PLLMul_x , x:[2,3,...16]

    15. * 举例:User_SetSysClock(RCC_PLLMul_9);  则设置系统时钟为:8MHZ * 9 = 72MHZ

    16. * HSE作为时钟来源,经过PLL倍频作为系统时钟,这是通常的做法

    17. */



    18. void HSE_SetSysClock(uint32_t pllmul)

    19. {

    20.     __IO uint32_t HSEStartUpStatus = 0;



    21.   // 把RCC外设初始化成复位状态

    22.   RCC_DeInit();



    23.   //使能HSE,开启外部晶振,野火开发板用的是8M

    24.   RCC_HSEConfig(RCC_HSE_ON);



    25.   // 等待 HSE 启动稳定

    26.   HSEStartUpStatus = RCC_WaitForHSEStartUp();



    27.     // 只有 HSE 稳定之后则继续往下执行

    28.   if (HSEStartUpStatus == SUCCESS)

    29.   {

    30. //----------------------------------------------------------------------//

    31.     // 这两句是操作FLASH闪存用到的,如果不操作FLASH的话,这两个注释掉也没影响

    32.     // 使能FLASH 预存取缓冲区

    33.     FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);



    34.     // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2

    35.     // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,

    36.     // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了

    37.     // 0:0 < SYSCLK <= 24M

    38.     // 1:24< SYSCLK <= 48M

    39.     // 2:48< SYSCLK <= 72M

    40.     FLASH_SetLatency(FLASH_Latency_2);

    41. //----------------------------------------------------------------------//



    42.     // AHB预分频因子设置为1分频,HCLK = SYSCLK

    43.     RCC_HCLKConfig(RCC_SYSCLK_Div1);



    44.     // APB2预分频因子设置为1分频,PCLK2 = HCLK

    45.     RCC_PCLK2Config(RCC_HCLK_Div1);



    46.     // APB1预分频因子设置为1分频,PCLK1 = HCLK/2

    47.     RCC_PCLK1Config(RCC_HCLK_Div2);



    48. //-----------------设置各种频率主要就是在这里设置-------------------//

    49.     // 设置PLL时钟来源为HSE,设置PLL倍频因子

    50.     // PLLCLK = 8MHz * pllmul

    51.     RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);

    52. //------------------------------------------------------------------//



    53.     // 开启PLL

    54.     RCC_PLLCmd(ENABLE);



    55.     // 等待 PLL稳定

    56.     while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)

    57.     {

    58.     }



    59.     // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK

    60.     RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);



    61.     // 读取时钟切换状态位,确保PLLCLK被选为系统时钟

    62.     while (RCC_GetSYSCLKSource() != 0x08)

    63.     {

    64.     }

    65.   }

    66.   else

    67.   {

    68.     // 如果HSE开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理

    69.     // 当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,HSI是内部的高速时钟,8MHZ

    70.     while (1)

    71.     {

    72.     }

    73.   }

    74. }





    75. /*

    76. * 使用HSI时,设置系统时钟的步骤

    77. * 1、开启HSI ,并等待 HSI 稳定

    78. * 2、设置 AHB、APB2、APB1的预分频因子

    79. * 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置

    80. * 4、开启PLL,并等待PLL稳定

    81. * 5、把PLLCK切换为系统时钟SYSCLK

    82. * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟

    83. */



    84. /* 设置 系统时钟:SYSCLK, AHB总线时钟:HCLK, APB2总线时钟:HCLK2, APB1总线时钟:PCLK1

    85. * PCLK2 = HCLK = SYSCLK

    86. * PCLK1 = HCLK

    87. * 参数说明:pllmul是PLL的倍频因子,在调用的时候可以是:RCC_PLLMul_x , x:[2,3,...16]

    88. *

    89. * HSI作为时钟来源,经过PLL倍频作为系统时钟,这是在HSE故障的时候才使用的方法

    90. * HSI会因为温度等原因会有漂移,不稳定,一般不会用HSI作为时钟来源,除非是迫不得已的情况

    91. * 如果HSI要作为PLL时钟的来源的话,可选择二分频或者不分频直接输入PLL。芯片可以在使用HSI时工作在72MHz主频

    92. * HSI作为PLL输入时钟,演示系统时钟为 24MHz、48MHz、72MHz。

    93. * HSI/2作为PLL输入时钟,演示系统时钟为 24MHz、48MHz

    94. */

    95. void HSI_SetSysClock(uint32_t pllmul)

    96. {



    97. #if (PLL_Source == HSI)

    98.   EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;  //HSI时钟作为PLL输入时钟

    99. #endif



    100.   __IO uint32_t HSIStartUpStatus = 0;



    101.   // 把RCC外设初始化成复位状态

    102.   RCC_DeInit();



    103.   //使能HSI

    104.   RCC_HSICmd(ENABLE);



    105.   // 等待 HSI 就绪

    106.   HSIStartUpStatus = RCC->CTLR & RCC_HSIRDY;



    107.   // 只有 HSI就绪之后则继续往下执行

    108.   if (HSIStartUpStatus == RCC_HSIRDY)

    109.   {

    110. //----------------------------------------------------------------------//

    111.     // 这两句是操作FLASH闪存用到的,如果不操作FLASH的话,这两个注释掉也没影响

    112.     // 使能FLASH 预存取缓冲区

    113.     FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);



    114.     // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2

    115.     // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,

    116.     // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了

    117.     // 0:0 < SYSCLK <= 24M

    118.     // 1:24< SYSCLK <= 48M

    119.     // 2:48< SYSCLK <= 72M

    120.     FLASH_SetLatency(FLASH_Latency_2);

    121. //----------------------------------------------------------------------//



    122.     // AHB预分频因子设置为1分频,HCLK = SYSCLK

    123.     RCC_HCLKConfig(RCC_SYSCLK_Div1);



    124.     // APB2预分频因子设置为1分频,PCLK2 = HCLK

    125.     RCC_PCLK2Config(RCC_HCLK_Div1);



    126.     // APB1预分频因子设置为1分频,PCLK1 = HCLK/2

    127.     RCC_PCLK1Config(RCC_HCLK_Div2);



    128. //-----------------设置各种频率主要就是在这里设置-------------------//

    129.     // 若设置PLL时钟来源为HSI,设置PLL倍频因子

    130.     // PLLCLK = 8MHz * pllmul

    131.     // 若设置PLL时钟来源为HSI/2,设置PLL倍频因子

    132.     // PLLCLK = 4MHz * pllmul

    133.     RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);

    134. //------------------------------------------------------------------//



    135.     // 开启PLL

    136.     RCC_PLLCmd(ENABLE);



    137.     // 等待 PLL稳定

    138.     while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)

    139.     {

    140.     }



    141.     // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK

    142.     RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);



    143.     // 读取时钟切换状态位,确保PLLCLK被选为系统时钟

    144.     while (RCC_GetSYSCLKSource() != 0x08)

    145.     {

    146.     }

    147.   }

    148.   else

    149.   {

    150.     // 如果HSI开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理

    151.     // 当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,HSI是内部的高速时钟,8MHZ

    152.     while (1)

    153.     {

    154.     }

    155.   }

    156. }


    复制代码

    clk.c文件主要介绍使用HSE和HSI配置系统时钟,具体说明见程序注释。此处需要注意的是,当我们单独使用HSE和HSI配置系统时钟时,需要将system_ch32v10x.c文件的时钟全部注释掉,具体如下图:
    CH32V CH573单片机芯片-第一百零三章:CH32V103应用教程——RCC—HSE/HSI时钟配置risc-v单片机中文社区(5)
    Main.c文件
    1. /********************************** (C) COPYRIGHT *******************************

    2. * File Name          : main.c

    3. * Author             : WCH

    4. * Version            : V1.0.0

    5. * Date               : 2020/04/30

    6. * Description        : Main program body.

    7. *******************************************************************************/



    8. // 程序来到main函数之前,启动文件:statup_ch32v10x.S已经调用SystemInit()函数把系统时钟初始化成72MHZ

    9. // SystemInit()在system_ch32v10x.c中定义

    10. // 如果用户想修改系统时钟,可自行编写程序修改,本章教程主要介绍用户如何编写程序修改系统时钟以及通过PA8引脚输出相关时钟



    11. #include "debug.h"

    12. #include "clk.h"



    13. #define HSI      0   //HSI

    14. #define HSI_1_2  1   //HSI/2



    15. #define PLL_Source   HSI

    16. //#define PLL_Source   HSI_1_2



    17. //  软件延时函数,使用不同的系统时钟,延时不一样

    18. void Delay(__IO uint32_t nCount)

    19. {

    20.     for(; nCount != 0; nCount--);

    21. }



    22. void MCO_GPIO_Init(void)

    23. {

    24.     GPIO_InitTypeDef  GPIO_InitStructure;

    25.     // 开启GPIOA的时钟

    26.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);



    27.     // 选择GPIO8引脚

    28.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;



    29.     //设置为复用功能推挽输出

    30.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;



    31.     //设置IO的翻转速率为50M

    32.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;



    33.     // 初始化GPIOA8

    34.     GPIO_Init(GPIOA, &GPIO_InitStructure);

    35. }



    36. void LED_Init(void)

    37. {



    38.     GPIO_InitTypeDef  GPIO_InitStructure;                     //定义一个GPIO_InitTypeDef类型的结构体



    39.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);     //使能与LED相关的GPIO端口时钟



    40.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;      //配置GPIO引脚

    41.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          //设置GPIO模式为推挽输出

    42.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         //设置GPIO口输出速度

    43.     GPIO_Init(GPIOA, &GPIO_InitStructure);                    //调用库函数,初始化GPIOA



    44.     GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);                //设置引脚输出高电平



    45. }



    46. /*******************************************************************************

    47. * Function Name  : main

    48. * Description    : Main program.

    49. * Input          : None

    50. * Return         : None

    51. *******************************************************************************/

    52. int main(void)

    53. {





    54.     USART_Printf_Init(115200);

    55.         MCO_GPIO_Init();

    56.         LED_Init();



    57.         printf("This is HSE&HSI example\r\n");



    58.     // 重新设置系统时钟,根据需要修改,CH32V103最高可设置为80MH,但通常使用72M作为默认设置

    59.     // SYSCLK = 8M * RCC_PLLMul_x, x:[2,3,...16]

    60.     HSE_SetSysClock(RCC_PLLMul_9);

    61.     //HSI_SetSysClock(RCC_PLLMul_9);



    62.     // 设置MCO引脚输出时钟,用示波器即可在PA8测量到输出的时钟信号

    63.     // MCO引脚输出可以是HSE,HSI,PLLCLK/2,SYSCLK

    64.     //RCC_MCOConfig(RCC_MCO_HSE);             // 8M

    65.     //RCC_MCOConfig(RCC_MCO_HSI);             // 8M

    66.     //RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);     // 36M

    67.     RCC_MCOConfig(RCC_MCO_SYSCLK);            // 72M



    68.     while (1)

    69.     {

    70.         GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);        // 亮

    71.         Delay(0x0FFFFF);

    72.         GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);      // 灭

    73.         Delay(0x0FFFFF);

    74.     }

    75. }
    复制代码
    Main.c文件主要进行相关函数初始化以及通过MCO输出时钟,此外可通过LED闪烁情况查看系统时钟大小。

    4、下载验证

    将编译好的程序下载到开发板并复位,可通过LED灯闪烁查看系统时钟大小,系统时钟越大,闪烁越快。

    利用示波器检测MCO引脚PA8时钟输出频率,结果如下。
    CH32V CH573单片机芯片-第一百零三章:CH32V103应用教程——RCC—HSE/HSI时钟配置risc-v单片机中文社区(6)

    CH32V CH573单片机芯片-第一百零三章:CH32V103应用教程——RCC—HSE/HSI时钟配置risc-v单片机中文社区(7) 102、RCC-HSE、HSI时钟配置.rar (458.86 KB, 下载次数: 21)






    上一篇:第一百零二章:CH32V103应用教程——USART-DMA-TC中断
    下一篇:第一百零四章:CH32V103应用教程——通过EXTI控制LED灯
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-4-24 10:24 , Processed in 0.653635 second(s), 48 queries .

    快速回复 返回顶部 返回列表