打印
[RISC-V MCU 应用开发]

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

[复制链接]
5167|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
CH32V103应用教程——RCC—HSE/HSI时钟配置

本章教程主要讲述如何使用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 位,将产生相应中断。

注:负载电容需要尽可能地靠近振荡器引脚,并根据晶体厂家参数选择容值。
外部高速时钟源(HSE 旁路):此模式从外部直接送入时钟源到 OSC_IN 引脚,OSC_OUT 引脚悬空。最高支持 25MHz 频率。应用程序需在 HSEON 位为 0 情况下,置位 HSEBYP 位,打开 HSE 旁路功能,然后再置
位 HSEON 位。
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文件中查看。
当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时钟是否分频,具体如下图所示:
关于扩展配置寄存器,其功能描述具体可参考CH32V103应用手册。
关于CH32V103 RCC具体信息,可参考CH32V103应用手册。

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

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

#include  "ch32v10x.h"

void HSE_SetSysClock(uint32_t pllmul);
void HSI_SetSysClock(uint32_t pllmul);

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

#include "clk.h"

/*
* 使用HSE时,设置系统时钟的步骤
* 1、开启HSE ,并等待 HSE 稳定
* 2、设置 AHB、APB2、APB1的预分频因子
* 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置
* 4、开启PLL,并等待PLL稳定
* 5、把PLLCK切换为系统时钟SYSCLK
* 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
*/

/* 设置 系统时钟:SYSCLK, AHB总线时钟:HCLK, APB2总线时钟:PCLK2, APB1总线时钟:PCLK1
* PCLK2 = HCLK = SYSCLK
* PCLK1 = HCLK
* 参数说明:pllmul是PLL的倍频因子,在调用的时候可以是:RCC_PLLMul_x , x:[2,3,...16]
* 举例:User_SetSysClock(RCC_PLLMul_9);  则设置系统时钟为:8MHZ * 9 = 72MHZ
* HSE作为时钟来源,经过PLL倍频作为系统时钟,这是通常的做法
*/

void HSE_SetSysClock(uint32_t pllmul)
{
    __IO uint32_t HSEStartUpStatus = 0;

  // 把RCC外设初始化成复位状态
  RCC_DeInit();

  //使能HSE,开启外部晶振,野火开发板用的是8M
  RCC_HSEConfig(RCC_HSE_ON);

  // 等待 HSE 启动稳定
  HSEStartUpStatus = RCC_WaitForHSEStartUp();

    // 只有 HSE 稳定之后则继续往下执行
  if (HSEStartUpStatus == SUCCESS)
  {
//----------------------------------------------------------------------//
    // 这两句是操作FLASH闪存用到的,如果不操作FLASH的话,这两个注释掉也没影响
    // 使能FLASH 预存取缓冲区
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
    // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,
    // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了
    // 0:0 < SYSCLK <= 24M
    // 1:24< SYSCLK <= 48M
    // 2:48< SYSCLK <= 72M
    FLASH_SetLatency(FLASH_Latency_2);
//----------------------------------------------------------------------//

    // AHB预分频因子设置为1分频,HCLK = SYSCLK
    RCC_HCLKConfig(RCC_SYSCLK_Div1);

    // APB2预分频因子设置为1分频,PCLK2 = HCLK
    RCC_PCLK2Config(RCC_HCLK_Div1);

    // APB1预分频因子设置为1分频,PCLK1 = HCLK/2
    RCC_PCLK1Config(RCC_HCLK_Div2);

//-----------------设置各种频率主要就是在这里设置-------------------//
    // 设置PLL时钟来源为HSE,设置PLL倍频因子
    // PLLCLK = 8MHz * pllmul
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);
//------------------------------------------------------------------//

    // 开启PLL
    RCC_PLLCmd(ENABLE);

    // 等待 PLL稳定
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

    // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    // 读取时钟切换状态位,确保PLLCLK被选为系统时钟
    while (RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }
  else
  {
    // 如果HSE开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理
    // 当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,HSI是内部的高速时钟,8MHZ
    while (1)
    {
    }
  }
}


/*
* 使用HSI时,设置系统时钟的步骤
* 1、开启HSI ,并等待 HSI 稳定
* 2、设置 AHB、APB2、APB1的预分频因子
* 3、设置PLL的时钟来源,和PLL的倍频因子,设置各种频率主要就是在这里设置
* 4、开启PLL,并等待PLL稳定
* 5、把PLLCK切换为系统时钟SYSCLK
* 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟
*/

/* 设置 系统时钟:SYSCLK, AHB总线时钟:HCLK, APB2总线时钟:HCLK2, APB1总线时钟:PCLK1
* PCLK2 = HCLK = SYSCLK
* PCLK1 = HCLK
* 参数说明:pllmul是PLL的倍频因子,在调用的时候可以是:RCC_PLLMul_x , x:[2,3,...16]
*
* HSI作为时钟来源,经过PLL倍频作为系统时钟,这是在HSE故障的时候才使用的方法
* HSI会因为温度等原因会有漂移,不稳定,一般不会用HSI作为时钟来源,除非是迫不得已的情况
* 如果HSI要作为PLL时钟的来源的话,可选择二分频或者不分频直接输入PLL。芯片可以在使用HSI时工作在72MHz主频
* HSI作为PLL输入时钟,演示系统时钟为 24MHz、48MHz、72MHz。
* HSI/2作为PLL输入时钟,演示系统时钟为 24MHz、48MHz
*/
void HSI_SetSysClock(uint32_t pllmul)
{

#if (PLL_Source == HSI)
  EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;  //HSI时钟作为PLL输入时钟
#endif

  __IO uint32_t HSIStartUpStatus = 0;

  // 把RCC外设初始化成复位状态
  RCC_DeInit();

  //使能HSI
  RCC_HSICmd(ENABLE);

  // 等待 HSI 就绪
  HSIStartUpStatus = RCC->CTLR & RCC_HSIRDY;

  // 只有 HSI就绪之后则继续往下执行
  if (HSIStartUpStatus == RCC_HSIRDY)
  {
//----------------------------------------------------------------------//
    // 这两句是操作FLASH闪存用到的,如果不操作FLASH的话,这两个注释掉也没影响
    // 使能FLASH 预存取缓冲区
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    // SYSCLK周期与闪存访问时间的比例设置,这里统一设置成2
    // 设置成2的时候,SYSCLK低于48M也可以工作,如果设置成0或者1的时候,
    // 如果配置的SYSCLK超出了范围的话,则会进入硬件错误,程序就死了
    // 0:0 < SYSCLK <= 24M
    // 1:24< SYSCLK <= 48M
    // 2:48< SYSCLK <= 72M
    FLASH_SetLatency(FLASH_Latency_2);
//----------------------------------------------------------------------//

    // AHB预分频因子设置为1分频,HCLK = SYSCLK
    RCC_HCLKConfig(RCC_SYSCLK_Div1);

    // APB2预分频因子设置为1分频,PCLK2 = HCLK
    RCC_PCLK2Config(RCC_HCLK_Div1);

    // APB1预分频因子设置为1分频,PCLK1 = HCLK/2
    RCC_PCLK1Config(RCC_HCLK_Div2);

//-----------------设置各种频率主要就是在这里设置-------------------//
    // 若设置PLL时钟来源为HSI,设置PLL倍频因子
    // PLLCLK = 8MHz * pllmul
    // 若设置PLL时钟来源为HSI/2,设置PLL倍频因子
    // PLLCLK = 4MHz * pllmul
    RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);
//------------------------------------------------------------------//

    // 开启PLL
    RCC_PLLCmd(ENABLE);

    // 等待 PLL稳定
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

    // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    // 读取时钟切换状态位,确保PLLCLK被选为系统时钟
    while (RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }
  else
  {
    // 如果HSI开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理
    // 当HSE开启失败或者故障的时候,单片机会自动把HSI设置为系统时钟,HSI是内部的高速时钟,8MHZ
    while (1)
    {
    }
  }
}

clk.c文件主要介绍使用HSE和HSI配置系统时钟,具体说明见程序注释。此处需要注意的是,当我们单独使用HSE和HSI配置系统时钟时,需要将system_ch32v10x.c文件的时钟全部注释掉,具体如下图:
Main.c文件
/********************************** (C) COPYRIGHT *******************************
* File Name          : main.c
* Author             : WCH
* Version            : V1.0.0
* Date               : 2020/04/30
* Description        : Main program body.
*******************************************************************************/

// 程序来到main函数之前,启动文件:statup_ch32v10x.S已经调用SystemInit()函数把系统时钟初始化成72MHZ
// SystemInit()在system_ch32v10x.c中定义
// 如果用户想修改系统时钟,可自行编写程序修改,本章教程主要介绍用户如何编写程序修改系统时钟以及通过PA8引脚输出相关时钟

#include "debug.h"
#include "clk.h"

#define HSI      0   //HSI
#define HSI_1_2  1   //HSI/2

#define PLL_Source   HSI
//#define PLL_Source   HSI_1_2

//  软件延时函数,使用不同的系统时钟,延时不一样
void Delay(__IO uint32_t nCount)
{
    for(; nCount != 0; nCount--);
}

void MCO_GPIO_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    // 开启GPIOA的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 选择GPIO8引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;

    //设置为复用功能推挽输出
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    //设置IO的翻转速率为50M
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    // 初始化GPIOA8
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void LED_Init(void)
{

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

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

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;      //配置GPIO引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;          //设置GPIO模式为推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         //设置GPIO口输出速度
    GPIO_Init(GPIOA, &GPIO_InitStructure);                    //调用库函数,初始化GPIOA

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

}

/*******************************************************************************
* Function Name  : main
* Description    : Main program.
* Input          : None
* Return         : None
*******************************************************************************/
int main(void)
{


    USART_Printf_Init(115200);
        MCO_GPIO_Init();
        LED_Init();

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

    // 重新设置系统时钟,根据需要修改,CH32V103最高可设置为80MH,但通常使用72M作为默认设置
    // SYSCLK = 8M * RCC_PLLMul_x, x:[2,3,...16]
    HSE_SetSysClock(RCC_PLLMul_9);
    //HSI_SetSysClock(RCC_PLLMul_9);

    // 设置MCO引脚输出时钟,用示波器即可在PA8测量到输出的时钟信号
    // MCO引脚输出可以是HSE,HSI,PLLCLK/2,SYSCLK
    //RCC_MCOConfig(RCC_MCO_HSE);             // 8M
    //RCC_MCOConfig(RCC_MCO_HSI);             // 8M
    //RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);     // 36M
    RCC_MCOConfig(RCC_MCO_SYSCLK);            // 72M

    while (1)
    {
        GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);        // 亮
        Delay(0x0FFFFF);
        GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);      // 灭
        Delay(0x0FFFFF);
    }
}

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

4下载验证
将编译好的程序下载到开发板并复位,可通过LED灯闪烁查看系统时钟大小,系统时钟越大,闪烁越快。
利用示波器检测MCO引脚PA8时钟输出频率,结果如下。

102、RCC-HSE、HSI时钟配置.rar

458.86 KB

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

132

主题

293

帖子

41

粉丝