单片机开发思想进阶01:阻塞与非阻塞延时

一、前言——延时

延时在MCU开发过程中经常使用到,它可以控制程序的执行速度,使程序事件按照一定的时间分布有序进行。同时,它也应用在诸如单总线、I2C等通信协议的时序中,在MCU开发中发挥着重要作用。**有效的延时可以使MCU高效处理不同的任务,提升程序的运行速率;而错误使用延时将导致MCU处理事务的时间基准混乱,轻者导致MCU处理事务的结果达不到预期效果,重者导致MCU程序“跑飞”,CPU卡死。**本文从软件开发思想介绍阻塞延时和非阻塞延时。

二、阻塞延时和非阻塞延时

1. 阻塞延时

阻塞延时通常是令CPU连续执行一些无用的语句实现的,比如以下延时函数delay_ms函数,当调用此函数实现延时时,CPU会在函数中不断地执行一定次数的for循环空语句,执行完for循环的空语句后才能够跳延时执行下一个任务。因此,阻塞延迟会影响其他任务的执行,导致它们暂时停止,等待延迟结束后才能继续执行。这种延迟方式会浪费CPU,因为在延迟期间,CPU无法执行其他任务。

void delay_ms(unsigned int xms)   
{
	unsigned int i, j;
	for (i = xms; i > 0; i --)
	for (j = 110; j > 0; j --);
}

2. 非阻塞延时

非阻塞延时是指在延时的过程中CPU可以执行其他的任务,待延时时间到才回来执行相应的任务,**非阻塞延时极大地提高了CPU的利用率,但精度很低。**非阻塞延时通常使用循环计数的方式实现。

3. 实例

基于51内核实现D1和D2闪烁,但要求D1的闪烁时间为D2闪烁时间的2倍,不使用定时器的情况下如何实现?

(1)阻塞延时法
这个使用延时函数不是很简单吗?我刚入门时这样想,于是有了以下的代码。实际上这种阻塞延时是行不通的,由于阻塞延时对任务的阻塞作用,从仿真效果图可以发现D1和D2的闪烁时间与预想效果相差深远,阻塞延时的作用造成了延时的积累。

#include "reg52.h"
sbit D1 = P2^0;
sbit D2 = P2^1;
//预想目的:让D1的闪烁时间为D2的2倍(D1的闪烁频率为D2的1/2)
void delay_ms(unsigned int xms)
{
	unsigned int i, j;
	for (i = xms; i > 0; i --)
	for (j = 110; j > 0; j --);
}

void main(void)
{
	while(1)
	{
		D1 = 0;
		delay_ms(400);
		D1 = 1;
		delay_ms(400);
		
		D2 = 0; 
		delay_ms(200);
		D2 = 1;
		delay_ms(200);
	}
}

黄、蓝色波形分别为代表D1、D2亮灭时间,仿真效果图如下:
D1对应IO的电平高电平时间

D2对应IO的电平高电平时间
(2)非阻塞延时法
非阻塞延时法极大释放了CPU的处理灵活性,由于非阻塞延时在延时的过程中CPU可以执行其他任务,延时的效果不会造成阻塞延时的时间的积累。非阻塞延时可以实现对应的功能,代码如下:

#include "reg52.h"
#define D1_DELAY_TIME 8000   //D1定时的计数值
#define D2_DELAY_TIME 4000	 //D2定时的计数值
sbit D1 = P2^0;
sbit D2 = P2^1;
unsigned int D1_Delay_Count;   //D1的延时初始值
unsigned char D1_Delay_Flag;   //D1延时时间到标志位

unsigned int D2_Delay_Count;	//D2的延时初始值
unsigned char D2_Delay_Flag;	//D2延时时间到标志位
void main(void)
{
	D1_Delay_Count = D1_DELAY_TIME;  //给延时计数赋值
	D2_Delay_Count = D2_DELAY_TIME;  //给延时计数赋值
	
	while(1)
	{
		if(D1_Delay_Count)   //每次大循环只执行一次    
		{
			D1_Delay_Count --;    //只对计数值减一次1,就跳出if
			if(!D1_Delay_Count) D1_Delay_Flag = 1;   //定时时间到
		}
		
		if(D2_Delay_Count)   //每次大循环只执行一次    
		{
			D2_Delay_Count --;    //只对计数值减一次1,就跳出if
			if(!D2_Delay_Count) D2_Delay_Flag = 1;   //定时时间到
		}
		
		if(D1_Delay_Flag)   //定时时间到就执行一定的操作
		{
			D1_Delay_Flag = 0;  //定时到标志位清零
			D1 = ~D1;
			D1_Delay_Count = D1_DELAY_TIME;   //计数值重装,为下一次非阻塞延时做准备
		}
		
		if(D2_Delay_Flag)   //定时时间到就执行一定的操作
		{
			D2_Delay_Flag = 0;  //定时到标志位清零
			D2 = ~D2;
			D2_Delay_Count = D2_DELAY_TIME;   //计数值重装,为下一次非阻塞延时做准备
		}
	}
}

黄、蓝色波形分别为代表D1、D2亮灭时间,仿真效果图如下:
在这里插入图片描述
在这里插入图片描述

三、总结

1. 阻塞延时

阻塞延时是利用CPU不断重复执行空语句实现的,只有延时时间结束CPU才可以执行其他任务,延时时间具有“累加性”,在多任务处理中会造成任务的执行时间混乱,并且在中断处理函数中应谨慎使用阻塞延时。
2. 非阻塞延时
非阻塞延时是利用CPU对计数值加/减,计数值达到设定值才证明延时时间到,CPU对计数值进行一次操作后就可以跳出来执行其他任务了,没有延时时间的“累加性”。注意,当含有多个任务时如果还是用此类纯软的非阻塞延时也可能会造成系统的时基混乱,此时可以采用定时间+软件计数的方法实现非阻塞延时,下一章节将介绍此方法。

四、附言

此文章的思想来源为B站UP主金善愚老师的视频讲解:阻塞延时与非阻塞延时,本文仅为本人的学习记录,如发现不足之处欢迎交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值