You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32F446RE矩阵键盘按键打印功能故障排查求助

问题分析与修复方案

你的代码存在多个关键问题导致功能失效,以下是逐一分析和对应修复:

核心问题点

  1. printf 未重定向:Debug (printf) Viewer 依赖 SWO 输出,需实现标准库的 __io_putchar 函数完成重定向
  2. 列引脚读取掩码错误:列引脚为 8、10、12,但代码中错误使用了行引脚的掩码 0x0055
  3. 按键状态判断逻辑错误:列引脚按下后为低电平,原代码的匹配值与实际引脚状态不符
  4. 防抖延迟不足:原延迟函数无法有效消除按键抖动,导致误判
  5. 缺少初始提示信息:未按需求输出引导用户按键的文本

修复后的完整代码

#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 配套配置

  1. 打开 Options for Target -> Debug -> Settings -> Trace
  2. 勾选 Enable,设置 Core Clock 为你的系统时钟(如 180MHz),Trace Port Width 设为 1
  3. 在 Debug (printf) Viewer 窗口中选择 ITM Stimulus Port 0

内容的提问来源于stack exchange,提问作者GucciDonut

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 14:55:03