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

发布者:Lianai最新更新时间:2016-05-12 来源: eefocus关键字:调试  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  STM32  外部中断 引用地址:调试 ARM STM32 外部中断 遇到的一个问题

上一篇:STM32 外部中断 易出错总结
下一篇:ARM中外部中断的配置流程供参考

推荐阅读最新更新时间:2024-03-16 14:53

STM32串口发送字符串函数
最近由于要调试一个SMS发送短信的模块,该模块需要发送一系列AT指令,且需要字符串发送,但是STM32官方给的usart.c中并没有直接发送字符串的函数,因此写了一个发送字符串的函数。 其实发送字符串的本质还是发送一个个字符,所以只需在字符串结束标志之前,循环发送字符即可。不罗嗦,上程序。 //程序功能:利用串口发送一个字符串 // 参数:USARTx USART编号 可取 USART1、USART2、USART3、USART4、 USART5(STM32F103ZET6) str
[单片机]
第1天-ARM汇编指令ADD/SUB/MUL
ADD : 加法 (Addition) ADD{条件}{S} , , dest = op_1 + op_2 ADD 将把两个操作数加起来,把结果放置到目的寄存器中。操作数 1 是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即值: ADD R0, R1, R2 ; R0 = R1 + R2 ADD R0, R1, #256 ; R0 = R1 + 256 ADD R0, R2, R3,LSL#1 ; R0 = R2 + (R3 1) 加法可以在有符号和无符号数上进行。 ps:带进位的加法ADC SUB : 减法 (Subtraction) SUB{条件}{S} , , dest = op_1
[单片机]
ARM简单启动代码及中断处理分析
前言:本篇分析的是一个最精简的启动代码,并且包含一个简单的中断处理,C程序部分省略,重点分析汇编部分,这是因为对于我来说,汇编代码实在是让人厌烦,但是又不能不用。 下面是对代码的分析,红色部分是分析结果 .extern main //.extern 表示main在另外的文件中定义,在这里要引用,至于为什么只声明了extern而没 //有声明别的,目前还不知道,不过都声明一下,应该没问题 .text //.text 表示后面的内容编译出来放在代码段 .global _start //.gl
[单片机]
【话说定时器系列】之二:STM32常规定时器时基与时钟源
上节提到常规定时器包括:基本定时器、通用定时器和高级定时器。 基本定时器 基本定时器 :没有任何对外输入/输出,主要用作时基计数、定时。 通用定时器 通用定时器 :除了基本定时器的时基功能外,还可对外做输入捕捉、输出比较以及连接其它传感器接口【编码器和霍尔传感器】。 高级定时器 高级定时器: 此类定时器的功能最为强大,除了具备通用定时器的功能外,还包含一些与电机控制和数字电源应用相关的功能,比方带死区控制的互补信号输出、紧急刹车关断输入。 了解STM32定时器 从功能模块整体了解STM32定时器 从寄存器特色了解STM32定时器 定时器中的PS
[单片机]
聚首泉城,广开言路,赋能AI发展
经过60余年的发展,AI在算法、算力和算料(数据)“三算”方面取得了重要突破,处于从“不能用”到“可以用”的技术拐点,但是距离“很好用”还有诸多瓶颈。而这期间也会催生新技术、新产品、新产业、新模式,引发经济结构重大变革,对未来企业的发展带来机遇与挑战。2019中德中小企业合作交流大会·AI分论坛在泉城济南成功举办。本次论坛由济南高新技术产业开发区管委会 、济南市科学技术局、山东国惠安创智能物联发展有限公司主办,安创加速器(Arm Accelerator)、济南高新区齐鲁软件园发展中心承办。 期间在AI分论坛上,聚焦于人工智能和物联网产业的创新创业服务平台“安创加速器”举行揭牌仪式。济南高新区管委会常务副主任寇梅、济南市科学技术
[物联网]
聚首泉城,广开言路,赋能AI发展
STM32开发笔记54:STM32F4+DP83848以太网通信指南系列(八)
本章为系列指南的第八章,讲述如何使用STM32F407芯片配合DP83848进行以太网数据的收包流程,将监听到的网络包数据通过UART传给PC,同时辅以WireShark监听对比验证。 关于UART,也就是串口通信的使用,这里不做赘述,我们这里预设两个函数分别为UART6Init()和UART6Send(),实现的功能是串口6的初始化和发送。 以太网中断 在《STM32F4+DP83848以太网通信指南第五章:MAC+DMA配置》中,我们已经添加了以太网中断,其思路就是想让每次以太网上有收到包都能触发中断,我们可以在中断中将DMA中的数据包取出来进行分析,然后复位,让芯片下一次继续响应中断。 配置中断的代码非常简单,跟其他任何中
[单片机]
<font color='red'>STM32</font>开发笔记54:STM32F4+DP83848以太网通信指南系列(八)
STM32学习笔记:读写内部Flash
首先我们需要了解一个内存映射: stm32的flash地址起始于0x0800 0000,结束地址是0x0800 0000加上芯片实际的flash大小,不同的芯片flash大小不同。 RAM起始地址是0x2000 0000,结束地址是0x2000 0000加上芯片的RAM大小。不同的芯片RAM也不同。 Flash中的内容一般用来存储代码和一些定义为const的数据,断电不丢失, RAM可以理解为内存,用来存储代码运行时的数据,变量等等。掉电数据丢失。 STM32将外设等都映射为地址的形式,对地址的操作就是对外设的操作。 stm32的外设地址从0x4000 0000开始,可以看到在库文件中,是通过基于0x4000 0000地址的偏
[单片机]
<font color='red'>STM32</font>学习笔记:读写内部Flash
Dialog将ARM处理器集成到下一代智能手机的电源管理芯片之中
德国Kirchheim/Teck和中国上海, 2012年3月21日 — 高集成度和创新的电源管理、音频和近距离无线技术解决方案提供商Dialog 半导体有限公司(法兰克福股票交易所代码:DLG)日前宣布:该公司已获得授权将ARM® Cortex™-M0处理器用于其未来的电源管理芯片(PMIC)之中。 这是首次将一个标准32位处理器集成到一款混合信号PMIC之中。这种结合提供了卓越的数字处理性能,使Dialog系统级的PMIC为便携设备提供了广泛的功率控制和精确的电池管理功能。这些PMIC主要面向基于高端多核应用处理器的平台,可提供行业领先的性能与效率,以及显著延长的电池寿命。 Dialog首席执行官Jalal Bagher
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved