GD32开发实战指南(基础篇) 第6章 按键

发布者:美丽花朵最新更新时间:2024-11-07 来源: elecfans关键字:GD32  开发实战  按键 手机看文章 扫描二维码
随时随地手机看文章

开发环境:

MDK:Keil 5.30

开发板:GD32F207I-EVAL

MCU:GD32F207IK


1 普通方式

1.1 普通方式工作原理

按键 GPIO 端口有两个方案可以选择,一是采用上拉输入模式,因为按键在没按下的时候,是默认为高电平的,采且内部上拉模式正好符合这个要求。第二个方案是直接采用浮空输入模式,因为按照硬件电路图,在芯片外部接了上拉电阻,其实就没必要再配置成内部上拉输入模式了,因为在外部上拉与内部上拉效果是一样的。

1683728778172wwrwwyb44i

笔者本文将会使用KEY1。


1.2 普通方式实现

主函数代码如下:


/*

    brief      main function

    param[in]  none

    param[out] none

    retval     none

*/

int main(void)

{

    //systick init

    sysTick_init();

    /* configure LED1 GPIO port */

    led_init(LED1);


    /* configure LED2 GPIO port */

    led_init(LED2);


    /* configure LED3 GPIO port */

    led_init(LED3);


    /* configure LED4 GPIO port */

    led_init(LED4);


    //key init

    key_init(KEY_WAKEUP);

    while(1) 

    {

        delay_ms(100);

        if(key_scan(KEY_WAKEUP))

        {

            /* turn toggle LED */

            led_toggle(LED1);

            led_toggle(LED2);

            led_toggle(LED3);

            led_toggle(LED4);

        }

    }

}

GPIO 初始化配置

/*

    brief      configure key

    param[in]  keynum: specify the key to be configured

      arg        KEY_TAMPER: tamper key

      arg        KEY_WAKEUP: wakeup key

      arg        KEY_USER: user key

    param[out] none

    retval     none

*/

void key_init(key_typedef_enum keynum)

{

    /* enable the key clock */

    rcu_periph_clock_enable(KEY_CLK[keynum]);

    rcu_periph_clock_enable(RCU_AF);


    /* configure button pin as input */

    gpio_init(KEY_PORT[keynum], GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, KEY_PIN[keynum]);

}

key_init()与 LED 的 GPIO 初始化函数 led_init()类似,区别只是在这个函数中,要开启的 GPIO 的端口时钟不一样,并且把检测按键用的引脚 Pin 的模式设置为适合按键应用的上拉输入模式(由于接了外部上拉电阻,也可以使用浮空输入,读者可自行修改代码做实验)。


按键消抖

/*

    brief      return the key state

    param[in]  keynum: specify the key to be checked

      arg        KEY_TAMPER: tamper key

      arg        KEY_WAKEUP: wakeup key

      arg        KEY_USER: user key

    param[out] none

    retval     the key's GPIO pin value

*/

key_state_enum key_scan(key_typedef_enum keynum)

{

    /* check whether the button is pressed */

    if(RESET == gpio_input_bit_get(KEY_PORT[keynum], KEY_PIN[keynum]))

    {

        delay_ms(100);


        /* check whether the button is pressed */

        if(RESET == gpio_input_bit_get(KEY_PORT[keynum], KEY_PIN[keynum]))

        {

            while(RESET == gpio_input_bit_get(KEY_PORT[keynum], KEY_PIN[keynum]))

            {

                return KEY_ON;

            }

        }

    }

    return KEY_OFF;

}

相信延时消抖的原理大家在学习其他单片机时就已经了解了,本函数的功能就是扫描输入参数中指定的引脚,检测其电平变化,并作延时消抖处理,最终对按键消息进行确认。


利用 gpio_input_bit_get() 读取输入数据,若从相应引脚读取的数据等于 0(KEY_ON),低电平,表明可能有按键按下,调用延时函数。否则返回 KEY_OFF,表示按键没有被按下。

延时之后再次利用 gpio_input_bit_get()读取输入数据,若依然为低电平,表明确实有按键被按下了。否则返回 KEY_OFF,表示按键没有被按下。

循环调用gpio_input_bit_get() 一直检测按键的电平,直至按键被释放,被释放后,返回表示按键被按下的标志 KEY_ON。以上是按键消抖的流程,调用了一个库函数 gpio_input_bit_get()。输入参数为要读取的端口、引脚,返回引脚的输入电平状态,高电平为 1,低电平为 0。

2 EXTI方式

2.1 EXTI的工作原理

EXTI(External Interrupt) 就是指外部中断,通过 GPIO 检测输入脉冲,引起中断事件,打断原来的代码执行流程,进入到中断服务函数中进行处理,处理完后再返回到中断之前的代码中执行。


GD32的中断和异常

Cortex内核具有强大的异常响应系统,它把能够打断当前代码执行流程的事件分为异常(exception)和中断(interrupt),并把它们用一个表管理起来,编号为 0 ~ 15 的称为内核异常,而 16 以上的则称为外部中断(外是相对内核而言),这个表就称为中断向量表。


而 GD32 对这个表重新进行了编排,把编号从-3 至 6 的中断向量定义为系统异常,编号为负的内核异常不能被设置优先级,如复位(Reset)、不可屏蔽中断 (NMI)、硬错误(Hardfault)。从编号 7 开始的为外部中断,这些中断的优先级都是可以自行设置的。详细的 GD32中断向量表见下表。

168372877887081545qixac

16837287794042gup5an3bx

……

完整向量表请参考《GD32F20x_User_Manual_EN_Rev2.4》。

GD32的中断如此之多,配置起来并不容易,因此我们需要一个强大而方便的中断控制器 NVIC (Nested Vectored Interrupt Controller)。NVIC 是属于 Cortex 内核的器件,不可屏蔽中断 (NMI)和外部中断都由它来处理,而 SYSTICK 不是由 NVIC 来控制的。

1683728779796q7jzrhgrc4

  • NVIC 结构体成员

当我们要使用 NVIC 来配置中断时,自然想到GD库肯定也已经把它封装成库函数了。查找库帮助文档,发现在 gd32f20x_misc查找到一个nvic_irq_enable() 函数。


/*!

    \brief      enable NVIC request

    \param[in]  nvic_irq: the NVIC interrupt request, detailed in IRQn_Type

    \param[in]  nvic_irq_pre_priority: the pre-emption priority needed to set

    \param[in]  nvic_irq_sub_priority: the subpriority needed to set

    \param[out] none

    \retval     none

*/

void nvic_irq_enable(uint8_t nvic_irq,

                     uint8_t nvic_irq_pre_priority,

                     uint8_t nvic_irq_sub_priority)

{

    uint32_t temp_priority = 0x00U, temp_pre = 0x00U, temp_sub = 0x00U;


    /* use the priority group value to get the temp_pre and the temp_sub */

    switch((SCB->AIRCR) & (uint32_t)0x700U) {

    case NVIC_PRIGROUP_PRE0_SUB4:

        temp_pre = 0U;

        temp_sub = 0x4U;

        break;

    case NVIC_PRIGROUP_PRE1_SUB3:

        temp_pre = 1U;

        temp_sub = 0x3U;

        break;

    case NVIC_PRIGROUP_PRE2_SUB2:

        temp_pre = 2U;

        temp_sub = 0x2U;

        break;

    case NVIC_PRIGROUP_PRE3_SUB1:

        temp_pre = 3U;

        temp_sub = 0x1U;

        break;

    case NVIC_PRIGROUP_PRE4_SUB0:

        temp_pre = 4U;

        temp_sub = 0x0U;

        break;

    default:

        nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);

        temp_pre = 2U;

        temp_sub = 0x2U;

        break;

    }


    /* get the temp_priority to fill the NVIC->IP register */

    temp_priority = (uint32_t)nvic_irq_pre_priority << (0x4U - temp_pre);

    temp_priority |= nvic_irq_sub_priority & (0x0FU >> (0x4U - temp_sub));

    temp_priority = temp_priority << 0x04U;

    NVIC->IP[nvic_irq] = (uint8_t)temp_priority;


    /* enable the selected IRQ */

    NVIC->ISER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU);

}


该函数有三个参数,需要配置的中断向量,中断向量抢占优先级和中断向量的响应优先级。

前面两个结构体成员都很好理解,首先要用 nvic_irq参数来选择将要配置的中断向量。用nvic_irq_pre_priority参数要配置中断向量的抢占优先级,用nvic_irq_sub_priority参数配置中断向量的响应优先级。对于中断的配置,最重要的便是配置其优先级,但 GD32 的同一个中断向量为什么需要设置两种优先级?这两种优先级有什么区别?

  • 抢占优先级和响应优先级

GD32的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。

抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数 A 的过程中被中断 B 打断,执行完中断服务函数 B 再继续执行中断服务函数A),抢占属性由nvic_irq_pre_priority参数配置。

而响应属性则应用在抢占属性相同的情况下,当 两个中断向量的抢占优先级相同时,如 果两个中断同时到达,则先处理响应优先级高的中断,响应属性 由nvic_irq_sub_priority参数配置。例如,现在有三个中断向量,见下表。

中断向量抢占优先级响应优先级
A00
B10
C11

若内核正在执行 C 的中断服务函数,则它能被抢占优先级更高的中断 A 打断,由于 B和 C 的抢占优先级相同,所以 C 不能被 B 打断。但如果 B 和 C 中断是同时到达的,内核就会首先响应响应优先级别更高的 B 中断。

  • NVIC 的优先级组

在配置优先级的时候,还要注意一个很重要的问题,即中断种类的数量。NVIC 只可以配置 16 种中断向量的优先级,也就是说,抢占优先级和响应优先级的数量由一个 4 位的数字来决定,把这个 4 位数字的位数分配成抢占优先级部分和响应优先级部分。有 5 组分配方式 :

  • 第 0 组:所有 4 位用来配置响应优先级。即 16 种中断向量具有都不相同的响应优先级。

  • 第 1 组:最高 1 位用来配置抢占优先级,低 3 位用来配置响应优先级。表示有 21=2 种级别的抢占优先级(0 级,1 级),有 23=8 种响应优先级,即在 16 种中断向量之中,有8 种中断,其抢占优先级都为 0 级,而它们的响应优先级分别为 0~7,其余 8 种中断向量的抢占优先级则都为 1 级,响应优先级别分别为 0~7。

  • 第 2 组:2 位用来配置抢占优先级,2 位用来配置响应优先级。即 22=4 种抢占优先级,22=4 种响应优先级。

  • 第 3 组:高 3 位用来配置抢占优先级,最低 1 位用来配置响应优先级。即有 8 种抢占优先级,2 种响应 2 优先级。

  • 第 4 组:所有 4 位用来配置抢占优先级,即 NVIC 配置的 24 =16 种中断向量都是只有抢占属性,没有响应属性。

要配置这些优先级组,可以采用库函数 nvic_priority_group_set(),可输入的参数为NVIC_PRIGROUP_PRE0_SUB4 ~ NVIC_PRIGROUP_PRE4_SUB0,分别为以上介绍的 5 种分配组。

GD32的所有 I/O 端口都可以配置为 EXTI 中断模式,用来捕捉外部信号,可以配置为下降沿中断、上升沿中断和上升下降沿中断这三种模式。它们以图2所示方式连接到外部中断 / 事件线上。

  • EXTI 外部中断

GD32的所有 GPIO 都引入到 EXTI 外部中断线上,使得所有的 GPIO 都能作为外部中断的输入源。GPIO 与 EXTI 的连接方式见下表。

由下表可知,PA0 ~ PI0 连接到 EXTI0 、PA1~ PI1 连接到 EXTI1、……、PA15 ~ PI15 连接到 EXTI15。这里大家要注意的是 :PAx ~ PIx 端口的中断事件都连接到了 EXTIx,即同一时刻 EXTIx 只能响应一个端口的事件触发,不能够同一时间响应所有GPIO 端口的事件,但可以分时复用。它可以配置为上升沿触发、下降沿触发或双边沿触发。EXTI 最普通的应用就是接上一个按键,设置为下降沿触发,用中断来检测按键。

16837287803731iicow74v2

2.2 2 EXTI的寄存器描述

EXTI 寄存器的寄存器主要有6个,下面分别描述。

  • 中断使能寄存器(EXTI_INTEN)

168372878086925fpvkv808

  • 事件使能寄存器(EXTI_EVEN)

1683728781270z65ikushbx

1683728781667lr52pege20

  • 上升沿触发选择寄存器(EXTI_RTEN)

16837287819546y9sa7lt8l

注意: 外部唤醒线是边沿触发的,这些线上不能出现毛刺信号。在写EXTI_RTSR寄存器时,在外部中断线上的上升沿信号不能被识别,挂起位也不会被置位。在同一中断线上,可以同时设置上升沿和下降沿触发。即任一边沿都可触发中断。

  • 下降沿触发选择寄存器(EXTI_FTEN)

1683728782395kdebg1j966

注意: 外部唤醒线是边沿触发的,这些线上不能出现毛刺信号。在写EXTI_FTSR寄存器时,在外部中断线上的下降沿信号不能被识别,挂起位不会被置位。在同一中断线上,可以同时设置上升沿和下降沿触发。即任一边沿都可触发中断。

  • 软件中断事件寄存器(EXTI_SWIEV)

1683728782836102ykdro3b

  • 挂起寄存器(EXTI_PD)

1683728783212gqkpbuuo0g


2.3 EXTI方式实现

主函数代码如下:


/*

    brief      main function

    param[in]  none

    param[out] none

    retval     none

*/

int main(void)

{

    //systick init

    sysTick_init();

    /* configure LED1 GPIO port */

    led_init(LED1);


    /* configure LED2 GPIO port */

    led_init(LED2);


    /* configure LED3 GPIO port */

    led_init(LED3);


    /* configure LED4 GPIO port */

    led_init(LED4);

    //key init

    key_init(KEY_WAKEUP, KEY_MODE_EXTI);

    while(1) 

    {

        delay_ms(100);

    }

}

配置外部中断

现在我们重点分析 key_init() 这个函数,它完成了配置一个 I/O 为 EXTI 中断的一般步骤,主要有以下功能 :


1)使能 EXTIx 线的时钟和第二功能 AFIO 时钟。


2)配置 EXTIx 线的中断优先级。


3)配置 EXTI 中断线 I/O。

[1] [2]
关键字:GD32  开发实战  按键 引用地址:GD32开发实战指南(基础篇) 第6章 按键

上一篇:零基础学习GD32 红外遥控器原理
下一篇:GD32单片机STM32远程下载手机程序升级固件下载局域网网页升级工具

推荐阅读最新更新时间:2024-11-12 19:07

GD32 MCU产品荣膺十大创新成果奖
        2014年1月15日下午,2013年度中关村十大系列榜单发布会在北京湖北大厦东湖厅隆重举行,北京兆易创新科技股份有限公司参选的GD32 MCU 产品荣膺十大创新成果奖。 GD32 MCU是兆易创新于2013年推出的中国首款基于ARM Cortex-M3内核的32位通用微控制器系列产品,该产品填补了国内高端32位微控制器领域的空白,提升了国内IC企业的整体技术水准,是中国微控制器产业史上的一座里程碑,鉴于GD32 MCU显著的产品创新性和技术先进性,以及对国内IC产业的巨大贡献,在本次评选中脱颖而出一举入选。   中国技术交易所总裁郭书贵颁发2013中关村十大创新成果奖项 目前GD32 MCU系
[手机便携]
一文解析STM32、GD32、ESP32差异
前言 STM32:意法半导体在 2007 年 6 月 11 日发布的产品,32位单片机。 GD32:兆易创新 2013 年发布的产品,在芯片开发、配置、命名上基本模仿 STM32,甚至 GPIO 和 STM32 都是 pin to pin 的,封装不改焊上去直接用。有时候 STM32 的源码不修改,重新编译烧写到 GD32 上就可以跑。当然也有很多不同,比如串口驱动、USB 、库文件等。 ESP32:乐鑫公司 2017 年开发的产品,和 STM32、GD32 不同,ESP32 主要面向物联网领域,支持功能很多,但引出 GPIO pin 脚很少,因此大多数 GPIO 都有很多复用功能。出厂就集成蓝牙、WiFi 等物联网必备功能
[单片机]
一文解析STM32、<font color='red'>GD32</font>、ESP32差异
单片机-4x4个矩阵按键控制数码管显示数字程序
1 #include 8051.h 2 typedef unsigned char u8; 3 typedef unsigned int u16; 4 u8 smgduan = { 5 /*0 1 2 3 4 5 6 7 */ 6 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 7 /*8 9 A B C D E F */ 8 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71}; 9 10 // P0口为数码管的位选的8位输入引脚 11 // P
[单片机]
51单片机之独立按键和矩阵键盘
一、基本知识 1.按键分类与输入原理 按键按照结构原理科分为两类,一类是触点式开关按键,如机械式开关、导电橡胶式开关灯;另一类是无触点式开关按键,如电气式按键,磁感应按键等。前者造价低,后者寿命长。目前,微机系统中最常见的是触点式开关按键。 在单片机应用系统中,除了复位按键有专门的复位电路及专一的复位功能外,其他按键都是以开关状态来设置控制功能或输入数据的。当所设置的功能键或数字键按下时,计算机应用系统应完成该按键所设定的功能,键信息输入时与软件结构密切相关的过程。 对于一组键或一个键盘,总有一个接口电路与CPU相连。CPU可以采用查询或中断方式了解有无将按键输入,并检查是哪一个按键按下,将该键号
[单片机]
51单片机之独立<font color='red'>按键</font>和矩阵键盘
51单片机-按键部分(2)
程序功能:数码管前三位显示一个跑表,从000到999之间以1%秒速度运行,当按下一个独立键盘时跑表停止,松开手后跑表继续运行。(用定时器设计表)。按下第二个时计时开始,按下第三个是计数值清零。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 7
[单片机]
iPhone 7并没取消物理按键 只是做平了一点
    根据早前的传闻,虽然iPhone 7的整体外观和iPhone 6s相似,但采用了很多激进的设计。如,去掉了3.5mm耳机接口。此外,前段时间流传出了疑似iPhone 7的谍照,显示iPhone 7将取消物理Home键。不过,供应链的最新消息是,iPhone 7其实没有取消Home键,仅是做平了一点而已。   上图是前段时间曝光的iPhone 7谍照图。从图谍照图来看,Home键外圈的金属环设计已被取消。此外,据称iPhone 7的Home键不再支持按压,而支持触控操作。这意味着苹果放弃了物理按压Home键的设计。     不过,我们从供应链获得的消息是,iPhone 7其实并没有取消物理按压式的Home键,
[手机便携]
2440test中按键的分析
这里分析了4个按键,还有 ENIT8 和 EINT19没有,不过原理一样的。 4个按键,分别是 EINT11 / GPG3 EINT13 / GPG5 EINT14 / GPG6 EINT15 / GPG7 外部上拉电阻。 EINT 8-23 共用一个IRQ向量。 初始化步骤 1,设置IO的功能,00 输入 01输出 02 第二功能 P292 设置的方法是 rGPGCON &= ~(3 3*2 | 3 5*2 | 3 6*2 | 3 7*2); rGPGCON |= (2 3*2 | 2 5*2 | 2 6*2 | 2 7*2); 2,设置 EXTINT 系列寄存器,设定中断触发的类型 P301 其中这次用到的 EIN
[单片机]
51单片机18b20温度及按键可调上下限报警源程序
于18b20显示2位温度并显示上下限,超限报警,温度上下限均可调整,附带闪烁灯 单片机源程序如下: #include reg52.h #define uchar unsigned char #define uint unsigned int sbit ds=P2^2; sbit key1=P3^4; sbit key2=P3^5; sbit key3=P3^6; sbit dula=P2^6; sbit wela=P2^7; sbit beep=P2^3; bit key=0; sbit led=P1^2; uint get_temp(); void send(uchar); uint temp,num,p,
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved