• STM32中断调试中遇到的问题


    STM32应用过程中遇到的问题

    实现功能:

    1、自动流水灯:在LED1~LED4上实现自动流水灯,流水间隔时间为200ms/bit,然后通过按键KEY1改变流水灯的速度,每次按键间隔时间增加200ms:当间隔时间增加到1s后(蜂鸣器报警),再次按键间隔时间恢复为初始值200ms。//注:按键同通过中断实现

    2、手动流水灯功能:通过按键KEY1控制流水灯一位,每次按键流水灯移动一位,可循环实现。//(1)、在新的程序中实现,不涵盖上题功能。(2)按键通过中断实现

    3、综合流水灯:1)通过按键KEY2实现自动流水灯和手动流水灯两种模式的切换。初始模式为自动流水灯模式。当处于自动流水灯模式时,KEY1用于改变流水灯速度,如1题所述。当处于手动流水灯模式时,KEY1用于控制流水灯移位,如2提所述。//注:所有按键用中断实现,延时采用定时器中断实现,操作稳定,可循环实现,对按键抖动和按键时间具有鲁棒性。

    对于功能1的实现,我书写的中断内容为:

    /*	@函数名称 :中断服务子程序0
    	@函数功能 :响应中断0的子程序
    	@输入参数 :无
    	@返回值   :无
    	@注意     :中断中不要放延时过长的程序,也不要写死循环
    */
    void EXTI0_IRQHandler(void)
    {
    	delay_ms(20);		//消抖		——请不要在中断消抖,删掉这句
    	if(EXTI_GetFlagStatus(EXTI_Line0)==SET)		//标志位是否使能
    	{
    		if(time_flag==5)		//200*5=1s
    		{
    			time_flag=0;
    		}
    		else if(time_flag<5)
    			time_flag=time_flag+1;
    	}
    	EXTI_ClearITPendingBit(EXTI_Line0);
    }
    

    然后功能实现程序如下图所示:

    /*	@函数名称:void LED_Base_200ms(u16 t)
    	@函数功能:以200ms为基准时间进行延时
    	@输入参数:t延时基准
    	@返回值	 :无
    */
    void LED_Base_200ms(void)
    {
    //	u16 temp;		//延时基准时间
    //	temp=200*(time_flag+1);
    //	u8 i=1;
    //	for(;i<=4;i++)
    //	{
    //		LED_ON(i);
    //		delay_ms(temp);
    //		LED_OFF(i);
    //		delay_ms(temp);
    //	}
    	u8 i;
    	switch(time_flag+1)
    	{
    		case 1 : {
    			for(i=1;i<=4;i++)
    			{
    				LED_ON(i);
    				delay_ms(200);
    				LED_OFF(i);
    				delay_ms(200);
    			}
    			
    		};
    		break;
    		
    		case 2 : {
    			for(i=1;i<=4;i++)
    			{
    				LED_ON(i);
    				delay_ms(400);
    				LED_OFF(i);
    				delay_ms(400);
    			}
    			
    		};
    		break;
    		case 3 : {
    			for(i=1;i<=4;i++)
    			{
    				LED_ON(i);
    				delay_ms(600);
    				LED_OFF(i);
    				delay_ms(600);
    			}
    			
    		};
    		break;
    		case 4 : {
    			for(i=1;i<=4;i++)
    			{
    				LED_ON(i);
    				delay_ms(800);
    				LED_OFF(i);
    				delay_ms(800);
    			}
    			
    		};
    		break;
    		default:{
    			for(i=1;i<=4;i++)
    			{
    				LED_ON(i);
    				delay_ms(1000);
    				LED_OFF(i);
    				delay_ms(1000);
    				Beep_Two_DiDi();
    			}
    			
    		}
    		
    	}
    }
    
    

    解决问题来了,问题出在延时函数中,其实在中断中最好不要用延时函数,用延时函数容易导致程序跑飞。在本次历程中,进入中断的delay_ms(20);延时还是滴答时钟的延时,虽然精确,但是精确的实验现象无法出来也不行啊,所以解决方案是去掉延时程序,或者换成普通的的循环,最好不用循环。通过本例也说明了在中断可不能乱用延时啊,能不用延时就不用延时。一个小小的破程序,调了我一天,可真是太难受。
    对于题目一的实现代码如下所示,每按一次,时间标志位加一,当大于等于5时候清零,主要最好不要包含任何延时,简单的修改标志位就行:

    /*	@函数名称 :中断服务子程序0
    	@函数功能 :响应中断0的子程序
    	@输入参数 :无
        @返回值   :无
    	@注意     :中断中不要放延时过长的程序,也不要写死循环
    */
    void EXTI0_IRQHandler(void)
    {
    	if(EXTI_GetFlagStatus(EXTI_Line0)==SET)		//标志位是否使能
    	{
    		if(time_flag==5)		//200*5=1s
    		{
    			time_flag=0;
    		}
    		else 
    			time_flag++;
    		
    	}
    	EXTI_ClearITPendingBit(EXTI_Line0);
    }
    

    注意:time_flag是全局变量,并且在h文件中用“extern”修饰,便可在外部调用了

    然后实现函数如下:

    /*	@函数名称:void LED_Base_200ms(u16 t)
    	@函数功能:以200ms为基准时间进行延时
    	@输入参数:time延时基准
    	@返回值	 :无
    */
    void LED_Base_200ms(u16 time)
    {
    	u16 temp_time=(time+1)*200;
    	u8 i=1;
    	for(;i<=4;i++)
    	{
    		LED_ON(i);
    		delay_ms(temp_time);
    		LED_OFF(i);
    		delay_ms(temp_time);
    	}
    }
    

    然后在主函数中调用LED_Base_200ms即可

    	LED_Base_200ms(time_flag);
    

    对于功能二实现,还是上面的那个中断程序,但是考虑到,由于用按键触发中断,因此还是会存在一定的抖动,我们就使用非滴答时钟写的延时程序消抖,就普通用循环来消抖,注意最好不用滴答时钟写的延时程序,具体原因由于学的不精,不能解释,如果有大佬可以解释一下,感激不尽。

    /*	@函数名称 :中断服务子程序0
    		@函数功能 :响应中断0的子程序
    		@输入参数 :无
    		@返回值   :无
    		@注意     :中断中不要放延时过长的程序,也不要写死循环
    */
    void EXTI0_IRQHandler(void)
    {
    	delay_non_ms(10);
    	if(EXTI_GetFlagStatus(EXTI_Line0)==SET)		//标志位是否使能
    	{	
    		if(time_flag==5)		//200*5=1s
    		{
    			time_flag=0;
    		}
    		else 
    			time_flag++;
    		
    	}
    	EXTI_ClearITPendingBit(EXTI_Line0);
    

    然后还是通过标志位来调用灯的亮灭,具体函数如下所示

    /*	@函数名称:中断标志控制函数
    		@函数功能:根据中断的标志来执行响应的功能
    		@输入参数:无
    		@返回值	 :无
    */
    void flag_Control(void)
    {
    	switch(time_flag)
    	{
    		case 1 : {LED_Close_All(); LED_ON(1);}break;
    		case 2 : {LED_Close_All(); LED_ON(2);}break;
    		case 3 : {LED_Close_All(); LED_ON(3);}break;
    		case 4 : {LED_Close_All(); LED_ON(4);}break;
    		default: LED_Close_All();
    	}
    }
    

    然后在主函数中调用flag_Cont();即可实现

    对于功能3的实现,就更容易了,首先定义一个模式选择器,全局外部变量哦;然后中断函数是这样写的

    /*	@函数名称:中断服务子程序2
    		@函数功能:响应中断2的子程序
    		@输入参数:无
    		@返回值	 :无
    		@注意		 :中断中不要放延时过长的程序,也不要写死循环
    */
    void EXTI2_IRQHandler(void)
    {
    	delay_non_ms(10);
    	if(EXTI_GetFlagStatus(EXTI_Line2)==SET)
    	{
    		if(mode==1)
    		{
    			mode=0;
    		}
    		else
    			mode++;
    	}
    	EXTI_ClearITPendingBit(EXTI_Line2);
    	
    }
    

    然后模式选择器函数是这样写的

    /*	@函数名称:void Select_Mode(void)
    		@函数功能:模式选择器
    		@输入参数:无
    		@返回值	 :无
    		@注意		 :
    */
    void Select_Mode(void)
    {
    	 switch(mode+1)
    	 {
    		 case 1 : LED_Base_200ms(time_flag);break;
    		 default: flag_Control();
    	 }
    }
    
    

    后面就可在主程序中调用flag_Control();

  • 相关阅读:
    Elasticsearch 支持拼音自动补全
    laravel自动补全链接
    laravel的服务容器(药箱)、服务提供者(小盒子)、Facades(更方便用药),方便大家透彻理解
    php static静态属性和静态方法
    php面向对象的构造方法与析构方法
    MySQL事务-ROLLBACK,COMMIT用法详解
    php 事务处理transaction
    Python:初步学习Python
    iOS:自己写的一个星级评价的小Demo
    iOS:枚举enum的使用
  • 原文地址:https://www.cnblogs.com/liyingji/p/14241900.html
Copyright © 2020-2023  润新知