矩阵键盘扫描调试:按下*键LCD无响应问题排查
问题排查与修复方案:按下*键无响应且LCD无显示
核心问题点分析
引脚资源冲突:LCD与键盘的多个GPIO引脚重复定义,导致硬件信号互相干扰,这是最根本的问题:
- LCD的
D4_PIN(PC_7)与键盘ROW4_PIN重复 - LCD的
D5_PIN(PB_6)与键盘ROW3_PIN重复 - LCD的
D6_PIN(PA_7)与键盘ROW2_PIN重复 - LCD的
D7_PIN(PA_6)与键盘ROW1_PIN重复 - LCD的
E_PIN(PB_4)与键盘COL4_PIN重复
- LCD的
按键扫描无防抖处理:按键按下时的机械抖动会导致扫描逻辑误判或无法稳定检测到按键触发。
主循环无效字符累积:无按键时
keypad_scan()返回空字符'\0',主循环持续将空字符加入sequence,导致输出混乱且占用资源。LCD输出逻辑不完善:设置模式下无光标定位,输入数字时文字重叠;错误提示无延时,用户无法看清;缺少需求中要求的AM/PM设置环节。
修复后的完整代码
#include <iostream> #include <string> #include "mbed.h" #include "TextLCD.h" // 键盘布局定义 char key_map[4][4] = { {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'} }; // 键盘GPIO引脚(无冲突) #define ROW1_PIN PA_6 #define ROW2_PIN PA_7 #define ROW3_PIN PB_6 #define ROW4_PIN PC_7 #define COL1_PIN PA_9 #define COL2_PIN PA_8 #define COL3_PIN PB_10 #define COL4_PIN PB_4 // LCD GPIO引脚(重新分配,避免与键盘冲突) #define RS_PIN PB_5 #define E_PIN PB_3 #define D4_PIN PC_8 #define D5_PIN PC_9 #define D6_PIN PC_10 #define D7_PIN PC_11 // 函数声明 void initialize_gpio(); void set_row(int row); void reset_rows(); char keypad_scan(); int col_scan(); void set_mode(); // 全局GPIO对象 DigitalOut ROW1(ROW1_PIN); DigitalOut ROW2(ROW2_PIN); DigitalOut ROW3(ROW3_PIN); DigitalOut ROW4(ROW4_PIN); DigitalIn COL1(COL1_PIN, PullUp); DigitalIn COL2(COL2_PIN, PullUp); DigitalIn COL3(COL3_PIN, PullUp); DigitalIn COL4(COL4_PIN, PullUp); TextLCD lcd(RS_PIN, E_PIN, D4_PIN, D5_PIN, D6_PIN, D7_PIN); int main() { initialize_gpio(); std::string sequence; while (true) { char key = keypad_scan(); if (key != '\0') { sequence += key; std::cout << "Pressed key: " << key << std::endl; if (key == '*') { set_mode(); } } wait_ms(20); // 降低扫描频率,节省资源 } return 0; } void initialize_gpio() { ROW1 = ROW2 = ROW3 = ROW4 = 1; } void set_row(int row) { switch (row) { case 0: ROW1 = 0; ROW2 = 1; ROW3 = 1; ROW4 = 1; break; case 1: ROW1 = 1; ROW2 = 0; ROW3 = 1; ROW4 = 1; break; case 2: ROW1 = 1; ROW2 = 1; ROW3 = 0; ROW4 = 1; break; case 3: ROW1 = 1; ROW2 = 1; ROW3 = 1; ROW4 = 0; break; } } void reset_rows() { ROW1 = ROW2 = ROW3 = ROW4 = 1; } char keypad_scan() { char key_pressed = '\0'; for (int row = 0; row < 4; ++row) { set_row(row); wait_ms(5); // 防抖延时 int col = col_scan(); if (col != -1) { // 等待按键释放,避免重复触发 while (col_scan() != -1); key_pressed = key_map[row][col]; break; } } reset_rows(); return key_pressed; } int col_scan() { if (!COL1) return 0; if (!COL2) return 1; if (!COL3) return 2; if (!COL4) return 3; return -1; } void set_mode() { // 设置小时 lcd.cls(); lcd.locate(0, 0); lcd.printf("HOUR: "); int hour = 0; while (true) { char key = keypad_scan(); if (key >= '0' && key <= '9') { hour = hour * 10 + (key - '0'); lcd.locate(5, 0); lcd.printf("%02d", hour); } else if (key == '#') { if (hour >= 1 && hour <= 12) { break; } else { lcd.cls(); lcd.printf("INVALID HOUR"); wait_ms(1000); lcd.cls(); lcd.locate(0, 0); lcd.printf("HOUR: "); hour = 0; } } } // 设置分钟 lcd.cls(); lcd.locate(0, 0); lcd.printf("MIN: "); int minute = 0; while (true) { char key = keypad_scan(); if (key >= '0' && key <= '9') { minute = minute * 10 + (key - '0'); lcd.locate(4, 0); lcd.printf("%02d", minute); } else if (key == '#') { if (minute >= 0 && minute <= 59) { break; } else { lcd.cls(); lcd.printf("INVALID MIN"); wait_ms(1000); lcd.cls(); lcd.locate(0, 0); lcd.printf("MIN: "); minute = 0; } } } // 设置秒 lcd.cls(); lcd.locate(0, 0); lcd.printf("SEC: "); int second = 0; while (true) { char key = keypad_scan(); if (key >= '0' && key <= '9') { second = second * 10 + (key - '0'); lcd.locate(4, 0); lcd.printf("%02d", second); } else if (key == '#') { if (second >= 0 && second <= 59) { break; } else { lcd.cls(); lcd.printf("INVALID SEC"); wait_ms(1000); lcd.cls(); lcd.locate(0, 0); lcd.printf("SEC: "); second = 0; } } } // 设置AM/PM lcd.cls(); lcd.locate(0, 0); lcd.printf("AM(1)/PM(2): "); char ampm = ' '; while (true) { char key = keypad_scan(); if (key == '1' || key == '2') { ampm = (key == '1') ? 'A' : 'P'; lcd.locate(14, 0); lcd.printf("%cM", ampm); } else if (key == '#') { if (ampm != ' ') { break; } else { lcd.cls(); lcd.printf("SELECT AM/PM"); wait_ms(1000); lcd.cls(); lcd.locate(0, 0); lcd.printf("AM(1)/PM(2): "); } } } // 设置温度(支持小数输入,用A键作为小数点) lcd.cls(); lcd.locate(0, 0); lcd.printf("TEMP: "); float temperature = 0.0f; bool has_decimal = false; float decimal_factor = 0.1f; while (true) { char key = keypad_scan(); if (key >= '0' && key <= '9') { if (!has_decimal) { temperature = temperature * 10 + (key - '0'); } else { temperature += (key - '0') * decimal_factor; decimal_factor *= 0.1f; } lcd.locate(6, 0); lcd.printf("%.2f", temperature); } else if (key == 'A' && !has_decimal) { has_decimal = true; lcd.locate(6, 0); lcd.printf("%.2f", temperature); } else if (key == '#') { break; } } // 显示最终设置结果 lcd.cls(); lcd.locate(0, 0); lcd.printf("%02d:%02d:%02d %cM", hour, minute, second, ampm); lcd.locate(0, 1); lcd.printf("TEMP: %.2f C", temperature); wait_ms(3000); // 停留3秒后返回主循环 }
修复说明
- 引脚冲突解决:重新分配LCD引脚,确保与键盘引脚完全不重叠,适配Nucleo开发板空闲引脚。
- 按键防抖:在扫描时增加5ms延时,并等待按键释放,避免抖动导致的误触发或漏检测。
- 主循环优化:仅在检测到有效按键时才处理输入,避免空字符累积。
- LCD交互优化:添加光标定位、错误提示延时、两位数格式化显示,提升用户体验。
- 补充AM/PM设置:按照需求实现上/下午选择逻辑。
- 温度输入优化:支持小数输入(使用键盘A键作为小数点),符合实际温度设置需求。
内容的提问来源于stack exchange,提问作者Calvin
相关产品推荐
相关产品推荐

