STM32的delay函数详解以及带不带操作系统的区别(基于HAL库)

我们从接触的第一个跑马灯项目开始,就用到了delay函数,初学者只知道调用一下delay_ms,delay_us就可以进行系统延时。那么对于这个函数,你了解多少呢?下面让我们根据代码分析一下这个函数实现的过程。

首先,delay函数分为裸机版本和操作系统版本,如果你在操作系统的项目下面运行裸机版本的delay函数,那么程序不会按你所想的方式运行,所以在操作系统下面我们应该修改delay函数为兼容系统的,下面我们以ucos系统为例,说说这两个版本的区别。

1.delay_init函数

第一个我们来分析初始化函数delay_init,宏定义SYSTEM_SUPPORT_OS为1的时候是支持操作系统,为0的时候是裸机程序,SysTick 是 MDK 定义了的一个结构体(在 core_m4.h 里面),里面包含 CTRL、LOAD、VAL、CALIB 4 个寄存器。


SysTick->CTRL 的各位定义:

SysTick-> LOAD 的定义:

SysTick-> VAL 的定义:

SysTick-> CALIB的话我们目前不用到,不做描述。

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);这行代码的意思就是SysTick的时钟选择为系统内核时钟,SysTick的时钟来源于HCLK,和系统时钟同样为180Mhz,SysTick-> VAL每减小1就过去了1/180us,fac_us=SYSCLK,这里我们传入的SYSCLK为180,意思就是延时1us需要180个 SysTick 时钟周期。

在不使用操作系统的时候,只要给fac_us写入周期数就可以了,但是在使用操作系统的时候,fac_us不被写入SysTick->LOAD 实现延时,fac_ms=1000/delay_ostickspersec; 代表操作系统可以延时的最少毫秒(当delay_ostickspersec=1000的时候,表示操作系统最少可以延迟1毫秒)

2.delay_us  函数

2.1带操作系统的delay_us函数

//延时nus
//nus:要延时的us数.	
//nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)	    								   
void delay_us(u32 nus)
{		
	u32 ticks;
	u32 told,tnow,tcnt=0;
	u32 reload=SysTick->LOAD;				//LOAD的值	    	 
	ticks=nus*fac_us; 						//需要的节拍数 
	delay_osschedlock();					//阻止OS调度,防止打断us延时
	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;			//时间超过/等于要延迟的时间,则退出.
		}  
	};
	delay_osschedunlock();					//恢复OS调度											    

 此函数想对于不带操作系统的delay_us函数,多一个delay_osschedlock(); 来阻止OS调度,防止打断us延时,然后在延时函数结束之前,调用delay_osschedunlock();恢复OS调度。

2.2不带操作系统的delay_us函数

这里利用了时钟摘取法,ticks 是延时 nus 需要等待的 SysTick 计数次数,told 用于记录最近一次的 SysTick->VAL 值,然后 tnow 则是当前的SysTick->VAL 值,通过他们的对比累加,实现 SysTick 计数次数的统计,统计值存放在 tcnt 里面,通过对比 tcnt 和 ticks,来判断延时是否完成,实现 nus 的延时。

3. delay_ms 

3.1带操作系统的delay_ms函数

//延时nms
//nms:要延时的ms数
//nms:0~65535
void delay_ms(u16 nms)
{	
	if(delay_osrunning&&delay_osintnesting==0)//如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)	    
	{		 
		if(nms>=fac_ms)						//延时的时间大于OS的最少时间周期 
		{ 
   			delay_ostimedly(nms/fac_ms);	//OS延时
		}
		nms%=fac_ms;						//OS已经无法提供这么小的延时了,采用普通方式延时    
	}
	delay_us((u32)(nms*1000));				//普通方式延时
}

带操作系统的delay_ms函数,会判断需要延时的时间是否大于之前在init里面设置的最小时间周期fac_ms,如果大于fac_ms,就会进行nms/fac_ms次时间延时,并且这段时间操作系统进行正常的任务调度,最后计算nms%=fac_ms,如果存在余数表示最后差nms没有延时完成,所以又用delay_us补上剩余没有延时的时间,此时不进行任务调度。

3.2不带操作系统的delay_ms函数

这个没什么好说的,就是调用了n次delay_us实现。

4.总结

在裸机程序中,delay_ms函数与delay_us函数正常使用即可,在操作系统中,使用delay_ms函数的时候,如果设置的延时时间大于操作系统设置的最小的调度时间,就会进行正常的任务调度拉起当前任务,执行下一个已就绪的任务,直到剩余延时时间小于操作系统设置的最小的调度时间,就进行delay_us函数,注意的是,只要是delay_us函数被调用的时候,系统不会进行任务调度

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千千道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值