KEY

  • 按键输入:通过按键向芯片输入信号,一般的按键都会接上拉电阻接芯片,另一端接地,按下将芯片的引脚拉低。因此在初始化函数中芯片引脚的工作模式为上拉输入,引脚的初始电平为高电平。
  • 矩阵按键:通过芯片向矩阵按键(4*4)的行和列接口分别输入0000 1111和1111 0000检测两次,判断出按键的坐标。

1.KEY初始化

CT117E开发板KEY1-4引脚,分别为PA0、PA8、PB1、PB2。
KEY_Init()函数注意的问题:
按键输入时GPIO的工作模式为上拉输入。

void KEY_Init()
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_8;GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU;  //上拉输入GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;GPIO_Init(GPIOB,&GPIO_InitStructure);
}

2.按键抖动产生的原因

通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。

由于单片机的运行速度非常快,按下一次按键,可能在A点检测到一次低电平,在B点检测到一次高电平,在C点又检测到一次低电平。同时抖动是随机,不可测的。那么按下一次按键,抖动可能对会让单片机误以为按下多次按键。
设置系统定时器,50ms进行一次按键扫描,就尽可能的避免按键抖动对按键检测的影响。

3.短按和长按

按键输入最主要的一步是按键扫描,方法有很多。
三行代码的按键扫描(不是原创)

#define KEY1 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)//读取指定端口管脚的输入
#define KEY2 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8)
#define KEY3 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)
#define KEY4 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2)
#define KEYINPUT KEY1|(KEY2<<1)|(KEY3<<2)|(KEY4<<3)|0XF0 //将四个按键的IO口附在0xf0的后四位上,分别为KEY4/3/2/1
u8 trg = 0;
u8 cont = 0;void KEY_Reading()
{u8 read_data = (KEYINPUT)^(0xff);trg = read_data&(read_data^cont);cont = read_data;
}
  • 按键配置的上拉输入,当没有按键按下时,KEYINPUT为0xff;read_data = (KEYINPUT)^(0xff) = 0x00;trg = 0x00&0x00 = 0x00;cont = 0x00;
  • 当按键4按下时,KEYINPUT为0xf7; read_data = 0x08;计算trg的cont为按键未按下时的cont:trg = 0x08&(0x08^0x00) = 0x08;cont = 0x08依次列推,trg 为0x04、0x02、0x01;
  • 当按键4长按时,第二次进入按键扫描时,按键4 trg = 0x08&(0x08^0x08) = 0x00;cont的值保持不变,按键4、3、2、1分别长按:cont = 0x08、0x04、0x02、0x01;
  • 按键短按的标志位为trg 按键长按的标志位为cont.
  • 在SYSTICK中断处理函数每50ms进行一次按键扫描,如果按键判断语句放在中断外时,必须要用(长按标志位cont & KEY_FLAG按键扫描标志位)作为判断条件,if语句中变量累加20次后在执行操作,达到地效果为按键长按1s后执行操作;如果按键判断语句放在SYSTICK中断内则不需要。

按键1短按:蜂鸣器响100ms,长按:点亮第二个LED;

按键4短按:点亮第三个LED,长按:熄灭第三个LED;

u8 time_count = 0;  //长按时间累加位
u8 time_count1 = 0;
extern u8 trg;
extern u8 cont;
 if(KEY_FLAG) //按键扫描{KEY_FLAG = 0;  KEY_Reading();}if(trg == 0x01){Beep_Flag = 1;}if(cont == 0x01 && KEY_FLAG) //按键1长按 按键判断语句放在中断外应把(长按标志位cont & KEY_FLAG按键扫描标志位)作为判断条件{time_count ++;if(time_count == 20)//50ms进行一次按键扫描,长按时间为1s触发{time_count = 0;GPIOC->ODR &= ~(1<<8);GPIOD->ODR |= (1<<2);//打开锁存器GPIOD->ODR &= ~(1<<2);//关闭锁存器  }}if(trg == 0x08){GPIOC->ODR &= ~(1<<9);GPIOD->ODR |= (1<<2);//打开锁存器GPIOD->ODR &= ~(1<<2);//关闭锁存器}if(cont == 0x08 && KEY_FLAG){time_count1 ++;if(time_count1 == 20)//50ms进行一次按键扫描,长按时间为1s触发{time_count1 = 0;GPIOC->ODR |= (1<<9);GPIOD->ODR |= (1<<2);//打开锁存器GPIOD->ODR &= ~(1<<2);//关闭锁存器  }}

4.矩阵按键

经过三行代码的按键扫描的启发,自己写了一下矩阵按键的三行代码。
与按键输入不同的点:

  1. 矩阵按键需要扫描两次,分别是行和列。
  2. 矩阵按键的行和列端口的初始值由芯片输出,要自己设定。
    矩阵按键扫描函数

  • 4*4的矩阵按键需要8个IO口,我把它接在了PC0-PC7;矩阵按键第一列到第四列是PC0-PC3,第一行到第四行是PC4-PC7。
  • 检测方式:行或列分别赋0和1,检测赋0的行或列,变为1的行或列为按键的坐标。
  • KEYINPUT宏定义,PC0-PC8由低位到高位赋值,与按键输入方法相同。
#define KEY0 GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_0)//读取指定端口管脚的输入
#define KEY1 GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_1)
#define KEY2 GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_2)
#define KEY3 GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_3)
#define KEY4 GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_4)
#define KEY5 GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5)
#define KEY6 GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_6)
#define KEY7 GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_7)#define KEYINPUT   KEY0|(KEY1<<1)|(KEY2<<2)|(KEY3<<3)|(KEY4<<4)|(KEY5<<5)|(KEY6<<6)|(KEY7<<7)|0X00//矩阵按键的八个IO口附在0x00的上,分别为KEY7/6/5/4/3/2/1/0
  • 矩阵按键的检测分为检测行和列,因此分成了两个函数,行检测函数和列检测函数。
u8 trg_line = 0;    //行检测标志位
u8 cont_line = 0;
u8 trg_column = 0; //列检测标志位
u8 cont_column = 0;

行检测函数

初始值设置:列的IO口均为1,行的IO口均为0,因此KEYINPUT = 0x0f;与按键扫描的原理相同,readline =(KEYINPUT)^(0x0f);因为KEYINPUT异或和它相等的值才为0x00,这样trg_line和trg_column的初始值也为0。当第一行有按键按下时,KEYINPUT = 0x1f, readline = 0x1f^0x0f = 0x10;  trg_line = 0x10(0x10^0x00)= 0x10; 依次列推,trg_line = 0x20、0x40、0x80。
void MATRIX_KEY_Readline()       //读取是来自矩阵按键的哪一行
{u8 read_line;GPIO_SetBits(GPIOC, GPIO_Pin_0);  //0000 1111GPIO_SetBits(GPIOC, GPIO_Pin_1);GPIO_SetBits(GPIOC, GPIO_Pin_2);GPIO_SetBits(GPIOC, GPIO_Pin_3);GPIO_ResetBits(GPIOC, GPIO_Pin_4);GPIO_ResetBits(GPIOC, GPIO_Pin_5);GPIO_ResetBits(GPIOC, GPIO_Pin_6);GPIO_ResetBits(GPIOC, GPIO_Pin_7);read_line = (KEYINPUT)^(0x0f);trg_line = read_line&(read_line^cont_line);cont_line = read_line;
}

列检测函数

初始值设置:列的IO口均为0,行的IO口均为1,因此KEYINPUT = 0xf0,与按键扫描的原理相同,read_column =
(KEYINPUT)^(0xf0);因为KEYINPUT异或和它相等的值才为0x00,这样cont_line和cont_column的初始值也为0。当第一列有按键按下时,KEYINPUT = 0xf1, read_column = 0xf1^0xf0 = 0x01;  trg_column = 0x01(0x01^
0x00) = 0x01; 依次列推,trg_column = 0x02、0x04、0x08。
void MATRIX_KEY_Readcolumn() //读取是来自矩阵按键的哪一列
{u8 read_column;GPIO_ResetBits(GPIOC, GPIO_Pin_0);  //1111 0000GPIO_ResetBits(GPIOC, GPIO_Pin_1);GPIO_ResetBits(GPIOC, GPIO_Pin_2);GPIO_ResetBits(GPIOC, GPIO_Pin_3);GPIO_SetBits(GPIOC, GPIO_Pin_4);GPIO_SetBits(GPIOC, GPIO_Pin_5);GPIO_SetBits(GPIOC, GPIO_Pin_6);GPIO_SetBits(GPIOC, GPIO_Pin_7);read_column = (KEYINPUT)^(0xf0);trg_column = read_column&(read_column^cont_column);cont_column = read_column;
}

行、列检测函数内要用GPIO_SetBits();函数设置行和列端口的初始值;

5.矩阵按键初始化函数

void MATRIX_KEY_Init()   //矩阵按键初始化
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_Out_PP;  //与按键输入配置的上拉输入不同,矩阵按键配置推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC,&GPIO_InitStructure);
}

注意:GPIO的工作模式为推挽输出。

按键(1,1)点亮第三个LED,按键(2,2)熄灭第三个LED。

 if(KEY_FLAG) //按键扫描{KEY_FLAG = 0;      MATRIX_KEY_Readline();MATRIX_KEY_Readcolumn();          }if(trg_line == 0x10 && trg_column == 0x01) //第一行第一个按键按下{GPIOC->ODR &= ~(1<<9);GPIOD->ODR |= (1<<2);//打开锁存器GPIOD->ODR &= ~(1<<2);//关闭锁存器}if(trg_line == 0x20 && trg_column == 0x02)   //第二行第二个按键按下{GPIOC->ODR |= (1<<9);GPIOD->ODR |= (1<<2);//打开锁存器GPIOD->ODR &= ~(1<<2);//关闭锁存器}

6.GPIO口输入和输出类型

Cortex-M3 里,GPIO 工作模式的配置有 8 种:

  1. GPIO_Mode_IPU 上拉输入
  2. GPIO_Mode_IPD 下拉输入
  3. GPIO_Mode_AIN 模拟输入
  4. GPIO_Mode_IN_FLOATING 浮空输入
  5. GPIO_Mode_Out_OD 开漏输出
  6. GPIO_Mode_AF_OD 复用开漏输出
  7. GPIO_Mode_AF_OD 复用开漏输出
  8. GPIO_Mode_AF_PP 复用推挽输出

上拉输入/下拉输入/模拟输入

字面意思理解即可。

浮空输入

浮空输入状态下,IO的电平状态是不确定的,完全由外部输入决定,如果在该引脚悬空的情况下,读取该端口的电平
是不确定的。

推挽输出

可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通
的时候另一个截止。

开漏输出

输出端相当于三极管的集电极。要得到高电平状态需要上拉电阻才行。适合于做电流型的驱动其吸收电流的能力相对
强(一般 20MA 以内)。
一般来说,开漏是用来连接不同电平的器件,匹配电平用的,因为开漏引脚不连接外部的上拉电阻时,只能输出低电
平,如果需要同时具备输出高电平的功能,则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以
改变传输电平。比如加上上拉电阻就可以提供 TTL/CMOS 电平输出等。(上拉电阻的阻值决定了逻辑电平转换的沿的
速度 。阻值越大,速度越低功耗越小, 所以负载电阻的选择要兼顾功耗和速度。)

图中,左边是推挽输出模式,其中比较器输出高电平时下面的 PNP 三极管截止,而上面 NPN 三极管导通,输出电
平VS+;当比较器输出低电平时则恰恰相反, PNP 三极管导通,输出和地相连,为低电平。右边可以理解为开漏输
出形式,需要接上拉。

复用开漏输出、复用推挽输出

可以理解为 GPIO 口被用作第二功能时的配置情况(即并非作为通用 IO口使用)。

(以上仅个人观点)

蓝桥杯嵌入式总结(KEY配置_按键扫描(三行代码)_矩阵按键_GPIO口输入和输出类型)相关推荐

  1. 【蓝桥杯嵌入式备赛】10.拓展板数码管、ADC按键及光敏电阻

    引子 进入国赛以后,拓展板也是比较重要的一部分.上面的有些东西也是第一次接触,所以借这个机会把学习的过程记录下来,帮助大家排雷. 拓展板上资源大致分为:数码管.ADC按键.光敏电阻.温度传感器.温湿度 ...

  2. 【蓝桥杯嵌入式】【STM32】11_2016_第七届_蓝桥杯_省赛_电压测量监控设备

    文章目录 前言 设置滴答定时器抢占优先级为0 快速码出LED驱动 使用串口发送中断发送数据 ADC快速配置 RTC快速配置 前言   代码量较大,本文只介绍写题目时使用的固件库文件及其修改.   下载 ...

  3. 蓝桥杯嵌入式模板的构建——STM32G431RB(LCD和ADC采集电压)

    基于LED和按键的模板来写的 下载工程的链接 蓝桥杯嵌入式模板的构建--STM32G431RB(LED和按键的配置)-其它文档类资源-CSDN下载这个模板亲测有效只写了按键三和按键4对LED进行点灯和 ...

  4. 【蓝桥杯嵌入式】比赛笔记(2)根据固件库快速配置各模块初始化

    [蓝桥杯嵌入式]比赛笔记(2)根据固件库快速配置各模块初始化 因为蓝桥杯比赛时间很短,并且如果自己去记忆各模块初始化的话,也难免有问题,所以这里给出一个通过固件库的快速初始化模块的方法. 比赛提供固件 ...

  5. 【蓝桥杯嵌入式(G431-HAL库)】Led 与按键

    [备赛蓝桥杯嵌入式(G431-HAL库)]Led 与按键 实训平台:STM32G431RBT6 辅助工具:STM32CubeMx 01 基本简介 Led和按键是GPIO口输入和输出的应用. Led考察 ...

  6. 【STM32G431RBTx】备战蓝桥杯嵌入式→基本模块→KEY→单击

    文章目录 前言 KEY 1.原理图以及配置元素 2.CubeMx的配置步骤 3.生成工程 4.测试代码 5.演示效果 总结 前言 学完了LED和LCD后,我们开始学习按键. KEY 1.原理图以及配置 ...

  7. 蓝桥杯嵌入式1--滴答定时器,输入输出模式,LED,按键

    蓝桥杯嵌入式1--滴答定时器,输入输出模式,LED,按键 滴答定时器 输入输出模式 LED 按键 滴答定时器 滴答定时器是放在stm32内核中的一个定时器,用户不可以随便操作滴答定时器的寄存器,滴答定 ...

  8. 蓝桥杯嵌入式(G431RBT6):按键

    文章目录 前言 一.单击按键 二.按键长短按 总结 前言 按键可以说是蓝桥杯嵌入式组必考的内容,按键简单,但却很重要.板子上的功能都是围着按键展开的. 本文主要讲解按键长短按,定义了一个结构体,用来存 ...

  9. 突击蓝桥杯嵌入式(四)——滴答定时器、按键的三行代码消抖、LCD与ADC

    突击蓝桥杯嵌入式(四)--滴答定时器.按键的三行代码消抖.LCD与ADC 1.滴答定时器定时运行 我们利用滴答定时器,让LED每隔1s闪烁一次 //首先我们定义一个变量 __IO uint32_t u ...

  10. stm32入门学什么板子_“蓝桥杯”嵌入式stm32开发入门(1)概述

    声明:笔者绝不是给"蓝桥杯"打广告,而是笔者曾经参加过蓝桥杯嵌入式设计与开发比赛,想借此机会总结并分享自己的学习中的点点滴滴. 此系列教程将以蓝桥杯官方指定的开发板和接口板来深入浅 ...

最新文章

  1. java string与integer_Java中Integer和String浅谈
  2. idea创建、运行、打包控制台程序
  3. 定时器初值的计算方法
  4. TypeScript 的 generic 函数
  5. mysql parameter_C#MySqlParameter问题
  6. 【RAC】How to Proceed from Failed 11gR2 CRS Installation
  7. jQery 操作CSS
  8. Win10系统,开机后提示Desktp不可用的故障解决方法。
  9. 关于Python的那些话
  10. php 禁止转换,php实现十进制、三十六进制转换的函数
  11. 【python爬虫】《中华诗词大会》诗词接龙代码实现
  12. Aspose.Words 使用InsertNode()在文档末尾插入分页符
  13. 清华大学何平:央行数字货币具备许多优势 但不可盲目推进
  14. Java--->奥运五环
  15. Flutter之国际化语言
  16. cannon的英文名_卡农的作者是谁啊 此曲的赏析 英文名cannon不是大炮吗
  17. linux风扇转速,ubuntu系统调节GPU风扇转速
  18. 基于IDL的高分二号影像批量预处理程序
  19. oppo怎么广告接入_oppo信息流广告投放操作指南
  20. Supporting Online Material for Lab Experiments for the Study of Social-Ecological Systems

热门文章

  1. oracle数据库数值函数,oracle数据库函数对照表
  2. java web批量下载
  3. rails分页(kaminari)
  4. 用MySQL后电脑频繁蓝屏_电脑容易蓝屏怎么办_电脑突然开始频繁蓝屏修复方法-win7之家...
  5. 关于openlaeyrs获取谷歌卫星地图的无偏瓦片
  6. linux无线usb网卡,linux usb 无线网卡
  7. Win10 开启虚拟桌面+投屏
  8. 计算机用户名怎么改好听,电脑版本优酷视频如何设置呢称_昵称起名
  9. 【总结】1298- 如何用油猴提升前端开发效率
  10. IIS6/IIS7以上、Nginx、Apache拦截屏蔽垃圾蜘蛛UA爬行降低负载方法IIS7.5如何限制某UserAgent 禁止访问