分享

STM32中断配置

 心不留意外尘 2016-05-07

http://blog.sina.com.cn/s/blog_60cf05130100wrnb.html

2011

今天用按键中断来控制LED灯闪烁。当时有时候经常会出现自己设置的LED闪烁是4下,但是结果却是双倍次数,8下。很不解。源代码如下。

Test3_Led1(void)
{
 int K;
 for(K=0;K<4;K++)
  {
  GPIO_SetBits(GPIOF,GPIO_Pin_6);
  Mydelay_ms(1000);
  GPIO_ResetBits(GPIOF, GPIO_Pin_6);
  Mydelay_ms(1000);
 }

经过研究,发现应该是按键中断没做好,每次都产生2次的中断。这是为什么?

中断服务函数如下:

 void EXTI0_IRQHandler(void)
  {
  //if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
     if (EXTI_GetITStatus( EXTI_Line0)!= RESET)
      {

       EXTI_ClearITPendingBit(EXTI_Line0);  //0
   Led1_Flag++;//做测试用,发现每次都加了2.
   Test3_Led1();
   //EXTI_ClearITPendingBit(EXTI_Line0); //最好放在后面以免造成一清0后,因为按键延时又变1。
      }
  }

于是我继续研究。发现原来是这样的。

每次按键按下是一定有抖动的,抖动应该会有很多下,5下以上是很正常的。按理论上将每次抖动都会长生一个脉冲,也就是一个中断。应该会有不下于5次中断,为什么只响应2次呢。

其实这个很简单。因为相同管脚每次中断都是相同优先级的,所以即使产生中断也不会响应。只有在第一个中断函数结束之后再来中断才会响应。虽然不响应,但是却会改变中断标志位。一定还有有人问,不是服务函数中把标志位清0了吗?但是由于单片机执行代码速度很快,你请0之后,抖动如果还在那么就还会产生中断号(虽然不响应)。只有在第一次中断结束之后,再次去查看中断号的时候才响应。(注:响应中断首先是直接给总线发命令,不需要单片机查询。因此采用中断比采用查询方式要好的多,减少了CPU的工作量。一旦中断标志位变1,而且没有优先级更高的中断在执行,那么就会响应中断服务函数)现在我们就是每次按键按下以后,虽然中断标志经过清0,但是由于抖动的存在,清0后很有可能再变1。当时不会立刻执行。只有当这个中断服务函数执行完毕才执行。所以每次都有可能调用2次中断服务函数。

那么我们应该怎么改变呢?

其实很简单,最好在中断服务函数开头和结尾都把中断标志位清0,那么即使第一次清0后再变1,但是第二次清0是一定会成功的。或者是一次清0,但是在清0之前先延时,确保已经没有抖动。

我是选用第一种方案。

代码如下:

 void EXTI0_IRQHandler(void)
  {
  //if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
     if (EXTI_GetITStatus( EXTI_Line0)!= RESET)
      {

Mydelay_ms(1000);

       EXTI_ClearITPendingBit(EXTI_Line0);  //0
   Led1_Flag++;//做测试用,发现每次都加了2.
   Test3_Led1();
   //EXTI_ClearITPendingBit(EXTI_Line0); //最好放在后面以免造成一清0后,因为按键延时又变1。
      }
  }

这样即使有抖动每次也只会调用一次中断服务函数。

 

全部代码如下:


#include "stm32f10x.h"
//#include "stm32f10x_conf.h"
#include <stdio.h>



void delay(void)
{
  __IO uint32_t i = 0;
 
   for(i = 0xFF; i != 0; i--)
   {
   }
 
}
Mydelay_ms(u16 a)
{
 u16 i,j;
 for(j=0;j<a;j++)
 for(i=0;i<6000;i++); 
}

Test1_GPIO(void)
{
 GPIO_Write(GPIOC, 0xfc4f);
 Mydelay_ms(1);
 //delay();

 GPIO_Write(GPIOC, 0xfd8f);
 Mydelay_ms(1000);
 GPIO_Write(GPIOC, 0xfd0f);
 Mydelay_ms(1000);
 GPIO_Write(GPIOC, 0xfe0f);
 Mydelay_ms(1000);
}
Test2_GPIO(void)
{
 GPIO_SetBits(GPIOF,GPIO_Pin_6);
 Mydelay_ms(1000);
 GPIO_ResetBits(GPIOF, GPIO_Pin_6) ;
 Mydelay_ms(1000);
 

}
Test3_Led1(void)
{
 int K;
 for(K=0;K<4;K++)
  {
  GPIO_SetBits(GPIOF,GPIO_Pin_6);
  Mydelay_ms(1000);
  GPIO_ResetBits(GPIOF, GPIO_Pin_6);
  Mydelay_ms(1000);
 }
 

}
Test3_Led2(void)
 {
 int t;
 for(t=0;t<4;t++)
  {
  GPIO_SetBits(GPIOF,GPIO_Pin_7);
  Mydelay_ms(1000);
  GPIO_ResetBits(GPIOF, GPIO_Pin_7);
  Mydelay_ms(1000);
 }
 


  }
void GPIO_Configuration(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;
 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;
 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
 //GPIO_Init(GPIOA,&GPIO_InitStructure);
 //GPIO_Init(GPIOB,&GPIO_InitStructure);
 //GPIO_Init(GPIOC,&GPIO_InitStructure);
 //GPIO_Init(GPIOD,&GPIO_InitStructure);
 //GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
 GPIO_Init(GPIOF,&GPIO_InitStructure);
 
}







 
//定时函数有有问题



  void NVIC_Configuration(void)

{

NVIC_InitTypeDef NVIC_InitStructure;                   //中断管理恢复默认参数

#ifdef   VECT_TAB_RAM   //如果C/C++ Compiler\Preprocessor\Defined symbols中的定义了VECT_TAB_RAM(见程序库更改内容的表格)

NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //则在RAM调试

#else                                                                   //如果没有定义VECT_TAB_RAM

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);//则在Flash里调试

#endif                                                                   //结束判断语句

//以下为中断的开启过程,不是所有程序必须的。

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//设置NVIC优先级分组,方式。

//注:一共16个优先级,分为抢占式和响应式。两种优先级所占的数量由此代码确定,NVIC_PriorityGroup_x可以是0、1、2、3、4,分别代表抢占优先级有1、2、4、8、16个和响应优先级有16、8、4、2、1个。规定两种优先级的数量后,所有的中断级别必须在其中选择,抢占级别高的会打断其他中断优先执行,而响应级别高的会在其他中断执行完优先执行。

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//中断通道名; //开中断,中断名称见函数库

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;         //响应优先级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启动此通道的中断

NVIC_Init(&NVIC_InitStructure);                                     //中断初始化
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;//中断通道名; //开中断,中断名称见函数库

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         //响应优先级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启动此通道的中断

NVIC_Init(&NVIC_InitStructure); 
}



void RCC_Configuration(void)
{
ErrorStatus HSEStartUpStatus;


// RCC_DeInit();


RCC_HSEConfig(RCC_HSE_ON);


HSEStartUpStatus = RCC_WaitForHSEStartUp();

if(HSEStartUpStatus == SUCCESS)
{
   
    RCC_HCLKConfig(RCC_SYSCLK_Div1);

   
    RCC_PCLK2Config(RCC_HCLK_Div1);

   
    RCC_PCLK1Config(RCC_HCLK_Div2);

   
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

   
    FLASH_SetLatency(FLASH_Latency_2);

   
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

   
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);//Pll在最后设置

   
    RCC_PLLCmd(ENABLE);

   
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

   
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

   
    while(RCC_GetSYSCLKSource() != 0x08)
    {
    }
}
////注:AHB主要负责外部存储器时钟。APB2负责AD,I/O,高级TIM,串口1。APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM?

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC
         | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE| RCC_APB2Periph_GPIOF| RCC_APB2Periph_AFIO, ENABLE);


RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);


//RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE);
}



void GPIO_Configuration1(void)

{

GPIO_InitTypeDef GPIO_InitStructure;                      //GPIO状态恢复默认参数

//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_标号 | GPIO_Pin_标号 ;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输出速度2MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;

GPIO_Init(GPIOF, &GPIO_InitStructure); //C组GPIO初始化

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);

}

 


void EXTI_Configuration(void)

{
EXTI_InitTypeDef EXTI_InitStructure;

EXTI_ClearITPendingBit(EXTI_Line0);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource8);
//GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5);
//GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource6);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;  


EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 
EXTI_InitStructure.EXTI_LineCmd = ENABLE;    
EXTI_Init(&EXTI_InitStructure);     
EXTI_InitStructure.EXTI_Line = EXTI_Line8;
EXTI_Init(&EXTI_InitStructure); 

}


                                                                                                                            


//下面这段是标准,一定要先配置中断线,也就是管教选择,才能进行线路选择。




      

 

 

u16 Led1_Flag=0,Led2_Flag=0;
void EXTI0_IRQHandler(void);
int main(void)
{
  
 
   SystemInit();
   RCC_Configuration();
   GPIO_Configuration1();
 EXTI_Configuration();
 NVIC_Configuration();  

         
 
 
 
 

 

 

 
GPIO_SetBits(GPIOF,GPIO_Pin_6);
  GPIO_SetBits(GPIOF,GPIO_Pin_7);
  while (1)
  {
   //Mydelay_ms(1000);
  //if(Led_Flag==1)
  //{
  //Mydelay_ms(1000);
 
//} 
  //GPIO_ResetBits(GPIOF,GPIO_Pin_6);
 
  //Test2_GPIO();
 
  }
}

 void EXTI0_IRQHandler(void)
  {
  //if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
     if (EXTI_GetITStatus( EXTI_Line0)!= RESET)
      {

       EXTI_ClearITPendingBit(EXTI_Line0);  //0
   Led1_Flag++;
  
   Test3_Led1();
   EXTI_ClearITPendingBit(EXTI_Line0); //最好放在后面以免造成一清0后,因为按键延时又变1。
    
      }
    
   
  }
  void EXTI9_5_IRQHandler(void)
  {
  //if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
     if (EXTI_GetITStatus( EXTI_Line8)!= RESET)
      {

       EXTI_ClearITPendingBit(EXTI_Line8);  //0
   Led2_Flag++;
  
   Test3_Led2();
   EXTI_ClearITPendingBit(EXTI_Line8); //最好放在后面以免造成一清0后,因为按键延时又变1。
    
      }
    
   
  }

//#ifdef  USE_FULL_ASSERT


//void assert_failed(uint8_t* file, uint32_t line)
//{
 

 
 // while (1)
  //{
 // }
//}
//#endif



 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多