6、TM4C12G单片机中断方式实现按键检测

本文介绍TM4C12G单片机通过中断方式实现按键检测。先分析板子上按键原理图,指出传统循环读取IO状态检测按键的不足。接着解释单片机中断概念,最后详细阐述代码实现步骤,包括开启端口外设、配置输入、设置中断触发类型等,还附上测试源代码,功能为按键切换红蓝灯闪烁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TM4C12G单片机中断方式实现按键检测

1、首先看一下板子上按键的原理图

在这里插入图片描述
可以发现PF0和PF4两个引脚接了两个按键,当按键按下的时候就会接地,即变成低电平,前面讲GPIO作输入时是利用GPIOPinRead()函数来循环读取io状态判断按键是否被按下,但这样显然有一个问题,即CPU在这一时刻不能作其他事情,所以能不能通过一种办法使CPU做其他事情的同时还能检测按键,即今天我们要说的内容:外部中断。

2、中断

什么是单片机的中断?举一个例子,假如说玩游戏时,女朋友突然打电话过来,我们不得不停下游戏去接这个电话,然后接完电话继续进行我们的游戏。在这个过程中,电话铃声就可以理解为一个中断的触发,然后我们接了电话,即响应了这个中断,通话过程是中断程序做的事情,然后接完电话玩游戏就是单片机做完中断以后继续原来的工作。

那么在单片机的C语言代码是怎么实现的呢?

其实,我们会定义一个函数,但是这个函数和我们一般使用的函数不同,它是不需要被我们手动调用的,它的执行完全被单片机自动唤醒,唤醒的条件被用户定义。以今天要说的按键为例,即当检测到有低电平或下降沿时,单片机会停下当前正在进行的代码,转到我们定义的中断函数中,执行一系列操作。

3、代码实现

这里我们用按键1,即PF4。

(1)首先开启PORTF端口外设

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF))
{}

(2)配置PF0为上拉输入

GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_DIR_MODE_IN);
GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);//上拉

GPIO_PIN_TYPE_STD_WPU的意思是配置成上拉模式,即没有按键按下时高电平,有按键按下变为低电平。

(3)配置中断触发类型

GPIOIntTypeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_FALLING_EDGE);//下降沿

GPIO_FALLING_EDGE的意思是下降沿,这里也可以设置为GPIO_LOW_LEVEL,即低电平。按键按下这个事件,电平由高到底,所以下降沿或低电平作为中断触发类型都可以。

可以看下库函数中的其他参数:

//*****************************************************************************
//
// Values that can be passed to GPIOIntTypeSet as the ui32IntType parameter,
// and returned from GPIOIntTypeGet.
//
//*****************************************************************************
#define GPIO_FALLING_EDGE       0x00000000  // Interrupt on falling edge
#define GPIO_RISING_EDGE        0x00000004  // Interrupt on rising edge
#define GPIO_BOTH_EDGES         0x00000001  // Interrupt on both edges
#define GPIO_LOW_LEVEL          0x00000002  // Interrupt on low level
#define GPIO_HIGH_LEVEL         0x00000006  // Interrupt on high level
#define GPIO_DISCRETE_INT       0x00010000  // Interrupt for individual pins

从宏的名称便可以看出中断触发类型。

(4)设置中断处理程序的地址

GPIOIntRegister(GPIO_PORTF_BASE, io_interrupt);  

这里解释一下,io_interrupt是一个函数,在C语言里面函数名其实也是一个地址,将该中断处理程序的函数名作为参数传进这个GPIOIntRegister函数中,即告诉了单片机这个中断处理函数的存储地址,当有中断事件发生时便进行寻址,找到这个函数进行执行。

如果不用这个函数,我们也可以到中断向量表里手动进行注册,TM4C123G单片机的中断向量表位于startup_TM4C123.s文件。但是因为手动注册比较麻烦,所以推荐大家使用GPIOIntRegister函数。

(5)开启中断

GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4);
IntEnable(INT_GPIOF);
IntMasterEnable();

在单片机中,好多外设都有中断,为了有序的管理这些中断,便有了一些“开关”,通过代码控制各个部分中断的开启关闭。以这三行代码为例:

在这里插入图片描述

IntMasterEnable();开启了总开关

IntEnable(INT_GPIOF);开启了F端口的开关,类似的其他外设:

//*****************************************************************************
//
// The following are defines for the interrupt assignments.
//
//*****************************************************************************
#define INT_ADC0SS0             INT_CONCAT(INT_ADC0SS0_, INT_DEVICE_CLASS)
#define INT_AES0                INT_CONCAT(INT_AES0_, INT_DEVICE_CLASS)
#define INT_CAN0                INT_CONCAT(INT_CAN0_, INT_DEVICE_CLASS)
#define INT_COMP0               INT_CONCAT(INT_COMP0_, INT_DEVICE_CLASS)
#define INT_DES0                INT_CONCAT(INT_DES0_, INT_DEVICE_CLASS)
#define INT_EMAC0               INT_CONCAT(INT_EMAC0_, INT_DEVICE_CLASS)
#define INT_EPI0                INT_CONCAT(INT_EPI0_, INT_DEVICE_CLASS)
#define INT_FLASH               INT_CONCAT(INT_FLASH_, INT_DEVICE_CLASS)
#define INT_GPIOC               INT_CONCAT(INT_GPIOC_, INT_DEVICE_CLASS)

GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4);开启了PF4的开关

(6)编写中断处理函数

void io_interrupt(void)
{
    uint32_t s = GPIOIntStatus(GPIO_PORTF_BASE, true);
    GPIOIntClear(GPIO_PORTF_BASE, s);
    
    if((s&GPIO_PIN_4) == GPIO_PIN_4)
    {
        while(!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4))//等待按键松开
            ;
        //TODO
        flag = !flag;
    }
}

中断处理函数即是中断被触发后单片机做的事情,其实中断的触发在单片机底层是将一个寄存器的某一位写为1,然后这个1触发了中断,因此为了保证下一次中断被重新触发,在进入中断后我们需要用GPIOIntStatus函数获取这个F端口的状态,然后用GPIOIntClear将其清零。

因为所有F端口下的引脚都会进入这个函数中进行处理,比如说这个单片机是PF0 - PF7,所以我们需要用这个if((s&GPIO_PIN_4) == GPIO_PIN_4)语句判断究竟是那个引脚触发了中断,这里是PF4,按键引脚。

然后等待按键松开,进行自己想要的操作,这里是将一个变量进行了非操作。

最后附上测试的源代码:

程序功能:通过按键切换红蓝灯的闪烁。

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/hw_ints.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"

void io_interrupt(void);
void delay_ms(uint32_t n);

bool flag = 1;

int main(void)
{
    //时钟频率:40MHz
    SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |  SYSCTL_XTAL_16MHZ);

    //开启外设
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF))
    {}
        
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);//设置PF1为输出,红灯
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);//设置PF2为输出,蓝灯
        
    //设置PF4为上拉输入模式
    GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_DIR_MODE_IN);
    GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
    
    //配置中断
    GPIOIntTypeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_FALLING_EDGE);
    GPIOIntRegister(GPIO_PORTF_BASE, io_interrupt);   
        
    //开启中断
    GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4);
    IntEnable(INT_GPIOF);
    IntMasterEnable();
     
    while(1)
    {
        if(flag == 1)
        {
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0xFF);
            delay_ms(100);
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x00);
            delay_ms(100);
        }
        else
        {
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0xFF);
            delay_ms(100);
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0x00);
            delay_ms(100);
        }
    }
}

//中断处理程序
void io_interrupt(void)
{
    uint32_t s = GPIOIntStatus(GPIO_PORTF_BASE, true);
    GPIOIntClear(GPIO_PORTF_BASE, s);
    
    if((s&GPIO_PIN_4) == GPIO_PIN_4)
    {
        while(!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4))//等待按键松开
            ;
        flag = !flag;
    }
}

void delay_ms(uint32_t n)
{
    for(uint32_t i = 0; i < n; i++)
        SysCtlDelay(SysCtlClockGet()/3000);
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值