STM32滴答定时器与UCOS时钟系统,以及心跳和延时函数的实现.

   Systick就是一个定时器而已,只是它放在了NVIC中,主要的目的是为了给操作系统提供一个硬件上的中断(号称滴答中断)。滴答中断?这里来简单地解释一下。操作系统进行运转的时候,也会有“心跳”。它会根据“心跳”的节拍来工作,把整个时间段分成很多小小的时间片,每个任务每次只能运行一个“时间片”的时间长度就得退出给别的任务运行,这样可以确保任何一个任务都不会霸占整个系统不放。或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。 只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。

     知道systick在系统中的地位后,我们来了解systick的实现。这里只是举例说明systick的使用。它有四个寄存器,笔者把它列出来:

    SysTick->CTRL,        --控制和状态寄存器

    SysTick->LOAD,        --重装载寄存器

    SysTick->VAL,          --当前值寄存器

   SysTick->CALIB,        --校准值寄存器    

库里SysTick相关的函数我们能找到两个

一个在msic.h中

void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
        /* Check the parameters */
        assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
        if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
       {
                SysTick->CTRL |= SysTick_CLKSource_HCLK;
        }
        else
        {
                 SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
        }
}

一个在core_m3.h中


 

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
         if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);                    /* Reload value impossible */
         SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
         NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); 

                                                                              /* set Priority for Cortex-M0 System Interrupts */
         SysTick->VAL   = 0;                                       /* Load the SysTick Counter Value */
         SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
         SysTick_CTRL_TICKINT_Msk   | 
         SysTick_CTRL_ENABLE_Msk;                       /* Enable SysTick IRQ and SysTick Timer */
         return (0);                                                  /* Function successful */
}

我们一般只需要后一个就可以了

需要的操作在SysTick_Handler()(库版本不同可能是SysTickHandler)中添加就好了,意思每到加载到SysTick中的值减到0时就执行SysTick();

SystemInit();

    这个函数可以让主频运行到72M。可以把它作为systick的时钟源。

    接着开始配置systick,实际上配置systick的严格过程如下:

    1、调用SysTick_CounterCmd()       --失能SysTick计数器

    2、调用SysTick_ITConfig()          --失能SysTick中断

    3、调用SysTick_CLKSourceConfig()  --设置SysTick时钟源。

    4、调用SysTick_SetReload()         --设置SysTick重装载值。

    5、调用SysTick_ITConfig()          --使能SysTick中断

    6、调用SysTick_CounterCmd()       --开启SysTick计数器                                                      

    这里大家一定要注意,必须使得当前寄存器的值VAL等于0!

    SysTick->VAL  = (0x00);只有当VAL值为0时,计数器自动重载RELOAD。

接下来就可以直接调用Delay();函数进行延迟了。延迟函数的实现中,要注意的是,全局变量TimingDelay必须使用volatile,否则可能会被编译器优化。(以上的过程 在库函数SysTick_Config() 里已经配置稍做了解即可);

时钟的选择 在库文件 system_stm32f10x.c 里面

static void SetSysClock(void);

#ifdef SYSCLK_FREQ_HSE
  static void SetSysClockToHSE(void);
#elif defined SYSCLK_FREQ_24MHz
  static void SetSysClockTo24(void);
#elif defined SYSCLK_FREQ_36MHz
  static void SetSysClockTo36(void);
#elif defined SYSCLK_FREQ_48MHz
  static void SetSysClockTo48(void);
#elif defined SYSCLK_FREQ_56MHz
  static void SetSysClockTo56(void);  
#elif defined SYSCLK_FREQ_72MHz
  static void SetSysClockTo72(void);
#endif

以上 引用参考https://blog.csdn.net/yx_l128125/article/details/7884423

SetSysClock() 函数在SystemInit() 里面被调用 

SystemInit() 在startup_stm32f10x_hd.s 启动文件中被调用

Systick作为操作系统的心跳:

UCOS 系统的os_cfg.h文件中 #define OS_TICKS_PER_SEC           100u   /* Set the number of ticks in one second                        */

定义了 每秒心跳(中断的次数);  100次.  假设是72MH的频率 , 在这里ucos给systick 的reload值应该为720000.

100次中断耗时1s.

心跳()时间片)原理为 SysTick_Handler() 中断函数. 里面增加 OSIntEnter() (OSIntNesting++),然后调用OSTimeTick() ucos的时钟服务程序,

最后OSIntExit() 任务调度一次.   

OSTimeTick()的功能: 为OSTime+1 以及为所有等待任务控制块的OSTCBDly-1 (如果-1为0 由后面的OSIntExit启动调度)

OSTimeDly()与OSTimeDlyHMSM();

OSTimeDly(INT16U ticks)比较简单就是 任务控制块 状态改为等待 ,OSTCBCur->OSTCBDly赋值延迟心跳次数ticks.并任务调度.

OSTimeDlyHMSM(INT8U hours, INT8U minutes, INT8U seconds, INT16U ms)复杂了一点

小时分钟秒的延时简单的计算出对应的ticks 并调用OSTimeDly();毫秒级别的:

注意:最小延时时间为心跳 (1/OS_TICKS_PER_SEC);  

 ticks= OS_TICKS_PER_SEC * ((INT32U)ms + 500L / OS_TICKS_PER_SEC) / 1000L;

这里有个500L的取值.我们暂定为MINTIME变量吧. 

邵贝贝书里说:这里的500的取值 与OS_TICKS_PER_SEC 有关. OS_TICKS_PER_SEC为100时为500. 

目的是为了(假设OS_TICKS_PER_SEC=100 最小心跳10ms) 延时4ms的时候不延时,5ms以上的时候才延时. 依次14ms延迟一个节拍 15ms两个节拍.

个人感觉怪怪的....

那么如果想单纯的引发一次任务调度.那么OSTimeDlyHMSM(0,0,0,5) 至少是5ms才行.

对于单位 10ms以内的精确延时还是要抛弃ucos的函数而自己写.

为了实现小于(1/OS_TICKS_PER_SEC)的延时函数. 

传统的做法是定义delay_us()函数.函数功能,对Systicks 定时器的reload 重装值赋值 需要延时的时间对应的数值.

程序较为简单,但是会影响ucos的心跳.不能在操作系统下使用;

为了能在操作系统下实现us级别的延时.

参考正点原子教程:可以定义一个delay_us()的延时函数

void delay_us(u32 nus)
{        
    u32 ticks;
    u32 told,tnow,tcnt=0;
    u32 reload=SysTick->LOAD;    //LOAD的值             
    ticks=nus*fac_us;             //需要的节拍数               fac_us=时钟频率/1000000(这里是72)
    tcnt=0;
    told=SysTick->VAL;            //刚进入时的计数器值
    while(1)
    {
        tnow=SysTick->VAL;    
        if(tnow!=told)
        {        
            if(tnow<told)tcnt+=told-tnow;//这里注意一下SYSTICK是一个递减的计数器就可以了.
            else tcnt+=reload-tnow+told;        
            told=tnow;
            if(tcnt>=ticks)break;//时间超过/等于要延迟的时间,则退出.
        }  
    };                                         
}

这段函数功能,就是不断的读取SysTick->VAL ; Systick定时器的计数值;并计算差值. 如果大于需要的ticks 啧延时结束

如果需要的ticks大于重装载值,  用systick递减的原理.判断如果取得计数值大于记录计数值,啧更新记录值,并对cnt计数加reload重装载值. 实现us延时

猜你喜欢

转载自blog.csdn.net/justsure91/article/details/81081161