离线
TA的每日心情 | 慵懒 2021-7-23 17:16 |
---|
签到天数: 17 天 [LV.4]
|
有人预言,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 位,将产生相应中断。
注:负载电容需要尽可能地靠近振荡器引脚,并根据晶体厂家参数选择容值。
外部高速时钟源(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时钟输出频率,结果如下。
完
|
上一篇: 第一百零二章:CH32V103应用教程——USART-DMA-TC中断下一篇: 第一百零四章:CH32V103应用教程——通过EXTI控制LED灯
|