使用定时器中断进行延时,取代delay,不影响主流程的运行

在单片机开发中,我们经常会用到延时函数-delay(); 比如LED的闪烁、ADC采集、向其他设备发送指令后等待回复数据等等,应用非常广泛,也很好用。
但它也有一个致命的缺点——死等,举个例子,一个工程中有A、B、C三个任务,如果是裸机开发,不考虑中断的话,它会按while(1)中固定的顺序去执行。
由于任务需要,B中会经常delay_ms(500);,那么在delay过程中,整个程序都会在B中等待500ms,然后才会继续执行。一次还好,如果经常delay,那就会严重影响程序主流程的运行。
一个工程中往往有很多事情要做,如果因为一件事情就让其他的事情也无法运行,这是非常低效且不被允许的。

问题1

怎么解决delay死等的问题?实现延时的同时,又不影响程序主流程的运行。

解决1

用定时器中断实现延时。可以取代delay,不影响主流程的运行。(具体的代码在最下方

以发送AT指令为例,MCU发送AT指令后,需要等待wifi模块响应,再根据接收到的数据判断该条AT指令是否设置成功。 因此发出指令之后,需要等待一段时间(我所使用的这款wifi模块,大概需要500ms的等待时间)

问题2:

用定时器中断(TIM2)代替延时函数(delay_ms)时,又遇一些问题:
1,如何控制延时开始时间,从而保证延时时间的准确呢?
2,进入一次中断,再回到主函数时,会从头开始执行。因此我们并不想每次都给timer_cnt1赋值为100,只需要第一次给它赋值就行。那怎么办呢?

解决2

1,用timer_cnt1–
在main.c中,发送完AT指令,需要延时了,就给timer_cnt1赋值为100。(类似跑步比赛时的发令枪,可以确保延时时间的准确)
在timer.c中,当timer_cnt1>0时,每次进入中断都timer_cnt1–。
这样就可以控制延时的开始时间。

2,用状态机
定义一个状态变量at_state,把它定义为静态局部变量,这样每次at_state进入函数时,都会保持上一次的值。
at_state为2时,发送AT指令,timer_cnt1赋值为100,再把at_state赋值为3。
这样下次程序就会进入at_state为3的状态。也就实现了只在第一次给timer_cnt1赋值。

代码如下,省略了中间test的版本,直接给出最终结果:

timer.c中,

uint8_t timer_flag = 0;
uint16_t timer_cnt = 0;

/***********************************************************************
函数名称:TIM2_Configuration(unsigned int time)
函数功能:定时器2配置
输入参数:定时器定时时间,单位为5毫秒
***********************************************************************/
void TIM2_Configuration(unsigned int time)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);
  TIM_DeInit(TIM2);
  TIM_TimeBaseStructure.TIM_Period = (time*2-1);																	
  TIM_TimeBaseStructure.TIM_Prescaler = (36000 - 1);				    
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 			
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 		
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  TIM_ClearFlag(TIM2, TIM_FLAG_Update);							   
  TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
  TIM_Cmd(TIM2, ENABLE);											
}

/***********************************************************************
定时器2中断函数
***********************************************************************/
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET ) 
	{
		TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);
			
		if(timer_cnt > 0)
		{
			timer_cnt--;
			if(timer_cnt == 0) //500ms
			{
				timer_flag = 1;
				//timer_cnt = 0;
			}
		}
	}
}

main.c中

if(at_state == 2)
{
    Send_AT("AT+E\r");
    timer_cnt1 = 100;	//开始计时
    at_state = 3;
}

if(at_state == 3)
{	
    //delay_ms(500);
    if(timer_flag1 == 1)	//500ms时间到了
    {
        timer_flag1 = 0;
        ......
    }

这个方法非常通用,可以实现用定时器中断去替换delay函数。

如果对您有所帮助,麻烦点赞分享,这对我非常重要,感谢!

更新

后续使用发现的一个小bug,及解决方法:
https://blog.csdn.net/qq_44139306/article/details/136751008

你可能感兴趣的:(bug解决,经验分享,单片机,嵌入式硬件)