STM32嵌入式系统延时函数8

自己学习总结用,有些乱勿怪

1、delay 文件夹代码介绍

delay 文件夹内包含了 delay.c 和 delay.h 两个文件,这两个文件用来实现系统的延时功能,

其中包含 7 个函数:

void delay_osschedlock(void);

void delay_osschedunlock(void);

void delay_ostimedly(u32 ticks);

void SysTick_Handler(void);

void delay_init(u8 SYSCLK);

void delay_ms(u16 nms);

void delay_us(u32 nus);

前面 4 个函数,仅在支持操作系统(OS)的时候,需要用到,而后面 3 个函数,则不论是否支持 OS 都需要用到。在介绍这些函数之前,我们先了解一下编程思想:CM4 内核的处理和 CM3 一样,内部都包含了一个 SysTick 定时器,SysTick 是一个 24 位的倒计数定时器,当计到 0 时,将从 RELOAD寄存器中自动重装载定时初值。只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。SysTick 在《STM32xx 中文参考手册》里面基本没有介绍,其详细介绍,请参阅《STM32F3 与 F4 系列Cortex M4内核编程手册》第230 页。我们就是利用 STM32的内部 SysTick来实现延时的,这样既不占用中断,也不占用系统定时器。这里我们将介绍的是 ALIENTEK 提供的最新版本的延时函数,该版本的延时函数支持在任意操作系统(OS)下面使用,它可以和操作系统共用 SysTick 定时器。这里,我们以 UCOSII 为例,介绍如何实现操作系统和我们的 delay 函数共用 SysTick 定时器。首先,我们简单介绍下 UCOSII 的时钟:ucos 运行需要一个系统时钟节拍(类似 “心跳”),而这个节拍是固定的(由 OS_TICKS_PER_SEC 宏定义设置),比如要求 5ms 一次(即可设置:OS_TICKS_PER_SEC=200),在 STM32 上面,一般是由 SysTick 来提供这个节拍,也就是 SysTick要设置为 5ms 中断一次,为 ucos 提供时钟节拍,而且这个时钟一般是不能被打断的(否则就不准了)。

因为在 ucos 下 systick 不能再被随意更改,如果我们还想利用 systick 来做 delay_us 或者delay_ms 的延时,就必须想点办法了,这里我们利用的是时钟摘取法。以 delay_us 为例,比如delay_us(50),在刚进入 delay_us 的时候先计算好这段延时需要等待的 systick 计数次数,这里为 50*21(假设系统时钟为 168Mhz,因为 systick 的频率为系统时钟频率的 1/8,那么 systick每增加 1,就是 1/21us),然后我们就一直统计 systick 的计数变化,直到这个值变化了 50*21,一旦检测到变化达到或者超过这个值,就说明延时 50us 时间到了。这样,我们只是抓取 SysTick计数器的变化,并不需要修改 SysTick 的任何状态,完全不影响 SysTick 作为 UCOS 时钟节拍的功能,这就是实现 delay 和操作系统共用 SysTick 定时器的原理。

2、操作系统支持宏定义及相关函数

当需要 delay_ms 和 delay_us 支持操作系统(OS)的时候,我们需要用到 3 个宏定义和 4个函数,宏定义及函数代码如下:

//本例程仅作 UCOSII 和 UCOSIII 的支持,其他 OS,请自行参考着移植

//支持 UCOSII

#ifdef OS_CRITICAL_METHOD

//OS_CRITICAL_METHOD 定义了,说明要支持 UCOSII

#define delay_osrunning

OSRunning

//OS 是否运行标记,0,不运行;1,在运行

#define delay_ostickspersec OS_TICKS_PER_SEC //OS 时钟节拍,即每秒调度次数

#define delay_osintnesting OSIntNesting

//中断嵌套级别,即中断嵌套次数

#endif

//支持 UCOSIII

#ifdef CPU_CFG_CRITICAL_METHOD

//CPU_CFG_CRITICAL_METHOD 定义了,说明要支持 UCOSIII

#define delay_osrunning

OSRunning

//OS 是否运行标记,0,不运行;1,在运行

#define delay_ostickspersec OSCfg_TickRate_Hz //OS 时钟节拍,即每秒调度次数

#define delay_osintnesting OSIntNestingCtr

//中断嵌套级别,即中断嵌套次数

#endif

//us 级延时时,关闭任务调度(防止打断 us 级延迟)

void delay_osschedlock(void)

{

#ifdef CPU_CFG_CRITICAL_METHOD //使用 UCOSIII

OS_ERR err;

OSSchedLock(&err);

//UCOSIII 的方式,禁止调度,防止打断 us 延时

#else

//否则 UCOSII

OSSchedLock();

//UCOSII 的方式,禁止调度,防止打断 us 延时

#endif

}

//us 级延时时,恢复任务调度

void delay_osschedunlock(void)

{

#ifdef CPU_CFG_CRITICAL_METHOD //使用 UCOSIII

OS_ERR err;

OSSchedUnlock(&err);

//UCOSIII 的方式,恢复调度

#else

//否则 UCOSII

OSSchedUnlock();

//UCOSII 的方式,恢复调度

#endif

}

//调用 OS 自带的延时函数延时

//ticks:延时的节拍数

void delay_ostimedly(u32 ticks)

{

#ifdef CPU_CFG_CRITICAL_METHOD //使用 UCOSIII 时

OS_ERR err;

OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err);//UCOSIII 延时采用周期模式

#else

OSTimeDly(ticks);

//UCOSII 延时

#endif

}

//systick 中断服务函数,使用 ucos 时用到

void SysTick_Handler(void)

{

if(delay_osrunning==1)

//OS 开始跑了,才执行正常的调度处理

{

OSIntEnter();

//进入中断

OSTimeTick();

//调用 ucos 的时钟服务程序

OSIntExit();

//触发任务切换软中断

}

}

以上代码,仅支持 UCOSII 和 UCOSIII,不过,对于其他 OS 的支持,也只需要对以上代

码进行简单修改即可实现。

支持 OS 需要用到的三个宏

#define delay_osrunning OSRunning //OS 是否运行标记,0,不运行;1,在运行

#define delay_ostickspersec OS_TICKS_PER_SEC //OS 时钟节拍,即每秒调度次数

#define delay_osintnesting OSIntNesting //中断嵌套级别,即中断嵌套次数

宏定义:delay_osrunning,用于标记 OS 是否正在运行,当 OS 已经开始运行时,该宏定义值为 1,当 OS 还未运行时,该宏定义值为 0。

宏定义:delay_ ostickspersec,用于表示 OS 的时钟节拍,即 OS 每秒钟任务调度次数。

宏定义:delay_ osintnesting,用于表示 OS 中断嵌套级别,即中断嵌套次数,每进入一个中断,该值加 1,每退出一个中断,该值减 1。

支持 OS 需要用到的 4 个函数,即

函数:delay_osschedlock,用于 delay_us 延时,作用是禁止 OS 进行调度,以防打断 us 级延时,导致延时时间不准。

函数:delay_osschedunlock,同样用于 delay_us 延时,作用是在延时结束后恢复 OS 的调度,继续正常的 OS 任务调度。

函数:delay_ostimedly,则是调用 OS 自带的延时函数,实现延时。该函数的参数为时钟节拍数。

函数:SysTick_Handler,则是 systick 的中断服务函数,该函数为 OS 提供时钟节拍,同时可以引起任务调度。以上就是 delay_ms 和 delay_us 支持操作系统时,需要实现的 3 个宏定义和 4 个函数。

3、delay_init 函数

该函数用来初始化 2 个重要参数:fac_us 以及 fac_ms;同时把 SysTick 的时钟源选择为外部时钟,如果需要支持操作系统(OS),只需要在 sys.h 里面,设置 SYSTEM_SUPPORT_OS 宏的值为 1 即可,然后,该函数会根据 delay_ostickspersec 宏的设置,来配置 SysTick 的中断时间,并开启 SysTick 中断。具体代码如下[这里介绍两个版本的delay_int,一个是M3的72MHz和M4的168MHZ]:

开启 SysTick 中断。具体代码如下:

//初始化延迟函数

//当使用 OS 的时候,此函数会初始化 OS 的时钟节拍

//SYSTICK 的时钟固定为 HCLK 时钟的 1/8

void delay_init()

{

#if SYSTEM_SUPPORT_OS

//如果需要支持 OS.

u32 reload;

#endif

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

//选择外部时钟 HCLK/8

fac_us=SystemCoreClock/8000000;

//为系统时钟的 1/8

#if SYSTEM_SUPPORT_OS

//如果需要支持 OS.

reload=SystemCoreClock/8000000; //每秒钟的计数次数 单位为 M 1

reload*=1000000/delay_ostickspersec; 2

//根据 delay_ostickspersec 设定溢出时间

//reload 为 24 位寄存器,最大值:16777216,在 168M 下,约合 0.7989s 左右

fac_ms=1000/delay_ostickspersec; 3

(这里是168M下,因为这里直接把每秒的计数次数变成了秒的计算,1的reload=21次每us,第二行reload=21*1000000/200=105000,下面3的定时器时间,即最好系统时间单元是5ms,而105000/21us=5000us=5ms,也是5ms一次中断。即168M下105000次递减一次中断是5ms,而reload最大值=16777216,在168M下是24位寄存器的理论值,它的延时是16777216/21us=798915.0476us=0.7989s)


//代表 OS 可以延时的最少单位

SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;

//开启 SYSTICK 中断

SysTick->LOAD=reload;

//每 1/delay_ostickspersec 秒中断一次

SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;

//开启 SYSTICK

#else

fac_ms=(u16)fac_us*1000;

//非 OS 下,代表每个 ms 需要的 systick 时钟数

#endif

}

另一个M3的版本

void delay_init()

{

#if SYSTEM_SUPPORT_OS //如果需要支持OS.

u32 reload;

#endif

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8

fac_us=SystemCoreClock/8000000; //为系统时钟的1/8

#if SYSTEM_SUPPORT_OS //如果需要支持OS.

reload=SystemCoreClock/8000000; //每秒钟的计数次数 单位为M

reload*=1000000/delay_ostickspersec; //根据delay_ostickspersec设定溢出时间

//reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右

①设置滴答定时器时钟源,F103为72M,72M/8=9M;

②用当前时钟源9M,9M/S=9/us;故1us计数器跳动9次;

③同②;

④根据delay_ostickspersec设置溢出时间,在默认值下,delay_ostickspersec=200;

reload在后边将会当做计数初值,放入重装载计数器,故它为溢出所需的计数次数;

reload=1/200*1000000*9;

溢出时间为1/200s,在乘以1000000,变为us级别,再乘以1us跳动次数;

一秒钟跳200次(设定计时);每次0.005s,即5ms;0.005s有多少个us,即0.005*1000000=5000个;1us跳9次,故在乘以9,等于45000次,装入SysTick->LOAD这个寄存器;

说白了就是:滴答定时器1us跳9次,你将45000次放入SysTick->LOAD这个寄存器,它就开始倒计时,滴答定时器进入它的中断的时间是1/200s=5ms,也是45000/9us=5000us=5ms,也就是5ms进一次中断。以最大reload=16777216/9us=1864135.111us=1.86s)

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);这一句把SysTick的时钟选择外部时钟,SysTick 的时钟源自 HCLK 的 8 分频,假设我们外部晶振为 8M,然后倍频到 72M,那么 SysTick 的时钟即为 9Mhz,也就是SysTick 的计数器 VAL 每减 1,就代表时间过了 1/9us。

fac_us=SystemCoreClock/8000000;这句话就是计算在 SystemCoreClock 时钟频率下延时

1us 需要多少个 SysTick 时钟周期。

fac_ms=(u16)fac_us*1000;就是计算延时 1ms 需要多少个 SysTick 时钟周期,它自然是 1us

的 1000 倍。

在不使用 OS 的时候:fac_us为 us 延时的基数,也就是延时 1us时候SysTick->LOAD 所应设置的值。fac_ms 为 ms 延时的基数,也就是延时 1ms,SysTick->LOAD 所应设置的值。fac_us为 8 位整形数据,fac_ms 为 16 位整形数据。

当使用 OS 的时候,fac_us还是 us 延时的基数,不过这个值不会被写到 SysTick->LOAD寄存器来实现延时,而是通过时钟摘取的办法实现的。而 fac_ms 则代表 ucos自带的延时函数所能实现的最小延时时间(如 delay_ostickspersec=200,那么 fac_ms 就是 5ms)



fac_ms=1000/delay_ostickspersec; //代表OS可以延时的最少单位

SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断

SysTick->LOAD=reload; //每1/delay_ostickspersec秒中断一次

SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK

#else

fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数

#endif

}

展开阅读全文

页面更新:2024-03-16

标签:函数   寄存器   嵌套   定时器   节拍   时钟   也就是   次数   定义   级别   操作系统   代码   代表   时间   系统   科技

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top