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