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函数被调用的时候,系统不会进行任务调度

引用\[1\]和引用\[2\]提供了一种使用中断方式实现延时的方法。在这种方法中,首先需要定义一个全局变量time_delay来表示延时的时间。然后使用SysTick_Config()函数来定义中断的时间段,通过中断中递减time_delay来实现延时。具体的延时函数包括delay_ms()和delay_us(),分别用于延时指定的毫秒数和微秒数。在中断处理函数SysTick_Handler()中,如果time_delay不为0,则将其递减。最后,在延时结束后,需要关闭计数器和清空计数器的值。 所以,如果你想在STM32中实现延时函数delay,你可以使用中断方式来实现。具体的代码可以参考引用\[2\]中的示例代码。 #### 引用[.reference_title] - *1* [STM32延时函数的四种方法](https://blog.csdn.net/huaweibiancheng3/article/details/124956486)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [STM32延时函数的三种方法](https://blog.csdn.net/u010312937/article/details/103855115)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [STM32延时函数的四种方法:普通延时(2种)、SysTick 定时器延时(2种)](https://blog.csdn.net/weibo1230123/article/details/81136564)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

千千道

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

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

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

打赏作者

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

抵扣说明:

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

余额充值