STM32F446RE矩阵键盘按键打印功能故障排查求助
问题分析与修复方案
你的代码存在多个关键问题导致功能失效,以下是逐一分析和对应修复:
核心问题点
- printf 未重定向:Debug (printf) Viewer 依赖 SWO 输出,需实现标准库的
__io_putchar函数完成重定向 - 列引脚读取掩码错误:列引脚为 8、10、12,但代码中错误使用了行引脚的掩码
0x0055 - 按键状态判断逻辑错误:列引脚按下后为低电平,原代码的匹配值与实际引脚状态不符
- 防抖延迟不足:原延迟函数无法有效消除按键抖动,导致误判
- 缺少初始提示信息:未按需求输出引导用户按键的文本
修复后的完整代码
#include "stm32f4xx.h" #include <stdio.h> void delay(void); void delayMs(int n); void keypad_init(void); char keypad_getkey(void); int __io_putchar(int ch); int main(void) { unsigned char key; keypad_init(); printf("Please press a key:\r\n"); // 输出初始提示 while(1) { key = keypad_getkey(); if(key != 0) { printf("KEY PRESSED: %c\r\n", key); delayMs(200); // 按键消抖,避免重复输出 } } } void keypad_init(void) { RCC->AHB1ENR |= (1<<2); // 使能 GPIOC 时钟 // 清除行引脚(0、2、4、6)和列引脚(8、10、12)的模式配置 GPIOC->MODER &= ~( (0x3<<0) | (0x3<<4) | (0x3<<8) | (0x3<<12) | (0x3<<16) | (0x3<<20) | (0x3<<24) ); // 列引脚配置为上拉输入 GPIOC->PUPDR &= ~( (0x3<<16) | (0x3<<20) | (0x3<<24) ); GPIOC->PUPDR |= (0x1<<16) | (0x1<<20) | (0x1<<24); } char keypad_getkey(void) { int row, col; // 行引脚输出模式配置值 const uint32_t row_mode[] = {0x1<<0, 0x1<<4, 0x1<<8, 0x1<<12}; // 行引脚拉低(BSRR 高16位) const uint32_t row_low[] = {0x1<<(0+16), 0x1<<(2+16), 0x1<<(4+16), 0x1<<(6+16)}; // 行引脚拉高(BSRR 低16位) const uint32_t row_high[] = {0x1<<0, 0x1<<2, 0x1<<4, 0x1<<6}; // 键盘映射表 const char keypad[4][3] = { {'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'}, {'*', '0', '#'} }; // 列引脚掩码(8、10、12) const uint32_t col_mask = (1<<8)|(1<<10)|(1<<12); // 所有列未按下时的状态 const uint32_t col_all_high = col_mask; // 快速检测是否有按键按下 GPIOC->MODER |= (0x1<<0)|(0x1<<4)|(0x1<<8)|(0x1<<12); // 所有行设为输出 GPIOC->BSRR = row_low[0] | row_low[1] | row_low[2] | row_low[3]; // 所有行拉低 delayMs(5); // 防抖延迟 col = GPIOC->IDR & col_mask; GPIOC->MODER &= ~( (0x3<<0)|(0x3<<4)|(0x3<<8)|(0x3<<12) ); // 行设为输入 if(col == col_all_high) // 无按键按下 return 0; // 逐行扫描确定按键位置 for(row=0; row<4; row++) { GPIOC->MODER &= ~( (0x3<<0)|(0x3<<4)|(0x3<<8)|(0x3<<12) ); // 关闭所有行输出 GPIOC->MODER |= row_mode[row]; // 开启当前行输出 GPIOC->BSRR = row_low[row]; // 当前行拉低 delayMs(5); // 防抖延迟 col = GPIOC->IDR & col_mask; // 读取列状态 GPIOC->BSRR = row_high[row]; // 当前行拉高 if(col != col_all_high) // 当前行有按键按下 break; } // 恢复行引脚为输入状态 GPIOC->MODER &= ~( (0x3<<0)|(0x3<<4)|(0x3<<8)|(0x3<<12) ); if(row ==4) // 未检测到有效行 return 0; // 确定具体列 if(col == (col_all_high ^ (1<<8))) // 第0列(引脚8) return keypad[row][0]; else if(col == (col_all_high ^ (1<<10))) // 第1列(引脚10) return keypad[row][1]; else if(col == (col_all_high ^ (1<<12))) // 第2列(引脚12) return keypad[row][2]; return 0; } // 短延迟函数 void delay(void) { int i; for(i=0; i<1000; i++); } // 毫秒级延迟函数 void delayMs(int n) { for(; n>0; n--) { delay(); delay(); delay(); } } // 重定向printf到SWO,用于Debug Viewer输出 int __io_putchar(int ch) { // 等待SWO数据寄存器为空 while((ITM->PORT[0].u32 & ITM_PORT_Ready) == 0); // 发送字符 ITM->PORT[0].u8 = (uint8_t)ch; return ch; }
uVision 配套配置
- 打开
Options for Target->Debug->Settings->Trace - 勾选
Enable,设置Core Clock为你的系统时钟(如 180MHz),Trace Port Width设为 1 - 在
Debug (printf) Viewer窗口中选择ITM Stimulus Port 0
内容的提问来源于stack exchange,提问作者GucciDonut
相关产品推荐
相关产品推荐

