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

自定义C语言内核Shift键大小写切换失效问题求助

自定义内核键盘Shift键切换大小写失效问题

我用C语言开发自定义内核,键盘输入基本正常,但Shift键存在问题:第一次按左/右Shift键可以输入大写字母,但再次按下Shift键无法切换回小写,始终输出大写。

现有键盘处理主函数代码

void keyboard_handler_main(void){

    unsigned char status;
    char keycode;

    /* write EOI */

    write_port(0x20, 0x20);
    status = read_port(KEYBOARD_STATUS_PORT);
    /* lowest bit of status will be set if buffer is not empty */
    
    if(status & 0x1){
        keycode = read_port(KEYBOARD_DATA_PORT);
        if(keycode < 0)
            return;

        if(keycode == ENTRY_KEY_CODE){
            kprint_newline();
            return;
        }
        // change the state when the shift is pressed
        if (keycode == 0x2A || keycode == 0x36) {  // Left or Right Shift pressed
            if(shift_pressed)
                shift_pressed = 0;
            else
                shift_pressed = 1;
            vidptr[current_loc++] = '1';  // Debugging output (you can remove this later)
            vidptr[current_loc++] = 0x07;  // Light grey on black background
             return;
        }
}

全局变量char shift_pressed初始值为0,负责控制大小写切换状态。

尝试过的修改(未解决问题)

if(keycode == 0x2A || keycode == 0x36 ){
    shift_pressed = !(keycode & 0x80);
    vidptr[current_loc++] ='1'; /* the character's  ASCII */
    vidptr[current_loc++] = 0x07; /* attribute byte : light grey black screen */
    return;
} 

问题原因与解决方案

核心问题

你没处理键盘的断码(Break Code)。键盘的工作逻辑是:

  • 按下按键时发送通码(Make Code):比如左Shift按下发0x2A,右Shift按下发0x36;
  • 松开按键时发送断码(Break Code):通码加上0x80,比如左Shift松开发0xAA,右Shift松开发0xB6。

你当前的代码只处理了Shift的通码,每次按Shift都会把shift_pressed设为1,但松开Shift时没有处理断码,状态永远不会变回0,导致一直输出大写。另外char keycode是有符号类型,断码(大于0x80)会被当成负数,被if(keycode <0) return;过滤掉,根本没机会处理。

修改方案

  1. 把char keycode改成unsigned char keycode,避免符号错误导致断码被过滤;
  2. 移除if(keycode <0) return;这行,保留断码的处理机会;
  3. 同时处理Shift的通码和断码,根据按键的按下/松开状态切换shift_pressed。

修改后的代码示例

void keyboard_handler_main(void){

    unsigned char status;
    unsigned char keycode;  // 改为无符号char,避免断码被误判为负数

    /* write EOI */
    write_port(0x20, 0x20);
    status = read_port(KEYBOARD_STATUS_PORT);
    
    if(status & 0x1){
        keycode = read_port(KEYBOARD_DATA_PORT);
        // 移除这行,断码不会再被过滤
        // if(keycode < 0)
        //     return;

        if(keycode == ENTRY_KEY_CODE){
            kprint_newline();
            return;
        }

        // 分离通码和断码
        unsigned char make_code = keycode & 0x7F;  // 取低7位得到原始通码
        int is_key_released = (keycode & 0x80) != 0;  // 判断是否是松开按键的断码

        // 匹配Shift键的通码
        if (make_code == 0x2A || make_code == 0x36) {
            shift_pressed = !is_key_released;  // 按下设为1,松开设为0
            // 调试输出可以改为显示当前状态,方便排查
            vidptr[current_loc++] = shift_pressed ? '1' : '0';
            vidptr[current_loc++] = 0x07;
            return;
        }

        // 后续处理普通字符逻辑,根据shift_pressed决定输出大小写
        // ...
    }
}

为什么之前的修改无效

你尝试的shift_pressed = !(keycode & 0x80)逻辑本身没问题,但你只判断了keycode == 0x2A || 0x36——这两个都是通码,keycode & 0x80结果为0,!(0)就是1,所以每次执行这行都会把shift_pressed设为1,永远不会触发断码的判断逻辑,自然没法切回小写。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 10:05:10