自研操作系统内核键盘输入返回乱码 键盘驱动扫描码问题排查
键盘驱动乱码问题修复
你的代码存在3个直接导致乱码的核心错误,还有1个潜在隐患:
- 扫描码映射完全错误:PS/2键盘的Set1扫描码是按照键盘物理键位排布编码的,不是按照字母表顺序递增编码。你写的
0x1C对应A、后续扫描码依次对应B~Z的逻辑完全不符合实际编码规则,比如0x1C实际是回车键的通码,A键的通码实际是0x1E,所有字母映射全部错位。 - 未过滤按键释放事件:PS/2键盘按下按键时发送通码(值范围0~0x7F,最高位为0),松开按键时发送断码(值为对应通码 | 0x80,范围0x80~0xFF,最高位为1)。你当前的逻辑没有过滤断码,松开按键时也会读入无效值进入判断分支。
- 函数无兜底返回值:
keyboard_handle_input仅在匹配到你列的26个扫描码时才有返回值,读入断码、特殊键码、未覆盖按键的码值时,函数会走到末尾无return语句,返回栈上的随机垃圾值,打印出来就是无意义乱码。 - 潜在隐患:
keyboard.c没有引入自己的keyboard.h头文件,编译器无法做函数返回值、参数的类型校验,容易触发隐式类型转换bug。
修复步骤
- 替换掉手写的一堆if判断,用查表法做扫描码到ASCII的映射,匹配真实的PS/2 Set1扫描码规则
- 增加断码过滤逻辑,读入最高位为1的断码时直接返回0(代表无有效字符输入)
- 补全头文件引用,给映射表未覆盖的按键统一返回0
- 上层调用打印逻辑增加判断:仅当
keyboard_handle_input返回值非0时,才往屏幕输出字符
修复后的keyboard.c完整代码如下:
#include <stdint.h> #include "keyboard.h" uint8_t inb(uint16_t port){ uint8_t ret; asm volatile("inb %1, %0" : "=a"(ret) : "Nd"(port)); return ret; } uint8_t keyboard_read_input(){ // 等待键盘输出缓冲区就绪 while(!(inb(0x64) & 1)); return inb(0x60); } // PS/2 Set1通码转ASCII映射表,索引为扫描码值,无对应字符则存0 static const char scancode_to_ascii[128] = { 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.' }; char keyboard_handle_input(){ uint8_t scancode = keyboard_read_input(); // 过滤按键松开的断码 if (scancode & 0x80) { return 0; } // 查表返回对应ASCII字符,无映射则返回0 return scancode_to_ascii[scancode]; }
后续扩展提示
如果需要支持大写输入、Shift上档符号,可以加一个全局shift状态标志:
- 读入左Shift(通码0x2A)、右Shift(通码0x36)的通码时,把shift标志位置1
- 读入这两个键的断码时,把shift标志位清0
- 查表时如果shift标志位为1,返回对应大写字符/上档符号即可
内容的提问来源于stack exchange,提问作者Pixel Coin
相关产品推荐
相关产品推荐

