引言:微控制器允许输出时钟信号到外部MCO管脚。配置选择外部HSE时钟输入12MHz,作为PLL时钟源,经过6倍倍频到72MHz后作为系统时钟(通常的配置是HSE=8MHz,PLL 的倍频因子为9,配置流程同理),通过MCO输出系统时钟SYSCLK,芯片:STM32F103VCT6。
1、时钟树中工作流程
如上图所示,时钟树中工作流程为1,2,3,4,5,6。
1:HSE OSC,外部HSE输入12MHz时钟信号;
2:PLLXTPRE,HSE分频器作为PLL输入,此处选择不分频;(可以选择不分频,或者2分频)
3:PLLSRC,HSE时钟作为PLL输入时钟;
4:PLLMUL,PLL倍频系数,此处设置为6,得到PLLCLK;
5: SW,系统时钟SYSCLK选择PPLLCLK;(如图,这里系统时钟有三种选择,HSI,HSE,PPLLCLK)
6:MCO,此处选择输出SYSCLK信号。(MCO可以输出四个时钟信号SYSCLK,HSI,HSE,除2的PLL时钟,需要配置相应的输出管脚及功能)
2、程序的实现(基于寄存器)
由上面步骤可知,需要用到以下寄存器红色框中的位。
(1)所用到的寄存器
时钟控制寄存器(RCC_CR)
时钟配置寄存器(RCC_CFGR)
APB2外设时钟使能寄存器(RCC_APB2ENR)
端口配置高寄存器(GPIOx_CRH)
(2)代码实现
#include "sys.h"
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
void Stm32_Clock_Init()
{
uint8_t temp=0; /* 定义temp */
RCC_DeInit (); /* 复位RCC */
RCC->CR|=1<<16; /* HSE使能 */
while(!(RCC->CR>>17)); /* 判断HSE是否Ready */
RCC->CFGR|=1<<16; /* 配置HSE时钟作为PLL时钟输入 */
RCC->CFGR|=0x00100000; /* PLL 6倍输出 */
RCC->CR|=1<<24; /* PLL使能 */
while(!(RCC->CR>>25)); /* 判断PLL时钟是否Ready */
RCC->CFGR|=0x02; /* 选择PLL为SYSCLK */
while(temp!=0x02) /* 判断SYSCLK是否设置成功 */
{
temp=RCC->CFGR>>2;
temp&=0x03;
}
}
int main(void)
{
Stm32_Clock_Init();
RCC->APB2ENR|=1<<2; /* IOPA使能 */
GPIOA->CRH&=0xFFFFFFF0; /* PA8输出,CHR后四位清零 */
GPIOA->CRH|=0x0000000B; /* 推挽输出,最高50MHZ */
RCC->CFGR|=4<<24; /* SYSCLK输出 */
//RCC->CFGR|=5<<24; /* HSI输出 */
//RCC->CFGR|=6<<24; /* HSE输出 */
//RCC->CFGR|=7<<24; /* PLL 2分频后输出 */
}
(3)代码解释
RCC->CR|=1<<16;
RCC_CR中第16位置1,HSE使能
while(!(RCC->CR>>17));
RCC_CR中第17位置1,判断HSE是否Ready
RCC->CFGR|=1<<16;
RCC_CFGR中第16位置1,配置HSE时钟作为PLL时钟输入
RCC->CFGR|=0x00100000;
RCC_CFGR中第18-21位为0100,PLL 6倍频输出
RCC->CR|=1<<24;
RCC_CR中第24位置1,打开PLL
while(!(RCC->CR>>25));
RCC_CR中第25位置1,判断PLL时钟是否Ready
RCC->CFGR|=0x02;
RCC_CFGR中第0-1位置为10, 选择PLL为系统时钟SYSCLK
while(temp!=0x02)
{
temp=RCC->CFGR>>2;
temp&=0x03;
}
判断SYSCLK是否设置成功,若位3:2为10,则PLL作为系统时钟就绪
Stm32_Clock_Init();
系统时钟初始化
RCC->APB2ENR|=1<<2;
RCC_APB2ENR中第二位置1,IOPA使能,此时系统时钟为72MHz,选择APB2外设时钟使能寄存器,(APB1最高频率为36MHz),且由硬件原理图知MCO输出引脚为PA8,因此需要配置IOPA8;
GPIOA->CRH&=0xFFFFFFF0;
由于是PA8引脚输出,端口8属于高位,选择端口配置高寄存器,有前面的寄存器GPIOx_CRH知其 复位值并不为0,这里需要用到该寄存器的后四位(PA8引脚)进行输出管脚配置,因此需要将后四位先清零。
GPIOA->CRH|=0x0000000B;
MCO属于输出模式,且配置为推挽输出,则GPIOA_CRH寄存器的后四位配置为1011,即十六进制的B。
RCC->CFGR|=4<<24; ,
RCC_CFGR中第24-26位置为100,MCO输出SYSCLK时钟信号
同理:HSE、HSI、PPL/2的MCO输出代码
//RCC->CFGR|=7<<24; /* PLL 2分频后输出 */
//RCC->CFGR|=6<<24; /* HSE输出 */
//RCC->CFGR|=5<<24; /* HSI输出 */
3、信号输出
生成代码编译后烧写至芯片内,通过示波器显示如下图:
MCO输出SYSCLK时钟信号 MCO输出HSE时钟信号
4、基于库函数的MCO输出代码
基于库函数的代码直接调用固件库中的函数固件库中对每个所调用的函数都有专门的解释及使用说明,不在赘述。
void RCC_Init(void)
{
ErrorStatus HSEStartUpStatus; /* 枚举型变量 */
RCC_DeInit(); /*重置RCC */
RCC_HSEConfig(RCC_HSE_ON); /* HSE使能 */
HSEStartUpStatus=RCC_WaitForHSEStartUp(); /* 等待HSE起振 */
if(HSEStartUpStatus==SUCCESS) /* 判断HSE起振成功 */
{
RCC_HCLKConfig(RCC_SYSCLK_Div1); /* 设置AHB时钟HCLK */
RCC_PCLK1Config(RCC_HCLK_Div2); /* 设置低俗APB1时钟PLCK1*/
RCC_PCLK2Config(RCC_HCLK_Div1); /* 设置高速APB2时钟PLCK2*/
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_6); /* 6倍频 */
}
else
{
/*do nothing*/
}
RCC_PLLCmd(ENABLE); /* PLL使能 */
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET) /* 检查使能标志位 */
{
}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* PLLCLK作为SYSCLK */
while (RCC_GetSYSCLKSource()!=0x08) /* 判断SYSCLK是否设置成功
0x08意思是PLL作为系统时钟 */
{
}
}
void MCO_GPIO_Config(void) /* GPIO配置 */
{
GPIO_InitTypeDef GPIO_InitStruct; /* 结构体定义 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* GPIOA使能 */
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8; /* PA8 */
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; /* 推挽输出 */
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; /* 50MHz*/
GPIO_Init(GPIOA,&GPIO_InitStruct); /*初始化GPIOA */
}
int main(void)
{
RCC_Init();
MCO_GPIO_Config();
RCC_MCOConfig(RCC_MCO_SYSCLK); /* MCO输出SYSCLK */
}