《DNK210使用指南 -SDK版 V1.0》第八章 按键输入实验

第八章 按键输入实验


       本章实验将介绍如何使用SDK编程让Kendryte K210获取板载按键的状态。通过本章的学习,读者将学习到用SDK编程技术读取Kendryte K210的GPIO上的高低电平状态。

       本章分为如下几个小节:

       8.1 GPIOHS介绍

       8.2 硬件设计

       8.3 程序设计

       8.4 运行验证


        8.1 GPIOHS介绍

       本章实验用到的外设是GPIOHS,通用GPIO和GPIOHS除了中断功能有差异的话,其他都差不多,前面第六章实验我们已经介绍了GPIOHS外设的功能,接下来我们看下GPIOHS 的一些函数。


       1,gpiohs_set_drive_mode函数

       该函数用来配置指定管脚的工作模式,包括输入输出模式、上下拉等,如下代码所示:

/* 函数原型 */
void gpiohs_set_drive_mode(uint8_t pin, gpio_drive_mode_t mode);
/**
 * gpiohs_set_drive_mode功能的mode配置参数
 */
typedef enum _gpio_drive_mode
{
    GPIO_DM_INPUT,
    GPIO_DM_INPUT_PULL_DOWN,
    GPIO_DM_INPUT_PULL_UP,
    GPIO_DM_OUTPUT,
} gpio_drive_mode_t;

       下面我们来讲解一下这几个变量的作用。

       ①:参数pin为引脚软件编号,最大值要小于8。

       ②:参数mode为指定的配置模式,如下表所示:


表8.1.1 引脚配置的模式


       该函数无返回值。


       2,gpiohs_set_pin函数

       该函数用来设置GPIO的电平状态,该函数原型及参数描述如下所示:

void gpiohs_set_pin(uint8_t pin, gpio_pin_value_t value);
 
 
typedef enum _gpio_pin_value
{
    GPIO_PV_LOW,
    GPIO_PV_HIGH
} gpio_pin_value_t;

       下面我们讲解下这个函数的两个参数,第一个是引脚的软件编号,同上,第二个参数用于设置IO管脚的高低电平状态,GPIO_PV_LOW为指向IO输出低电平, GPIO_PV_HIGH为输出的高电平。该函数无返回值。


       3,gpiohs_get_pin函数

       该函数用来获取IO的电平状态,该函数原型如下所示:

gpio_pin_value_t gpiohs_get_pin(uint8_t pin);

       pin用来指向获取那个IO的电平,该函数的返回值就是指定IO的电平状态0或1。


       4,gpiohs_set_pin_edge函数

       该函数用来配置指定管脚中断触发方式,如上升沿触发、低电平触发,如下代码所示:

/* 函数原型 */
void gpiohs_set_pin_edge(uint8_t pin, gpio_pin_edge_t edge);
 
/* gpiohs_set_pin_edge功能的edge配置参数 */
typedef enum _gpio_pin_edge
{
    GPIO_PE_NONE,
    GPIO_PE_FALLING,
    GPIO_PE_RISING,
    GPIO_PE_BOTH,
    GPIO_PE_LOW,
    GPIO_PE_HIGH = 8,
} gpio_pin_edge_t;

       下面我们来讲解一下这几个变量的作用。

       ① :参数pin为引脚软件编号,最大值要小于8。

       ②:参数edge为配置引脚的中断方式,如下表所示:


表8.1.2 引脚中断触发方式配置


       该函数无返回值。


       5,gpiohs_irq_register函数

       该函数用来注册指定引脚的中断,设置优先级、中断回调函数和回调参数,该函数原型如下所示:

/* 函数原型 */
void gpiohs_irq_register(uint8_t pin, uint32_t priority, plic_irq_callback_t callback, void *ctx);
 
/* gpiohs_irq_register功能的callback配置参数 */
typedef int (*plic_irq_callback_t)(void *ctx);

       pin用来指向注册中断的IO,priority是中断优先级,数字越小中断优先级越高,后面两个是设置中断回调函数和回调的参数,该函数无返回值。

       可以看到,前几个函数用法基本和通用GPIO一样,本章实验也没有用到中断,无法区分明显差异,我们在下一章会讲解按键中断实验。


        8.2 硬件设计


       8.2.1 例程功能


       1. 当KEY0按键被按下后,双色LED的两个灯均熄灭

       2. 当KEY1按键被按下后,双色LED的蓝灯亮起,红灯熄灭

       3. 当KEY2按键被按下后,双色LED的红灯亮起,蓝灯熄灭


       8.2.2 硬件资源


       1. 双色LED

              LEDR - IO24

              LEDB - IO25


       2. 独立按键

              KEY0按键 - IO18

              KEY1按键 - IO19

              KEY2按键 - IO16


       8.2.3 原理图

       本章实验内容,需要读取独立按键连接IO上的电平状态,正点原子DNK210开发板上独立按键的连接原理图,如下图所示:


图8.2.3.1 独立按键连接原理图


       通过以上原理图可以看出,KEY0按键、KEY1按键和KEY2按键对应的IO编号分别为IO18、IO19和IO16,当独立按键没有被按下时,其对应的IO将处于悬空状态,此时读取到的电平将由IO的上下拉决定,当独立按键被按下后,其对应IO的电平将被拉低。


        8.3 程序设计


       8.3.1 按键驱动代码

       按键驱动源码包括两个文件:key.c和key.h,我们先介绍key.h。

/* 硬件IO口,与原理图对应 */
#define PIN_KEY_0              (18)
#define PIN_KEY_1              (19)
#define PIN_KEY_2              (16)
/*****************************SOFTWARE-GPIO********************************/
/* 软件GPIO口,与程序对应 */
#define KEY0_GPIONUM           (0)
#define KEY1_GPIONUM           (1)
#define KEY2_GPIONUM           (2)
/*****************************FUNC-GPIO************************************/
/* GPIO口的功能,绑定到硬件IO口 */
#define FUNC_KEY0              (FUNC_GPIOHS0 + KEY0_GPIONUM)
#define FUNC_KEY1              (FUNC_GPIOHS0 + KEY1_GPIONUM)
#define FUNC_KEY2              (FUNC_GPIOHS0 + KEY2_GPIONUM)
/******************************************************************************************/

       首先是硬件管脚和GPIOHS的功能号绑定,我们这里用到的是GPIOHS0、GPIOHS1和GPIOHS2。

#define KEY0         gpiohs_get_pin(KEY0_GPIONUM)      /* 读取KEY0引脚 */
#define KEY1         gpiohs_get_pin(KEY1_GPIONUM)      /* 读取KEY1引脚 */
#define KEY2         gpiohs_get_pin(KEY2_GPIONUM)      /* 读取KEY2引脚 */
 
#define KEY0_PRES    1              /* KEY0按下 */
#define KEY1_PRES    2              /* KEY1按下 */
#define KEY2_PRES    3              /* KEY2按下 */

       这里KEY0、KEY1和KEY2是对应按键状态的宏定义,用来接收对应引脚的电平信号,gpiohs_get_pin函数返回值就是IO口的状态,取值0或者1。

       KEY0_PRES、KEY1_PRES和KEY2_PRES则是按键对应的三个键值宏定义标识符。

       接下来我们看key.c文件,主要包括初始化函数key_init()和按键读取函数key_scan(),我们先看key_init()函数。

void key_init(void)
{
    fpioa_set_function(PIN_KEY_0, FUNC_KEY0);
    fpioa_set_function(PIN_KEY_1, FUNC_KEY1);
    fpioa_set_function(PIN_KEY_2, FUNC_KEY2);
 
    gpiohs_set_drive_mode(KEY0_GPIONUM, GPIO_DM_INPUT_PULL_UP);  /*输入上拉*/
    gpiohs_set_drive_mode(KEY1_GPIONUM, GPIO_DM_INPUT_PULL_UP);  /*输入上拉*/
    gpiohs_set_drive_mode(KEY2_GPIONUM, GPIO_DM_INPUT_PULL_UP);  /*输入上拉*/
 
}

       可以看到,GPIOHS外设使用并不需要使用GPIO口使能,关联硬件管脚后直接设置模式即可,因为独立按键默认悬空,按下后接入低电平,所以悬空状态我们要将引脚拉高,使用输入上拉模式,接下来我们看另外个函数。

uint8_t key_scan(uint8_t mode)
{
    static uint8_t key_up = 1;  /* 按键按松开标志 */
    uint8_t keyval = 0;
 
    if (mode) key_up = 1;       /* 支持连按 */
 
    if (key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0)) 
    {
        msleep(50);           /* 去抖动 */
        key_up = 0;
 
        if (KEY0 == 0)  keyval = KEY0_PRES;
 
        if (KEY1 == 0)  keyval = KEY1_PRES;
 
        if (KEY2 == 0)  keyval = KEY2_PRES;
    }
    else if (KEY0 == 1 && KEY1 == 1 && KEY2 == 1)
    {
        key_up = 1;
    }
 
    return keyval;              /* 返回键值 */
}

       key_scan函数用于扫描这3个IO口是否有按键按下。它支持两种扫描方式,通过mode参数来设置。

       当mode为0的时候,key_scan函数将不支持连续按,扫描某个按键,该按键按下之后必须要松开,才能第二次触发,否则不会再响应这个按键,这样的好处就是可以防止按一次多次触发,而坏处就是在需要长按的时候比较不合适。

       当mode为1的时候,key_scan函数将支持连续按,如果某个按键一直按下,则会一直返回这个按键的键值,这样可以方便的实现长按检测。

       有了mode这个参数,大家就可以根据自己的需要,选择不同的方式。这里要提醒大家,因为该函数里面有static变量,所以该函数不是一个可重入函数,在有OS的情况下,这个大家要留意下。msleep()是毫秒级延时函数,可以看到该函数的消抖延时是50ms。同时还有一点要注意的是,该函数的按键扫描是有优先级的,最高优先级的是KEY2,第二优先的是KEY1, 最后是按键KEY0。该函数有返回值,如果有按键按下,则返回非0值,如果没有或者按键不正确,则返回0。


       8.3.2 main.c代码

       main.c中的代码如下所示:

#include 
#include 
#include 
#include "./BSP/KEY/key.h"
#include "./BSP/LED/led.h"
 
int main(void)
{
    uint8_t key;
 
    led_init();    /* LED初始化 */
    key_init();    /* 按键初始化 */
 
    while (1)
    {
        key = key_scan(0);                  /* 得到键值 */
 
        if (key)
        {
            switch (key)
            {
                case KEY2_PRES:             /* 控制红灯亮 */
                    LEDR(0);
                    LEDB(1);
                    break;
 
                case KEY1_PRES:             /* 控制蓝灯亮 */
                    LEDR(1);
                    LEDB(0);
                    break;
 
                case KEY0_PRES:             /* 同时关闭红灯和蓝灯 */
                    LEDR(1);
                    LEDB(1);
                    break;
            }
        }
        else
        {
            msleep(10);
        }
    }
}

       可以看到,首先执行LED初始化和按键初始化,初始化完成之后就能实现LED的亮灭和按键的读取,最后在一个循环分别读取KEY0按键、KEY1按键和KEY2按键对应的GPIO输入电平,以判断独立按键是否被按下,若KEY0按键被按下,则控制对应的GPIO输出高电平以控制两个LED灯熄灭,KEY1按键和KEY2按键的读取和按键解释同理。


        8.4 运行验证

       将DNK210开发板连接到电脑主机,通过VSCode将固件烧录到开发板中,此时,若按下板载的KEY0按键,则能看到双色LED的两个灯熄灭,若按下KEY1按键,则能看到双色LED的蓝色灯亮起,红灯熄灭,若按下KEY2按键,则能看到双色LED的红色灯亮起,蓝灯熄灭,这与理论推断的结果一致。


请使用浏览器的分享功能分享到微信等