Arduino Pro Micro自制HID游戏手柄开发求助:矩阵按键与触控板代码问题
DIY HID游戏手柄开发问题(Arduino Pro Micro)
我是Arduino/MCU开发新手,正在用Arduino Pro Micro制作一款可后续集成到更大项目的DIY HID游戏手柄。硬件包含一个4x4按键矩阵和两块Azoteq TPS43-201A-S电容触控板(内置Azoteq IQS570电容触摸控制器)。目前遇到两个问题:
- 不确定自己编写的矩阵按键代码是否正确
- 完全不知道如何编写触控板的Arduino驱动代码
以下是我目前完成的代码:
#include <Keypad.h> #include <Gamepad.h> // Define the 4x4 button matrix const byte ROWS = 4; const byte COLS = 4; char keys[ROWS][COLS] = { {'Y', 'X', 'B', 'A'}, {'LEFT', 'RIGHT', 'UP', 'DOWN'}, {'LB', 'RB', 'SE', 'MU'}, {'JL', 'JR', 'E1', 'E2'} }; // Define the pin numbers for the rows and columns of the matrix byte rowPins[ROWS] = {9, 8, 7, 6}; // Row pins are connected to Arduino pins 9, 8, 7, 6 byte colPins[COLS] = {5, 4, 3, 2}; // Column pins are connected to Arduino pins 5, 4, 3, 2 Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); Gamepad gp; void setup() { for (byte i = 0; i < ROWS; i++) { pinMode(rowPins[i], INPUT_PULLUP); } for (byte i = 0; i < COLS; i++) { pinMode(colPins[i], INPUT_PULLUP); } pinMode(A0, INPUT); //right stick y-axis pinMode(A1, INPUT); //right stick x-axis pinMode(A2, INPUT); //left stick y-axis pinMode(A3, INPUT); //left stick x-axis pinMode(A4, INPUT); //left trigger pinMode(A5, INPUT); //right trigger calibrate(); } void loop() { int rt, lt, lx, ly, rx, ry; rt = analogRead(A5); lt = analogRead(A4); lx = analogRead(A3); ly = analogRead(A2); rx = analogRead(A1); ry = analogRead(A0); // Convert 0-1000 to -127 - 127 rt = floor((rt - rightTcenter) * multiplierRT); lt = floor((lt - leftTcenter) * multiplierLT); lx = floor((lx - leftXcenter) * multiplierLX); ly = floor((ly - leftYcenter) * multiplierLY); rx = floor((rx - rightXcenter) * multiplierRX); ry = floor((ry - rightYcenter) * multiplierRY); if (rt > 127) rt = 127; if (lt > 127) lt = 127; if (lx > 127) lx = 127; if (ly > 127) ly = 127; if (rx > 127) rx = 127; if (ry > 127) ry = 127; gp.setRightTaxis(rt); gp.setLeftTaxis(lt); gp.setLeftXaxis(lx); gp.setRightXaxis(rx); gp.setLeftYaxis(ly); gp.setRightYaxis(ry); char key = keypad.getKey(); if (key) { handleButtonPress(key); } delay(20); } void handleButtonPress(char button) { for (byte row = 0; row < ROWS; row++) { for (byte col = 0; col < COLS; col++) { if (button == keys[row][col]) { if (digitalRead(rowPins[row]) == LOW && digitalRead(colPins[col]) == LOW) { gp.setButtonState(row * COLS + col, true); } else { gp.setButtonState(row * COLS + col, false); } } } } } int rightTcenter = 500; int leftTcenter = 500; int rightXcenter = 500; int rightYcenter = 500; int leftXcenter = 500; int leftYcenter = 500; double multiplierRT = 0.254; //127 / 500 double multiplierLT = 0.254; double multiplierRX = 0.254; double multiplierRY = 0.254; double multiplierLX = 0.254; double multiplierLY = 0.254; void calibrate() { int rt, lt, lx, ly, rx, ry; int i = 0; while (i < 13) { rt = analogRead(A5); lt = analogRead(A4); lx = analogRead(A3); ly = analogRead(A2); rx = analogRead(A1); ry = analogRead(A0); bool validRT = rt > (rightTcenter - 100) && rt < (rightTcenter + 100); bool validLT = lt > (leftTcenter - 100) && lt < (leftTcenter + 100); bool validLX = lx > (leftXcenter - 100) && lx < (leftXcenter + 100); bool validLY = ly > (leftYcenter - 100) && ly < (leftYcenter + 100); bool validRX = rx > (rightXcenter - 100) && rx < (rightXcenter + 100); bool validRY = ry > (rightYcenter - 100) && ry < (rightYcenter + 100); if (validRT && validLT && validLX && validLY && validRX && validRY) { i++; //nothing to do here! } else i = 0; delay(20); } rightTcenter = rt; leftTcenter = lt; leftXcenter = lx; leftYcenter = ly; rightXcenter = rx; rightYcenter = ry; multiplierRT = (double)127 / (double)rt; multiplierLT = (double)127 / (double)lt; multiplierLX = (double)127 / (double)lx; multiplierLY = (double)127 / (double)ly; multiplierRX = (double)127 / (double)rx; multiplierRY = (double)127 / (double)ry; }
我曾尝试给每个按键单独分配引脚,但引脚数量不足,因此改用矩阵方案。也尝试在网上查找TPS43-201A-S或IQS570的相关资料,但信息极少,仅在Reddit上看到一位用户分享了Linux下的使用经验,而我需要的是Arduino Pro Micro上的实现方法。
一、矩阵按键代码修正与优化
你的代码框架没问题,但存在几个关键错误,以下是修正说明和优化后的代码:
主要问题点
- 按键定义错误:
keys数组中使用LEFT、RIGHT这类多字符常量,而char类型只能存储单个字符,会导致编译失败。需改为单字符标识。 - handleButtonPress逻辑冗余:Keypad库的
getKey()已经能返回当前按键的按下/释放状态,无需再通过digitalRead判断引脚电平,该判断逻辑还会导致状态识别错误。 - 重复设置引脚模式:Keypad库初始化时会自动配置行列引脚为INPUT_PULLUP,setup中的手动设置属于重复操作,可删除。
优化后的矩阵按键核心代码
#include <Keypad.h> #include <Gamepad.h> const byte ROWS = 4; const byte COLS = 4; // 改用单字符标识按键,避免编译错误 char keys[ROWS][COLS] = { {'Y', 'X', 'B', 'A'}, {'L', 'R', 'U', 'D'}, {'1', '2', 'S', 'M'}, {'J', 'K', 'E', 'F'} }; byte rowPins[ROWS] = {9, 8, 7, 6}; byte colPins[COLS] = {5, 4, 3, 2}; Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); Gamepad gp; void setup() { pinMode(A0, INPUT); //right stick y-axis pinMode(A1, INPUT); //right stick x-axis pinMode(A2, INPUT); //left stick y-axis pinMode(A3, INPUT); //left stick x-axis pinMode(A4, INPUT); //left trigger pinMode(A5, INPUT); //right trigger calibrate(); } void loop() { // 摇杆和扳机处理逻辑保留... char key = keypad.getKey(); if (key != NO_KEY) { // 判断按键是按下还是释放 bool isPressed = (keypad.getState() == PRESSED); // 根据按键位置计算按钮索引 int btnIndex = -1; for (byte row = 0; row < ROWS; row++) { for (byte col = 0; col < COLS; col++) { if (keys[row][col] == key) { btnIndex = row * COLS + col; break; } } if (btnIndex != -1) break; } if (btnIndex != -1) { gp.setButtonState(btnIndex, isPressed); } } delay(20); } // 其余calibrate等函数保留...
二、Azoteq IQS570触控板的Arduino驱动实现
IQS570支持I2C通信协议,以下是Arduino Pro Micro上的实现方案:
1. 硬件连接调整
- IQS570是3.3V器件,TPS43-201A-S的VCC必须接3.3V,不能接5V
- 两个触控板需要设置不同的I2C地址(通过硬件引脚ADDR调整,默认地址为0x44)
- 若当前A4/A5引脚用于扳机输入,需改用软件I2C(模拟I2C),选择其他空闲引脚(比如A6、A7)
2. 基础驱动代码框架
#include <Wire.h> // 若用软件I2C,替换为#include <SoftwareWire.h>,并初始化SoftwareWire对象 #define IQS570_ADDR_1 0x44 // 第一个触控板地址 #define IQS570_ADDR_2 0x45 // 第二个触控板地址 void setup() { Wire.begin(); // 硬件I2C初始化,SDA=A4, SCL=A5 // 软件I2C示例:SoftwareWire wire(A6, A7); wire.begin(); Serial.begin(9600); } // 读取IQS570寄存器 uint8_t iqs570ReadReg(uint8_t addr, uint8_t reg) { Wire.beginTransmission(addr); Wire.write(reg); Wire.endTransmission(false); Wire.requestFrom(addr, 1); return Wire.read(); } // 读取触摸坐标(X/Y) void iqs570ReadTouch(uint8_t addr, uint16_t &x, uint16_t &y) { // 读取X坐标(寄存器0x03-0x04) uint8_t x_low = iqs570ReadReg(addr, 0x03); uint8_t x_high = iqs570ReadReg(addr, 0x04); x = (x_high << 8) | x_low; // 读取Y坐标(寄存器0x05-0x06) uint8_t y_low = iqs570ReadReg(addr, 0x05); uint8_t y_high = iqs570ReadReg(addr, 0x06); y = (y_high << 8) | y_low; } // 读取触摸状态(是否有触摸) bool iqs570IsTouched(uint8_t addr) { uint8_t status = iqs570ReadReg(addr, 0x01); return (status & 0x01) == 1; // 第0位表示触摸状态 } void loop() { uint16_t x1, y1, x2, y2; bool touch1 = iqs570IsTouched(IQS570_ADDR_1); bool touch2 = iqs570IsTouched(IQS570_ADDR_2); if (touch1) { iqs570ReadTouch(IQS570_ADDR_1, x1, y1); // 将坐标转换为游戏手柄摇杆的-127~127范围 int joyX = map(x1, 0, 4095, -127, 127); int joyY = map(y1, 0, 4095, -127, 127); gp.setLeftXaxis(joyX); gp.setLeftYaxis(joyY); } if (touch2) { iqs570ReadTouch(IQS570_ADDR_2, x2, y2); int joyX = map(x2, 0, 4095, -127, 127); int joyY = map(y2, 0, 4095, -127, 127); gp.setRightXaxis(joyX); gp.setRightYaxis(joyY); } delay(20); }
关键注意事项
- 必须参考Azoteq官方的IQS570 datasheet,里面有完整的寄存器定义和通信时序
- 若出现I2C通信失败,检查电平转换(5V转3.3V)和接线是否正确
- 两个触控板的ADDR引脚需分别接GND或VCC,设置不同的I2C地址,避免冲突
内容的提问来源于stack exchange,提问作者BONIIXSAbonion 1500
相关产品推荐
相关产品推荐

