古人来者_昌 发表于 5 天前

行列矩阵扫描多按键识别问题

最近做的PC键盘,需要处理多按键识别问题,键盘口使用8x11矩阵方式。

目前的情况是,在同一行上的键,多键按下能识别,不同行上的键多键被按下会出现误判情况。
尝试过扫描本行时,把其它行端口设为高阻输入无上拉模式,也会出现误判情况,俗称的“鬼影”
论坛里有人做过相关的经验可以分享交流下吗?
我的多键识别代码如下:


void         MainKeyPressScan(void)
{
        u32 KeyData;
       
        if( MainKeyPressStructure.KeyScanEnable == DISABLE )
                return;
               
        MainKeyPressStructure.KeyScanEnable= DISABLE;
        MainKeyPressStructure.KeyScanTime1ms = 0;       
       
        KeyData = GetMainKeyPort();

        if( KeyData != MainKeyPressStructure.KeyPressCode )                                                //按键有变化
                {
                        MainKeyPressStructure.KeyPressCode = KeyData;                                        //取数据
                        MainKeyPressStructure.KeyDebounce = KEY_DEBOUNCE_TIME;                        //开始抖动
                }
        else
                {
                        if( MainKeyPressStructure.KeyDebounce )
                                {
                                        MainKeyPressStructure.KeyDebounce--;
                                        if( MainKeyPressStructure.KeyDebounce == 0)
                                                {
                                                        MainKeyPressStructure.KeyPressCode = KeyData;
                                                        MainKeyPressStructure.KeyPressSuccess= SUCCESS;
                                                }
                                }
                }

}


//矩阵扫描部分代码,P0口做扫描数据口,P2口和P7.5-7做按键返回数据口。

#define                KEY_SCAN_LINE1_DATA                                ~0x01                        //第1行扫描数据       

#define                KEY_SCAN_LINE2_DATA                                ~0x02                        //第2行扫描数据       



u32         GetMainKeyPort(void)
{
        u8        KeyTempPort2,KeyTempPort7;
        u16 KeyReturnData;
        u32        KeyResult,KeyPressData;

        KeyResult = 0;

        SetPort0Mode(KEY_SCAN_LINE1_DATA, 0, ~KEY_SCAN_LINE1_DATA);                //P00设为准双向口+上拉电阻,其它的设为高祖输入+无上拉电阻
        IO_KeyDelay(IO_KEY_DELAY_TIME);
       
        KeyPressData = MAIN_KEY_WAS_NOT_PRESSED;       
        KEY_IO_SCAN_PORT0 = KEY_SCAN_LINE1_DATA;                                                                //扫描第1行按键
        IO_KeyDelay(IO_KEY_DELAY_TIME);       
        KeyTempPort2 = KEY_IO_RETURN_PORT2;                                                                                //读取按键返回值       
        KeyTempPort7 = KEY_IO_RETURN_PORT7;                                                                                //读取按键返回值
        KeyTempPort7 = ( KeyTempPort7 & KEY_RETURN_PORT7_MARSK ) >> 5;
        KeyTempPort7 |= 0xF8;       
        KeyReturnData = ~((((u16)KeyTempPort7) << 8) | (u16)KeyTempPort2);        
        if( KeyReturnData != MAIN_KEY_WAS_NOT_PRESSED )
                {
                        KeyPressData = ~KEY_SCAN_LINE1_DATA;                                                        //处理第1行按键数据
                        KeyPressData = ( KeyPressData << 16 ) | (u32)KeyReturnData;       
                        KeyResult |= KeyPressData;
                       
                }



        SetPort0Mode(KEY_SCAN_LINE2_DATA, 0, ~KEY_SCAN_LINE2_DATA);
        IO_KeyDelay(IO_KEY_DELAY_TIME);
       
        KeyPressData = MAIN_KEY_WAS_NOT_PRESSED;       
        KEY_IO_SCAN_PORT0 = KEY_SCAN_LINE2_DATA;                                                                //扫描第2行按键
        IO_KeyDelay(IO_KEY_DELAY_TIME);       
        KeyTempPort2 = KEY_IO_RETURN_PORT2;                                                                                //读取按键返回值       
        KeyTempPort7 = KEY_IO_RETURN_PORT7;                                                                                //读取按键返回值
        KeyTempPort7 = ( KeyTempPort7 & KEY_RETURN_PORT7_MARSK ) >> 5;
        KeyTempPort7 |= 0xF8;       
        KeyReturnData = ~((((u16)KeyTempPort7) << 8) | (u16)KeyTempPort2);        
        if( KeyReturnData != MAIN_KEY_WAS_NOT_PRESSED )
                {
                        KeyPressData = ~KEY_SCAN_LINE2_DATA;                                                        //处理第2行按键数据
                        KeyPressData = ( KeyPressData << 16 ) | (u32)KeyReturnData;       
                        KeyResult |= KeyPressData;                       
                }

}

国芯人工智能 发表于 5 天前

在处理8x11矩阵键盘的多按键识别问题时,您遇到的“鬼影”现象(即不同行上的多键按下时出现误判)是一个常见的技术挑战。这种现象通常是由于矩阵扫描时,电流通过未被激活的行或列,导致误触发。以下是一些可能的解决方案和优化建议:

1. 二极管隔离
在键盘矩阵的每个按键上添加二极管,可以有效地防止电流反向流动,从而避免“鬼影”现象。二极管的方向应确保电流只能从行流向列,或从列流向行,具体取决于您的扫描方式。

2. 改进扫描算法
您的扫描代码可以进一步优化,以确保在扫描某一行时,其他行处于高阻状态,并且没有上拉电阻。以下是一个改进的扫描算法示例:

c
void MainKeyPressScan(void) {
    u32 KeyData;
   
    if (MainKeyPressStructureKeyScanEnable == DISABLE)
      return;
      
    MainKeyPressStructureKeyScanEnable = DISABLE;
    MainKeyPressStructureKeyScanTime1ms = 0;   
   
    for (int row = 0; row < 8; row++) {
      // 激活当前行
      SetRowActive(row);
      
      // 读取列数据
      KeyData = GetColumnData();
      
      // 处理按键数据
      if (KeyData != MainKeyPressStructureKeyPressCode) {
            MainKeyPressStructureKeyPressCode = KeyData;
            MainKeyPressStructureKeyDebounce = KEYDEBOUNCETIME;
      } else {
            if (MainKeyPressStructureKeyDebounce) {
                MainKeyPressStructureKeyDebounce--;
                if (MainKeyPressStructureKeyDebounce == 0) {
                  // 处理按键事件
                  ProcessKeyPress(MainKeyPressStructureKeyPressCode);
                }
            }
      }
      
      // 关闭当前行
      SetRowInactive(row);
    }
}

3. 去抖动处理
按键抖动是另一个常见问题,可能导致误判。您可以在硬件或软件层面进行去抖动处理。软件去抖动通常通过延时和多次采样来实现。以下是一个简单的去抖动处理示例:

c
define DEBOUNCETIME 20 // 去抖动时间,单位为毫秒

void DebounceKeyPress(u32 KeyData) {
    static u32 LastKeyData = 0;
    static u32 DebounceCounter = 0;
   
    if (KeyData != LastKeyData) {
      DebounceCounter = DEBOUNCETIME;
      LastKeyData = KeyData;
    } else {
      if (DebounceCounter > 0) {
            DebounceCounter--;
            if (DebounceCounter == 0) {
                // 处理按键事件
                ProcessKeyPress(KeyData);
            }
      }
    }
}

4. 硬件设计优化
在硬件设计上,确保矩阵的行和列之间有足够的隔离,避免信号串扰。使用高质量的PCB材料和合理的布线设计,可以减少信号干扰和误触发。

5. 使用专用芯片
如果上述方法仍无法解决问题,可以考虑使用专用的键盘扫描芯片。这些芯片通常内置了多按键识别和去抖动功能,可以大大简化软件设计和提高系统可靠性。

6. 调试与测试
在实际应用中,调试和测试是确保系统稳定性的关键。使用逻辑分析仪或示波器,可以实时监控矩阵扫描过程中的信号变化,帮助定位和解决问题。

总结
通过上述方法,您可以有效地解决8x11矩阵键盘的多按键识别问题,避免“鬼影”现象。具体实施方案需要根据您的硬件设计和应用需求进行调整。希望这些建议对您有所帮助,祝您的项目顺利成功!

[本文内容由国芯人工智能辅助生成,仅供参考]

国芯人工智能 发表于 5 天前

在处理PC键盘的多按键识别问题时,尤其是采用8x11矩阵扫描的方式,确实会遇到“鬼影”(Ghosting)问题。这是由于矩阵扫描的物理特性导致的,特别是在不同行上的按键同时按下时,可能会出现误判。以下是一些专业的建议和解决方案,帮助你优化代码并解决这一问题。

1. 理解“鬼影”问题的根源
“鬼影”问题通常是由于矩阵扫描中的电气特性导致的。当多个按键按下时,电流可能会通过未预期的路径流动,导致误判。具体来说,当不同行和列上的按键同时按下时,可能会形成一个“虚拟”的按键,从而产生误判。

2. 优化扫描算法
为了避免“鬼影”问题,可以采取以下优化措施:

a. 逐行扫描
在扫描某一行时,将该行的输出设置为低电平,同时将所有其他行的输出设置为高阻态或高电平。这样可以确保只有当前行的按键状态被读取,避免其他行的干扰。

b. 使用二极管隔离
在矩阵的每个按键上添加一个二极管,可以防止电流反向流动,从而避免“鬼影”问题。这种方法虽然增加了硬件成本,但能有效解决多按键识别问题。

c. 软件去抖
在读取按键状态后,加入去抖处理,可以有效避免由于按键抖动导致的误判。你已经在代码中实现了去抖处理,这是正确的做法。

3. 代码优化建议
基于上述分析,以下是代码的优化建议:

c
void MainKeyPressScan(void)
{
    u32 KeyData;
   
    if (MainKeyPressStructureKeyScanEnable == DISABLE)
      return;
      
    MainKeyPressStructureKeyScanEnable = DISABLE;
    MainKeyPressStructureKeyScanTime1ms = 0;   
   
    // 逐行扫描
    for (int row = 0; row < 8; row++) {
      // 设置当前行为低电平
      SetRowLow(row);
      
      // 读取列数据
      KeyData = GetMainKeyPort();
      
      // 处理按键状态
      if (KeyData != MainKeyPressStructureKeyPressCode) {
            MainKeyPressStructureKeyPressCode = KeyData;
            MainKeyPressStructureKeyDebounce = KEYDEBOUNCETIME;
      } else {
            if (MainKeyPressStructureKeyDebounce) {
                MainKeyPressStructureKeyDebounce--;
                if (MainKeyPressStructureKeyDebounce == 0) {
                  // 处理稳定的按键状态
                  ProcessKeyPress(MainKeyPressStructureKeyPressCode);
                }
            }
      }
      
      // 恢复当前行为高阻态或高电平
      SetRowHigh(row);
    }
   
    MainKeyPressStructureKeyScanEnable = ENABLE;
}

void SetRowLow(int row) {
    // 设置指定行为低电平,其他行为高阻态或高电平
    // 具体实现取决于硬件设计
}

void SetRowHigh(int row) {
    // 恢复指定行为高阻态或高电平
    // 具体实现取决于硬件设计
}

void ProcessKeyPress(u32 keyCode) {
    // 处理稳定的按键状态
    // 具体实现取决于应用需求
}

4. 硬件设计建议
除了软件优化,硬件设计也是解决“鬼影”问题的关键。以下是一些硬件设计建议:

a. 使用二极管隔离
如前所述,在矩阵的每个按键上添加二极管,可以有效防止电流反向流动,避免“鬼影”问题。

b. 增加上拉电阻
在列线上增加上拉电阻,可以确保在未按下按键时,列线处于高电平状态,从而避免误判。

c. 优化PCB布局
合理的PCB布局可以减少电气干扰,降低“鬼影”问题的发生概率。确保行线和列线之间的间距足够大,避免交叉干扰。

5. 测试与验证
在优化代码和硬件设计后,建议进行充分的测试,确保多按键识别功能正常工作。测试时,可以模拟各种按键组合,特别是不同行上的按键同时按下的情况,验证是否会出现“鬼影”问题。

总结
通过逐行扫描、使用二极管隔离、软件去抖以及优化硬件设计,可以有效解决8x11矩阵键盘的多按键识别问题,避免“鬼影”现象。希望以上建议能帮助你优化键盘设计,提升用户体验。如有进一步问题,欢迎继续交流。

[本文内容由国芯人工智能辅助生成,仅供参考]

ercircle 发表于 5 天前

原理图建议也上传下,有没有楼上说的二极管

古人来者_昌 发表于 5 天前

没有二极管隔离,最初设计时没考虑要多个按键情况。
矩阵8x11,8各行扫描使用P0口,11个列扫描使用P2口和P7口的4、5、6位,没有二极管隔离。
单个键没问题。同一个行扫描信号所在的键也可以识别多个按键,不同行扫描信号所在的键识别有误。

古人来者_昌 发表于 5 天前

改硬件的话,在每行扫描信号加入MOS管(总共8个),扫描该行时,MOS管打开,其它的行MOS管关闭,这个应该可以解决“鬼影”问题。

DebugLab 发表于 5 天前

没有二极管,如矩阵中某个矩形三个顶点同时导通,就无法判断矩形中第四个顶点的状态

ccczzzwwwyy 发表于 前天 07:44

学习了。
页: [1]
查看完整版本: 行列矩阵扫描多按键识别问题