关于030锁相环PLL时钟

问题起源:

前提条件:用stm32f030k6t6,然后库函数是和启动文件是hk32f030的(因为绝大部分是兼容·)。然后没有外部晶振;

  在使用定时器做捕获的时候,发现数据是对不上的;后面经过不断的仿真,不断的代码测试(更新中断,点灯);发现:定时有问题,最终追查到了时钟身上;

时钟树

 观察时钟树;跟随箭头就是正常的无晶振下的pll锁相环倍频的出来的sysclk;然后到AHB 、APB

APB2 ,定时器再APB2;

为什么是时钟?

最初是用库函数的RCC_GetClocksFreq(& RCC_Clocks1); 这个函数来获取频率数据的;是的这个很好用;得到的数据如下;

弊端:

但是这个是有弊端的,因为这个测量的依据是按照寄存器的配置位来计算的;比如定性的认为外部输入时8m。如果外部时钟是12m,经过PLL时钟就错误了大了;

结果:用了这个函数计算出来的是sysclk频率48M。当初以为没错,其实错大了;

计算:这个计算是通过syscllk选择器的时钟源 ,如选择的PLL,启动文件外部时钟源8m定值,然后根据当前配置的输入pll的分频,8m/分频数。经过倍频的配置得到数值A ,8m/分频数*8 就得到了频率;

措施:

1、把方向放在了设置AHB和APB的身上,设置这两个的不分频;但是定时依然不准;

   RCC_HCLKConfig( RCC_SYSCLK_Div1 );RCC_PCLKConfig( RCC_HCLK_Div1 );

2、再次用寄存器观察当前的sysclk的时钟源选择上,结合手册可以知道选择了锁相环作为时钟源, 并且改变时钟源选择为内部验证定时发生了改变:更接近了准确的频率,所以断定问题出在锁相环上面;

rcc_temp = RCC_GetSYSCLKSource();//观察
RCC_SYSCLKConfig( RCC_SYSCLKSource_HSI); //时钟源选择内部时钟

再次确认是否是锁相环这个环节出错

 验证操作:利用寄存器结合手册设计如下程序;此程序实验是为了观察pll所有的设置操作,计算出倍频频率;

typedef struct{uint8_t  PLL_enable_flag;//开启?uint8_t  HSE_enable_flag; //开启  ?uint8_t  HSI_enable_flag; //开启?uint8_t  PLL_MUL_x_num ; //pll倍频数uint8_t  HSE_to_PLL_perscale_num; //PLL输入的外时钟源分频,如果是内部的8M/2uint8_t  PLL_IN_Clk_HSE_HSI_flag; //外1uint8_t  SYSclk_out_clk_HSI;//选择输出源是HSI?uint8_t  SYSclk_out_clk_HSE;//选择输出源是HSE?uint8_t  SYSclk_out_clk_PLL;//选择输出源是PLL?
}RCC_parameter;RCC_parameter rcc_parameter;void RCC_PLL_test(   )
{uint8_t PLLXTPRE_flag=0,sw=0;uint32_t temp=0;rcc_parameter. PLL_enable_flag =  ( RCC->CR >>25 )  & 0x01  ;rcc_parameter.HSE_enable_flag =  ( RCC->CR >>16 )  & 0x01 ;rcc_parameter.HSI_enable_flag =  ( RCC->CR >>0 )  & 0x01 ;rcc_parameter.PLL_MUL_x_num   = ( ( RCC->CFGR >>18 ) & 0x0f )+ 2;PLLXTPRE_flag   =  ( RCC->CFGR >>17 ) & 0x01; //此数应该与 temp的最低位相同temp = RCC->CFGR2 ; rcc_parameter.HSE_to_PLL_perscale_num = temp+1;rcc_parameter.PLL_IN_Clk_HSE_HSI_flag =  ( RCC->CFGR >>16 ) & 0x01;sw = ( RCC->CFGR >> 0 ) & 0x03 ;switch( sw ){case 0: rcc_parameter.SYSclk_out_clk_HSI = 1; break;case 1: rcc_parameter.SYSclk_out_clk_HSE = 1; break;case 2: rcc_parameter.SYSclk_out_clk_PLL = 1; break;}}

观察到的结果如下:

 分析:PLL_enable_flag 开始顺序分析(结合程序和手册不详解,只分析结果) :

pll使能  hse不使能  hsi使能 锁相环 12倍的倍频,然后3倍的倍频,外部时钟进入pll的时钟分频为1 ,选择让内部时钟作为pll输入 , sysclk时钟源选择外部hsi;配置数值一切正常;

然后选择内部时钟源RCC_SYSCLKConfig( RCC_SYSCLKSource_HSI); 程序就可以正常的8M进行。所以还是PLL配置或者PLL的问题上;

正确配置PLL作为时钟源48M

RCC_ClocksTypeDef  RCC_Clocks1; //频率数据
uint8_t rcc_temp;
uint8_t z=0;
void PLL_IN_HSI_to_48M_SYSCLK()
{  RCC_ClockSecuritySystemCmd( ENABLE );RCC_SYSCLKConfig( RCC_SYSCLKSource_HSI);//  sysclk 先用上内部时钟while( !(RCC_GetSYSCLKSource() == 0 ) ){ }  // 等待 sysclk 用上内部时钟成功RCC_PLLCmd(DISABLE);  //禁止PLL 才能配置PLL相关while(  !( ( ( RCC->CR >>25 ) & 0x01 ) == 0  ) );  //等待PLL禁止成功RCC_PLLConfig( RCC_PLLSource_HSI_Div2 , RCC_PLLMul_12 ); //8m 内clk /2 后倍频12倍 RCC_PLLSource_HSI_Div2RCC_PLLCmd(ENABLE);while(  !( ( ( RCC->CR >>25 ) & 0x01 ) == 1  ) ); //等待PLL开启成功RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK); //选择pllclk作为sysclk的时钟源;while( !(RCC_GetSYSCLKSource() == 0x08 ) ){ }RCC->CFGR &=~ 3 ;RCC->CFGR |=  2  ;while(  !( z == 0x02)  ){  z=( RCC->CFGR >> 2 ) & 0x03; };RCC_HCLKConfig( RCC_SYSCLK_Div1 ); //sysclk不分频,送给AHclkRCC_PCLKConfig( RCC_HCLK_Div1 );  //hclk不分频 送给APBRCC_GetClocksFreq(& RCC_Clocks1); //获取当前频率 rcc_temp = RCC_GetSYSCLKSource();  //检测当前时钟源用的是不是0x08 == PLL 
}

先选择sysclk的时钟源输入为内部时钟(为了保险一点),选择输入PLL的HSI,倍频12;启动PLL, 选择PLL作为sysclk的时钟;对后面的AH、PB的都不分频;

第二天:连选择hsi作为sysclk时钟源也不能正常定时了;所以以上配置没起多大作用;

所以得出结论:也许不是PLL锁相环的问题;

提出第二个方向:我该怎么知道PLL出来的时钟有没有问题,或者说内部时钟没有问题,或者说sysclk时钟是没问题的?所以输出时钟

配置时钟映射输出到GPIOA_8

void wait_time()  //随意的延时功能
{int i;for( i =0 ;i<60000;i++){}
}void MCO_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;// RCC_ClockSecuritySystemCmd(ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_0); //gpioA 的引脚8 的映射复用为 MCO  MCU时钟输出 /* Configure MCO (PA8) */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//引脚复用配置GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &GPIO_InitStructure);wait_time(); /*选择mco  输出时钟 选择 sysclk 进行输出  */RCC_MCOConfig(RCC_MCOSource_SYSCLK, RCC_MCOPrescaler_16);//RCC_MCOSource_SYSCLK   RCC_MCOSource_HSI wait_time();while (1){;}
}

然后通过GPIOA 的引脚 8     在示波器上可以观察到输出的时钟;RCC_MCOConfig(RCC_MCOSource_SYSCLK, RCC_MCOPrescaler_16)

RCC_MCOPrescaler_16  分频16    输出的时钟为 48M/16=3M ;

所以通过上面的函数可以在使用pll下进行观察输出:

结论:sysclk输出没有问题的 在sysclk 不分频  ,AHclk 不分频,APBclk 不分频   ;时钟依然是错的;

方向放在了库文件上面:

毕竟是用stm32芯片,然后用的是hk的库;然后先找了HK的好的工程来验证;结果他m的居然能用,TIM1定时居然是对的;

最终方向放在了工程配置上:

最终验证成功在这个选项卡里面:优化选项  优化等级   ;

首先不应该选择优化;下图是正确的配置;

之前是选在了medium上面,所以导致了我莫名其妙的出错;

 附加:不能偷懒的新建工程

IAR上新建工程stm32f030_qq_36658033的博客-CSDN博客

附加:关于手册的寄存器的最终观察结果 (根据时钟树)

1、观察到使用PLL的输入时钟:                   (时钟控制寄存器(RCC_CR))

2、观察时钟源内外的的寄存器状使能位            (时钟控制寄存器(RCC_CR))

3、在使能 PLL 之前,必须先完成 PLL 配置(选择输入时钟,预分频器和乘法因子)。一旦启用
了 PLL,就无法更改这些参数。

PLL 输出频率必须设置在 16-48 MHz 的范围内。

4、库函数的测量频率的方法是按照配置位来计算;

5、时钟配置寄存器(RCC_CFGR) 如下关键

位 21:18 PLLMUL [3:0]: PLL 乘法因子

Bit 17 PLLXTPRE:PLL 输入时钟的 HSE 分频器

位 16 PLLSRC: PLL 输入时钟源

位 1:0 SW [1:0]:系统时钟切换

6、时钟配置寄存器 2(RCC_CFGR2)  外时钟进PLL分频寄存器

MCO输出

微控制器时钟输出(MCO)功能允许将时钟输出到外部 MCO 引脚。相应 GPIO 端口的配置
寄存器必须在备用功能模式下进行编程。可以选择以下时钟信号之一作为 MCO 时钟:
• HSI14
• SYSCLK
• 恒指
• HSE
• PLL 时钟被 2 分频或直接分频(STM32F030x8 器件上不提供直接连接)
• LSE
• LSI
该选择由时钟配置寄存器的 MCO [3:0]位控制
(RCC_CFGR)。

在 STM32F030x4,STM32F030x6,STM32F070x6,STM32F070xB 和 STM32F030xC 上
器件,该寄存器的附加位 PLLNODIV 控制输入到 MCO 的 PLL 时钟的分频器旁路。可通过
可配置的二进制分频器降低 MCO 频率,该二进制分频器由时钟配置寄存器(RCC_CFGR)
的 MCOPRE [2..0]位控制。

(RCC_CFGR)寄存器的  MCO [3:0]  配置输出GPIO的源

(RCC_CFGR)  MCOPRE [2..0]   输出分频数;


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部