电子产业一站式赋能平台

PCB联盟网

搜索
查看: 105|回复: 0
收起左侧

如何用单片机高效处理矩阵按键?

[复制链接]

1001

主题

1001

帖子

8807

积分

高级会员

Rank: 5Rank: 5

积分
8807
发表于 2025-3-15 08:01:00 | 显示全部楼层 |阅读模式

gtajhuabvgp64010115.gif

gtajhuabvgp64010115.gif
! @7 `! P  e2 @3 Z3 e$ o& \
点击上方蓝色字体,关注我们  o; f$ |3 P+ B8 U9 J% ^# o
: H& D5 y0 b+ y1 h

! r2 [+ b' }9 F  k7 ^! _- I假设有一个4×4的矩阵按键,它由4行(Row)和4列(Column)组成,共16个按键。6 m* l/ y5 x9 \3 w! u
; P9 E( F3 t) `3 I2 [( k6 s+ ]0 W
通常,行连接到单片机的GPIO输出端,列连接到GPIO输入端,且列端口通常需要上拉电阻来保持默认高电平。
3 F! r& \- t& @- D6 Y1 s% p3 e% b7 c: @, F0 Z! U! c  h2 U
硬件连接示例:
3 a$ K9 m+ L& r4 M! Z! u
! @7 F9 O) R& h* ?

qaktvud54em64010215.png

qaktvud54em64010215.png
0 {+ y  t, F" E, f$ j! f% k
1, `1 E( L* D: `0 O# _/ X3 @+ V
矩阵按键的基本扫描方法
8 R2 g% s( d. e* m$ f; _依次拉低每一行的电平,并读取列信号,判断是否有按键按下。
  }5 U, @( i/ K4 L% s
6 `+ G9 W# f3 l- o9 z4 x实现步骤:
4 Z1 b7 K3 X  m1 D
  • 设定所有行(Row)为高电平,所有列(Column)为输入模式,并上拉。
  • 依次将每一行拉低(低电平),然后读取所有列的状态。
  • 如果某列检测到低电平,说明该行与该列的交点处按键被按下。
  • 记录按键位置,并等待去抖动处理。
  • 继续扫描下一行,直到所有行扫描完毕。
    $ }3 `9 u! w5 L& M9 `
    6 T& ~6 {& Z/ Z# T  H
    + a6 x& ?4 j2 ~+ t
    示例代码(基于C语言):
    - }$ g/ }. `+ r
      u  y5 y" ~9 G& K

    ( k+ {% ]5 f2 j. I( v, S2 k
  • #define ROWS 4#define COLS 4constuint8_t row_pins[ROWS] = {ROW1, ROW2, ROW3, ROW4};constuint8_t col_pins[COLS] = {COL1, COL2, COL3, COL4};void scan_matrix_keypad() {    for (int i = 0; i         // 设定当前行为低电平        gpio_write(row_pins, LOW);        delay_us(5);  // 确保稳定        // 读取列状态        for (int j = 0; j             if (gpio_read(col_pins[j]) == LOW) {                printf("按键[%d,%d]被按下9 Q4 c% Q6 ]$ D1 C
    ", i, j);            }        }        // 恢复当前行为高电平        gpio_write(row_pins, HIGH);    }}& v. r3 C/ I2 ~( {7 ^
    2
    " h6 M2 r9 q7 `8 q( w: P5 n低功耗优化  l7 R* Q. [# a; x; \; O0 I5 T
    如果单片机支持外部中断,可以利用外部中断检测按键按下,降低CPU负载。
    4 o4 d: O3 R7 ]: T
    8 R  {% f' G5 T- ?- P: y  }方法如下:
    - j6 V9 w4 x- h8 |" n
  • 初始状态:所有行设为高电平,所有列配置为带上拉输入,并开启中断。
  • 进入低功耗模式,等待外部中断。
  • 当按键按下时,列引脚的电平变化触发中断。
  • 进入中断后,采用行列扫描法识别具体按键。
  • 处理按键逻辑后,恢复低功耗状态。, y2 |0 C* |4 r8 l) e6 [2 r" H
    ; D4 ]: I. x' I7 |1 m2 M+ a2 C

    ) k  @. B' x" ^  h) U8 I示例代码(基于C语言):6 B5 c' ~, P% Z6 ?$ a7 D
    ' W1 \. w6 Z% U
    % E* a' C# p/ O. {# H& f
  • void EXTI_Handler() {    for (int j = 0; j         if (gpio_read(col_pins[j]) == LOW) {            scan_matrix_keypad(); // 仅在有按键按下时扫描            break;        }    }}void setup() {    for (int i = 0; i         gpio_mode(row_pins, OUTPUT);        gpio_write(row_pins, HIGH);    }    for (int j = 0; j         gpio_mode(col_pins[j], INPUT_PULLUP);        attach_interrupt(col_pins[j], EXTI_Handler, FALLING);    }}
      d& d5 E. `+ u$ M3) f" Y& o% l, }4 R3 q3 {+ R8 Q- q
    按键去抖动策略- `( r: W+ Y# w0 e9 O, y
    按键在机械接触时会出现抖动,可能会误触发多次按键事件,因此需要去抖动处理。
    : Q1 G) o2 c" q, |8 a0 x0 p2 @( |
    3.1、软去抖动
    " z, `4 b1 r" c$ h! q' N5 k! r) y通过软件延迟来过滤抖动信号,例如检测到按键按下后,延迟20ms再次检测是否仍然按下。
      B, I; ]7 K# V: z0 {  X2 l! \$ P5 U  T* ~3 h0 N% r
  • bool is_key_pressed(uint8_t row, uint8_t col) {    if (gpio_read(col_pins[col]) == LOW) {        delay_ms(20); // 20ms去抖        if (gpio_read(col_pins[col]) == LOW) {            return true;        }    }    return false;}
    ( O3 [/ |9 W$ y& s: l9 a9 K( z- D" i3.2、硬件去抖动
    # J9 j. Y, l, }  i/ q6 S0 L可在矩阵按键电路中增加一个小电容(如0.1uF)或者使用施密特触发器来稳定按键信号。
    ) ]) Q; f3 G/ A+ Y. G1 ]" F! {) Q) s- i

    b3gy0an3ywk64010315.png

    b3gy0an3ywk64010315.png
    & ^* J' G/ i2 T8 d
    - A% \' U% R- Y% M9 x, m
    在资源受限的嵌入式系统中,如果单片机 没有足够的外部中断资源,可以使用 定时器 进行周期性扫描矩阵按键,以减少CPU占用。
    9 j: f; ?: p- E+ \9 {. d! Z/ F$ r! }* |/ s9 c
    同时,为了避免主循环(while(1))中阻塞等待按键事件,使用FIFO(First In, First Out)队列 存储按键事件,以提高系统响应速度。, M6 g6 ]' l% u/ O
    4/ [, m! O$ c! d+ D0 l* Q: V4 Z5 _
    进一步优化) W1 W9 e) T1 v: [9 q6 F: Y+ g
    基本原理:
    * ?* u2 L5 s( C
  • 定时器周期性触发扫描,间隔通常设为 10~20ms,以确保能及时捕获按键事件,同时避免过于频繁地占用CPU资源。
  • 在定时器中断函数内,执行一次完整的行列扫描,如果检测到按键按下,则将其加入FIFO队列。9 F+ m: M1 X! N# h) ?0 k( f9 c
    以下是基于 STM32定时器中断方式 进行按键扫描的示例代码:+ }% p+ S* R) i6 i0 H

    9 z! n4 R' N1 r, O/ N7 |! o# Q& C6 ~
  • #define ROWS 4#define COLS 4constuint8_t row_pins[ROWS] = {ROW1, ROW2, ROW3, ROW4};constuint8_t col_pins[COLS] = {COL1, COL2, COL3, COL4};// FIFO 队列结构体#define KEY_FIFO_SIZE 10typedefstruct {    uint8_t keys[KEY_FIFO_SIZE];  // 按键事件队列    uint8_t head;  // 队列头    uint8_t tail;  // 队列尾} KeyFIFO;KeyFIFO key_fifo = {{0}, 0, 0};// 按键事件入队void key_fifo_enqueue(uint8_t key) {    uint8_t next = (key_fifo.tail + 1) % KEY_FIFO_SIZE;    if (next != key_fifo.head) {  // 队列未满        key_fifo.keys[key_fifo.tail] = key;        key_fifo.tail = next;    }}// 读取FIFO队列中的按键uint8_t key_fifo_dequeue() {    if (key_fifo.head == key_fifo.tail) {        return0; // 队列为空    }    uint8_t key = key_fifo.keys[key_fifo.head];    key_fifo.head = (key_fifo.head + 1) % KEY_FIFO_SIZE;    return key;}// 定时器中断回调函数,每10ms扫描按键void TIM2_IRQHandler() {    for (int i = 0; i         gpio_write(row_pins, LOW);        delay_us(5); // 确保稳定        for (int j = 0; j             if (gpio_read(col_pins[j]) == LOW) {                uint8_t key_id = (i * COLS) + j + 1;                key_fifo_enqueue(key_id);            }        }        gpio_write(row_pins, HIGH);    }    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  // 清除定时器中断标志}// 定时器初始化void timer2_init() {    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;    NVIC_InitTypeDef NVIC_InitStructure;    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);    TIM_TimeBaseStructure.TIM_Period = 10000 - 1;  // 10ms定时    TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;  // 1MHz时钟    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);    TIM_Cmd(TIM2, ENABLE);    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    NVIC_Init(&NVIC_InitStructure);}这样,我们就能在 低资源占用高响应速度 之间取得 良好平衡,构建更高效的 单片机矩阵按键控制系统: N' z0 M* P# c& h1 k, O+ [1 d

    moyw1nbns3y64010415.jpg

    moyw1nbns3y64010415.jpg
    6 T3 n' P2 O) [7 ~* }# A

    2wl2ms3x0cq64010515.gif

    2wl2ms3x0cq64010515.gif

    1 s( t7 K  w& d& w/ x点击阅读原文,更精彩~
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表