• 回复
  • 收藏
  • 点赞
  • 分享
  • 发新帖

标准库STM32的时钟配置,且实现Systick_clock 以及轮询任务调度问题

大家在学习STM32是,肯定被复杂的时钟搞得晕头转向。只不过在学习了很多内容之后就会忽略这个问题,直到自己需要创建工程,从12M的外部晶振换成8M外部晶振时,总会对程序的异常运行搞得炸开了头,例如串口通信的处理。大家在反复确认过程序的基础配置没有出错之后,有的人只能赞叹科技的玄学,然后把别人的工程拷过来,自己添进去自己的内容。

今天呢,我们就一劳永逸的解决这个问题,从系统初始来解决这个问题,并且介绍一个Systick定时器的实用方法。

0.STM32启动文件  eg:stratup_stmf10x_md.s

    STM32的keil工程里,经常会出现像上面eg类似的一个.s文件,这就是一个启动文件,启动文件里有好多东西,其中我们这次感兴趣的内容是这一点

; Reset handlerReset_Handler    PROC                 EXPORT  Reset_Handler             [WEAK]                 IMPORT  __main                 IMPORT  SystemInit                 LDR     R0, = SystemInit                 BLX     R0                 LDR     R0, =__main                 BX      R0                 ENDP

里面有一个SystemInit()和一个__main();

这个就是要接下来说的内容,主要意思是,先执行SysemInit函数,再执行main函数。可能大家对这个有印象,如果有兴趣的话,可以了解一下Thumb汇编指令集,这个我也是只了解一点点,具体我也不太清楚,大家可以有兴趣一起学习。

现在看一看SystemInit函数,

void SystemInit (void){RCC->CR |= (uint32_t)0x00000001;RCC->CFGR &= (uint32_t)0xF8FF0000;RCC->CR &= (uint32_t)0xFEF6FFFF;RCC->CR &= (uint32_t)0xFFFBFFFF;RCC->CFGR &= (uint32_t)0xFF80FFFF;RCC->CIR = 0x009F0000;SetSysClock();}

大家按照这个代码继续读下去,会发现依次进行

SystemTnit();

SrtSysClock();

SetSysClockTo72();

其中进去SetSysClockTo72()函数的原因是,这里定义了SYSCLK_FREQ_72MHz,用来记录系统时钟的主频,72000000

其中SetSysClockTo72()的决定性代码是

这段代码实在是太长了,为了便于观察,我留下需要的部分,上面有关的寄存器是RCC_CR和RCC_CFGR寄存器下面重点解释这两个寄存器。

*********************以上截屏来自于system_stm32f10x.c*****************

1.STM32系列的RCC寄存器

*************截屏来自于STM32中文参考手册****************

SystemInit()函数里的

 #ifndef STM32F10X_CL  RCC->CFGR &= (uint32_t)0xF8FF0000;可以看到清空了CFGR里的数值关键数值,ADC APB1 APB2 AHB这些时钟总线分频系数部分全部选择不分频,HSI作为系统时钟

    RCC->CR &= (uint32_t)0xFEF6FFFF;可以看到0-15全部置为1,第16位为0,第16位在图片中查表得知,是HSE外部高速时钟的使能位

这样可以理解,SystemInit()函数的工作。

    实际上,我们经常选择HSE外部时钟作为时钟来源,我们更信任晶振提供周期频率,但是就算是外部时钟,主流的8M.12M时钟也显得太慢了,所以我们会配置PLL倍频,而这就引出了下一个函数SetSysClockTo72(),为什么会选择72M,为什么不能更高或者更低?

    我查阅的资料告诉我,频率更高对于STM32F103系列的板子会不稳定,太低又不满足需求。(所以,例如STM32F4,STM32F7系列有更高的主频,甚至有的支持超频,当然我没有试过)

问:72M,我们选择的外部晶振晶振HSE不过是8M,12M怎么达到72M呢?

答:用PLL倍频输出,8M晶振9倍,12M晶振6倍。

问:怎么实现呢?

答:

 1054    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |1055                                        RCC_CFGR_PLLMULL));

1056    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);

就是这一段,RCC_CFGR_PLLMULL9,9倍频,8M晶振被频出72M,作为系统时钟,提供SYSCLK

        RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;              RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;            RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

这是提供AHB总线时钟,APB1总线时钟,APB2总线时钟

正点原子的:  8M晶振

Onenet麒麟板的    12M晶振

********时钟到此便配置好了,如果这里配置好,就不会出现类似于串口传输乱码的问题了**********

当然喜欢直接配置寄存器的同学,在添加启动文件(.s)时会自己注释掉SystemInit()函数,用直接操作寄存器的方法实现功能配置,这里也就不多说了,反正具体的步骤都是相同的。

2.Systick 滴答定时器

**********初始化滴答定时器**********

void SysTick_Configuration(void){        RCC_ClocksTypeDef  rcc_clocks;        uint32_t         cnts;        RCC_GetClocksFreq(&rcc_clocks);        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);        cnts = (uint32_t)rcc_clocks.HCLK_Frequency / TICK_PER_SECOND;    cnts = cnts/8;        SysTick->LOAD  = cnts - 1;                                                             NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);           SysTick->VAL   = 0;                                                                     SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |                     SysTick_CTRL_TICKINT_Msk   |                     SysTick_CTRL_ENABLE_Msk;                                     }Systick作为Cortex-M3内核的内容,定义在core_cm3.h中

*******************截图来自于*******************

这段代码是,先获取系统RCC时钟的信息,然后选取系统频率的8分频,9M作为Systick的时钟,定时为1ms中断一次。

Systick定时器VAL也就是当前值为0,就ROAD里重装值,同时产生中断。

计数值=cnts=72000000/1000/8=9000;   

时间t = cnts/时钟频率 = 9000/9000000=0.001s = 1ms

就是这样计算的,大家也可以梳理一下思路。

3.轮询式的程序调度实现

    当然是用Systick定时器产生的1ms中断来生产任务调度函数了。不同于FreeRTOS RT-Thread这样的高级的抢占式任务操作系统,我们只是简单的使用它作为轮询式的简易系统。

eg:

//程序运行时间统计typedef struct{    u8 count_1ms;    u8 count_2ms;    u8 count_5ms;    u8 count_50ms;    u8 count_100ms;    u8 count_500ms;                    //u8类型最大计数256,所以大家使用时候,不要出现超出计数范围的情况    u8 count_1s;}timer_user;extern timer_user TIMER;void SysTick_Handler(void){    Text_timer();}

void Text_time(){    TIMER.count_1ms++;    TIMER.count_2ms++;    TIMER.count_5ms++;    TIMER.count_100ms++;        LED_Status_Display();        if(TIMER.count_2ms == 2)    {        TIMER.count_2ms = 0;            }        if(TIMER.count_5ms == 5)    {        TIMER.count_5ms = 0;            }        if(TIMER.count_50ms == 50)    {        TIMER.count_50ms = 0;            }    if(TIMER.count_100ms == 100)    {        TIMER.count_100ms =0;        TIMER.count_500ms++;        LED_Status_Show();            }        if(TIMER.count_500ms == 5)    {        TIMER.count_500ms = 0;        TIMER.count_1s++;        DT_Send_RESADC_status();            }        if(TIMER.count_1s == 2)    {        TIMER.count_1s = 0;        wait_for_translate = 1;         //等待系统稳定,开启传输            }}如果有很多任务的时候,这样写可读性会很好,而且在控制任务的执行频率上可以很容易的调整。

全部回复(2)
正序查看
倒序查看
chebd
LV.6
2
2018-06-12 08:50
沙发
0
回复
2018-06-12 15:41
0
回复