电子产业一站式赋能平台

PCB联盟网

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

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

[复制链接]

989

主题

989

帖子

8575

积分

高级会员

Rank: 5Rank: 5

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

gtajhuabvgp64010115.gif

gtajhuabvgp64010115.gif
( {2 ^- \9 w$ D8 [5 O
点击上方蓝色字体,关注我们1 l1 W; e! I5 Z( z# q8 e+ t' f( K
/ d/ |  S5 f4 b6 u! y# [
4 {( O& w( `9 s$ r9 P. M
假设有一个4×4的矩阵按键,它由4行(Row)和4列(Column)组成,共16个按键。
5 h$ m- A' c, c  f! Z9 @; L8 @+ v$ M  |
通常,行连接到单片机的GPIO输出端,列连接到GPIO输入端,且列端口通常需要上拉电阻来保持默认高电平。
, e: F3 L& q% H2 s6 V; w3 a
. z, p  h+ r5 r! u/ p硬件连接示例:% c+ J! h$ z5 {$ Z

' d* D6 R- }3 _3 P

qaktvud54em64010215.png

qaktvud54em64010215.png

! ?, F/ g* [/ z$ U% y1; k: h+ }& O* J! G7 q
矩阵按键的基本扫描方法% w; w0 H  ]6 a$ ]
依次拉低每一行的电平,并读取列信号,判断是否有按键按下。
+ f7 X, @& {8 L( E( G& e" U& B2 @1 c+ H( [$ x4 J
实现步骤:" K% n* b- k4 c
  • 设定所有行(Row)为高电平,所有列(Column)为输入模式,并上拉。
  • 依次将每一行拉低(低电平),然后读取所有列的状态。
  • 如果某列检测到低电平,说明该行与该列的交点处按键被按下。
  • 记录按键位置,并等待去抖动处理。
  • 继续扫描下一行,直到所有行扫描完毕。; t1 \8 s+ U" X3 s1 q

    * i  _- ^+ ?  p1 k" ?
    - I+ I: c" G; r& X+ o5 m$ L1 C, b
    示例代码(基于C语言):6 A% P' G+ v2 K* F7 Q( u

      o+ Z( r, J) v1 [, E' v

    9 v$ K# [. b4 V' s7 l# 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]被按下, C, q/ [, T0 V: _  n$ t! T
    ", i, j);            }        }        // 恢复当前行为高电平        gpio_write(row_pins, HIGH);    }}* d" z5 n  b- M- o2 ^+ r3 U! B
    26 D/ {! Z8 n# N5 y! V" Q
    低功耗优化
    9 i; w) u9 U0 V% |' e2 q如果单片机支持外部中断,可以利用外部中断检测按键按下,降低CPU负载。+ U- _9 P8 p. a% P7 L
    7 f. m) A! x3 G% S
    方法如下:
    : B* i4 R8 p4 S' z& @$ r
  • 初始状态:所有行设为高电平,所有列配置为带上拉输入,并开启中断。
  • 进入低功耗模式,等待外部中断。
  • 当按键按下时,列引脚的电平变化触发中断。
  • 进入中断后,采用行列扫描法识别具体按键。
  • 处理按键逻辑后,恢复低功耗状态。
    6 u/ p; t  t0 p! \4 X- i
    , j8 E$ o6 L) [2 c6 s
    % q2 y. {& e9 u4 O
    示例代码(基于C语言):
    9 Z; F; _3 F; J6 m- @* q. G" G. R
    2 G: }2 o2 l& D7 w
    ) r8 n# ?5 F/ X8 L. I
  • 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);    }}
    " @1 M( y$ D! h  K3; g6 d# m/ c& K1 V7 \: s1 f" z3 Q* n
    按键去抖动策略
    0 P5 \7 {0 f. c9 ^, D+ C按键在机械接触时会出现抖动,可能会误触发多次按键事件,因此需要去抖动处理。- D8 ^! m6 n+ ?/ p9 b

    , A& s: }% D* K( L+ @7 h/ x3.1、软去抖动
    7 ~- ^1 [) T  Y7 a0 p' ^: X通过软件延迟来过滤抖动信号,例如检测到按键按下后,延迟20ms再次检测是否仍然按下。3 K& ^6 [" y  I6 @+ I" A8 Z

    # Z5 w1 o! Q3 D# B
  • 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;}
    6 W' A7 u, i; I$ C  p. e: M3.2、硬件去抖动
    0 l$ H1 H, X. a可在矩阵按键电路中增加一个小电容(如0.1uF)或者使用施密特触发器来稳定按键信号。/ h6 g! C. q7 K4 Q

    , n0 B, ~- w, J0 a

    b3gy0an3ywk64010315.png

    b3gy0an3ywk64010315.png

    : W" ]* |( r1 e. h4 B6 g3 p8 B
    , @2 _2 y$ U9 N! D: Q& B" C- _在资源受限的嵌入式系统中,如果单片机 没有足够的外部中断资源,可以使用 定时器 进行周期性扫描矩阵按键,以减少CPU占用。
    3 v% g2 u  [+ X4 V; ]$ I) Q6 B# B1 d; e) V2 A7 N; O
    同时,为了避免主循环(while(1))中阻塞等待按键事件,使用FIFO(First In, First Out)队列 存储按键事件,以提高系统响应速度。" z& z, Y4 ]3 e$ t& w
    4) m: y) ]& _' q
    进一步优化
    6 Z* G5 j' v! D# {1 z! h8 e/ f6 D" F7 E基本原理:% s" ^+ C  Y  i* K! M9 I
  • 定时器周期性触发扫描,间隔通常设为 10~20ms,以确保能及时捕获按键事件,同时避免过于频繁地占用CPU资源。
  • 在定时器中断函数内,执行一次完整的行列扫描,如果检测到按键按下,则将其加入FIFO队列。
    6 Y& K) h4 G& l8 q以下是基于 STM32定时器中断方式 进行按键扫描的示例代码:8 r% x  \2 U5 z# O, {0 c5 t

    5 Z! _" b5 U5 C
  • #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);}这样,我们就能在 低资源占用高响应速度 之间取得 良好平衡,构建更高效的 单片机矩阵按键控制系统8 |) w+ d2 V1 y6 v( d

    moyw1nbns3y64010415.jpg

    moyw1nbns3y64010415.jpg
      T! }# H" s1 d+ ]! {

    2wl2ms3x0cq64010515.gif

    2wl2ms3x0cq64010515.gif

    4 z: ^5 P" m& r( w; P% Q- A点击阅读原文,更精彩~
  • 回复

    使用道具 举报

    发表回复

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

    本版积分规则


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