Arduino 井字棋游戏:MAX7219 点阵屏 + 纸板外壳 DIY
元器件清单
| 元器件 | 数量 | 备注 |
|---|---|---|
| Arduino UNO | 1 | 主控板 |
| MAX7219 LED 点阵模块 (8×8) | 1 | 显示棋盘与图案 |
| 薄膜按键键盘 (4×3) | 1 | 玩家输入 |
所需工具
- 热熔胶枪
所需软件
- Arduino IDE
项目简介
这是一个基于 Arduino 的井字棋游戏,使用 MAX7219 驱动的 8×8 LED 点阵显示棋盘,薄膜键盘输入坐标。外壳采用纸板和热熔胶自制,成本极低且极具创意。游戏支持双人对战,自动判断胜负并在屏幕显示获胜玩家编号,按键"0"可重置游戏。
游戏规则与交互
- 落子:按下键盘数字 1-9 对应 3×3 网格位置(1=左上,9=右下)
- 自动切换玩家:每次落子后自动切换到另一玩家
- 玩家标识:玩家1显示"\"图案(2×2 LED),玩家2显示"/"图案
- 胜负判定:任意行、列、对角线三子连线即胜利,屏幕显示"1"或"2"
- 重新开始:按键"0"触发看门狗复位重启游戏
硬件接线
| 模块 | Arduino 引脚 |
|---|---|
| MAX7219 DIN | 12 |
| MAX7219 CLK | 10 |
| MAX7219 CS | 11 |
| 键盘行引脚 (4根) | 8, 7, 6, 5 |
| 键盘列引脚 (3根) | 4, 3, 2 |
完整代码
#include
#include
#include
#include
#define Reset_AVR() wdt_enable(WDTO_30MS); while(1) {}
// MAX7219: DIN=12, CLK=10, CS=11, 1个设备
LedControl lc = LedControl(12, 10, 11, 1);
bool player1 = true; // 当前玩家标志
bool q[9] = {false}; // 9个位置是否已占用
int plays[3][3] = {{0}}; // 棋盘状态:0=空,1=玩家1,2=玩家2
int winner = 0; // 获胜者:0=未分胜负,1=玩家1,2=玩家2
// 键盘映射
char hexaKeys[4][3] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[4] = {8, 7, 6, 5};
byte colPins[3] = {4, 3, 2};
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, 4, 3);
void setup() {
lc.shutdown(0, false); // 唤醒 MAX7219
lc.setIntensity(0, 0); // 亮度最低(0-15)
lc.clearDisplay(0); // 清屏
}
void loop() {
char key = customKeypad.getKey();
painting(key); // 处理落子
win(); // 检查胜负
// 玩家1获胜显示
if (winner == 1) {
lc.clearDisplay(0);
while (winner == 1) {
char key = customKeypad.getKey();
if (key == '0') Reset_AVR(); // 重启
drawPlayer1Win(); // 显示"1"字样
}
}
// 玩家2获胜显示
else if (winner == 2) {
lc.clearDisplay(0);
while (winner == 2) {
char key = customKeypad.getKey();
if (key == '0') Reset_AVR();
drawPlayer2Win(); // 显示"2"字样
}
}
}
// 切换玩家并返回当前玩家编号
int round_player() {
if (player1) {
player1 = false;
return 1;
} else {
player1 = true;
return 2;
}
}
// 检查胜负
void win() {
int p = (player1) ? 2 : 1; // 上一步落子的玩家
// 检查行、列、对角线
for (int i = 0; i < 3; i++) {
if (plays[i][0]==p && plays[i][1]==p && plays[i][2]==p) { winner = p; return; }
if (plays[0][i]==p && plays[1][i]==p && plays[2][i]==p) { winner = p; return; }
}
if (plays[0][0]==p && plays[1][1]==p && plays[2][2]==p) { winner = p; return; }
if (plays[0][2]==p && plays[1][1]==p && plays[2][0]==p) { winner = p; return; }
}
// 点亮对应位置 LED 并记录落子
void switch_on_led(int col, int row, int colPlays, int rowPlays) {
if (player1) { // 玩家1显示"\"
lc.setLed(0, col, row, true);
lc.setLed(0, col+1, row-1, true);
} else { // 玩家2显示"/"
lc.setLed(0, col, row-1, true);
lc.setLed(0, col+1, row, true);
}
plays[rowPlays][colPlays] = round_player();
}
// 按键处理
void painting(char key) {
switch (key) {
case '1': if (!q[0]) { switch_on_led(0,7,0,0); q[0]=true; } break;
case '2': if (!q[1]) { switch_on_led(3,7,0,1); q[1]=true; } break;
case '3': if (!q[2]) { switch_on_led(6,7,0,2); q[2]=true; } break;
case '4': if (!q[3]) { switch_on_led(0,4,1,0); q[3]=true; } break;
case '5': if (!q[4]) { switch_on_led(3,4,1,1); q[4]=true; } break;
case '6': if (!q[5]) { switch_on_led(6,4,1,2); q[5]=true; } break;
case '7': if (!q[6]) { switch_on_led(0,1,2,0); q[6]=true; } break;
case '8': if (!q[7]) { switch_on_led(3,1,2,1); q[7]=true; } break;
case '9': if (!q[8]) { switch_on_led(6,1,2,2); q[8]=true; } break;
case '0': Reset_AVR(); break; // 重启游戏
}
}
// 显示玩家1获胜图案("1")
void drawPlayer1Win() {
lc.setLed(0,1,4,true); lc.setLed(0,2,5,true); lc.setLed(0,3,6,true);
lc.setLed(0,4,7,true); lc.setLed(0,4,6,true); lc.setLed(0,4,5,true);
lc.setLed(0,4,4,true); lc.setLed(0,4,3,true); lc.setLed(0,4,2,true);
lc.setLed(0,4,1,true); lc.setLed(0,4,0,true); lc.setLed(0,5,0,true);
lc.setLed(0,6,0,true); lc.setLed(0,3,0,true); lc.setLed(0,2,0,true);
}
// 显示玩家2获胜图案("2")
void drawPlayer2Win() {
lc.setLed(0,2,0,true); lc.setLed(0,3,0,true); lc.setLed(0,4,0,true);
lc.setLed(0,5,0,true); lc.setLed(0,2,1,true); lc.setLed(0,2,2,true);
lc.setLed(0,2,3,true); lc.setLed(0,2,4,true); lc.setLed(0,3,4,true);
lc.setLed(0,4,4,true); lc.setLed(0,5,4,true); lc.setLed(0,5,5,true);
lc.setLed(0,5,6,true); lc.setLed(0,5,7,true); lc.setLed(0,4,7,true);
lc.setLed(0,3,7,true); lc.setLed(0,2,7,true);
}
代码亮点
- 使用
Keypad库简化矩阵键盘扫描 - 使用
LedControl库驱动 MAX7219,操作简便 - 看门狗定时器实现软件复位,无需额外硬件复位电路
- 二维数组
plays[3][3]记录棋盘状态,胜负判定逻辑清晰
外壳制作提示
作者使用纸板和热熔胶制作外壳,建议:
- 先用纸板剪切面板,预留屏幕和键盘开孔
- 热熔胶固定电路板和模块,注意绝缘
- 可根据个人喜好设计造型(如作者的心形/宇宙主题)
- 留出 USB 口和电源开关位置
制造工艺
- 用 Arduino 打造绚丽显示器 Ambilight 系统
- 用 Arduino UNO 和 OLED 显示屏制作经典 Pong 游戏:分步教程
- 自制巨型乐高迷你人偶动画操作游戏:Arduino 控制舵机与音效
- Arduino 音频频率检测:基于 FFT 测量最响亮声音峰值
- 自制 Arduino 电子宠物「Tamaguino」:复刻经典 Tamagotchi 全攻略
- 智能无线开关控制器:Arduino + 433MHz 远程操控墙壁开关
- 基于 Arduino Nano 的智能运动感应水枪
- 使用 SparkFun 7 段数码管构建可靠的 Arduino 倒计时器
- 语音控制家电:Google Assistant + 蓝牙 + Arduino UNO
- Arduino 向日葵:制作电子向日葵追踪阳光
- 自制 Arduino 遥控保时捷赛车:从改装底盘到手机 App 控制全攻略
- Arduino 合成乐音:从零实现 88 键钢琴全音域