IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 11.嵌入式控制器EC学习 KBC矩阵键盘Keyboard开发流程 -> 正文阅读

[嵌入式]11.嵌入式控制器EC学习 KBC矩阵键盘Keyboard开发流程

笔记本矩阵键盘概述

在EC中支持两种形式的键盘,一种是PS/2接口的键盘,这种键盘几乎已经被USB接口的键盘所取代,所以PS/2接口的键盘不在我们的讨论范围。
另一种键盘则是矩阵键盘,也是笔记本电脑的内置键盘,该键盘类似于学习单片机时使用的4*4的那种键盘,笔记本使用的矩阵键盘有三种规格,分别是16*8、17*8和18*8。16、17和18代表着该款键盘的列数,8代表着键盘的行数,我们这里只讨论最简单的16*8。

笔记本的内置键盘的实物图如下图所示:

矩阵键盘对应的码值表,这个由键盘卖家提供,如下图所示:

矩阵键盘背面的排线,这个排线直接与EC芯片的矩阵键盘接口连接,如下两张图片所示:

矩阵键盘的扫描方式

行列扫描方式

这种方式适合于输入输出方向可以变化的场景,在EC中的矩阵键盘行为输入,列为输出且无法改变方向,这里不适合这种方式,在这里也不过多讨论。

逐行逐列扫描方式

这种方式则适合于行列数据线方向无法改变的情况,这里也以8行16列的键盘为例进行讨论,在EC中将所有的8行连接到了EC的KSI[7:0],EC接收着8根数据线的数据,将所有的16列连接到EC的KSO[15:0],EC通过该16根数据线输出数据。
在代码中是通过一个for循环来实现的,循环次数为列数,这里为16次。

  1. 通过KSO对第0列输出低电平,其他为高,即KSO[15:0]=0xFFFE,0b1111111111111110。
  2. 读取KSI[7:0],
    如果KSI[7:0]!=0xFF,第1步中的输出低电平的列有按键按下,然后再通过KSI[7:0]的第几位为低电平确定按键按下的行号;
    如果KSI[7:0]==0xFF,则代表没有按键按下,对第1步的列号加1,继续执行1-2步。
  3. 如果最后获取到了键码值,就是KSI[7:0]!=0xFF,就设置F_Service_SEND = 1,这样就可以通过KBC往主机发送按键值了,当然这里需要把行列位置转换为相应按键的Keyboard Scan Codes: Set 2对应的值。

EC中矩阵键盘是如何扫描并得到码值的(代码分析)

在主函数的服务函数中存在Service_KEYSCAN矩阵键盘键盘码扫描服务函数,该函数通过轮询的方式执行,会一直监视着矩阵键盘是否由相关动作产生,入口函数在CORE_MAIN.C函数中,如下图所示:

Service_KEYSCAN矩阵键盘扫描服务函数在CORE_SCAN.C中,我们关注scan_key函数,该函数完成了矩阵键盘按键位置的获取,如下图所示:

分析BYTE scan_keys(void)中的这个for循环,键盘的行列位置扫描就是在这个for循环中完成的,使用的方法为前面内容中介绍的逐行逐列扫描方式,其中循环次数SCAN_Cnt为矩阵键盘的列数,Write_Strobe函数的作用是将当前操作的列数SCAN_Cnt电平拉低,其他15列的信号线为高,函数内容在下面有列出,

for (SCAN_Cnt=0;SCAN_Cnt<16;SCAN_Cnt++)
{
    Write_Strobe(SCAN_Cnt);
    #if ( _KBSCAN_DELAY > 0 )
    Microsecond_Delay(_KBSCAN_DELAY);
    #endif
    SCAN_Val = KSI;     // Read in KI 0 - 7 sense line data. */
    SCAN_Val = (~SCAN_Val) ^ bscan_matrix[SCAN_Cnt];

    if (SCAN_Val != 0)
    {
        check_scan(SCAN_Val, SCAN_Cnt);
    }

    if (bscan_matrix[SCAN_Cnt])
    {   // Here, if current still active.
        scan.saf_keys = 1;      // Set keys active bits. Check all key release.
    }

    KSOL  = 0xFF;
    KSOH1 = 0xFF;
    KSOH2 = 0xFF;

    #if (ExternMatrixGPO)
    GPOKeyPin1_HI();
    #endif
}
void Write_Strobe(BYTE scan_line_num)
{
    if (scan_line_num < 8)
    {
        KSOL  =~ (0x01 << scan_line_num);
        KSOH1 = 0xFF;
        KSOH2 = 0xFF;

        #if (ExternMatrixGPO)
        GPOKeyPin1_HI();
        #endif
    }
    else
    {
        KSOL  = 0xFF;
        KSOH1 =~ (0x01<<(scan_line_num-0x08));
        KSOH2 = 0xFF;

        #if (ExternMatrixGPO)
        GPOKeyPin1_HI();
        #endif
    }
}
void Service_SEND(void)
{
    //-------------------------------------------------------------------------
    BYTE nKBData;
    FLAG bBreak;
    BYTE KBCmdAck;
    //-------------------------------------------------------------------------
    KeyboardSendTimer++;

    TH1 = (T_Timer_SEND)>>8;
    TL1 = (T_Timer_SEND);       // Reload timer

    TF1 = 0;
    TR1 = 1;
    ET1 = 1;
    Timer_B.fbit.SEND_ENABLE = 1;
    
    //-------------------------------------------------------------------------
    if( IsFlag1(KBHISR,OBF) || IsFlag1(KBHISR,IBF) )
    {
        return;
    }
    if ( KBPendingRXCount > 0 )
    {
        KBCmdAck = GetKB_PendingData();
        KBC_DataToHost(KBCmdAck);
        return;
    }

    /* ------------------------------------------------------------------------
    * Keyboard Buffer Data Send to Host(System)
    * ---------------------------------------------------------------------- */
    //if( Int_Var.Scan_Lock )     return;
    if( Ccb42_DISAB_KEY )       return;

    nKBData = Get_Buffer();
    FORCE_NO_XLATE = 0;

    if ( nKBData == 0xFF )  //0xFF: No key data in buffer
    {
        Timer_B.fbit.SEND_ENABLE = 0;
        return;
    }
    else
    {
        bBreak = Gen_Info_BREAK_SCAN;
        Gen_Info_BREAK_SCAN = 0;
        #if ENABLE_S5_KEYBOARD_INTR
        if( CORE_PMFLAG_S5 )        return;
        #endif
        #if ENABLE_S3_KEYBOARD_INTR
        if( CORE_PMFLAG_S3 )        return;
        #endif
        if ( send_to_pc(nKBData, bBreak) )
        {
            Gen_Info_BREAK_SCAN = 1;    // Break prefix code.
        }
    }
    //-------------------------------------------------------------------------
}

首先通过Get_Buffer函数获取到刚刚按键的行列位置,再通过send_to_pc函数发送位置,进入send_to_pc函数:

static BYTE send_to_pc( BYTE data_word, BYTE break_prefix_flag )
{
    return( common_send_to_pc( data_word, break_prefix_flag ) );
}

static BYTE common_send_to_pc( BYTE data_word, BYTE break_prefix_flag )
{
    BYTE send_it = FALSE;

    if ( Ccb42_XLATE_PC == 0 )          // Send data as is.
    {
        send_it = TRUE;
        break_prefix_flag = FALSE;
    }
    else    // Translation mode is enabled.
    {
        data_word = translate_to_pc( data_word, break_prefix_flag );
        if ( data_word == 0xFF )
        {
            break_prefix_flag = TRUE;   // Don't send break code prefix.
        }
        else if( data_word == 0x00 )
        {
            break_prefix_flag = TRUE;;  // Don't send break code prefix.
        }
        else
        {
            break_prefix_flag = FALSE;
            send_it = TRUE;
        }
    }

    if ( send_it ) {    Data_To_Host(data_word);   }

    return( break_prefix_flag );
}

里面会通过translate_to_pc函数将按键行列号转换为Set2中的按键键码值,

static BYTE translate_to_pc( BYTE data_word, BYTE break_prefix_flag )
{
    /* Scan code set 2 to scan code set 1 translation table.  First byte is a
       dummy entry because scan code "0" is not translated. */
    static const BYTE code scan2_table[] =
    {
        0x00, 0x43, 0x41, 0x3F, 0x3D, 0x3B, 0x3C, 0x58,
        0x64, 0x44, 0x42, 0x40, 0x3E, 0x0F, 0x29, 0x59,
        0x65, 0x38, 0x2A, 0x70, 0x1D, 0x10, 0x02, 0x5A,
        0x66, 0x71, 0x2C, 0x1F, 0x1E, 0x11, 0x03, 0x5B,
        0x67, 0x2E, 0x2D, 0x20, 0x12, 0x05, 0x04, 0x5C,
        0x68, 0x39, 0x2F, 0x21, 0x14, 0x13, 0x06, 0x5D,
        0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5E,
        0x6A, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5F,
        0x6B, 0x33, 0x25, 0x17, 0x18, 0x0B, 0x0A, 0x60,
        0x6C, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0C, 0x61,
        0x6D, 0x73, 0x28, 0x74, 0x1A, 0x0D, 0x62, 0x6E,
        0x3A, 0x36, 0x1C, 0x1B, 0x75, 0x2B, 0x63, 0x76,
        0x55, 0x56, 0x77, 0x78, 0x79, 0x7A, 0x0E, 0x7B,
        0x7C, 0x4F, 0x7D, 0x4B, 0x47, 0x7E, 0x7F, 0x6F,
        0x52, 0x53, 0x50, 0x4C, 0x4D, 0x48, 0x01, 0x45,
        0x57, 0x4E, 0x51, 0x4A, 0x37, 0x49, 0x46, 0x54
    };

    BYTE check_break_bit = FALSE;

    if ( data_word == 0xF0 )
    {   /* Signify that break code prefix was encountered. */
        data_word = 0xFF;
    }
    else if ( data_word == 0x00 )
    {
        data_word = 0x00;           /* Key detection error/overrun. */
    }
    else if ( (data_word & 0x80) == 0 )
    {   /* Translate codes 01 thru 7F. */
        /* The variable "data" has scan code (set 2) to translate.
           Set "data" to the translated (to set 1) scan code. */
        data_word = scan2_table[data_word];
        check_break_bit = TRUE;
    }
    else if ( data_word == 0x83 )   /* ID code for 101/102 keys. */
    {
        data_word = 0x41;           /* Translate ID code. */
        check_break_bit = TRUE;
    }
    else if ( data_word == 0x84 )   /* ID code for 84 keys. */
    {
        data_word = 0x54;           /* Translate ID code. */
        check_break_bit = TRUE;
    }

    if ( check_break_bit && break_prefix_flag )
    {   /* Last code received by this routine was the break prefix.This must be
           a break code. Set high bit to indicate that this is a break code. */
        data_word |= 0x80;
    }

    return(data_word);
}

最后通过Data_To_Host函数将键码值发送给操作系统或者BIOS:

void Data_To_Host(BYTE data_byte)
{
    #if OEM_HOOK_KB_DATA
    if( OemHandle_Data_To_Host(data_byte) )
    {
        return;
    }
    #endif

    SET_BIT( KBHISR,4 );      //Keyboard Enabled
    CLEAR_BIT( KBHISR,5 );    //Transmit timeout

    if ( Ccb42_INTR_KEY )
    {
        SET_BIT( KBHICR, 0 );
    }
    else
    {
        CLEAR_BIT( KBHICR, 0 );
    }

    KBHIKDOR = data_byte;
    #if En_RecordKB60
    RamDebug(data_byte);
    #endif

}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:17:54  更:2022-05-08 08:18:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:53:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码