[其他ST产品] stm32以中断方式扫描矩阵键盘

[复制链接]
1808|19
 楼主| yellow555 发表于 2023-11-23 23:02 | 显示全部楼层 |阅读模式
矩阵键盘
普通的按键,都是一个IO口控制一个按键,但是当按键数量变多时,单片机可能没有那么多的IO口来供给按键使用,这时候就需要用到矩阵键盘,比如45的矩阵键盘,用到了4+5共9根IO口线,可以控制45=20个按键。相应的,程序也要复杂些。

98766655f698553982.png
98633655f698dc7077.png

 楼主| yellow555 发表于 2023-11-23 23:02 | 显示全部楼层
cubemx配置
X0到X3这4个IO口配置为低电平输出,无上拉,无下拉,即推挽方式,速度为中
Y0到Y4这5个IO口配置为EXTI外部中断模式,上拉,下降沿触发。
840655f69a0c7051.png
 楼主| yellow555 发表于 2023-11-23 23:03 | 显示全部楼层
编程思路
1 X0~3配置为输出低电平
2 Y0~4配置为下降沿中断,但是芯片内部配置了上拉,所以无按键触发时不会进中断。
3 当有按键按下,比如X2Y4按下时,X2路的低电平会进入到Y4路,触发下降沿中断。
4 在Y4中断内,将Y4配置为低电平输出,而X0~3配置为上拉输入,记录下Y值为4
5 逐一检测X0~3的电平,哪一路为低,即对应的为X键值,本例X值为2
6 综合x=2, y=4, 即可知道是哪一个按键按下了。
 楼主| yellow555 发表于 2023-11-23 23:04 | 显示全部楼层
代码编写
定义一个结构体,有按键触发时,active置1,x,y分别记录行列值,num是最后的键值。

  1. typedef struct KEYBOARD_TYPEDEF
  2. {
  3.   u8 active;
  4.   u8 x;
  5.   u8 y;
  6.   u8 num;
  7. }KEYBOARD_Typedef;

  8. KEYBOARD_Typedef keyboard;
 楼主| yellow555 发表于 2023-11-23 23:04 | 显示全部楼层
以Y4为例,列出一路中断如下:

  1. void EXTI15_10_IRQHandler(void)
  2. {
  3.   /* USER CODE BEGIN EXTI15_10_IRQn 0 */
  4.     if(__HAL_GPIO_EXTI_GET_FLAG(KEY_Y4_Pin))
  5.     {
  6.         delay_us(10);
  7.         if(KEY_Y4_READ() != 0)
  8.         {
  9.             HAL_GPIO_EXTI_IRQHandler(KEY_Y4_Pin);
  10.             return;
  11.         }
  12.         delay_us(10);
  13.         if(KEY_Y4_READ() != 0)
  14.         {
  15.             HAL_GPIO_EXTI_IRQHandler(KEY_Y4_Pin);
  16.             return;
  17.         }
  18.         delay_us(10);
  19.         if(KEY_Y4_READ() != 0)
  20.         {
  21.             HAL_GPIO_EXTI_IRQHandler(KEY_Y4_Pin);
  22.             return;
  23.         }
  24.         keyboard.active = 1;
  25.         keyboard.y = 4;
  26.     }
  27.     keyboard_scan();
  28.   /* USER CODE END EXTI15_10_IRQn 0 */
  29.   HAL_GPIO_EXTI_IRQHandler(KEY_Y4_Pin);
  30.   /* USER CODE BEGIN EXTI15_10_IRQn 1 */

  31.   /* USER CODE END EXTI15_10_IRQn 1 */
  32. }
 楼主| yellow555 发表于 2023-11-23 23:04 | 显示全部楼层
增加了几次延时,作为键盘消抖的判断
确认不是误触发后,active置1,并相应的y值。
最后调用键盘扫描函数,来确认X的键值。
 楼主| yellow555 发表于 2023-11-23 23:04 | 显示全部楼层
引脚功能模式切换
  1. //切换GPIO引脚的方向
  2. //port 端口号
  3. //pin号
  4. //dir 方向,0为输入,1为输出, 2为EXTI
  5. void pin_io_switch(GPIO_TypeDef *port, u32 pin, u8 mode)
  6. {
  7.     switch(mode)
  8.     {
  9.         case GPIO_IN://输入
  10.             GPIO_InitStruct.Pin = pin;
  11.             GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  12.             GPIO_InitStruct.Pull = GPIO_PULLUP;
  13.             HAL_GPIO_Init(port, &GPIO_InitStruct);
  14.             break;
  15.         case GPIO_OUT://输出
  16.             GPIO_InitStruct.Pin = pin;
  17.             GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  18.             GPIO_InitStruct.Pull = GPIO_NOPULL;
  19.             GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
  20.             HAL_GPIO_Init(port, &GPIO_InitStruct);
  21.             break;
  22.         case GPIO_EXTI:
  23.             GPIO_InitStruct.Pin = pin;
  24.             GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  25.             GPIO_InitStruct.Pull = GPIO_PULLUP;
  26.             HAL_GPIO_Init(port, &GPIO_InitStruct);
  27.             break;
  28.     }
  29. }
 楼主| yellow555 发表于 2023-11-23 23:05 | 显示全部楼层
本函数用来确认X轴的键值。


  1. void keyboard_scan(void)
  2. {
  3.     u16 i,j;
  4.     u8 x0, x1, x2, x3;
  5.     u8 temp, current;
  6.    
  7.     if(keyboard.active == 0)
  8.     return;

  9.         //关中断
  10.     HAL_NVIC_DisableIRQ(EXTI3_IRQn);       
  11.     HAL_NVIC_DisableIRQ(EXTI4_IRQn);
  12.     HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
  13.     HAL_NVIC_DisableIRQ(EXTI15_10_IRQn);

  14.         //X0~3切换为输入状态
  15.     pin_io_switch(KEY_X0_PORT, KEY_X0_Pin, GPIO_IN);    //X0  PC13
  16.     pin_io_switch(KEY_X1_PORT, KEY_X1_Pin, GPIO_IN);    //X1  PC14
  17.     pin_io_switch(KEY_X2_PORT, KEY_X2_Pin, GPIO_IN);    //X2  PC15
  18.     pin_io_switch(KEY_X3_PORT, KEY_X3_Pin, GPIO_IN);    //X3  PB7

  19.         //只将相应的一路Y口切换为输出状态,Y的数值,在中断函数中设置过
  20.     switch(keyboard.y)
  21.     {
  22.         case 0:
  23.             pin_io_switch(KEY_Y0_PORT, KEY_Y0_Pin, GPIO_OUT);
  24.             KEY_Y0_SET(0);  //Y0  PB6
  25.             break;
  26.         case 1:
  27.             pin_io_switch(KEY_Y1_PORT, KEY_Y1_Pin, GPIO_OUT);
  28.             KEY_Y1_SET(0);  //Y0  PB5
  29.             break;
  30.         case 2:
  31.             pin_io_switch(KEY_Y2_PORT, KEY_Y2_Pin, GPIO_OUT);
  32.             KEY_Y2_SET(0);  //Y0  PB4
  33.             break;
  34.         case 3:
  35.             pin_io_switch(KEY_Y3_PORT, KEY_Y3_Pin, GPIO_OUT);
  36.             KEY_Y3_SET(0);  //Y0  PB3
  37.             break;
  38.         case 4:
  39.             pin_io_switch(KEY_Y4_PORT, KEY_Y4_Pin, GPIO_OUT);
  40.             KEY_Y4_SET(0);  //Y0  PA15
  41.             break;
  42.     }

  43.     //读取X轴的状态
  44.     x0 = KEY_X0_READ();   //X0  PC13
  45.     x1 = KEY_X1_READ();   //X1  PC14
  46.     x2 = KEY_X2_READ();   //X2  PC15
  47.     x3 = KEY_X3_READ();   //X3  PB7
  48.     if(x0 == 0)
  49.         keyboard.x = 3;     //X方向从左开始为x3, x2, x1, x0
  50.     else if(x1 == 0)
  51.         keyboard.x = 2;     //与电路图上相反,下一版电路要把方向改过来,改为x0,x1,x2,x3
  52.     else if(x2 == 0)
  53.         keyboard.x = 1;
  54.     else
  55.         keyboard.x = 0;
  56.     keyboard.num = keyboard.y*4 + keyboard.x; //计算键值,0~19

  57.         //将Y口配置为EXTI,上拉
  58.     pin_io_switch(KEY_Y0_PORT, KEY_Y0_Pin, GPIO_EXTI);
  59.     pin_io_switch(KEY_Y1_PORT, KEY_Y1_Pin, GPIO_EXTI);
  60.     pin_io_switch(KEY_Y2_PORT, KEY_Y2_Pin, GPIO_EXTI);
  61.     pin_io_switch(KEY_Y3_PORT, KEY_Y3_Pin, GPIO_EXTI);
  62.     pin_io_switch(KEY_Y4_PORT, KEY_Y4_Pin, GPIO_EXTI);
  63.    
  64.     //将X口配置为输出,低电平
  65.     pin_io_switch(KEY_X0_PORT, KEY_X0_Pin, GPIO_OUT);
  66.     pin_io_switch(KEY_X1_PORT, KEY_X1_Pin, GPIO_OUT);
  67.     pin_io_switch(KEY_X2_PORT, KEY_X2_Pin, GPIO_OUT);
  68.     pin_io_switch(KEY_X3_PORT, KEY_X3_Pin, GPIO_OUT);
  69.     KEY_X0_SET(0);
  70.     KEY_X1_SET(0);
  71.     KEY_X2_SET(0);
  72.     KEY_X3_SET(0);
  73.     HAL_NVIC_EnableIRQ(EXTI3_IRQn);
  74.     HAL_NVIC_EnableIRQ(EXTI4_IRQn);
  75.     HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
  76.     HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
  77. }
 楼主| yellow555 发表于 2023-11-23 23:05 | 显示全部楼层
最后在主程序中检查keyboard.active,若不为0,则表示有按键按下,此时需要做相应处理,并且将keyboard中的各成员清零。
EmmaTT 发表于 2024-4-8 14:10 来自手机 | 显示全部楼层
用定时器隔一段时间判断一次呗
童雨竹 发表于 2024-7-16 08:26 | 显示全部楼层

确保当开关管导通,
Wordsworth 发表于 2024-7-16 09:29 | 显示全部楼层

电阻元件的电阻值大小一般与温度,材料,长度,还有横截面积有关
Clyde011 发表于 2024-7-16 10:32 | 显示全部楼层

灌封之前需要使用抽空机进行天然脱泡和真空脱泡预处理
公羊子丹 发表于 2024-7-16 11:25 | 显示全部楼层

主要起到支承作用,板不会散掉。
万图 发表于 2024-7-16 12:28 | 显示全部楼层

当电压超过二极管的导通电压的时候
Uriah 发表于 2024-7-16 13:31 | 显示全部楼层

在低成本控制器内部有时候会没有钳位二极管
帛灿灿 发表于 2024-7-16 15:27 | 显示全部楼层

会使二极管导通
Bblythe 发表于 2024-7-16 16:30 | 显示全部楼层

对于电力电路来说串联的电阻起阻尼作用
周半梅 发表于 2024-7-16 18:26 | 显示全部楼层

将ESD静电保护二极管并联于电路中
Pulitzer 发表于 2024-7-16 19:29 | 显示全部楼层

电阻阻值大小则需根据元器件可承受电流大小而决定
您需要登录后才可以回帖 登录 | 注册

本版积分规则

40

主题

469

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部