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

Arduino C/C++编程:实现Shift组合大写字母按键检测

解决Arduino键盘Shift组合键检测与映射问题

需求说明

实现Arduino通过C/C++代码检测Shift键与字母按键的组合按下,当按住Shift时,从预设按键映射表中选取大写字母(或对应符号)输出到串口监视器。现有代码存在扫描值存储失效、Shift状态判断逻辑不完善等问题,需进行修复。

现有代码

#define CLOCK 6 //D-
#define DATA 7  //D+

const char keymap[][2] = {
  {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},                                                       //0
  {0,0}, {0,0}, {0,0}, {0,0}, {'`','~'}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},                                                   //10
  {0,0}, {'q','Q'}, {'1','!'}, {0,0}, {0,0}, {0,0}, {'z','Z'}, {'s','S'}, {'a','A'}, {'w','W'},                               //20
  {'2','@'}, {0,0}, {0,0}, {'c','C'}, {'x','X'}, {'d','D'}, {'e','E'}, {'4','$'}, {'3','#'}, {0,0},                           //30
  {0,0}, {' ',' '}, {'v','V'}, {'f','F'}, {'t','T'}, {'r','R'}, {'5','%'}, {0,0}, {0,0}, {'n','N'},                           //40
  {'b','B'}, {'h','H'}, {'g','G'}, {'y','Y'}, {'6','^'}, {0,0}, {0,0}, {0,0}, {'m','M'}, {'j','J'},                           //50
  {'u','U'}, {'7','&'}, {'8','*'}, {0,0}, {0,0}, {',','<'}, {'k','K'}, {'i','I'}, {'o','O'}, {'0',')'},                       //60
  {'9','('}, {0,0}, {0,0}, {'.','>'}, {'/','?'}, {'l','L'}, {';',':'}, {'p','P'}, {'-','_'}, {0,0},                           //70
  {0,0}, {0,0}, {'\'','"'}, {0,0}, {'[','{'}, {'=','+'}, {0,0}, {0,0}, {0,0}, {0,0},                                         //80
  {0,0}, {']','}'}, {0,0}, {'\\','|'}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},                                              //90
  {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {'1','!'}, {0,0}, {'4','$'}, {'7','&'}, {0,0},                                          //100
  {0,0}, {0,0}, {'0',')'}, {'.','>'}, {'2','@'}, {'5','%'}, {'6','^'}, {'8','*'}, {0,0}, {0,0},                               //110
  {0,0}, {'+','+'}, {'3','#'}, {'-','_'},{'*','*'}, {'9','('}, {0,0}, {0,0}, {0,0}, {0,0},                                    //120
  {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},                                                       //130
};
void setup() {
  Serial.begin(9600);
  pinMode(CLOCK, INPUT_PULLUP); 
  pinMode(DATA, INPUT_PULLUP);
}
uint8_t lastscan; // doesn't work

unsigned short detectKey(){
 unsigned short scanval = 0;
 for(int i = 0; i<11; i++)
  {
    while(digitalRead(CLOCK));
    scanval |= digitalRead(DATA) << i;
    while(!digitalRead(CLOCK));
  }
  scanval >>= 1;
  scanval &= 0xFF;
  return scanval;
}

void loop() {
  bool shiftPressed= true //when the new code I need detects the shift held down;
  if(lastscan != 0xF0 && detectKey() != 0xF0)
  keymap[detectKey()][shiftPressed];
  lastscan = detectKey(); //this doesn't work to save the scancode to a variable
   /* switch(detectKey())
  {
    case 0x5A: //Enter
     Serial.println("Enter");
    break;
    case 0x66: //Backspace
      
    break;
    case 0x12:  //left shift
    while(detectKey() != 0xF0){
      shiftPressed = true;
    }
    shiftPressed = false;
  }
  */
}

串口输出示例

16:47:56.832 -> 1C //hit "a"
16:47:56.924 -> F0 //let go
16:47:56.924 -> 1C 
16:47:57.948 -> 1B //hit "s"
16:47:58.119 -> F0 //let go
16:47:58.119 -> 1B
16:48:02.119 -> 23 //held down "d"
16:48:02.722 -> 23
16:48:02.758 -> 23
16:48:02.758 -> F0 //let go
16:48:02.758 -> 23

从输出可知:

  • 按键按下时输出对应扫描码,按住会重复输出
  • 按键松开时先输出0xF0,再输出对应扫描码
  • Shift键扫描码为0x12,按下/释放输出规律为12→F0→12

修复后的完整代码

#define CLOCK 6 //D-
#define DATA 7  //D+

const char keymap[][2] = {
  {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},                                                       //0
  {0,0}, {0,0}, {0,0}, {0,0}, {'`','~'}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},                                                   //10
  {0,0}, {'q','Q'}, {'1','!'}, {0,0}, {0,0}, {0,0}, {'z','Z'}, {'s','S'}, {'a','A'}, {'w','W'},                               //20
  {'2','@'}, {0,0}, {0,0}, {'c','C'}, {'x','X'}, {'d','D'}, {'e','E'}, {'4','$'}, {'3','#'}, {0,0},                           //30
  {0,0}, {' ',' '}, {'v','V'}, {'f','F'}, {'t','T'}, {'r','R'}, {'5','%'}, {0,0}, {0,0}, {'n','N'},                           //40
  {'b','B'}, {'h','H'}, {'g','G'}, {'y','Y'}, {'6','^'}, {0,0}, {0,0}, {0,0}, {'m','M'}, {'j','J'},                           //50
  {'u','U'}, {'7','&'}, {'8','*'}, {0,0}, {0,0}, {',','<'}, {'k','K'}, {'i','I'}, {'o','O'}, {'0',')'},                       //60
  {'9','('}, {0,0}, {0,0}, {'.','>'}, {'/','?'}, {'l','L'}, {';',':'}, {'p','P'}, {'-','_'}, {0,0},                           //70
  {0,0}, {0,0}, {'\'','"'}, {0,0}, {'[','{'}, {'=','+'}, {0,0}, {0,0}, {0,0}, {0,0},                                         //80
  {0,0}, {']','}'}, {0,0}, {'\\','|'}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},                                              //90
  {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {'1','!'}, {0,0}, {'4','$'}, {'7','&'}, {0,0},                                          //100
  {0,0}, {0,0}, {'0',')'}, {'.','>'}, {'2','@'}, {'5','%'}, {'6','^'}, {'8','*'}, {0,0}, {0,0},                               //110
  {0,0}, {'+','+'}, {'3','#'}, {'-','_'},{'*','*'}, {'9','('}, {0,0}, {0,0}, {0,0}, {0,0},                                    //120
  {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},                                                       //130
};

// 全局变量跟踪按键状态
uint8_t lastScanCode = 0;
bool shiftPressed = false;
bool isKeyReleased = false;

void setup() {
  Serial.begin(9600);
  pinMode(CLOCK, INPUT_PULLUP); 
  pinMode(DATA, INPUT_PULLUP);
}

unsigned short detectKey(){
 unsigned short scanval = 0;
 for(int i = 0; i<11; i++)
  {
    while(digitalRead(CLOCK));
    scanval |= digitalRead(DATA) << i;
    while(!digitalRead(CLOCK));
  }
  scanval >>= 1;
  scanval &= 0xFF;
  return scanval;
}

void loop() {
  uint8_t currentScan = detectKey();

  // 处理释放码0xF0
  if(currentScan == 0xF0){
    isKeyReleased = true;
    lastScanCode = currentScan;
    return;
  }

  // 处理释放后的收尾扫描码
  if(isKeyReleased){
    // 如果是Shift的释放码,更新Shift状态
    if(currentScan == 0x12){
      shiftPressed = false;
    }
    isKeyReleased = false;
    lastScanCode = currentScan;
    return;
  }

  // 处理Shift按键按下
  if(currentScan == 0x12 && lastScanCode != 0x12){
    shiftPressed = true;
    lastScanCode = currentScan;
    return;
  }

  // 处理普通按键,避免重复输出(按住时重复扫描码)
  if(currentScan != lastScanCode && currentScan != 0){
    char outputChar = keymap[currentScan][shiftPressed ? 1 : 0];
    if(outputChar != 0){
      Serial.print(outputChar);
    }
    // 特殊按键处理(示例:Enter输出换行)
    if(currentScan == 0x5A){
      Serial.println();
    }else if(currentScan == 0x66){
      Serial.print("\b"); // 退格
    }
  }

  lastScanCode = currentScan;
  delay(5); // 防抖延迟
}

关键修复说明

  • 扫描值存储修复:每次循环只调用一次detectKey(),将结果存入currentScan,避免多次读取导致状态不一致,确保lastScanCode能正确跟踪上一次的扫描码。
  • Shift状态管理:用全局变量shiftPressed跟踪Shift的按下/释放状态,识别0xF0释放码后,处理后续的收尾扫描码来更新Shift状态。
  • 单次触发逻辑:通过对比currentScan和lastScanCode,避免按住按键时重复输出字符,只在按键首次按下时输出一次。
  • 字符映射与输出:根据shiftPressed状态从keymap中选取对应位置的字符(索引0为普通字符,1为Shift后的字符),并处理Enter、Backspace等特殊按键的输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 10:50:25