Cortex-M0系列 ~ 配置延时函数(systick定时器)

M0系列作为stm32的入门,虽说现在用得比较多的是M3、M4系列的,不过刚好作者需要用到,网上查了大部分相关信息,也做过调试验证,顺便把代码配置也给说一下。


延时函数想必是每套代码里面最基本的函数之一了,但如果要自己写一套项目出来,配置延时函数就是必不可少的。我们可以利用32内部的SysTick定时器来实现延时,详情可参阅《ARM Cortex-M3权威指南》第133页,M3内核处理器包含了一个24位的倒计时定时器(systick),当计数到0时,则从RELOAD寄存器中自动重装载定时器的值。但其实在M0中也同样有systick可以利用,这样即不占用中断,也不占用系统定时器。

delay_init函数

该函数初始化了两个重要参数:fac_us和fac_ms,同时将systick是时钟源配置为外部时钟,具体代码如下~

void delay_init()
{
    //配置为外部时钟HCLK/8
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

    fac_us = SystemCoreClock/8000000;  //1us = 系统时钟/6
    fac_ms = fac_us * 1000;            //1ms = 1us * 1000
}`

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 这里把systick的时钟配置为外部时钟,要注意的是systick 的时钟源自与HCLK的8分频,如果外部晶振为8MHz,然后倍频到72MHz,那么systick的时钟就是72/8 = 9MHz,即是systick的计数器每减1,时间就过去了1/9us。所以fac_us = SystemCoreClock/8000000; 表示过了1us,可能有部分人对这句话不太理解,下面做个大概解释,上面说到systick为系统时钟的8分频,假设系统时钟倍频到72MHz,8分频即为9MHz,所以每一个systick时钟周期的时间为1/9us,SystemCoreClock/8000000 的意思是算出1us需要几个systick周期,即72000000/8000000 = 9个systick时钟周期,fac_us = 9 * 1/9us = 1us 没毛病,还没看懂的多看几遍就理解了,接下来的fac_ms = fac_us * 1000 1ms等于1000 us 这个不用怎么解释了。
该注意的是delay_init函数需要在main函数的开头调用作为对systick定时器的配置,fac_us和fac_ms变量需要在定义为全局变量,因为接下来的两个函数还将用到。

static uint8_t   fac_us;
static uint16_t  fac_ms;

delay_us函数

该函数用来延时指定的us,其参数nus为要延时的毫秒数,代码如下~

//function:     delay_us
//param:        nus
void delay_us(uint32_t nus)
{
    uint32_t temp;

    SysTick->LOAD = nus * fac_us;
    SysTick->VAL  = 0x00;
    SysTick->CTRL|= SysTick_CTRL_ENABLE_Msk;

    do{
        temp = SysTick->CTRL;
    }while(temp&0x01&&! (temp&(1<<16)));

    SysTick->CTRL&= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL  = 0x00;
}

以上是配置systick的寄存器实现的延时,有兴趣的可以参阅库文 stmfx_map.h 里面的 CTRL、LOAD、VAL、CALIB 这4个寄存器。

delay_ms函数

该函数用来延时指定的ms,其参数nms为要延时的毫秒数,代码如下~

//function:     delay_ms
//param:        nms
void delay_ms(uint16_t nms)
{
    uint32_t temp;

    SysTick->LOAD = nms * fac_ms;
    SysTick->VAL  = 0x00;
    SysTick->CTRL|= SysTick_CTRL_ENABLE_Msk;

    do
     {
        /* code */
        temp = SysTick->CTRL;
     } while (temp&0x01&&! (temp&(1<<16))); 

     SysTick->CTRL&= ~SysTick_CTRL_ENABLE_Msk;
     SysTick->VAL = 0x00;
}

创建 delay.h 加入上面3个函数的声明,然后在main函数或者其他函数中直接调用 delay_us() 函数和 delay_ms() 函数就可以了。

下面给上ucos系统的systick延时配置代码~

void delay_init()
{
    u32 reload;

    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
    fac_us = SystemCoreClock/8000000;

    reload = SystemCoreClock/8000000;
    reload* = 1000000/OS_TICKS_PER_SEC;

    fac_ms = 1000/OS_TICKS_PER_SEC;
    SysTick->CTRL| =SysTick_CTRL_TICKINT_Msk;
    SysTick->LOAD = reload;
    SysTick->CTRL| =SysTick_CTRL_ENABLE_Msk;
}

void delay_us(u32 nus)
{
    u32 ticks,told,tnow,tcnt = 0;
    u32 reload = SysTick->LOAD;
    tick = nus * fac_us;
    tcnt = 0;
    OSSchedLock();
    told = SysTick->VAL;
    while(1)
    {
        tnow = SysTick->VAL;
        if(tnow!=told)
        {
            if(tnow<told)
                tcnt += told - tnow;
            else
                tcnt += reload - tnow + wold;

            told = tnow;
            if(tcnt>=ticks)
                break;
        }
    };

    OSSchedUnlock();
}

void delay_ms(u16 nms)
{
    if(OSRunning == TRUE)
    {
        if(nms>=fac_ms){
            OSTimeDly(nms/fac_ms);
        }
        nms%=fac_ms;
    }
    delay_us((u32)(nms*1000));
}

可以将两套配置代码(无系统和ucos系统)在程序中加个宏判断合并起来,这样便于管理而已也减少内存的占用。

你可能感兴趣的:(Cortex-M0系列)