打印
[其他ST产品]

【正点原子K210连载】第八章 按键输入实验《DNK210使用指南-SDK版》

[复制链接]
770|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
第八章 按键输入实验
本章实验将介绍如何使用SDK编程让Kendryte K210获取板载按键的状态。通过本章的学习,读者将学习到用SDK编程技术读取Kendryte K210GPIO上的高低电平状态。
本章分为如下几个小节:
8.1 GPIOHS介绍
8.2 硬件设计
8.3 程序设计
8.4 运行验证
8.1 GPIOHS介绍
本章实验用到的外设是GPIOHS,通用GPIO和GPIOHS除了中断功能有差异的话,其他都差不多,前面第六章实验我们已经介绍了GPIOHS外设的功能,接下来我们看下GPIOHS 的一些函数。
1gpiohs_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为指定的配置模式,如下表所示:
模式名称
说明
GPIO_DM_INPUT
输入模式(不带上下拉)
GPIO_DM_INPUT_PULL_DOWN
输入下拉模式
GPIO_DM_INPUT_PULL_UP
输入上拉模式
GPIO_DM_OUTPUT
输出模式
表8.1.1 引脚配置的模式
该函数无返回值。
2gpiohs_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为输出的高电平。该函数无返回值。
3gpiohs_get_pin函数
该函数用来获取IO的电平状态,该函数原型如下所示:
gpio_pin_value_t gpiohs_get_pin(uint8_t pin);
pin用来指向获取那个IO的电平,该函数的返回值就是指定IO的电平状态01
4gpiohs_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为配置引脚的中断方式,如下表所示:
模式名称
说明
GPIO_PE_NONE
不触发
GPIO_PE_FALLING
下降沿触发
GPIO_PE_RISING
上升沿触发
GPIO_PE_BOTH
双边沿触发
GPIO_PE_LOW
低电平触发
GPIO_PE_HIGH
高电平触发
表8.1.2 引脚中断触发方式配置
该函数无返回值。
5gpiohs_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编号分别为IO18IO19IO16,当独立按键没有被按下时,其对应的IO将处于悬空状态,此时读取到的电平将由IO的上下拉决定,当独立按键被按下后,其对应IO的电平将被拉低。
8.3 程序设计
8.3.1 按键驱动代码
按键驱动源码包括两个文件:key.ckey.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的功能号绑定,我们这里用到的是GPIOHS0GPIOHS1GPIOHS2。
#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、KEY1KEY2是对应按键状态的宏定义,用来接收对应引脚的电平信号,gpiohs_get_pin函数返回值就是IO口的状态,取值0或者1
KEY0_PRESKEY1_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函数用于扫描这3IO口是否有按键按下。支持两种扫描方式,通过mode参数来设置。
mode0的时候,key_scan函数将不支持连续按,扫描某个按键,该按键按下之后必须要松开,才能第二次触发,否则不会再响应这个按键,这样的好处就是可以防止按一次多次触发,而坏处就是在需要长按的时候比较不合适。
mode1的时候,key_scan函数将支持连续按,如果某个按键一直按下,则会一直返回这个按键的键值,这样可以方便的实现长按检测。
有了mode这个参数,大家就可以根据自己的需要,选择不同的方式。这里要提醒大家,因为该函数里面有static变量,所以该函数不是一个可重入函数,在有OS的情况下,这个大家要留意下。msleep()是毫秒级延时函数,可以看到该函数的消抖延时是50ms。同时还有一点要注意的是,该函数的按键扫描是有优先级的,最优先的是KEY2,第优先的是KEY1, 最后是按键KEY0。该函数有返回值,如果有按键按下,则返回非0值,如果没有或者按键不正确,则返回0
8.3.2 main.c代码
main.c中的代码如下所示:
#include <stdio.h>
#include <unistd.h>
#include <sleep.h>
#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的红色灯亮起,蓝灯熄灭,这与理论推断的结果一致。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

91

主题

92

帖子

1

粉丝