第19章 STM32F429的GPIO应用之按键FIFO

发布者:RadiantBeauty最新更新时间:2022-04-11 来源: eefocus关键字:STM32F429  GPIO  按键FIFO 手机看文章 扫描二维码
随时随地手机看文章

19.1 初学者重要提示

学习本章节前,务必保证已经学习了第15,16和17章。


按键FIFO驱动扩展和移植更简单,组合键也更好用。支持按下、弹起、长按和组合键。


19.2 按键硬件设计

V6开发板有三个独立按键和一个五向摇杆,下面是三个独立按键的原理图:

注意,K1(S1)、K2(S2)和K3(S3)按键的上拉电阻是接在5V电压上,因为这三个按键被复用为PS/2键盘鼠标接口,而PS/2是需要5V供电的(注,V5和V6开发板做了PS/2复用)。实际测试,K1、K2、K3按键和PS/2键盘是可以同时工作的。


下面是五向摇杆的原理图:

通过这个硬件设计,有如下两个知识点为大家做介绍:


19.2.1 硬件设计

按键和CPU之间串联的电阻起保护作用。按键肯定是存在机械抖动的,开发板上面的硬件没有做硬件滤波处理,即使设计了硬件滤波电路,软件上还是需要进行滤波。


保护GPIO,避免软件错误将IO设置为输出,如果设置为低电平还好,如果设置输出的是高电平,按键按下会直接跟GND(低电平)连接,从而损坏MCU。


保护电阻也起到按键隔离作用,这些GPIO可以直接用于其它实验。


19.2.2 GPIO内部结构分析按键

详细的GPIO模式介绍,请参考第15章的15.3小节,本章仅介绍输入模式。下面我们通过一张图来简单介绍GPIO的结构。

红色的线条是GPIO输入通道的信号流向,作为按键检测IO,这些需要配置为浮空输入。按键已经做了5V上拉,因此GPIO内部的上下拉电阻都选择关闭状态。


19.3 按键FIFO的驱动设计

bsp_key按键驱动程序用于扫描独立按键,具有软件滤波机制,采用FIFO机制保存键值。可以检测如下事件:


  按键按下。

  按键弹起。

  长按键。

  长按时自动连发。

我们将按键驱动分为两个部分来介绍,一部分是FIFO的实现,一部分是按键检测的实现。


bsp_key.c 文件包含按键检测和按键FIFO的实现代码。


bsp.c 文件会调用bsp_InitKey()初始化函数。


bsp.c 文件会调用bsp_KeyScan按键扫描函数。


bsp_timer.c 中的Systick中断服务程序调用 bsp_RunPer10ms。


中断程序和主程序通过FIFO接口函数进行信息传递。


函数调用关系图:


 

19.3.1 按键FIFO的原理

FIFO是First Input First Output的缩写,先入先出队列。我们这里以5个字节的FIFO空间进行说明。Write变量表示写位置,Read变量表示读位置。初始状态时,Read = Write = 0。

我们依次按下按键K1,K2,那么FIFO中的数据变为:

如果Write!= Read,则我们认为有新的按键事件。


我们通过函数bsp_GetKey读取一个按键值进行处理后,Read变量变为1。Write变量不变。

我们继续通过函数bsp_GetKey读取3个按键值进行处理后,Read变量变为4。此时Read = Write = 4。两个变量已经相等,表示已经没有新的按键事件需要处理。


有一点要特别的注意,如果FIFO空间写满了,Write会被重新赋值为0,也就是重新从第一个字节空间填数据进去,如果这个地址空间的数据还没有被及时读取出来,那么会被后来的数据覆盖掉,这点要引起大家的注意。我们的驱动程序开辟了10个字节的FIFO缓冲区,对于一般的应用足够了。


 


设计按键FIFO主要有三个方面的好处:


  可靠地记录每一个按键事件,避免遗漏按键事件。特别是需要实现按键的按下、长按、自动连发、弹起等事件时。

  读取按键的函数可以设计为非阻塞的,不需要等待按键抖动滤波处理完毕。

  按键FIFO程序在嘀嗒定时器中定期的执行检测,不需要在主程序中一直做检测,这样可以有效地降低系统资源消耗。

19.3.2 按键FIFO的实现

在bsp_key.h 中定了结构体类型KEY_FIFO_T。这只是类型声明,并没有分配变量空间。


#define KEY_FIFO_SIZE    10

typedef struct

{

    uint8_t Buf[KEY_FIFO_SIZE];        /* 键值缓冲区 */

    uint8_t Read;                    /* 缓冲区读指针1 */

    uint8_t Write;                 /* 缓冲区写指针 */

    uint8_t Read2;                 /* 缓冲区读指针2 */

}KEY_FIFO_T;

在bsp_key.c 中定义s_tKey结构变量, 此时编译器会分配一组变量空间。


static KEY_FIFO_T s_tKey;        /* 按键FIFO变量,结构体 */

一般情况下,只需要一个写指针Write和一个读指针Read。在某些情况下,可能有两个任务都需要访问按键缓冲区,为了避免键值被其中一个任务取空,我们添加了第2个读指针Read2。出厂程序在bsp_Idle()函数中实现的按K1K2组合键截屏的功能就使用的第2个读指针。


当检测到按键事件发生后,可以调用 bsp_PutKey函数将键值压入FIFO。下面的代码是函数的实现:


/*

*********************************************************************************************************

*    函 数 名: bsp_PutKey

*    功能说明: 将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。

*    形    参: _KeyCode : 按键代码

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_PutKey(uint8_t _KeyCode)

{

    s_tKey.Buf[s_tKey.Write] = _KeyCode;


    if (++s_tKey.Write  >= KEY_FIFO_SIZE)

    {

        s_tKey.Write = 0;

    }

}

这个bsp_PutKey函数除了被按键检测函数调用外,还可以被其他底层驱动调用。比如红外遥控器的按键检测,也共用了同一个按键FIFO。遥控器的按键代码和主板实体按键的键值统一编码,保持键值唯一即可实现两套按键同时控制程序的功能。


应用程序读取FIFO中的键值,是通过bsp_GetKey函数和bsp_GetKey2函数实现的。我们来看下这两个函数的实现:


/*

*********************************************************************************************************

*    函 数 名: bsp_GetKey

*    功能说明: 从按键FIFO缓冲区读取一个键值。

*    形    参:  无

*    返 回 值: 按键代码

*********************************************************************************************************

*/

uint8_t bsp_GetKey(void)

{

    uint8_t ret;


    if (s_tKey.Read == s_tKey.Write)

    {

        return KEY_NONE;

    }

    else

    {

        ret = s_tKey.Buf[s_tKey.Read];


        if (++s_tKey.Read >= KEY_FIFO_SIZE)

        {

            s_tKey.Read = 0;

        }

        return ret;

    }

}


/*

*********************************************************************************************************

*    函 数 名: bsp_GetKey2

*    功能说明: 从按键FIFO缓冲区读取一个键值。独立的读指针。

*    形    参:  无

*    返 回 值: 按键代码

*********************************************************************************************************

*/

uint8_t bsp_GetKey2(void)

{

    uint8_t ret;


    if (s_tKey.Read2 == s_tKey.Write)

    {

        return KEY_NONE;

    }

    else

    {

        ret = s_tKey.Buf[s_tKey.Read2];


        if (++s_tKey.Read2 >= KEY_FIFO_SIZE)

        {

            s_tKey.Read2 = 0;

        }

        return ret;

    }

}

返回值KEY_NONE = 0, 表示按键缓冲区为空,所有的按键时间已经处理完毕。按键的键值定义在 bsp_key.h文件,下面是具体内容:


typedef enum

{

    KEY_NONE = 0,            /* 0 表示按键事件 */


    KEY_1_DOWN,            /* 1键按下 */

    KEY_1_UP,                /* 1键弹起 */

    KEY_1_LONG,            /* 1键长按 */


    KEY_2_DOWN,            /* 2键按下 */

    KEY_2_UP,                /* 2键弹起 */

    KEY_2_LONG,            /* 2键长按 */


    KEY_3_DOWN,            /* 3键按下 */

    KEY_3_UP,                /* 3键弹起 */

    KEY_3_LONG,            /* 3键长按 */


    KEY_4_DOWN,            /* 4键按下 */

    KEY_4_UP,                /* 4键弹起 */

    KEY_4_LONG,            /* 4键长按 */


    KEY_5_DOWN,            /* 5键按下 */

    KEY_5_UP,                /* 5键弹起 */

    KEY_5_LONG,            /* 5键长按 */


    KEY_6_DOWN,            /* 6键按下 */

    KEY_6_UP,                /* 6键弹起 */

    KEY_6_LONG,            /* 6键长按 */


    KEY_7_DOWN,            /* 7键按下 */

    KEY_7_UP,                /* 7键弹起 */

    KEY_7_LONG,            /* 7键长按 */


    KEY_8_DOWN,            /* 8键按下 */

    KEY_8_UP,                /* 8键弹起 */

    KEY_8_LONG,            /* 8键长按 */


    /* 组合键 */

    KEY_9_DOWN,            /* 9键按下 */

    KEY_9_UP,                /* 9键弹起 */

    KEY_9_LONG,            /* 9键长按 */


    KEY_10_DOWN,            /* 10键按下 */

    KEY_10_UP,            /* 10键弹起 */

    KEY_10_LONG,            /* 10键长按 */

}KEY_ENUM;

必须按次序定义每个键的按下、弹起和长按事件,即每个按键对象(组合键也算1个)占用3个数值。我们推荐使用枚举enum, 不用#define的原因:


  便于新增键值,方便调整顺序。

  使用{ } 将一组相关的定义封装起来便于理解。

  编译器可帮我们避免键值重复。

我们来看红外遥控器的键值定义,在bsp_ir_decode.h文件。因为遥控器按键和主板按键共用同一个FIFO,因此在这里我们先贴出这段定义代码,让大家有个初步印象。


/* 定义红外遥控器按键代码, 和bsp_key.h 的物理按键代码统一编码 */

typedef enum

{

    IR_KEY_STRAT     = 0x80,

    IR_KEY_POWER     = IR_KEY_STRAT + 0x45,

    IR_KEY_MENU     = IR_KEY_STRAT + 0x47, 

    IR_KEY_TEST     = IR_KEY_STRAT + 0x44,

    IR_KEY_UP     = IR_KEY_STRAT + 0x40,

    IR_KEY_RETURN    = IR_KEY_STRAT + 0x43,

    IR_KEY_LEFT    = IR_KEY_STRAT + 0x07,

    IR_KEY_OK        = IR_KEY_STRAT + 0x15,

    IR_KEY_RIGHT    = IR_KEY_STRAT + 0x09,

    IR_KEY_0        = IR_KEY_STRAT + 0x16,

    IR_KEY_DOWN    = IR_KEY_STRAT + 0x19,

    IR_KEY_C        = IR_KEY_STRAT + 0x0D,

    IR_KEY_1        = IR_KEY_STRAT + 0x0C,

    IR_KEY_2        = IR_KEY_STRAT + 0x18,

    IR_KEY_3        = IR_KEY_STRAT + 0x5E,

    IR_KEY_4        = IR_KEY_STRAT + 0x08,

    IR_KEY_5        = IR_KEY_STRAT + 0x1C,

    IR_KEY_6        = IR_KEY_STRAT + 0x5A,

    IR_KEY_7        = IR_KEY_STRAT + 0x42,

    IR_KEY_8        = IR_KEY_STRAT + 0x52,

    IR_KEY_9        = IR_KEY_STRAT + 0x4A,    

}IR_KEY_E;

我们下面来看一段简单的应用。这个应用的功能是:主板K1键控制LED1指示灯;遥控器的POWER键和MENU键控制LED2指示灯。


#include "bsp.h"


int main(void)

{

    uint8_t ucKeyCode;

            

    bsp_Init();


    IRD_StartWork();    /* 启动红外解码 */


    while(1)

    {

        bsp_Idle();

        

        /* 处理按键事件 */

        ucKeyCode = bsp_GetKey();

        if (ucKeyCode > 0)

        {

            /* 有键按下 */

            switch (ucKeyCode)

            {

                case KEY_DOWN_K1:        /* K1键按下 */

[1] [2] [3] [4] [5] [6]
关键字:STM32F429  GPIO  按键FIFO 引用地址:第19章 STM32F429的GPIO应用之按键FIFO

上一篇:第20章 STM32F429的GPIO应用之无源蜂鸣器
下一篇:第18章 STM32F429的GPIO应用之跑马灯

推荐阅读最新更新时间:2024-10-12 17:59

STM32F407学习笔记二(GPIO
从上文可以看到,在系统启动过程中会对系统时钟进行一次配置,有了时钟源以后,我们来看简单的GPIO配置:对于GPIO,使用寄存器进行配置时:我们先来看一个例子: #include stm32f4xx.h uint32_t Gb_TimingDelay; void Delay(uint32_t nTime); void main() { SysTick_Config(SystemCoreClock/1000);//1ms based time RCC- AHB1ENR |= RCC_AHB1ENR_GPIODEN; //ENABLE GPIOD RCC- APB2ENR |= RCC_APB2ENR_SYSCFGEN
[单片机]
STM32F407学习笔记二(<font color='red'>GPIO</font>)
基于MTM809的硬盘MP3的硬件设计与分析
  一 MTM809处理器系统简介 MTM809是一款以8051CPU为核心的嵌入式处理器。作为专门应用于便携式系统的MCU,它采用0.25μm CMOS工艺,内核运行电压2.5V,IO输入输出电压3.3V。它的在片资源丰富,内嵌有USB2.0控制器,一个FLASH卡读卡器控制器,ATA/ATAPI硬盘控制器及一个MP3解码器等外设。在各个外设之间的数据传送由于不需要CPU的介入而使得数据的传送效率很高。MTM809的主要特性如下:   1.256B的内部在片数据存储器,10KB的数据SRAM,64KB的ROM和内嵌I2C,SPI和UART/RS-232接口,2个在片ADC,RTC电路。   2.与USB2.0全兼容的
[嵌入式]
基于MTM809的硬盘MP3的硬件设计与分析
按键实验(GPIO的输入模式)
@按键实验(GPIO的输入模式) #引言 在之前的实验中,小罗同学使用的都只是GPIO的输出模式,这次的按键实验虽然比较简单,但也是我第一次接触GPIO的输入,所以还是想写点东西记录一下。 #按键模块电路图 我手中的开发板除去复位按键后还有其余四个按键,电路结构图如下: 以上四个按键所对应的管脚编号分别为:PA0、PE4、PE3、PE2。由图可知,KEY_UP为高电平有效,其他三个均为低电平有效。 #按键配置 大家都知道,在使用引脚之前首先要进行相应的配置来初始化,相关代码如下: void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; //先对挂接在
[单片机]
<font color='red'>按键</font>实验(<font color='red'>GPIO</font>的输入模式)
stm32_GPIO模拟I2c读写EEPROM
/* 下面给出STM32通过GPIO模拟I2C读写EEPROM程序 */ #define SCL_H GPIOB- BSRR = GPIO_Pin_6 #define SLC_L GPOIB- BRR = GPIO_Pin_6 #define SDA_H GPIOB- BSRR = GPIO_Pin_7 #define SDA_L GPIOB- BRR = GPIO_Pin_7 #define SCL_read GPIOB-IDR & GPIO_Pin_6 #define SDA_read GPIOB- IDR & GPIO_Pin_7 #define I2C_PageSize 8 void I2C_GPIO_C
[单片机]
STM32学习手记①-GPIO的输入、输出、检测
/**************************************************************************** * * 文件名: main.c * 内容简述: 本例程演示如何操作GPIO输入和输出。 * 通过检测USER1、USER2按键的状态,点亮不同的LED,同时输出键值到串口1 * * 按键口线分配: * USER1键 : PC6 (低电平表示按下) * USER2键 : PC7 (低电平表示按下) * * LED口线分配: * LED1 : PE0 (输出0点亮) * LED2 : PE1 (输出0点亮) * */ #include "stm32f10x_lib.h"
[单片机]
stm32中gpio的学习浅谈
在基本入门熟悉了开发环境后,我开始学习stm32中的gpio口用法,学习的方法还是最直观简便的先控制LED灯。然而stm32中点亮led倒没有51单片机那么简便。有过51单片机学习经验的伙伴们,肯定熟悉下图的代码(51中控制led的程序)。 如果同样方法写在stm32中肯定是不行的了,首先因为stm32中没有51头文件中那样定义好了P0口可以直接进行总线操作,其次stm32中的gpio口默认是输入模式并且还需要打开相关引脚口的时钟。可能很多人会觉得stm32中官方给的固件库可以直接操作寄存器从而控制gpio口,但是我觉得我这样的初学者还是多了解下底层的方法便于更好的理解。要实现stm32点亮一个led灯(即让gpio口输
[单片机]
stm32中<font color='red'>gpio</font>的学习浅谈
KST-STM32 学习之GPIO_Speed
一、 GPIO模式配置 1、输入/输出模式(参考stm32手册) 2、GPIO输出模式下,几种速度的区别: (1). GPIO 引脚速度: GPIO_Speed_2MHz (10MHz, 50MHz) ; 又称输出驱动电路的响应速度:(芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路,用户可以根据自己的需要选择合适的驱动电路,通过选择速度来选择不同的输出驱动模块,达到最佳的噪声控制和降低功耗的目的。 可理解为:输出驱动电路的带宽,即一个驱动电路可以不失真地通过信号的最大频率。 (如果一个信号的频率超过了驱动电路的响应速度,就有可能信号失真。) eg:如果信号频率为10MHz,而你配置了2M
[单片机]
S3C2440 gpio + main
举例 start.S .globl _start _start: /* 关看门狗 */ /* 往WTCON(0x53000000)写0 */ ldr r0, =0x53000000 mov r1, #0 str r1, @ str, store, /* 设置栈大小,不能大于4k,因为片内SRAM只有4k */ ldr sp, =1024*4 bl main halt: b halt main.c #define GPFCON (*(volatile unsigned int*)0x56000050) #define GPFDAT (*(volatile unsigned
[单片机]
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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