赞
踩
请先看硬件和扫描方法的内容,本文章基于此,传送门https://blog.csdn.net/m0_57585228/article/details/125228656
可以分别识别每个按键的按下与松开
需要初始化的外设有,GPIO,定时器中断
有2组,输入组设置为上拉输入,输出组设置为开漏上拉输出,详情之前介绍过
GPIOhttps://blog.csdn.net/m0_57585228/article/details/124498831
头文件(KeyBoard.h)
- #define Key_L0 GPIO_Pin_0
- #define Key_L1 GPIO_Pin_1
- #define Key_L2 GPIO_Pin_2
- #define Key_L3 GPIO_Pin_3
- #define Key_L4 GPIO_Pin_4
- #define Key_L5 GPIO_Pin_5
- #define Key_L_GPIOx GPIOA
- #define Key_L_GPIO_RCC RCC_AHB1Periph_GPIOA
-
- #define Key_R0 GPIO_Pin_0
- #define Key_R1 GPIO_Pin_1
- #define Key_R2 GPIO_Pin_10
- #define Key_R3 GPIO_Pin_3
- #define Key_R4 GPIO_Pin_4
- #define Key_R5 GPIO_Pin_5
- #define Key_R_GPIOx GPIOB
- #define Key_R_GPIO_RCC RCC_AHB1Periph_GPIOB
C文件(KeyBoard.c)
- void Key_Scan_GPIO_init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct; //声明GPIO初始化结构体
- RCC_AHB1PeriphClockCmd(Key_L_GPIO_RCC, ENABLE); //打开GPIO时钟
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //设置为输出模式
- GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; //设置为开漏输出
- GPIO_InitStruct.GPIO_Pin = Key_L0 | Key_L1 | Key_L2 | Key_L3 | Key_L4 | Key_L5; //设置引脚
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //设置为上拉
- GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed; //设置为快速模式
- GPIO_Init(Key_L_GPIOx, &GPIO_InitStruct);
- Set();
- RCC_AHB1PeriphClockCmd(Key_R_GPIO_RCC, ENABLE); //打开GPIO时钟
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //设置为输入模式
- GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; //
- GPIO_InitStruct.GPIO_Pin = Key_R0 | Key_R1 | Key_R2 | Key_R3 | Key_R4 | Key_R5; //设置引脚
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //设置为上拉
- GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed; //设置为快速模式
- GPIO_Init(Key_R_GPIOx, &GPIO_InitStruct); //初始化
- }
- void Set(void)
- {
- GPIO_SetBits(Key_L_GPIOx, Key_L0 | Key_L1 | Key_L2 | Key_L3 | Key_L4 | Key_L5);
- }
需要开启定时器中断,中断时间设置为几到几十毫秒即可,因为在定时器中使用了1us延迟,我们给定时器中断的个较低的优先级
定时器https://blog.csdn.net/m0_57585228/article/details/124520929
C文件(KeyBoard.c)
- void Key_Scan_Tim_init(void)
- {
- TIM_TimeBaseInitTypeDef TIM_Init_Struct; //声明定时器初始化结构体
- NVIC_InitTypeDef NVIC_Init_Struct; //声明NVIC初始化结构体
- RCC_APB1PeriphClockCmd(Key_Tim_RCC, ENABLE); //打开时钟
- TIM_Init_Struct.TIM_ClockDivision = TIM_CKD_DIV1; //滤波器不分频
- TIM_Init_Struct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
-
- //每次中断触发时间=[(TIM_Period+1)*(TIM_Prescaler+1)/(SystemCoreClock)] (s)
- //这里是10ms
- TIM_Init_Struct.TIM_Period = 840 - 1;
- TIM_Init_Struct.TIM_Prescaler = 1000 - 1;
- TIM_Init_Struct.TIM_RepetitionCounter = 0; //高级定时器特有,这里写0就行
- TIM_TimeBaseInit(Key_Timx, &TIM_Init_Struct); //调用函数初始
- TIM_ITConfig(Key_Timx, TIM_IT_Update, ENABLE); //启用溢出中断
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2
- NVIC_Init_Struct.NVIC_IRQChannel = Tim_IRQn; //中断名称
- NVIC_Init_Struct.NVIC_IRQChannelCmd = ENABLE; //使能
- NVIC_Init_Struct.NVIC_IRQChannelPreemptionPriority = 2; //主优先级1
- NVIC_Init_Struct.NVIC_IRQChannelSubPriority = 3; //副优先级1
- NVIC_Init(&NVIC_Init_Struct); //初始化NVIC
- TIM_Cmd(Key_Timx, ENABLE); //打开定时器
- }
C文件(KeyBoard.c)
- u8 Key_Scan_Read_IN(void)
- {
- u8 zj = 0;
- if (GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R0))
- zj |= 1 << 0;
- if (GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R1))
- zj |= 1 << 1;
- if (GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R2))
- zj |= 1 << 2;
- if (GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R3))
- zj |= 1 << 3;
- if (GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R4))
- zj |= 1 << 4;
- if (GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R5))
- zj |= 1 << 5;
- return (~zj) & 0x3f;
- }
- void Key_Scan_ReSet(u8 i)
- {
- if (i == 0)
- GPIO_ResetBits(Key_L_GPIOx, Key_L0);
- else if (i == 1)
- GPIO_ResetBits(Key_L_GPIOx, Key_L1);
- else if (i == 2)
- GPIO_ResetBits(Key_L_GPIOx, Key_L2);
- else if (i == 3)
- GPIO_ResetBits(Key_L_GPIOx, Key_L3);
- else if (i == 4)
- GPIO_ResetBits(Key_L_GPIOx, Key_L4);
- else if (i == 5)
- GPIO_ResetBits(Key_L_GPIOx, Key_L5);
- }
每一次触发中断扫描全部输出线,需要间隔一段时间避免频率过高导致误触
然后将其暂存起来、
C文件(KeyBoard.c)
- u8 Key_Scan_Dat[6] = {0};
- void Key_Scan_Code(void)
- {
- static u8 Dat1[6] = {0};
- static u8 Dat2[6] = {0};
- static u8 ins = 0;
- if (ins == 0)
- {
- for (int i = 0; i < 6; i++)
- {
- Set();
- Key_Scan_ReSet(i);
- Delay_us(1);
- Dat1[i] = Key_Scan_Read_IN();
- ins = 1;
- }
- Set();
- }
- else if (ins == 1)
- {
- for (int i = 0; i < 6; i++)
- {
- Set();
- Key_Scan_ReSet(i);
- Delay_us(1);
- Dat2[i] = Key_Scan_Read_IN();
- }
- Set();
- for (int i = 0; i < 6; i++)
- {
- if ((Dat2[i] == Dat1[i]))
- {
- if (Key_Scan_Dat[i] != Dat1[i])
- Key_OK = 1;
- Key_Scan_Dat[i] = Dat1[i];
- }
- }
- ins = 0;
- }
- }
为了增加通用性,这个函数可以在主函数调用,也可以在中断调用
使用了函数指针,使用时将两个参数传入即可,就j和i分别是行和列的序号
C文件(KeyBoard.c)
- void Key_Scan_Action(void (*Down)(u8 R, u8 L), void (*Up)(u8 R, u8 L))
- {
-
- if (Key_OK)
- {
- for (int i = 0; i < 6; i++)
- {
- for (int j = 0; j < 6; j++)
- {
- if (Key_Scan_Dat[i] & (1 << j))
- {
- Key_Key[j][i] = 1;
- Key_OK = 0;
- Down(j, i);
- }
- if ((Key_Key[j][i] == 1) && ((Key_Scan_Dat[i] & (1 << j)) == 0))
- {
- Key_Key[j][i] = 0;
- Up(j, i);
- }
- }
- }
- }
- }
输入和输出均设置为开漏上拉输出
C文件(KeyBoard.c)
- void Key_RLreverse_GPIO_init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct; //声明GPIO初始化结构体
- RCC_AHB1PeriphClockCmd(Key_R_GPIO_RCC, ENABLE); //打开GPIO时钟
- RCC_AHB1PeriphClockCmd(Key_L_GPIO_RCC, ENABLE); //打开GPIO时钟
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //设置为输出模式
- GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; //设置为开漏输出
- GPIO_InitStruct.GPIO_Pin = Key_R0 | Key_R1 | Key_R2 | Key_R3 | Key_R4 | Key_R5; //设置引脚
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //设置为上拉
- GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed; //设置为快速模式
- GPIO_Init(Key_R_GPIOx, &GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //设置为输出模式
- GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; //设置为开漏输出
- GPIO_InitStruct.GPIO_Pin = Key_L0 | Key_L1 | Key_L2 | Key_L3 | Key_L4 | Key_L5; //设置引脚
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //设置为上拉
- GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed; //设置为快速模式
- GPIO_Init(Key_L_GPIOx, &GPIO_InitStruct); //初始化
- }
定时器设置和扫描法基本相同
C文件(KeyBoard.c)
- void Key_RLreverse_Tim_init(void)
- {
- TIM_TimeBaseInitTypeDef TIM_Init_Struct; //声明定时器初始化结构体
- NVIC_InitTypeDef NVIC_Init_Struct; //声明NVIC初始化结构体
- RCC_APB1PeriphClockCmd(Key_Tim_RCC, ENABLE); //打开时钟
- TIM_Init_Struct.TIM_ClockDivision = TIM_CKD_DIV1; //滤波器不分频
- TIM_Init_Struct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
-
- //每次中断触发时间=[(TIM_Period+1)*(TIM_Prescaler+1)/(SystemCoreClock)] (s)
- //这里是5ms
- TIM_Init_Struct.TIM_Period = 840 - 1;
- TIM_Init_Struct.TIM_Prescaler = 1000 - 1;
- TIM_Init_Struct.TIM_RepetitionCounter = 0; //高级定时器特有,这里写0就行
- TIM_TimeBaseInit(Key_Timx, &TIM_Init_Struct); //调用函数初始
- TIM_ITConfig(Key_Timx, TIM_IT_Update, ENABLE); //启用溢出中断
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2
- NVIC_Init_Struct.NVIC_IRQChannel = Tim_IRQn; //中断名称
- NVIC_Init_Struct.NVIC_IRQChannelCmd = ENABLE; //使能
- NVIC_Init_Struct.NVIC_IRQChannelPreemptionPriority = 3; //主优先级1
- NVIC_Init_Struct.NVIC_IRQChannelSubPriority = 3; //副优先级1
- NVIC_Init(&NVIC_Init_Struct); //初始化NVIC
- TIM_Cmd(Key_Timx, ENABLE); //打开定时器
- }
C文件(KeyBoard.c)
- void Key_RLreverse_Set(u8 R_L, u8 bool)
- {
- if (R_L == 1)
- {
- if (bool == 1)
- GPIO_SetBits(Key_L_GPIOx, (Key_L0 | Key_L1 | Key_L2 | Key_L3 | Key_L4 | Key_L5));
- else if (bool == 0)
- GPIO_ResetBits(Key_L_GPIOx, (Key_L0 | Key_L1 | Key_L2 | Key_L3 | Key_L4 | Key_L5));
- }
- else if (R_L == 0)
- {
- if (bool == 1)
- GPIO_SetBits(Key_R_GPIOx, (Key_R0 | Key_R1 | Key_R2 | Key_R3 | Key_R4 | Key_R5));
- else if (bool == 0)
- GPIO_ResetBits(Key_R_GPIOx, (Key_R0 | Key_R1 | Key_R2 | Key_R3 | Key_R4 | Key_R5));
- }
- }
- u8 Key_RLreverse_Read(u8 R_L)
- {
- u8 zj = 0;
- if (R_L == 0)
- {
- zj |= GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R5);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R4);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R3);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R2);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R1);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_R_GPIOx, Key_R0);
- }
- else if (R_L == 1)
- {
- zj |= GPIO_ReadInputDataBit(Key_L_GPIOx, Key_L5);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_L_GPIOx, Key_L4);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_L_GPIOx, Key_L3);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_L_GPIOx, Key_L2);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_L_GPIOx, Key_L1);
- zj <<= 1;
- zj |= GPIO_ReadInputDataBit(Key_L_GPIOx, Key_L0);
- }
- return zj;
- }
这个函数是用来扫描的,需要在定时器中调用
也可以稍微更改一下放入外部中断中(可能需要额外硬件或者占用多个中断源,不推荐)
C文件(KeyBoard.c)
- void Key_RLreverse_Code()
- {
-
- static u8 zj_R;
- static u8 zj_L;
- u16 zj = 0;
- static u8 Key_Code_ins;
- static u16 Bef_Dat;
- if (Key_Code_ins == 1) //去抖
- {
- zj_R = Key_RLreverse_Read(Key_R);
- Key_RLreverse_Set(Key_R, 0); //行设为
- Key_RLreverse_Set(Key_L, 1); //列设成
- Key_Code_ins = 2;
- }
- else if (Key_Code_ins == 2)
- {
- zj_L = Key_RLreverse_Read(Key_L);
- zj = zj_L | (zj_R << 8);
- if (zj != Bef_Dat)
- {
- Key_OK = 1;
- Key_Data[Key_L] = zj_L;
- Key_Data[Key_R] = zj_R;
- Key_RLreverse_Decode();
- }
- Bef_Dat = zj;
- Key_Code_ins = 0;
- }
- else
- {
-
- Key_RLreverse_Set(Key_L, 0); //行设为1
- Key_RLreverse_Set(Key_R, 1); //列设为0
- if (Key_RLreverse_Read(Key_R) != 0x3F)
- Key_Code_ins = 1;
- else
- Key_Code_ins = 0;
- }
- }
和扫描法类似
C文件(KeyBoard.c)
- void Key_RLreverse_Action(void (*Down)(u8 R, u8 L), void (*Up)(u8 R, u8 L))
- {
- if (Key_OK)
- {
- Key_OK = 0;
- for (int i = 0; i < 6; i++)
- {
- for (int j = 0; j < 6; j++)
- {
- if (Key_Key[i][j] == Key_Down)
- {
-
- Down(i, j);
- }
- else if (Key_Key[i][j] == Key_Up)
- {
- Key_Key[i][j] = Key_Keep;
-
- Up(i, j);
- }
- }
- }
- }
- }
矩阵键盘演示,可以识别按键按下与松开
GitHubhttps://github.com/HZ1213825/STM32F4_Matrix_KeyBoard百度网盘https://pan.baidu.com/s/1U1B8jIfiXDHqmPbITeSRmw?pwd=mtlu
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。