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

systick STM32时钟 轮询式任务调度
数据终端
发布时间: 2018-05-23
阅读: 5122

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

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


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

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


S.png


; Reset handler
Reset_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


system_stm32.pngSetsys.png


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


Rcc_k.png


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

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


1.STM32系列的RCC寄存器

RCC_CR.pngRCC_CFGR.png


*************截屏来自于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晶振

原子的.png

Onenet麒麟板的    12M晶振麒麟板的.png


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

当然喜欢直接配置寄存器的同学,在添加启动文件(.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中

CORE3.png



core.png

*******************截图来自于<Cortex-M3权威指南中文版>*******************


这段代码是,先获取系统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)
数据终端
评论(1)

登录后可评论,请 登录注册

glenxu 210天前...
一直认为单片机都可以这么做,但很少有人这么清楚。 学习了。
0   回复
相关文章推荐
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回

我要举报该内容理由

×
请输入您举报的理由(50字以内)