打印

STM32时钟简介以及如何修改、测量时钟频率

[复制链接]
470|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 王小琪 于 2023-1-9 15:42 编辑

一、时钟简介先来个时钟总览图片,时钟的介绍一时半会也说不完,就挑重点的简单说说



①HSI 内部高速时钟,RC振荡器,频率为8MHz,精度不高。
②HSE 外部高速时钟,可接石英/陶瓷谐振器,频率范围为4MHz~16MHz,一般是8MHZ。
③LSI 内部低速时钟,RC振荡器,频率为40kHz,精度不高。
④LSE 外部低速时钟,接频率为32.768kHz的石英晶体。

⑤SYSCLK系统时钟三个来源HSI(8M)、PLLCLK(4M-128M)、HSE(4-16M)。一般是通过PLLCK倍频设置为72M,即SYSCLK=PLLCK=72M
二、为什么STM32F103开发板系统时钟SYSCLK为72MHZ?
首先我们随便打开一个开发板的标准库函数历程,此处以正点原子战舰开发板LED为例子
我们可以看到在main.c中并没有配置时钟,那么SYSCLK是在哪配置的呢。
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
int main(void)
{        
        delay_init();            //延时函数初始化         
        LED_Init();                          //初始化与LED连接的硬件接口
        while(1)
        {
                LED0=0;
                LED1=1;
                delay_ms(300);         //延时300ms
                LED0=1;
                LED1=0;
                delay_ms(300);        //延时300ms
        }
}
那是因为时钟是靠SystemInit函数来配置的,而且SystemInit函数是在main函数之前执行的。我们可以打开启动文件startup_stm32f10x_hd.s 如下图,可以很清楚的看出来在main()之前就配置好了时钟

那么为什么时钟是72M,不是36M呢,继续往下看,我们可以看到在system_stm32f10x.c里面看到官方固件库默认定义了SYSCLK_FREQ_72MHz,所以会调用SetSysClockTo72这个函数。

如果要使用其它频率,那就解开其他相应的注释(只保留一个不被注释)。
但是由于这是官方固件库的文件,建议不要随意修改。
至此,我们找到了72MHZ的来源,并没有在main()函数里面写出来,而是在启动之后,main()之前就将时钟配置好了。
三、如何修改系统时钟SYCSLK,并测量
上篇文章写了为什么STM32的系统时钟默认为72MHZ,ST官方固件库的启动文件会在main之前调用SystemInit(),初始化时钟,由于我们不可能去修改官方库文件,所以想要修改系统时钟,只能自己重新写时钟函数,需要引入一个源文件clk.c和一个头文件clk.h

//clk.h
#ifndef __CLKCONFIG_H
#define        __CLKCONFIG_H

#include "stm32f10x.h"

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

#endif
//clk.c
#include "clk.h"
#include "stm32f10x_rcc.h"

void HSE_SetSysClock(uint32_t pllmul)
{
        __IO uint32_t StartUpCounter = 0, HSEStartUpStatus = 0;        
        SystemInit();
  RCC_DeInit();// 把RCC外设初始化成复位状态,这句是必须的  
  RCC_HSEConfig(RCC_HSE_ON);//使能HSE,开启外部晶振8M
  HSEStartUpStatus = RCC_WaitForHSEStartUp();// 等待 HSE 启动稳定
  if (HSEStartUpStatus == SUCCESS)// 只有 HSE 稳定之后则继续往下执行
  {
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    FLASH_SetLatency(FLASH_Latency_2);  
    RCC_HCLKConfig(RCC_SYSCLK_Div1); // AHB预分频因子设置为1分频,HCLK = SYSCLK      
    RCC_PCLK2Config(RCC_HCLK_Div1);// APB2预分频因子设置为1分频,PCLK2 = HCLK     
    RCC_PCLK1Config(RCC_HCLK_Div2);// APB1预分频因子设置为1分频,PCLK1 = HCLK/2                        
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);// PLLCLK = 8MHz * pllmul
    RCC_PLLCmd(ENABLE);// 开启PLL     
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)// 等待 PLL稳定
    {
    }   
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);// 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK   
    while (RCC_GetSYSCLKSource() != 0x08)// 读取时钟切换状态位,确保PLLCLK被选为系统时钟
    {
    }
  }
  else
  {
    while (1)
    {
    }
  }
}
需要修改系统时钟的时候,只需要在main()里面加入相关函数即可,如下:
int main(void)
{               
        HSE_SetSysClock(RCC_PLLMul_9);//SYSCLK为8*9=72M
        while(1)
        {        
        }         
}
修改完了之后,怎么知道我的时钟有没有修改过来呢,有什么方法可以测量呢?
如下图,PA8有一个功能是MCO,在STM32F10系列中主要作用是可以对外提供时钟,相当于一个有源晶振。MCO的时钟来源可以是:PLLCLK/2、HSI、HSE、SYSCLK。除了对外提供时钟这个作用之外,我们还可以通过示波器监控 MCO 引脚的时钟输出来验证我们的系统时钟配置是否正确。



所以我们需要先将PA8设置为时钟输出脚,然后用示波器测量PA8引脚即可观察到时钟的频率。
//配置PA8作为MCO输出IO配置
void MCO_GPIO_Config(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;// 开启GPIOA的时钟        
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);               
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;// 选择GPIO8引脚               
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//设置为复用功能推挽输出               
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置IO的翻转速率为50M        
        GPIO_Init(GPIOA, &GPIO_InitStructure);// 初始化GPIOA8
}
 int main(void)
{               
        HSE_SetSysClock(RCC_PLLMul_9);//SYSCLK为8*9=72M
        MCO_GPIO_Config();//配置IO        
        RCC_MCOConfig(RCC_MCO_SYSCLK);
        //RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);
        while(1)
        {        
        }         
}        

从上图可以看到MCO输出需小于50MHZ,这个是单片机的性能决定的,实测外部晶振为8M时,7倍频后依旧可以测得56MHZ的频率,但为了稳定性,建议MCO输出不要超过50MHZ。
下图为设置频率和实测频率图片,注意第二张图和第三张图,因为MCO输出需小于50MHZ,所以当频率为72MHZ的时候,MCO只能输出五十几兆,而MCO也可以输出PLLCLK/2,即SYSCLK/2=36M<50M,所以也可以证明SYSCLK为72M。


四、如何通过串口调试助手打印系统时钟SYSCLK

首先代码如下
 int main(void)
{               
        RCC_ClocksTypeDef rcc_clocks; //定义结构体变量
        HSE_SetSysClock(RCC_PLLMul_9);//SYSCLK?8*9=72M
        delay_init();                     //延时函数初始化         
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);          //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
        uart_init(115200);         //串口初始化为115200
        RCC_GetClocksFreq(&rcc_clocks);//获取不同时钟频率
           while(1)
        {        
                printf("\r\nSYSCLK = %d MHz\n",rcc_clocks.SYSCLK_Frequency/1000000);
                delay_ms(1000);
        }         
}


首先需要定义一个结构体变量RCC_ClocksTypeDef rcc_clocks;,结构体中的成员可在stm32f2xx_rcc.c中看到。分别是SYSCLK 、HCLK 、PCLK1 、PCLK2 、ADCCLK ,所以上面五个频率均可以打印出来。我们只打印SYSCLK

代码中其他的就很常规了,配置时钟、初始化延时函数、配置中断、串口初始化。在大循环中间隔1s打印各个时钟频率可以看到结果如下。





2250663bbba6331c59.png (347.78 KB )

2250663bbba6331c59.png

使用特权

评论回复

相关帖子

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

本版积分规则

227

主题

578

帖子

6

粉丝