【STM32嵌入式系统设计与开发】——6矩阵按键应用(4x4)
这里写目录标题
- 一、任务描述
- 二、任务实施
- 1、SingleKey工程文件夹创建
- 2、函数编辑
- (1)主函数编辑
- (2)LED IO初始化函数(LED_Init())
- (3)开发板矩阵键盘IO初始化(ExpKeyBordInit())
- (3)开发板矩阵按键LED控制(ExpKeyBordInit())
- (4)键盘IO(PE12-PE15)设置为输入模式函数(KeyBordSetIn())
- (5)键盘IO(PE8-PE11)设置为输出模式函数(KeyBordSetOut())
- (6)矩阵键盘行列读写操作函数(GPIO_KEY_RW())
- (7)矩阵键盘键值扫描函数(KeyBoardScan())
- 3、宏定义
- (1)源文件添加头文件
- (2)按键头文件编辑
- 4、知识链接
- (1)GPIO_SetBits()
- (2)GPIO_ResetBits()
- (3)GPIO_ReadOutputData()
- (4)GPIO_ReadInputData()
- 5、工程测试
STM32资料包:
百度网盘下载链接:链接:https://pan.baidu.com/s/1mWx9Asaipk-2z9HY17wYXQ?pwd=8888
提取码:8888
一、任务描述
二、任务实施
观察电路图,DK1-DK16按键有按下PE8 - PE11端口下拉输入,PE12-PE15输出,按键按下从而组合成16种控制led灯。
参考排列组合,如同KE1按下时,PE8为与PE12导通。
1、SingleKey工程文件夹创建
步骤1:复制工程模板“1_Template”重命名为“4_KeyBoard”
步骤2:修改项目工程名,先删除projects文件夹内除了Template.uvprojx文件外的所有内容并修改为“KeyBoard.uvprojx”。并删除output/obj和output/lst中的所有文件。
步骤3:运行“KeyBoard.uvprojx”打开目标选项“Options for Target”中的“Output”输出文件,并修改可执行文件名称为“KeyBoard”点击“OK”保存设置。最后点击“Rebuild”编译该工程生成KeyBoard文件。
步骤4:复制“2_LEDTest”中的"1_LED"文件复制到hardware中。
步骤5:新建“2_KeyBoard”文件,并在该文件夹下新建“KeyBoard.c”和“KeyBoard.h”两个文件。
步骤6:工程组文件中添加“led.c”和“KeyBoard.c”文件。
步骤7:目标选项添加添加头文件路径
2、函数编辑
(1)主函数编辑
步骤1:端口初始化准备
//函数初始化,端口准备
delay_init(); //启动滴答定时器,延时函数
LED_Init(); //板载LED初始化
ExpKeyBordInit(); //开发板按键初始化
步骤2:循环工作代码编辑,读取按键并判断按键模式,按下则点亮led灯,否则LED灯熄灭
while(1)
{
KeyValue = ExpKeyScan(0);
switch(KeyValue)
{
case 1:LED_MODE1(KeyValue);
break;
case 2:LED_MODE1(KeyValue);
break;
case 3:LED_MODE1(KeyValue);
break;
case 4:LED_MODE1(KeyValue);
break;
case 5:LED_MODE1(KeyValue);
break;
case 6:LED_MODE1(KeyValue);
break;
case 7:LED_MODE1(KeyValue);
break;
case 8:LED_MODE1(KeyValue);
break;
case 9:LED_MODE1(KeyValue);
break;
case 10:LED_MODE1(KeyValue);
break;
case 11:LED_MODE1(KeyValue);
break;
case 12:LED_MODE1(KeyValue);
break;
case 13:LED_MODE1(KeyValue);
break;
case 14:LED_MODE1(KeyValue);
break;
case 15:LED_MODE1(KeyValue);
break;
case 16:LED_MODE1(KeyValue);
break;
}
}
(2)LED IO初始化函数(LED_Init())
/*********************************************************************
@Function : 开发板LED IO初始化
@Parameter : N/A
@Return : N/A
**********************************************************************/
void ExpLEDInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定义 GPIO 初始化结构体变量
/* 时钟使能 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能 GPIOA 时钟
/* 引脚配置 */
GPIO_InitStructure.GPIO_Pin = LED_ALL; // D1-D8->PA0-PA7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置引脚为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置引脚的输出速度为 50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); // 应用以上配置到 GPIOA 上
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
}
(3)开发板矩阵键盘IO初始化(ExpKeyBordInit())
初始化PE8 - PE15端口,并为推挽输出。
/*********************************************************************
@Function : 矩阵键盘IO初始化
@Parameter : None
@Return : None
**********************************************************************/
void ExpKeyBordInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*时钟使能*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
/*引脚配置*/
GPIO_InitStructure.GPIO_Pin = KEY_ALL; // 将开发板四个按键连接到PE8~PE15
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置 GPIO 输出速度为 50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure);
/*引脚初始电平设置*/
GPIO_SetBits(GPIOE,KEY_ALL);
}
(3)开发板矩阵按键LED控制(ExpKeyBordInit())
初始化PE8 - PE15端口,并为推挽输出。
/*********************************************************************
@Function : 开发板矩阵按键LED控制
@Parameter : KeyValue:矩阵按键状态
每一位表示一个矩阵按键的状态,每位按键对于相应led模式
@Return : N/A
**********************************************************************/
void LED_MODE1(uint32_t KeyValue)
{
switch(KeyValue)
{
case 1:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED1 = 0;delay_ms(1000);
break;
case 2:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED2 = 0;delay_ms(1000);
break;
case 3:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED3 = 0;delay_ms(1000);
break;
case 4:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED4 = 0;delay_ms(1000);
break;
case 5:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED5 = 0;delay_ms(1000);
break;
case 6:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED6 = 0;delay_ms(1000);
break;
case 7:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED7 = 0;delay_ms(1000);
break;
case 8:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED8 = 0;delay_ms(1000);
break;
case 9:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED1 = 0;
LED2 = 0;delay_ms(1000);
break;
case 10:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED3 = 0;
LED4 = 0;delay_ms(1000);
break;
case 11:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED5 = 0;
LED6 = 0;delay_ms(1000);
break;
case 12:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
LED7 = 0;
LED8 = 0;delay_ms(1000);
break;
case 13:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
delay_ms(1000);
break;
case 14:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
for(int i = 0;i < 8;i++)
{
PAout(i) = 0;
delay_ms(500);
}
for(int i = 0;i < 8;i++)
{
PAout(i) = 1;
delay_ms(500);
}
break;
case 15:
GPIO_SetBits(GPIOA, LED_ALL); // 将 GPIOA 的引脚 PA0-PA7 设置为高电平,使得开发板 LED 灭
break;
}
}
(4)键盘IO(PE12-PE15)设置为输入模式函数(KeyBordSetIn())
将指定的 GPIO 引脚设置为输入模式,并启用下拉输入。
/*********************************************************************
@Function : 键盘IO设置为输入模式
@Parameter : KEYIO :要设置的IO
@Return : N/A
**********************************************************************/
void KeyBordSetIn(uint16_t KEYIO)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*时钟使能*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
/*引脚配置*/
GPIO_InitStructure.GPIO_Pin = KEYIO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
(5)键盘IO(PE8-PE11)设置为输出模式函数(KeyBordSetOut())
将指定的 GPIO 引脚设置为输入模式,并启用下拉输入
/*********************************************************************
@Function : 键盘IO设置为输出模式
@Parameter : KEYIO :要设置为输出模式的GPIO引脚
@Return : N/A
**********************************************************************/
void KeyBordSetOut(uint16_t KEYIO)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能GPIOE时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
/* 配置引脚为输出模式 */
GPIO_InitStructure.GPIO_Pin = KEYIO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
(6)矩阵键盘行列读写操作函数(GPIO_KEY_RW())
从矩阵键盘中读取按键状态,通过设置特定的 GPIO 引脚为输入或输出模式,并根据参数设置写入行或列,并且根据读取的输入和输出数据返回一个表示按键状态的字节值
/*********************************************************************
@Function : 矩阵键盘行列读写操作
@Parameter : ReadIo :读输入的IO
WirteIo :写输出的IO
@Return : 行列IO输出状态
**********************************************************************/
uint8_t GPIO_KEY_RW(uint16_t ReadIo,uint16_t WirteIo)
{
uint16_t Wdata=0,Rdata=0;
//写
KeyBordSetOut(KEY_ALL); //设置IO
if(WirteIo==0x0f00)
GPIO_SetBits(GPIOE,KEY_LINE); //写行
else
GPIO_ResetBits(GPIOE,KEY_LIST); //写列
Wdata = GPIO_ReadOutputData(GPIOE); //读输出
Wdata &= WirteIo; //取有效区域
//读
KeyBordSetIn(ReadIo); //设置IO
Rdata = GPIO_ReadInputData(GPIOE); //读输入
Rdata &= ReadIo; //取有效区域
//状态返回
Rdata |= Wdata; //合并两次读取的数据
return (uint8_t)(Rdata>>8); //移位返回
}
(7)矩阵键盘键值扫描函数(KeyBoardScan())
扫描矩阵键盘的按键状态,通过检测行列按键的组合来确定按下的具体按键,并在按键按下后进行一定的延时消抖处理,最终返回表示按键值的一个字节。
/*********************************************************************
@Function : 矩阵键盘键值扫描
@Parameter : N/A
@Return : 键值
**********************************************************************/
uint8_t KeyBoardScan(void)
{
uint8_t KeyValue=0,Key=0;
uint8_t a = 0;
if(GPIO_KEY_RW(KEY_LIST,KEY_LINE)!=0x0f) //读取按键是否按下
{
delay_ms(10);//延时10ms进行消抖
if(GPIO_KEY_RW(KEY_LIST,KEY_LINE)!=0x0f)//再次检测键盘是否按下
{
//测试列
Key = GPIO_KEY_RW(KEY_LIST,KEY_LINE);
switch(Key)
{
case(0x1F): KeyValue=1;break;//1列
case(0x2F): KeyValue=2;break;//2列
case(0x4F): KeyValue=3;break;//3列
case(0x8F): KeyValue=4;break;//4列
}
//测试行
Key = GPIO_KEY_RW(KEY_LINE,KEY_LIST);
switch(Key)
{
case(0x0E): KeyValue=KeyValue;break; //1行
case(0x0D): KeyValue=KeyValue+4;break; //2行
case(0x0B): KeyValue=KeyValue+8;break; //3行
case(0x07): KeyValue=KeyValue+12;break;//4行
}
//松手检测
while((a<50)&&(Key!=0x00))
{
delay_ms(5);
Key = GPIO_KEY_RW(KEY_LINE,KEY_LIST);
a+=1;
}
}
}
return KeyValue;
}
3、宏定义
(1)源文件添加头文件
步骤1:按键功能文件中添加相关头文件,源文件报错消失部分
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include ".\delay\delay.h"
#include "SingleKey.h"
步骤2:主函数添加所需的led和KeyBoard头文件,主源文件部分报错消失
/***********Hardweare***************/
#include "led.h"
#include "KeyBoard.h"
(2)按键头文件编辑
点击编译显示报错
步骤1:创建一个宏定义保护
#ifndef _SINGLEKEY_H
#define _SINGLEKEY_H
#endif
步骤2:添加宏定义
/******************矩阵键盘IO预定义********************/
#define KEY_LINE GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 //行
#define KEY_LIST GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 //列
#define KEY_ALL GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | \
GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 //行和列
//行列操作定义
#define RLine_WList 0 //读行写列
#define RList_WLine 1 //读列写行
步骤3:添加函数声明
void ExpKeyBordInit(void); //矩阵键盘IO初始化
uint8_t KeyBoardScan(void); // 矩阵键盘键值扫描
步骤4:添加数据类型和宏的头文件
#include <stdint.h>
//键值枚举
enum KeyBoard
{
KEY_NO=0,
KEY_K1,
KEY_K2,
KEY_K3,
KEY_K4,
KEY_K5,
KEY_K6,
KEY_K7,
KEY_K8,
KEY_K9,
KEY_K10,
KEY_K11,
KEY_K12,
KEY_K13,
KEY_K14,
KEY_K15,
KEY_K16
};
4、知识链接
(1)GPIO_SetBits()
将指定的 GPIO 引脚设置为高电平(或逻辑“1”)状态
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
(2)GPIO_ResetBits()
将指定的 GPIO 引脚设置为低电平(或逻辑“0”)状态
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
(3)GPIO_ReadOutputData()
这个函数用于读取配置为输出的 GPIO(通用输入/输出)引脚的当前状态。换句话说,它读取当前从配置为输出的 GPIO 引脚驱动出去的数据。
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
GPIOx:指定要读取输出数据的 GPIO 端口,如 GPIOA、GPIOB 等
(4)GPIO_ReadInputData()
相反,这个函数用于读取配置为输入的 GPIO 引脚的当前状态。它读取当前在配置为输入的 GPIO 引脚上检测到的数据。
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
GPIOx:指定要读取输出数据的 GPIO 端口,如 GPIOA、GPIOB 等