最近在自学STM32,写了一个按键中断程序,但是中断总是不响应...望大虾赐教,谢谢....程序如下

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最近在自学STM32,写了一个按键中断程序,但是中断总是不响应...望大虾赐教,谢谢....程序如下相关的知识,希望对你有一定的参考价值。

#include <stm32f10x.h>
#include "stm32f10x_exti.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "misc.h"
#include "Delay_nms.h"

void RCC_Configuration(void)

SystemInit();



void EXTI_Configuration(void)

EXTI_InitTypeDef EXTI_InitStructure;

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);

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



void NVIC_Configuration(void)

NVIC_InitTypeDef NVIC_InitStructure;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);



void GPIO_Configuration(void)

GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOE |
RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);

/*GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);*/

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD,&GPIO_InitStructure);

GPIO_ResetBits(GPIOC, GPIO_Pin_6 | GPIO_Pin_7);
GPIO_ResetBits(GPIOD, GPIO_Pin_6 | GPIO_Pin_13);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStructure);


int main(void)

RCC_Configuration();
SysTick_Configuration();
EXTI_Configuration();
NVIC_Configuration();

GPIO_Configuration();

while(1);



void EXTI3_IRQHandler(void)

if (GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == 0)

Delay(5);
if (GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == 0)

GPIO_SetBits(GPIOC,GPIO_Pin_6);
GPIO_SetBits(GPIOD,GPIO_Pin_6);

else

GPIO_ResetBits(GPIOC,GPIO_Pin_6);
GPIO_ResetBits(GPIOD,GPIO_Pin_6);





//EXTI_ClearITPendingBit(EXTI_Line3);

EXTI_ClearFlag(EXTI_Line3);



#ifdef USE_FULL_ASSERT

void assert_failed(uint8_t* file,uint32_t line)

while(1)




#endif

有人说是因为没开中断的原因,但是我查了很多资料 也不知道中断是在哪开的?还望各位大虾赐教 谢谢

试了一下这个程序。。可以进中断的。。
如果你进不了中断可能是硬件连接的问题吧。。把PE3的GPIO_Mode改成IPU或者IPD再试试追问

恩 好的 谢谢 我再试试

刚试了下 还是不行....不过 如果把中断服务函数 EXTI3_IRQHandler(); 当成一般的子函数,然后再主函数main前添加函数声明 void EXTI3_IRQHandler(void); 最后 把中断服务函数写入主函数中的 while(1) EXTI3_IRQHandler();
这样确实能达到效果 但是 却失去了中断的意义了....

在你那能实现中断吧 那或许真的是我这硬件连接的问题?

追答

EXTI3_IRQHandler();不需要单独进行声明的,在芯片相应的startup.s文件里已经包括了中断向量表,在中端产生的时候可以直接进入中断处理程序,另外EXTI3_IRQHandler()一般是放在stm32f10x_it.h里声明,stm32f10x_it.c里面写具体内容的,另外可以在void EXTI3_IRQHandler(void)后面加个中断点debug一下,上面的程序我注释掉了delay的几个调用之后仿真运行可以进中断的…………

追问

哦 好的 我也这样改试试

参考技术A 大哥,我不知道我说的对不对,按键中断就是循环时候你扫描到按键的电平是高电平换低电平的时候,触发时间,还有就是,像时间,还有串口那样,配置寄存器时候进入中断,这个就要查单片机手册了。希望对你有帮助。。对了,还有一点,做这个,一定要下载那个单片机的手册。有的里边有例子程序的。。。

大哥刚没看你程序,我又看了一眼,你程序写的挺有意思,建议你下载几个短的例子看看,应该有,比如基于STM32的温湿度传感器程序怎么写的,有可能能在里边找到你想要的代码,这个单片机我没用过,呵呵,我只写过PIC,STC,51,ARM的。希望对你有帮助,没帮助别骂我就可以了追问

谢谢回复 呵呵 我是新手 自学的 我这个程序 就是参考例程写的 你说我的程序写的有意思?请问是哪写的不好嘛?如果是 我下回注意 呵呵 还望赐教....

追答

while(1);
这个有意思,你懂的呵呵。你的是不是所有任务都是中断触发哈?我也就顺着看一眼,好像是这个意思

追问

在这个程序中 是这样的 因为这也就是一个单任务程序 复杂的还没涉及到呢

STM32基本GPIO操作:按键输入(扫描+外部中断)

(涉及专有名词较多,难免解释不到位,若有错误还请指出,谢谢!)

硬件连接图如下:
技术图片

技术图片

一、扫描

思路是在main函数中通过死循环来扫描端口电平状态检测,以此判断按键是否按下。实现较为简单。

1.初始化(注意C语言中变量声明需放在函数开头)

以下是初始化PB5端口(LED灯)的代码,每一条语句的含义在我另一篇博客里

GPIO_InitTypeDef GPIO_Init1;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_Init1.GPIO_Pin = GPIO_Pin_5;
GPIO_Init1.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_SetBits(GPIOB, GPIO_Pin_5);                       //先熄灯

GPIO_Init(GPIOB, &GPIO_Init1);

以下是初始化PE3端口(按键)的代码

GPIO_InitTypeDef GPIO_Init2;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);

GPIO_Init2.GPIO_Pin = GPIO_Pin_3;           // 设置GPIO端口号为5
GPIO_Init2.GPIO_Mode = GPIO_Mode_IPU;       // 设置端口模式为输入上拉
// 设置为输入端口时不需要指定GPIO_Speed参数

GPIO_Init(GPIOE, &GPIO_Init2);

输入上拉与输入下拉的区别:
输入上拉(GPIO_Mode_IPU):端口与VCC通过一个电阻串连,因此没有输入或输入高电平时端口为高电平,输入低电平时端口为低电平
输入下拉(GPIO_Mode_IPD):端口与GND通过一个电阻串连,因此没有输入或输入低电平时端口为低电平,输入高电平时端口为高电平

从硬件图上得知按键与GND相连,如果端口设置为输入上拉,那么松开按键时端口为高电平,按下按键时端口为低电平,可以区分两种状态
如果端口设置为输入下拉,那么无论是按下还是松开按键时端口总为低电平,无法区分两种状态
类似地,如果按键与VCC相连,则端口需要设置为输入下拉才能区分按下/松开两种状态

2.扫描

读取PE3端口的状态:

GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)

返回值为SET则端口为高电位,返回值为RESET则端口为低电位

在main函数中放入以下死循环代码以实现扫描PE3端口并点灯的功能

while (1)
{
    if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==SET) // 如果按键对应端口为高电平
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_5);             // 熄灯(LED负极连接PB5,LED正极连接VCC,PB5高电平熄灯)
    }
    else                                             // 否则
    {
        GPIO_ResetBits(GPIOB, GPIO_Pin_5);           // 亮灯
    }
    delay_ms(10);                                    // 一些开发板例程当中提供了delay函数,需要通过delay_init()初始化后才可使用
                                                     // 若无现成delay函数,可通过一定次数的for循环来代替
}

3.例程

代码如下:

int main(void)
{
    GPIO_InitTypeDef GPIO_Init1, GPIO_Init2;
    delay_init();

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_Init1.GPIO_Pin = GPIO_Pin_5;
    GPIO_Init1.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init1.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_Init1);
    GPIO_SetBits(GPIOB, GPIO_Pin_5);                       //先熄灯

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
    GPIO_Init2.GPIO_Pin = GPIO_Pin_3;
    GPIO_Init2.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOE, &GPIO_Init2);
    
    delay_ms(200);

    while (1)
    {
        if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==SET)
        {
            GPIO_SetBits(GPIOB, GPIO_Pin_5);
        }
        else
        {
            GPIO_ResetBits(GPIOB, GPIO_Pin_5);
        }
        delay_ms(10);
    }
}

二、中断

0.相关概念

中断:程序运行过程中,系统外部、系统内部或者现行程序本身若出现紧急事件,处理机立即中止现行程序的运行,自动转入相应的处理程序(中断服务程序),待处理完后,再返回原来的程序运行
简而言之就是触发某一事件可以使得MCU跳转执行该事件的处理程序,而按键按下或放开(GPIO口电平改变)则可作为一个外部中断,通过编写这一事件的处理程序从而达到改变灯亮灭状态的目的
(这里提到的“事件”并不是STM32当中的专有名词“事件”,而是泛指发生了某一件事)

使用扫描方式获得按键输入的思路如下:

主函数()
{
    初始化()
    死循环
    {
        如果(按键按下)
        {……}
        否则
        {……}
    }
}

而使用中断获得按键输入的思路如下:

主函数()
{
    初始化()
    其它操作()
}

中断处理函数()
{
    如果(按键按下)
    {……}
    否则
    {……}
}

对比可知使用扫描方式将使得芯片无法(难以)处理其它事务

NVIC:全名为“内嵌向量中断控制器”,主要用来控制芯片中各个中断的优先级,在很多地方都会使用(串口通信、SPI通信、定时器、I?C通信等涉及到实时处理的功能都会与中断有关)

EXTI(不是EXIT):全名为“外部中断/事件控制器”,可以实现输入信号的上升沿检测和下降沿的检测

1.初始化(注意C语言中变量声明需放在函数开头)

1.1 NVIC

需要用到的初始化语句如下:

NVIC_InitTypeDef NVIC_I;                           //定义初始化结构体

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //设置整个系统的中断优先级分组

NVIC_I.NVIC_IRQChannel=EXTI3_IRQn;                 //设置初始化哪个中断
NVIC_I.NVIC_IRQChannelPreemptionPriority=0x02;     //设置中断抢占优先级
NVIC_I.NVIC_IRQChannelSubPriority=0x02;            //设置中断响应优先级(子优先级)
NVIC_I.NVIC_IRQChannelCmd=ENABLE;                  //中断使能(启动)

NVIC_Init(&NVIC_I);                                //初始化

中断优先级分组、抢占优先级和子优先级的关系:
STM32系列的芯片当中一般会有很多的中断,而当多个中断同时发生时就需要一个调度机制来控制它们的执行顺序,因此有了中断的优先级的概念。优先级遵循以下几点:
1.优先级的数字越小优先级越高
2.抢占优先级高的中断会先执行,它也可以打断抢占优先级低的中断
3.当抢占优先级相同时,响应优先级高的中断会先执行,但它不可以打断响应优先级低的中断
4.当两个中断的抢占优先级和响应优先级都相同时,先产生的中断先执行(按照时间顺序)

举个例子,现在有三个中断:
中断1:抢占优先级为2,响应优先级为1
中断2:抢占优先级为3,响应优先级为0
中断3:抢占优先级为2,响应优先级为0
则3个中断的优先级顺序是中断3>中断1>中断2,同时中断1、3可以打断中断2,中断3不能打断中断1

而两类优先级可以设置成哪些值呢?这取决于整个系统的中断优先级分组。通过

NVIC_PriorityGroupConfig();

可以设置整个系统的中断优先级分组,其参数可以是NVIC_PriorityGroup_0、NVIC_PriorityGroup_1、NVIC_PriorityGroup_2、NVIC_PriorityGroup_3、NVIC_PriorityGroup_4之一。具体关系如下:
NVIC_PriorityGroup_0:0位抢占优先级(无效)+4位响应优先级(0~15)
NVIC_PriorityGroup_1:1位抢占优先级(0~1)+3位响应优先级(0~7)
NVIC_PriorityGroup_2:2位抢占优先级(0~3)+2位响应优先级(0~3)
NVIC_PriorityGroup_3:3位抢占优先级(0~7)+1位响应优先级(0~1)
NVIC_PriorityGroup_4:4位抢占优先级(0~15)+0位响应优先级(无效)

例如中断分组设置为3,则所有中断的抢占优先级可以被设置为0~7,响应优先级可以被设置为0、1
需要注意的是如果程序中用到了二次封装的一些库(比如开发板例程中厂商为初学者写的串口库等),则NVIC_PriorityGroupConfig()可能已经被调用过了,此时再次修改可能会带来其它问题

1.2 EXTI

需要用到的初始化语句如下:

EXTI_InitTypeDef EXTI_I;                           //定义初始化结构体

EXTI_I.EXTI_Line=EXTI_Line3;                       //设置初始化哪条中断/事件线
EXTI_I.EXTI_Mode = EXTI_Mode_Interrupt;            //设置为产生中断(EXTI_Mode_Event为产生事件)
EXTI_I.EXTI_Trigger = EXTI_Trigger_Falling;        //设置为下降沿触发
EXTI_I.EXTI_LineCmd = ENABLE;                      //使能

EXTI_Init(&EXTI_I);                                //初始化

中断/事件的区别:
中断产生后经由NVIC交给MCU进行处理(软件层面)
事件最终作为一个脉冲信号直接触发其它硬件(硬件层面)
附一张EXTI的框图便于理解,蓝色是中断,红色是事件
技术图片

EXTI、NVIC与GPIO的对应关系:
如图所示
技术图片

上升/下降沿:
低电平跳到高电平为上升沿,高电平跳到低电平为下降沿

1.3 GPIO

与扫描方式的初始化代码相同

GPIO_InitTypeDef GPIO_Init1, GPIO_Init2;

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_Init1.GPIO_Pin = GPIO_Pin_5;
GPIO_Init1.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_Init1);
GPIO_SetBits(GPIOB, GPIO_Pin_5);                       //先熄灯

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
GPIO_Init2.GPIO_Pin = GPIO_Pin_3;
GPIO_Init2.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOE, &GPIO_Init2);
1.4 其它

目前不明确这两条语句的作用

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);          //开启端口复用,涉及到GPIO口做外部中断时都需要这一条语句

2.中断处理函数

函数名与中断/事件线有着对应关系,可参照上一张表

void EXTI3_IRQHandler(void)
{
    if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==SET)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_5);
    }
    else
    {
        GPIO_ResetBits(GPIOB, GPIO_Pin_5);
    }
    EXTI_ClearITPendingBit(EXTI_Line3);
}

最后的EXTI_ClearITPendingBit()用于清除中断标志位,避免对之后的中断造成影响

3.例程

代码如下:

int main(void)
{
    GPIO_InitTypeDef GPIO_Init1, GPIO_Init2;
    NVIC_InitTypeDef NVIC_I;
    EXTI_InitTypeDef EXTI_I;

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    
    NVIC_I.NVIC_IRQChannel=EXTI3_IRQn;
    NVIC_I.NVIC_IRQChannelPreemptionPriority=0x02;
    NVIC_I.NVIC_IRQChannelSubPriority=0x02;
    NVIC_I.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&NVIC_I);

    EXTI_I.EXTI_Line=EXTI_Line3;
    EXTI_I.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_I.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_I.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_I);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_Init1.GPIO_Pin = GPIO_Pin_5;
    GPIO_Init1.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init1.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_Init1);
    GPIO_SetBits(GPIOB, GPIO_Pin_5);                       //先熄灯

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
    GPIO_Init2.GPIO_Pin = GPIO_Pin_3;
    GPIO_Init2.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOE, &GPIO_Init2);

}

void EXTI3_IRQHandler(void)
{
    if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==SET)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_5);
    }
    else
    {
        GPIO_ResetBits(GPIOB, GPIO_Pin_5);
    }
    EXTI_ClearITPendingBit(EXTI_Line3);
}

2019.12.22

以上是关于最近在自学STM32,写了一个按键中断程序,但是中断总是不响应...望大虾赐教,谢谢....程序如下的主要内容,如果未能解决你的问题,请参考以下文章

stm32外部按键中断无法退出中断问题

stm32-浅谈中断

基于STM32入门2——按键点灯程序

对stm32中断的理解

STM32能通过按键中断实现连续按键吗

基于STM32F103入门2——按键点灯