在裸机(以stm32为例)编程中,常用的延时方法如下:
1.循环法
void delay_us(u16 time)
{
u16 i=0;
while(time--)
{
i=10; //自己定义
while(i--) ;
}
}
//毫秒级的延时
void delay_ms(u16 time)
{
u16 i=0;
while(time--)
{
i=12000; //自己定义
while(i--) ;
}
}
2.SysTick定时器中断机制
SysTick-系统定时器是CM3内核中的一个外设,内嵌在NVIC中,是一个24位向下递减的计数器,所有基于CM3内核的单片机都具有这个系统定时器,当重装载数值寄存器的值递减到0的时候,系统定时器就产生一次中断,以此循环往复!
*volatile unsigned long time_delay; // 延时时间,注意定义为全局变量
//延时n_ms
void delay_ms(volatile unsigned long nms)
{
//SYSTICK分频--1ms的系统时钟中断
if (SysTick_Config(SystemFrequency/1000))
{
while (1);
}
time_delay=nms;//读取定时时间
while(time_delay);
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时nus
void delay_us(volatile unsigned long nus)
{
//SYSTICK分频--1us的系统时钟中断
if (SysTick_Config(SystemFrequency/1000000))
{
while (1);
}
time_delay=nus;//读取定时时间
while(time_delay);
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//在中断中将time_delay递减。实现延时
void SysTick_Handler(void)
{
if(time_delay)
time_delay--;
}*
3.非中断方式,直接操作寄存器
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
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左右
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
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
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; //清空计数器
}
在RT-Thread中延时
-
使用 RTOS 的很大优势就是榨干 CPU 的性能,永远不能让它闲着,线程如果需要延 时也就不能再让 CPU 空等来实现延时的效果。
RTOS 中的延时叫阻塞延时,即线程需要延 时的时候, 线程会放弃 CPU 的使用权, CPU 可以去干其它的事情,当线程延时时间到,重
新获取 CPU 使用权, 线程继续运行,这样就充分地利用了 CPU 的资源,而不是干等着。当线程需要延时,进入阻塞状态,那 CPU 又去干什么事情了?如果没有其它线程可以 运行, RTOS 都会为 CPU创建一个空闲线程,这个时候 CPU 就运行空闲线程。 在 RTThread中,空闲线程是系统在初始化的时候创建的优先级最低的线程,空闲线程主体主要是做一些系统内存的清理工作。但是为了简单起见,我们本章实现的空闲线程只是对一个 全局变量进行计数。鉴于空闲线程的这种特性,在实际应用中,当系统进入空闲线程的时 候, 可在空闲线程中让单片机进入休眠或者低功耗等操作
void rt_thread_delay(rt_tick_t tick)
{
struct rt_thread *thread;
/* 获取当前线程的线程控制块 */
thread = rt_current_thread;
/* 设置延时时间 */
thread->remaining_tick = tick;
/* 进行系统调度 */
rt_schedule();
}
rt_current_thread 是一个在 scheduler.c
定义的全局变量,用于指向当前正在运行的线程的线程控制块
remaining_tick 是线程控制块的一个成员,用于记录线程需要延时
的时间,单位为 SysTick 的中断周期。比如我们本书当中 SysTick 的中断周期为 10ms,调
用 rt_thread_delay(2)则完成 2*10ms 的延时。