Arduino PS/2 键盘模拟器问题

电器工程 Arduino 键盘
2022-02-01 07:14:40

是的,我已经搜索了 Arduino.cc 论坛和这里。是的,我找到了有关 ps2dev 库的文章。是的,我已经阅读(好吧,我略读了一些)本网站上的权威 PS/2 界面文章。是的,我有这个工作,有点。我需要一些想法来实现全面工作。:)

不,我不能只模拟一个 USB HID 键盘并让它保持不变——它需要是 PS/2 键盘模拟。是的,我正在发送正确的接通和断开信号——它甚至可以处理非常复杂的按键组合。就目前而言,我已经为我的 Arduino 编写了代码,如下所示(技术上是 Freeduino 1.22),并且我已经通过串行监视器或 PuTTY 终端以及一个方便的 Python 包装器/驱动程序发送了实际的击键。 PS/2 扫描码信息——通常让我的生活更轻松——也减轻了 Arduino 的一些负担。

现在,我在 Arduino 上运行了一个模拟 PS/2 键盘的草图。自然,我必须启动我的“目标”机器(PS/2 Plug 进入的机器),然后我看到发生了“握手”。启动到 WinDoze,打开记事本,然后使用我的 Python“驱动程序”(成功)将击键驱动到屏幕上。(驱动程序简单地取代了串行监视器/PuTTY 终端,并使用名为 PySerial 的模块读取/写入串行端口。)这一切都是在华硕主板“目标”中的 AMD 上完成的。

现在,目标是让它在基于英特尔主板的“目标”中运行在我的英特尔上,我将它插入,启动,没有骰子。所以,我稍微修改了草图,试图让自己对我的小阿迪朋友的实际情况有所了解。修改后的版本如下所示。据我了解(代码是从另一个 Arduino.cc 论坛帖子“借来的”,这里)它将首先尝试通过 PS/2 与“目标”建立连接,以 0.5 秒的周期闪烁板载 LED,直到连接建立。英特尔目标没有超过 0.5 秒的闪烁周期,并且从未与“主机”建立串行连接。

我的问题是:ps/2 键盘与目标机器建立通信的方式有很大不同吗?这真的是设计差异还是我应该寻找更基本的东西,这就是这里的问题?我听说在数据/时钟输入上需要上拉电阻,但这应该在代码中处理,特别是因为它正在另一个目标上工作,而不是我需要它工作的那个。

有任何想法吗?我很想尽快让这个工作 - 我将继续进行调试,任何指针或建议将不胜感激。他们都将得到充分考虑,因为我需要对这个问题有一些新的看法。也许需要在 ps2dev 库中更好地实现?

#include "ps2dev.h" // to emulate a PS/2 device

// Orange = 2
// Blue = 3
// Red = 5V (3 in)
// Black = GND (4 in)
// EXT Power, USB for COM only

PS2dev keyboard(3,2); // PS2dev object (2:data, 3:clock)
int enabled = 0; // pseudo variable for state of "keyboard"
boolean serialConnected = false;
int incomingByte = 0;

void ack() {
  //acknowledge commands
  while(keyboard.write(0xFA));
}

int kbdCmd(int command) {
  unsigned char val;
  switch (command) {
  case 0xFF: //reset
    ack();
    //the while loop lets us wait for the host to be ready
    while(keyboard.write(0xAA)!=0);
    break;
  case 0xFE: //resend
    ack();
    break;
  case 0xF6: //set defaults
    //enter stream mode
    ack();
    break;
  case 0xF5: //disable data reporting
    //FM
    enabled = 0;
    ack();
    break;
  case 0xF4: //enable data reporting
    //FM
    enabled = 1;
    ack();
    break;
  case 0xF3: //set typematic rate
    ack();
    keyboard.read(&val); //do nothing with the rate
    ack();
    break;
  case 0xF2: //get device id
    ack();
    keyboard.write(0xAB);
    keyboard.write(0x83);
    break;
  case 0xF0: //set scan code set
    ack();
    keyboard.read(&val); //do nothing with the rate
    ack();
    break;
  case 0xEE: //echo
    //ack();
    keyboard.write(0xEE);
    break;
  case 0xED: //set/reset LEDs
    ack();
    keyboard.read(&val); //do nothing with the rate
    ack();
    break;
  }
}

void connectHost() {
  while (Serial.available() <= 0) {
    Serial.print('A');   // send a capital A
    delay(300);
  }
}

void setup() {
  pinMode(13, OUTPUT);
  //establish serial connection with host
  Serial.begin(9600);
  // establish ps/2 connection with target
  while(keyboard.write(0xAA)!=0){
    digitalWrite(13, HIGH);
    delay(500); 
    digitalWrite(13, LOW);
    delay(500);
  }
  delay(100);  
  
  connectHost();
  Serial.println("\nSerial Host Connected");
  Serial.flush();
}

void loop() {
  unsigned char c;
  if( (digitalRead(3)==LOW) || (digitalRead(2) == LOW)) {
    if(digitalRead(3)==LOW){
      Serial.println("pin 3  is LOW");
    } else {
      Serial.println("pin 2 is LOW");
    }
    while(keyboard.read(&c));
    kbdCmd(c);
    Serial.print("Target: 0x");
    Serial.println(c, HEX);
  }  
  else {//if host device wants to send a command:
    //echo ASCII code from terminal and write to ps/2
    if(Serial.available() > 0) {
      incomingByte = Serial.read();
      keyboard.write(incomingByte);      
      Serial.print("Host: 0x");
      Serial.print(incomingByte, HEX);
      Serial.print(" ");
      Serial.print(incomingByte);
      Serial.print(" ");
      Serial.println(incomingByte, BIN);
    }
  }
}
3个回答

据我了解,您将 Arduino 连接到两台不同的目标机器,一台可以工作,另一台不能。

所以看起来两台机器的初始化要求是有区别的。此页面的最底部,列出了可能的初始化序列。首先将您的初始化与那个进行比较。

使用逻辑分析仪会容易得多。我正在使用Intronix Logicport,但有更便宜和更好的,虽然不是同时。

接入集电极开路总线有点麻烦,因为您看不到哪个设备在说话。但是,如果您在没有上拉电阻的一端插入一个串联电阻,您可以通过电压电平判断哪个设备正在抑制总线。每个集电极开路总线(如 PS/2)都需要上拉电阻,通常它们内置在 PC 中。您可以在 DSO 上轻松查看不同的电压电平。只有 LA,您必须使用不同的阈值电压记录两次。

鉴于您的项目适用于一个主板而不是另一个主板,您似乎有一个“部分规范合规”的经典案例 - 在您的项目中,甚至可能在其中一个主板中。但是大多数键盘都可以与任何主板一起使用,因此强大的实现应该是可移植的。挑战是你必须弄清楚为什么你的不是。

你也许可以通过盯着问题并思考它应该如何工作来做到这一点(也许在休息后 - 或者有一天答案会在你洗澡时打到你)但如果你能监控你会更有效到底是怎么回事。对于电气问题意味着示波器,对于协议问题则意味着逻辑分析仪。该领域有一些便宜的选择,例如“总线盗版”板,它具有一些特定的键盘协议功能或基于 FPGA 的东西,它可能具有更长的捕获缓冲区(参见 sump.org)。

您可以尝试的另一件事是使用其他设备(微控制器或 FPGA)来构建键盘主机并使用它来测试您的项目是否符合规范的限制。

我还没有查看 ps2dev 库来确切了解它是如何工作的,但有一件事确实让我眼前一亮。

目前,尝试连接到“主机”计算机。如果失败,则等待一整秒(LED 亮 0.5 秒,LED 熄灭 0.5 秒),然后再进行另一次尝试。

如果英特尔主板没有为键盘检测等待足够长的时间,那么它可能永远不会在继续其引导序列之前获得连接尝试。

如果您将等待时间减少到 0.1 秒(将 delay(500) 行更改为 delay(50)),您可能会有一些运气。

如果没有,请尝试更快。地狱,甚至可以毫不拖延地尝试一下,看看效果如何。