目录

1.  GPIO 概述

2.  按键

3.  GPIO输出功能验证

4.  双按键功能实现

5.  EXTI简介

6.  通过外部中断实现按键功能


1.  GPIO 概述

        GPIO(General Purpose Input Output)即通用输入输出端口,是芯片可直接控制的引脚。通过将 GPIO 引脚与外部设备连接,可实现与外部设备的通讯、控制及数据采集等功能。所有 GPIO 引脚均具备基本的输入和输出能力:

功能类型原理核心作用典型应用
输出由MCU控制引脚输出高、低电平,实现开关控制控制外部设备通断 / 状态LED 驱动、继电器 / 三极管控制
输入检测外部输入的电平信号,通过电平高低区分不同状态。检测外部电平 / 状态按键检测、传感器信号采集

2.  按键

         按键机械触点断开、闭合时,由于触点的弹性作用,按键开关不会马上稳定接通或一下子断开,使用按键时会产生图按键抖动说明图中的带波纹信号,需要用软件消抖处理滤波,不方便输入检测。可以利用电容充放电的延时,消除了波纹,从而简化软件的处理,软件只需要直接检测引脚的电平即可。

3.  GPIO输出功能验证

        首先宏定义P2.12引脚,方便后续调用:

#define KEY_STOP_PIN  GPIO_Pin_12
#define KEY_STOP_PORT GPIO2
#define KEY_STOP_Source GPIO_PinSource_12
#define KEY_STOP GPIO_ReadInputDataBit(KEY_STOP_PORT, KEY_STOP_PIN)

        初始化按键为输入模式:

void GPIO_init_key(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Pin =  KEY_STOP_PIN;
    GPIO_Init(KEY_STOP_PORT, &GPIO_InitStruct);
}

        然后通过 GPIO_ReadInputDataBit() 函数读取指定 GPIO 引脚的电平状态(读取IDR(输入数据寄存器)),首先判断按键是否被按下,如果按下延时消抖一段时间,然后再次检测按键是否被按下,如果按下则将按键标志位置0:

    if(KEY_STOP == 0 && key_released)   /* 检测到按键按下,并且按键之前是释放状态 */
    {
        SoftDelay(0x500);               /* 延时消抖 */
        if(KEY_STOP == 0)               /* 再次检测到按键按下 */
        {
            key_released = 0;           /* 按键按下,标志位置0 */
            return 1;
        }
    }

        如果检测到按键释放,则将按键标志位置1:

    if(KEY_STOP != 0)                   /* 检测到按键释放 */
    {
        key_released = 1;               /* 按键释放,标志位置1 */
    }

        此时按键检测代码:

#include "gpio_key.h"
#include "delay.h"

int gpio_key_Scan(void)
{
    static u8 key_released = 1;         /* 按键释放标志,按键按下时为0,按键释放时为1 */

    if(KEY_STOP == 0 && key_released)   /* 检测到按键按下,并且按键之前是释放状态 */
    {
        SoftDelay(0x500);               /* 延时消抖 */
        if(KEY_STOP == 0)               /* 再次检测到按键按下 */
        {
            key_released = 0;           /* 按键按下,标志位置0 */
            return 1;
        }
    }

    if(KEY_STOP != 0)                   /* 检测到按键释放 */
    {
        key_released = 1;               /* 按键释放,标志位置1 */
    }

    return 0;
}

        主函数就很简单了,检测到按键按下,切换一次电平:

#include "lks32mc07x_lib.h"
#include "hardware_init.h"
#include "gpio_led.h"
#include "gpio_key.h"
#include "delay.h"

int main(void)
{
	u8 led_state = 0;

	Hardware_init();

	while(1)
	{
		if(gpio_key_Scan() == 1) 	/* 检测到按键按下 */
		{
			led_state = !led_state;	/* 切换LED状态 */
		}

		if(led_state == 1)				/* LED状态为点亮 */
		{
			led_all_on(); 					/* 点亮所有LED */
		}
		else
		{
			led_all_off();					/* 熄灭所有LED */
		}
	}
}

文章源码:凌鸥LKS32MC071系列单片机·单按键实现LED电平切换资源-CSDN下载

4.  双按键功能实现

        其实就是将上面流程走一遍,在上面代码的基础上,先进行宏定义对P2.11:

#define KEY_START_PIN  GPIO_Pin_11
#define KEY_START_PORT GPIO2
#define KEY_START_Source GPIO_PinSource_11
#define KEY_START GPIO_ReadInputDataBit(KEY_START_PORT, KEY_START_PIN)

        然后初始化:

void GPIO_init_key(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Pin =  KEY_STOP_PIN;
    GPIO_Init(KEY_STOP_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Pin =  KEY_START_PIN;
    GPIO_Init(KEY_START_PORT, &GPIO_InitStruct);
}

        然后添加START按键的函数:

    if(KEY_START == 0 && key_released_start)   /* 检测到按键按下,并且按键之前是释放状态 */
    {
        SoftDelay(0x500);               /* 延时消抖 */
        if(KEY_START == 0)               /* 再次检测到按键按下 */
        {
            key_released_start = 0;           /* 按键按下,标志位置0 */
            return 2;
        }
    }

    if(KEY_START != 0)                   /* 检测到按键释放 */
    {
        key_released_start = 1;               /* 按键释放,标志位置1 */
    }

        完整的代码:

#include "gpio_key.h"
#include "delay.h"

int gpio_key_Scan(void)
{
    static u8 key_released_stop = 1;         /* 按键释放标志,按键按下时为0,按键释放时为1 */
    static u8 key_released_start = 1;        /* 按键释放标志,按键按下时为0,按键释放时为1 */

    if(KEY_STOP == 0 && key_released_stop)   /* 检测到按键按下,并且按键之前是释放状态 */
    {
        SoftDelay(0x500);               /* 延时消抖 */
        if(KEY_STOP == 0)               /* 再次检测到按键按下 */
        {
            key_released_stop = 0;           /* 按键按下,标志位置0 */
            return 1;
        }
    }

    if(KEY_STOP != 0)                   /* 检测到按键释放 */
    {
        key_released_stop = 1;               /* 按键释放,标志位置1 */
    }

    if(KEY_START == 0 && key_released_start)   /* 检测到按键按下,并且按键之前是释放状态 */
    {
        SoftDelay(0x500);               /* 延时消抖 */
        if(KEY_START == 0)               /* 再次检测到按键按下 */
        {
            key_released_start = 0;           /* 按键按下,标志位置0 */
            return 2;
        }
    }

    if(KEY_START != 0)                   /* 检测到按键释放 */
    {
        key_released_start = 1;               /* 按键释放,标志位置1 */
    }

    return 0;
}

        之后回到主函数做判断:

#include "lks32mc07x_lib.h"
#include "hardware_init.h"
#include "gpio_led.h"
#include "gpio_key.h"
#include "delay.h"

int main(void)
{
	u8 led_state = 0;

	Hardware_init();

	while(1)
	{
		u8 key_value = gpio_key_Scan(); /* 检测按键值 */

		if(key_value == 1) 				/* 检测到STOP按键按下 */
		{
			led_state = !led_state;		/* 切换LED状态 */
		}
		else if(key_value == 2)			/* 检测到START按键按下 */
		{
			led_state = !led_state;		/* 切换LED状态 */
		}

		if(led_state == 1)				/* LED状态为点亮 */
		{
			led_all_on(); 				/* 点亮所有LED */
		}
		else
		{
			led_all_off();				/* 熄灭所有LED */
		}
	}
}

        此时实现的功能是START和STOP两个按键都可以对LED进行电平反转。

工程源码:凌鸥LKS32MC071系列单片机·双按键实现LED电平切换资源-CSDN下载

5.  EXTI简介

        EXTI(Extern Interrupt)外部中断 EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。

简单来说:电平变化,触发中断

        支持的触发方式如下:

/**
 * EXTI触发信号定义
 */
typedef enum
{
    EXTI_Trigger_None    = 0x00, /**< 不触发*/
    EXTI_Trigger_Negedge = 0x01, /**< 下降沿触发*/
    EXTI_Trigger_Posedge = 0x02, /**< 上升沿触发*/
    EXTI_Trigger_Edge    = 0x03  /**< 上升沿、下降沿均触发*/
} EXTIMode_TypeDef;

上升沿触发:数字电平从低电平(数字“0”)变为高电平(数字“1”)的那一瞬间叫作上升沿。 上升沿触发是当信号有上升沿时的开关动作,当电位由低变高而触发输出变化的就叫上升沿触发。也就是当测到的信号电位是从低到高也就是上升时就触发,叫做上升沿触发。

下降沿触发:数字电路中,数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间叫作下降沿。下降沿触发是当信号有下降沿时的开关动作,当电位由高变低而触发输出变化的就叫下降沿触发。也就是当测到的信号电位是从高到低也就是下降时就触发,叫做下降沿触发。

那么我们可以很好的理解两种触发:

上升沿触发 就是当电压从低变高时触发中断
下降沿触发 就是当电压从高变低时触发中断

双边沿触发:可以看做上升沿和下降沿的结合,也就是二者都可以触发中断。

6.  通过外部中断实现按键功能

        在使用之前,我们需要先了解,该引脚是否支持EXTI,通过芯片手册我们可以发现STOP也就是P0_12引脚具备EXTI功能:

        我们通过如下函数将引脚映射连接到对应的 EXTI 外部中断线路:

EXTI_Trigger_Config(GPIO_TypeDef* GPIOx, uint16_t PinSource, EXTI_TriggerTypeDef EdgeSel);

        在之前初始化的代码的基础上,增加代码,意思把 GPIO2 Pin12 这条引脚映射连接到对应的 EXTI 外部中断线路:

void GPIO_init_key(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Pin =  KEY_STOP_PIN;
    GPIO_Init(KEY_STOP_PORT, &GPIO_InitStruct);
	EXTI_Trigger_Config(KEY_STOP_PORT,KEY_STOP_Source,EXTI_Trigger_Negedge);

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Pin =  KEY_START_PIN;
    GPIO_Init(KEY_START_PORT, &GPIO_InitStruct);
}

        然后初始化部分设置NVIC中断:

    NVIC_SetPriority(GPIO_IRQn, 0x00);          /* 设置GPIO中断优先级为最高 */
    NVIC_EnableIRQ(GPIO_IRQn);                  /* 使能GPIO中断 */

        来到GPIO中断服务函数部分,创建一个状态标志位用于切换流水灯方向:

extern u8 flow_direction;

 void GPIO_IRQHandler(void)
{
    if(EXTI_GetIRQFlag(KEY_STOP_PORT,KEY_STOP_Source))
    {
        flow_direction = !flow_direction;                  /* 切换流水灯方向 */
        
        EXTI_ClearIRQFlag(KEY_STOP_PORT,KEY_STOP_Source);
    }
}

        流水灯流向控制代码如下,通过更改direction的值用来更改流向:

void led_water_flow(u8 direction)
{
    if(direction == 0)   /* 正向流水: LED1 → LED2 → LED3 */
    {
        LED1_ON;  LED2_OFF; LED3_OFF;
        SoftDelay(0x100000);
        LED1_OFF; LED2_ON;  LED3_OFF;
        SoftDelay(0x100000);
        LED1_OFF; LED2_OFF; LED3_ON;
        SoftDelay(0x100000);
    }
    else                 /* 反向流水: LED3 → LED2 → LED1 */
    {
        LED1_OFF; LED2_OFF; LED3_ON;
        SoftDelay(0x100000);
        LED1_OFF; LED2_ON;  LED3_OFF;
        SoftDelay(0x100000);
        LED1_ON;  LED2_OFF; LED3_OFF;
        SoftDelay(0x100000);
    }
}

        主函数循环执行流水灯程序:

#include "lks32mc07x_lib.h"
#include "hardware_init.h"
#include "gpio_led.h"
#include "gpio_key.h"
#include "delay.h"

u8 flow_direction = 0;       /* 全局变量: 流水灯方向, 0=正向, 1=反向 */

int main(void)
{
	Hardware_init();

    while(1)
    {
        led_water_flow(flow_direction);      /* 执行流水灯 */
    }
}

        最终可以发现STOP按键控制流水灯流向,每次点击按键流向改变。

工程源码:凌鸥LKS32MC071系列单片机·外部中断EXTI实现按键更改流水灯方向资源-CSDN下载

凌鸥LKS32_时光の尘的博客-CSDN博客

更多推荐