调试 ARM STM32 外部中断 遇到的一个问题

问题背景 : STM32f103zet6的PB9和PE0脚分别外接一个按键,希望通过这两个按键可以产生外部中断,点亮该按键对应的LED。使用EXTI[9:5]通道。

首先配置RCC:
void RCC_Configuration()
{
    ErrorStatus HSEStartUpStatus;
    RCC_DeInit(); //将外设RCC寄存器设为缺省值
    RCC_HSEConfig(RCC_HSE_ON); //使能HSE
    HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待HSE就绪
    if(HSEStartUpStatus == SUCCESS) //判断HSE是否起振成功
    {                                 
        RCC_HCLKConfig(RCC_SYSCLK_Div1);  //设置AHB时钟(HCLK) = 系统时钟
        RCC_PCLK2Config(RCC_HCLK_Div2);  //设置高速APB2时钟(PCLK2)= 系统时钟 
        RCC_PCLK1Config(RCC_HCLK_Div2);  //设置低速APB1时钟(PCLK1)= 系统时钟/2 

        /*****保证在将PLL切换时钟源之前使能Flash预取缓存*****/
        FLASH_SetLatency(FLASH_Latency_2); //设置等待时间 = 2个周期
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);//使能预取指缓存     
  
                    /*
                    这里插入设置需要开启的外设的时钟,比如
                    RCC_ADCCLKConfig(RCC_PCLK2_Div4); //设置ADC时钟=PCLK/4 =9MHz
                    */

        //设置PLL的输入时钟 = HSE时钟频率;倍频系数 = PLL输入时钟×9
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); 
        RCC_PLLCmd(ENABLE); //使能PLL

        //检查指定的RCC标志位(PLL就绪位)设置与否,若为否, 则等待
        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {/*NULL*/ }

        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //设置系统时钟(SYSCLK)为PLL

        //0x00:HSI作为系统时钟; 0x04:HSE作为系统时钟;0x08:PLL作为系统时钟
        while(RCC_GetSYSCLKSource() != 0x08) //等待直到PLL作为系统时钟源
        {/*NULL*/ }
          }

          /*****这里根据具体需要开启相应的时钟*****/
          // PB9: WORK PE0: ALARM
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  //开启功能复用I/O时钟
          
}

然后配置GPIO:
void GPIO_Configuration(void) //设置GPIO
{     
     GPIO_InitTypeDef GPIO_InitStructure;
               
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;            
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
     GPIO_Init(GPIOB, &GPIO_InitStructure);         

     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;            
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
     GPIO_Init(GPIOE, &GPIO_InitStructure);          
          
}
再然后配置NVIC:
void NVIC_Configuration(void)
{
          NVIC_InitTypeDef NVIC_InitStructure; //定义中断参数结构体变量
#ifdef  VECT_TAB_RAM  
          NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);//设置向量表的位置和偏移(0x20000000) 
#else  
          NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);//设置向量表的位置和偏移(0x20000000)   
#endif

          NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //选择使用优先级分组第0组       
        
    /*使能EXTI[9:5]通道,0级先占优先级,0级次占优先级*/
    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);     
} 
最后配置EXTI:
void EXTI_Configuration(void) //配置外部中断/事件控制器
{
    EXTI_InitTypeDef EXTI_InitStructure;

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource5|GPIO_PinSource6); 
    
    EXTI_InitStructure.EXTI_Line = EXTI_Line5|EXTI_Line6;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure); 
}
main函数如下:
int main(void)
{
     RCC_Configuration();
     GPIO_Configuration();
     NVIC_Configuration();
     EXTI_Configuration();
          
  while (1)
  { }
}
中断服务函数如下:
void EXTI9_5_IRQHandler(void)
{
     if (EXTI_GetITStatus(EXTI_Line5) != RESET)
     {
          if(led_bit3)
          {
               GPIO_SetBits(GPIOB,GPIO_Pin_9);
               led_bit3=0;
          }
          else
          {
               GPIO_ResetBits(GPIOB,GPIO_Pin_9);
               led_bit3=1;
          }
               EXTI_ClearFlag(EXTI_Line5);
     }
     
     if(EXTI_GetITStatus(EXTI_Line6) != RESET)
     {
          if(led_bit1)
          {
               GPIO_SetBits(GPIOE,GPIO_Pin_0);
               led_bit1=0;
          }
          else
          {
               GPIO_ResetBits(GPIOE,GPIO_Pin_0);
               led_bit1=1;
          }
          EXTI_ClearFlag(EXTI_Line6);
     }
}
问题描述:1. 按两个按键中的随便一个,都没反应,原因是没进入中断服务函数。
                2. 只使用其中一个按键产生中断,屏蔽另一个按键,结果正常,LED可以被点亮。
                也就是说,只开其中一个按键中断是可以的,同时打开两个按键中断,则不行。

问题解决:经过一定时间的纠结与不知所措,为何一个就可以,两个同时就不可以。看到EXTI_Configuration()中:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource5|GPIO_PinSource6); 
EXTI_InitStructure.EXTI_Line = EXTI_Line5|EXTI_Line6;
索性不使用那个或,每个都单独配置一下。于是乎: 
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource5); 
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource6); 
EXTI_InitStructure.EXTI_Line = EXTI_Line5;
EXTI_InitStructure.EXTI_Line = EXTI_Line6;
还是不行,经过若干次的组合尝试:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource5); 
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource6); 
EXTI_InitStructure.EXTI_Line = EXTI_Line6;
EXTI_InitStructure.EXTI_Line = EXTI_Line5;

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource6); 
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource5); 
EXTI_InitStructure.EXTI_Line = EXTI_Line5;
EXTI_InitStructure.EXTI_Line = EXTI_Line6;

...

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource5); 
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource6); 
EXTI_InitStructure.EXTI_Line = EXTI_Line5|EXTI_Line6;

发现只拆开GPIO_EXTILineConfig()中的或,即可。

问题分析:就这样,问题被瞎猫碰到死耗子地解决了。但是还是得找找为何这样可以。
找到GPIO_EXTILineConfig()函数的定义:
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
{
  uint32_t tmp = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource));
  assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));
  
  tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03));
  AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;
  AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)));
}
由于两种情况下的不同就在于GPIO_EXTILineConfig()函数的第二个参数。所以首先分析assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource)); 再找到IS_GPIO_PIN_SOURCE():
#define IS_GPIO_PIN_SOURCE(PINSOURCE) (((PINSOURCE) == GPIO_PinSource0) || \
                                       ((PINSOURCE) == GPIO_PinSource1) || \
                                       ((PINSOURCE) == GPIO_PinSource2) || \
                                       ((PINSOURCE) == GPIO_PinSource3) || \
                                       ((PINSOURCE) == GPIO_PinSource4) || \
                                       ((PINSOURCE) == GPIO_PinSource5) || \
                                       ((PINSOURCE) == GPIO_PinSource6) || \
                                       ((PINSOURCE) == GPIO_PinSource7) || \
                                       ((PINSOURCE) == GPIO_PinSource8) || \
                                       ((PINSOURCE) == GPIO_PinSource9) || \
                                       ((PINSOURCE) == GPIO_PinSource10) || \
                                       ((PINSOURCE) == GPIO_PinSource11) || \
                                       ((PINSOURCE) == GPIO_PinSource12) || \
                                       ((PINSOURCE) == GPIO_PinSource13) || \
                                       ((PINSOURCE) == GPIO_PinSource14) || \
                                       ((PINSOURCE) == GPIO_PinSource15))
其中:
#define GPIO_PinSource0            ((uint8_t)0x00)
#define GPIO_PinSource1            ((uint8_t)0x01)
#define GPIO_PinSource2            ((uint8_t)0x02)
#define GPIO_PinSource3            ((uint8_t)0x03)
#define GPIO_PinSource4            ((uint8_t)0x04)
#define GPIO_PinSource5            ((uint8_t)0x05)
#define GPIO_PinSource6            ((uint8_t)0x06)
#define GPIO_PinSource7            ((uint8_t)0x07)
#define GPIO_PinSource8            ((uint8_t)0x08)
#define GPIO_PinSource9            ((uint8_t)0x09)
#define GPIO_PinSource10           ((uint8_t)0x0A)
#define GPIO_PinSource11           ((uint8_t)0x0B)
#define GPIO_PinSource12           ((uint8_t)0x0C)
#define GPIO_PinSource13           ((uint8_t)0x0D)
#define GPIO_PinSource14           ((uint8_t)0x0E)
#define GPIO_PinSource15           ((uint8_t)0x0F)

这样,一切就明朗了。
GPIO_PinSource5|GPIO_PinSource6 等价于0x05|0x06,即0x07。而这个当然与我们要用的两个按键无关。

总结:GPIO_EXTILineConfig()的第二参数只能是GPIO_PinSource0~15中的一个,若要需要配置多个,只能单独配置,不能使用”或”。这种写法可能是受EXTI_InitStructure.EXTI_Line = EXTI_Line5|EXTI_Line6;这种写法的影响。











你可能感兴趣的:(ARM)