电赛井字棋无视觉方案:STM32按键矩阵与LED点阵实现
对于大一新生来说电赛的井字棋题目是一个很好的入门项目它综合了硬件控制、逻辑判断和状态管理。题目要求实现一个无视觉方案的井字棋对弈系统这意味着不能使用摄像头等视觉传感器来识别棋盘状态而是需要通过其他传感器如按键、触摸传感器、磁感应等来获取落子位置并通过点阵、LED灯阵或液晶屏来显示棋盘。无视觉方案的核心在于如何可靠地检测玩家的落子动作以及如何清晰地向玩家反馈当前棋盘状态。下面将围绕1到6问的实现提供一个完整的方案涵盖硬件选型、电路设计、核心逻辑、状态机和调试要点。1. 理解题目要求和核心挑战井字棋是一个3x3的棋盘两名玩家轮流在空格放置自己的棋子通常用X和O表示先连成一条直线横、竖、斜者获胜。如果棋盘填满仍未分出胜负则为平局。无视觉方案需要解决几个关键问题落子检测如何知道玩家在哪个位置落子。棋盘显示如何让玩家和裁判看清当前棋盘状态。游戏逻辑如何判断胜负、处理平局、管理游戏流程开始、轮流落子、结束、重置。人机交互除了基本的落子和显示可能还需要有开始按钮、重置按钮、胜负提示如LED灯、蜂鸣器等。对于大一新生建议从最基础的方案入手先实现功能再考虑优化。2. 硬件方案设计按键矩阵与LED点阵一个简单可靠的方案是使用按键矩阵作为输入LED点阵作为输出。2.1 硬件选型清单类别器件说明数量参考主控MCUSTM32F103C8T6 (核心板)资源丰富资料多性价比高1输入器件轻触按键用于9个棋格位置输入9独立按键用于开始/重置等功能1-2输出显示8x8 LED点阵屏 (带MAX7219驱动)可显示3x3棋盘驱动简单1状态指示LED灯红绿LED指示当前玩家、胜负状态2-3声音提示有源蜂鸣器落子提示、胜负提示1其他电阻、杜邦线、面包板/PCB10kΩ上拉电阻220Ω限流电阻若干注意如果题目对成本或功耗有特殊要求可以选用更简单的MCU如STC8系列、ATmega328P和更简单的显示方案如9个LED灯直接表示棋盘但点阵屏方案显示效果更直观专业。2.2 系统连接示意图[按键矩阵] ---- [MCU GPIO] ---- [LED点阵屏 (SPI)] [开始按键] ---- [MCU GPIO] ---- [状态LED] [重置按键] ---- [MCU GPIO] ---- [蜂鸣器]按键连接最简单的做法是9个棋格按键直接连接到MCU的9个GPIO口并配置为上拉输入。当按键按下时GPIO读到低电平。这种接法软件简单但占用IO口较多。如果IO口紧张可以排成3x3的矩阵只需要6个IO口但扫描程序稍复杂。LED点阵屏连接带MAX7219驱动的点阵屏通常通过SPI接口DIN, CLK, CS与MCU连接只需要3个IO口大大节省了资源。MAX7219芯片内部负责扫描显示MCU只需发送显示数据即可。3. 软件逻辑设计与实现软件是项目的核心需要清晰地管理游戏状态。3.1 定义关键数据结构和变量首先在代码开头定义核心的变量和数据结构。// 定义棋盘状态0为空1为玩家1(X)2为玩家2(O) uint8_t board[3][3] {0}; // 定义当前轮到哪位玩家落子 (1 或 2) uint8_t currentPlayer 1; // 定义游戏状态机 typedef enum { GAME_STATE_IDLE, // 空闲等待开始 GAME_STATE_PLAYING, // 对弈中 GAME_STATE_WIN, // 有玩家获胜 GAME_STATE_DRAW // 平局 } GameState_t; GameState_t gameState GAME_STATE_IDLE; // 按键引脚定义以STM32 HAL库为例假设使用9个独立GPIO #define KEY_START_PIN GPIO_PIN_0 #define KEY_START_PORT GPIOA // ... 定义其他9个棋格按键的PIN和PORT3.2 主循环与状态机主程序的核心是一个状态机根据不同的gameState执行不同的操作。int main(void) { // 硬件初始化 HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_SPI1_Init(); // 初始化SPI用于点阵屏 LEDMatrix_Init(); // 初始化点阵屏 Buzzer_Beep(100); // 开机提示音 while (1) { switch (gameState) { case GAME_STATE_IDLE: idle_state_handler(); break; case GAME_STATE_PLAYING: playing_state_handler(); break; case GAME_STATE_WIN: case GAME_STATE_DRAW: game_end_state_handler(); break; } HAL_Delay(50); // 简单的延时避免过于频繁扫描 } }3.3 关键函数实现3.3.1 空闲状态 (GAME_STATE_IDLE)等待玩家按下开始按钮。void idle_state_handler(void) { // 在点阵屏上显示欢迎动画或提示Press Start LEDMatrix_ShowMessage(START); if (HAL_GPIO_ReadPin(KEY_START_PORT, KEY_START_PIN) GPIO_PIN_RESET) { HAL_Delay(20); // 简单消抖 if (HAL_GPIO_ReadPin(KEY_START_PORT, KEY_START_PIN) GPIO_PIN_RESET) { // 确认按键按下初始化游戏 game_init(); gameState GAME_STATE_PLAYING; Buzzer_Beep(50); } while (HAL_GPIO_ReadPin(KEY_START_PORT, KEY_START_PIN) GPIO_PIN_RESET); // 等待按键释放 } } void game_init(void) { // 清空棋盘 for (int i 0; i 3; i) { for (int j 0; j 3; j) { board[i][j] 0; } } // 随机或指定先手玩家 currentPlayer 1; // 更新显示 update_display(); }3.3.2 对弈状态 (GAME_STATE_PLAYING)这是最核心的状态循环扫描9个棋格按键。void playing_state_handler(void) { // 扫描9个按键 for (int row 0; row 3; row) { for (int col 0; col 3; col) { // 获取对应按键的GPIO状态假设有函数get_key_state if (get_key_state(row, col) KEY_PRESSED) { // 处理落子 process_move(row, col); // 处理一次按键后退出扫描避免连续落子 return; } } } } void process_move(int row, int col) { // 检查该位置是否为空 if (board[row][col] ! 0) { // 位置已被占用可以给个提示音或闪烁提示 Buzzer_Beep(20); return; } // 落子有效 board[row][col] currentPlayer; Buzzer_Beep(50); // 落子音效 // 检查胜负 if (check_win(currentPlayer)) { gameState GAME_STATE_WIN; // 闪烁获胜方棋子或显示WIN indicate_winner(currentPlayer); return; } // 检查平局 if (check_draw()) { gameState GAME_STATE_DRAW; // 显示平局提示 indicate_draw(); return; } // 游戏继续切换玩家 currentPlayer (currentPlayer 1) ? 2 : 1; // 更新棋盘显示可以用不同亮度或符号区分玩家 update_display(); }3.3.3 胜负判断函数 (check_win)这是经典的逻辑判断检查8条线3横、3竖、2斜。uint8_t check_win(uint8_t player) { // 检查行 for (int i 0; i 3; i) { if (board[i][0] player board[i][1] player board[i][2] player) { return 1; } } // 检查列 for (int j 0; j 3; j) { if (board[0][j] player board[1][j] player board[2][j] player) { return 1; } } // 检查对角线 if (board[0][0] player board[1][1] player board[2][2] player) { return 1; } if (board[0][2] player board[1][1] player board[2][0] player) { return 1; } return 0; } uint8_t check_draw(void) { for (int i 0; i 3; i) { for (int j 0; j 3; j) { if (board[i][j] 0) { return 0; // 还有空位不是平局 } } } return 1; // 棋盘已满平局 }3.3.4 显示更新函数 (update_display)将内存中的board数组映射到LED点阵屏的显示缓冲区。void update_display(void) { uint8_t displayBuffer[8] {0}; // 8x8点阵的显示缓冲区每字节代表一行 // 遍历3x3棋盘在8x8点阵上画出3x3的格子 // 例如可以用左上角的3x3区域来显示每个棋格占2x2个点 for (int i 0; i 3; i) { for (int j 0; j 3; j) { if (board[i][j] 1) { // 玩家1显示X // 在对应位置画X例如在(i*2, j*2)为中心的2x2区域 draw_x(displayBuffer, i*2, j*2); } else if (board[i][j] 2) { // 玩家2显示O draw_o(displayBuffer, i*2, j*2); } // 为空则什么都不画 } } // 还可以画上棋盘网格线增强可读性 draw_grid(displayBuffer); // 将缓冲区数据发送到点阵屏 LEDMatrix_SendBuffer(displayBuffer); } // 简单的画X函数示例在指定坐标画一个2x2的X void draw_x(uint8_t buf[8], int startRow, int startCol) { buf[startRow] | (1 (7 - startCol)) | (1 (7 - (startCol1))); buf[startRow1] | (1 (7 - startCol)) | (1 (7 - (startCol1))); // 这是一个非常简单的X实际可以画得更复杂美观 }4. 进阶优化与功能扩展实现基本功能后可以考虑以下优化来提升完成度和用户体验以应对电赛的评分要求。4.1 增加人机对战模式题目要求是双人对战但增加一个简单AI作为可选模式是很好的亮点。// 简单的AI寻找获胜机会否则随机落子 void ai_make_move(void) { // 1. 检查AI自己是否能一步获胜 for (int i 0; i 3; i) { for (int j 0; j 3; j) { if (board[i][j] 0) { board[i][j] currentPlayer; // 模拟落子 if (check_win(currentPlayer)) { // 就是这一步了 return; // 棋盘已经修改 } board[i][j] 0; // 撤销模拟 } } } // 2. 检查是否需要阻挡玩家获胜 uint8_t humanPlayer (currentPlayer 1) ? 2 : 1; for (int i 0; i 3; i) { for (int j 0; j 3; j) { if (board[i][j] 0) { board[i][j] humanPlayer; // 模拟玩家落子 if (check_win(humanPlayer)) { board[i][j] currentPlayer; // 实际落下AI的棋子进行阻挡 return; } board[i][j] 0; // 撤销模拟 } } } // 3. 如果中心空着优先占中心 if (board[1][1] 0) { board[1][1] currentPlayer; return; } // 4. 随机选择一个空位落子 int emptyCells[9][2]; int count 0; for (int i 0; i 3; i) { for (int j 0; j 3; j) { if (board[i][j] 0) { emptyCells[count][0] i; emptyCells[count][1] j; count; } } } if (count 0) { int choice rand() % count; board[emptyCells[choice][0]][emptyCells[choice][1]] currentPlayer; } }在playing_state_handler中如果当前是AI回合则调用ai_make_move然后直接进行胜负判断和切换玩家。4.2 完善显示与交互当前玩家提示用不同颜色的LED灯或点阵屏上的特定位置指示当前轮到谁。落子动画落子时让对应的棋子图案有一个短暂的闪烁或放大效果。胜负高亮当有玩家获胜时让连成一条线的三个棋子闪烁高亮。音效多样化为落子、获胜、平局、非法操作设置不同的音调或节奏。4.3 软件抗干扰处理按键消抖上面的代码使用了简单的延时消抖。更可靠的方法是使用状态机或定时器进行软件消抖。防止按键长按在等待按键释放的循环中增加超时判断避免程序卡死。// 改进的按键检测示例状态机消抖 typedef enum {KEY_IDLE, KEY_DEBOUNCE, KEY_PRESSED, KEY_RELEASE} KeyState_t; KeyState_t key_check(GPIO_TypeDef* Port, uint16_t Pin, KeyState_t currentState) { switch (currentState) { case KEY_IDLE: if (HAL_GPIO_ReadPin(Port, Pin) GPIO_PIN_RESET) { currentState KEY_DEBOUNCE; } break; case KEY_DEBOUNCE: HAL_Delay(10); if (HAL_GPIO_ReadPin(Port, Pin) GPIO_PIN_RESET) { currentState KEY_PRESSED; } else { currentState KEY_IDLE; } break; case KEY_PRESSED: if (HAL_GPIO_ReadPin(Port, Pin) GPIO_PIN_SET) { currentState KEY_RELEASE; } break; case KEY_RELEASE: HAL_Delay(10); if (HAL_GPIO_ReadPin(Port, Pin) GPIO_PIN_SET) { currentState KEY_IDLE; } else { currentState KEY_PRESSED; } break; } return currentState; }5. 常见问题与调试要点在实现过程中大一新生最容易遇到以下问题问题现象可能原因检查与解决思路按键无反应1. GPIO模式配置错误应为上拉输入2. 硬件连接虚焊或断路3. 消抖逻辑过于严格或错误1. 用万用表测量按键按下前后引脚电平变化。2. 写一个简单的测试程序只检测一个按键并点亮一个LED。点阵屏不显示或乱码1. SPI时序或模式不匹配2. MAX7219初始化命令错误3. 数据发送顺序MSB/LSB错误1. 查阅MAX7219数据手册确认初始化序列。2. 先尝试发送固定的简单图案如全亮、第一行亮来测试。游戏逻辑混乱如连续落子1. 按键扫描和处理逻辑有漏洞未处理好按键释放2. 状态机切换条件不清晰1. 在按键处理函数中加入while循环等待按键释放。2. 使用调试器或串口打印当前状态和按键值进行逻辑分析。胜负判断错误1.check_win函数逻辑错误2. 棋盘数组board的索引使用错误行列混淆1. 在平局、各种获胜情况下手动设置board数组单步调试check_win函数。程序运行不稳定1. 缺少看门狗2. 中断冲突3. 堆栈溢出1. 开启独立看门狗IWDG。2. 检查是否有不必要的中断或中断服务函数执行时间过长。调试建议分模块调试先让按键和LED灯正常工作再调试点阵屏显示最后整合游戏逻辑。善用串口打印使用printf通过串口输出变量值、函数执行状态这是最有效的调试手段之一。使用逻辑分析仪如果条件允许用逻辑分析仪检查SPI信号可以快速定位点阵屏的通信问题。6. 项目总结与扩展方向通过这个项目你能够掌握嵌入式系统开发的基本流程需求分析、硬件选型、电路搭建、软件模块化设计、状态机编程、调试排错。无视觉方案的核心是构建一个可靠的人机交互闭环。完成基础要求后可以考虑以下扩展来提升项目竞争力更复杂的AI实现极小化极大算法Minimax带Alpha-Beta剪枝使AI不可战胜。多种显示模式增加比分记录、历史对局回顾等功能。无线通信通过蓝牙或Wi-Fi实现两个独立设备的联机对弈。低功耗设计在空闲状态进入低功耗模式通过按键中断唤醒。这个项目很好地融合了硬件和软件是电子设计竞赛中控制类题目的典型代表。把基础功能做稳定、做流畅是取得好成绩的关键。

相关新闻

嵌入式ISP编程实战:寄存器访问模型与帧间操作详解

嵌入式ISP编程实战:寄存器访问模型与帧间操作详解

1. Camera ISP编程模型核心概念解析 在嵌入式视觉系统开发中,图像信号处理器(ISP)扮演着将原始传感器数据转化为可用图像的关键角色。它本质上是一个高度可配置的硬件流水线,而编程的核心,就是通过读写一系列硬件寄存器…

2026/7/19 20:31:53 阅读更多 →
OpenAI实时语音API与Asterisk SIP集成实战指南

OpenAI实时语音API与Asterisk SIP集成实战指南

1. 项目概述:这不是一个“调用API”的玩具,而是一套可落地的语音交互基础设施 你有没有遇到过这样的场景:客服系统永远在说“请按1转人工”,电话接通后却要等三分钟;或者企业想给老客户加个语音回访功能,结…

2026/7/19 20:31:53 阅读更多 →
USB技术演进与转串口开发实战指南

USB技术演进与转串口开发实战指南

1. USB技术基础与协议解析USB(Universal Serial Bus)作为现代计算机最常用的外设连接标准,其技术演进已经深刻影响了我们的数字生活。从1996年的USB 1.0到最新的USB4 2.0,传输速率从1.5Mbps提升至80Gbps,这种指数级增长…

2026/7/19 20:31:53 阅读更多 →

最新新闻

互联网大厂常见Java面试题及答案汇总(2026持续更新)

互联网大厂常见Java面试题及答案汇总(2026持续更新)

金九银十即将来袭,又是一个跳槽的好季节,准备跳槽的同学都摩拳擦掌准备大面好几场,今天为大家准备了互联网面试必备的 1 到 5 年 Java 面试者都需要掌握的面试题,分别 JVM,并发编程,MySQL,Tomca…

2026/7/20 0:15:40 阅读更多 →
ngx_output_chain_get_buf

ngx_output_chain_get_buf

1 定义 ngx_output_chain_get_buf 函数 定义在 src/core/ngx_output_chain.cstatic ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize) {size_t size;ngx_buf_t *b, *in;ngx_uint_t recycled;in ctx->in->buf;size ctx->buf…

2026/7/20 0:13:39 阅读更多 →
python数据可视化技巧的100个练习 -- 31. 类别数据的点图

python数据可视化技巧的100个练习 -- 31. 类别数据的点图

重要性★★★☆☆ 难度★★☆☆☆ 你是一家零售公司的数据分析师。你的经理要求你可视化最近产品发布的客户满意度评级分布。评级是分类的,范围从“非常不满意”到“非常满意”。创建一个点图以显示每个评级类别的频率。使用 Python 进行数据处理和可视化。在代码中生成输入…

2026/7/20 0:12:39 阅读更多 →
智能体走进物理世界,千里科技携舱驾协同成果亮相WAIC 2026

智能体走进物理世界,千里科技携舱驾协同成果亮相WAIC 2026

在2026世界人工智能大会(WAIC 2026)举办期间,千里科技董事长、阶跃星辰董事长印奇作为特邀嘉宾出席大会开幕式并在大会主论坛(上午场)发表主题演讲《当智能体进入物理世界》。在印奇看来,"智能体"…

2026/7/20 0:12:39 阅读更多 →
商汤大装置发布“技术-生态-商业”闭环布局,共启“国产AI基础设施规模化商用元年”

商汤大装置发布“技术-生态-商业”闭环布局,共启“国产AI基础设施规模化商用元年”

7月18日,在WAIC 2026商汤科技 “基座大模型架构创新与生态合作论坛”上,商汤科技联合创始人、大装置事业群总裁杨帆发表《智变共生——加速AI基础设施持续升级》主题演讲,系统呈现了商汤大装置国产AI基础设施“技术-生态-商业”闭环布局&…

2026/7/20 0:12:39 阅读更多 →
2026年具身智能领域代表性机器人产品观察:普渡一脑多形底座与实景落地解析

2026年具身智能领域代表性机器人产品观察:普渡一脑多形底座与实景落地解析

前言2026年被行业视为具身智能从"实验室炫技"走向"规模化量产"的关键拐点。据弗若斯特沙利文《全球商用服务机器人市场研究报告》,普渡科技以23%市占率位居全球商用服务机器人第一,业务覆盖85+个国家和地区,累…

2026/7/20 0:11:39 阅读更多 →

日新闻

2026 WAIC:努比亚二代“豆包手机”NaviX Ultra亮相,智能体验全面升级!

2026 WAIC:努比亚二代“豆包手机”NaviX Ultra亮相,智能体验全面升级!

7月18日智东西消息,在2026 WAIC期间,努比亚联合字节豆包打造的二代“豆包手机”努比亚NaviX Ultra首次亮相,相比一代有诸多升级。智能体手机理念中兴通讯终端事业部总裁、努比亚总裁倪飞表示,智能体手机要从人操作手机变为手机帮人…

2026/7/20 0:00:34 阅读更多 →
努比亚NaviX Ultra亮相WAIC,智能体手机能否让用户生活更简单?

努比亚NaviX Ultra亮相WAIC,智能体手机能否让用户生活更简单?

努比亚NaviX Ultra:外观与功能双升级在2026 WAIC期间,首次亮相的努比亚NaviX Ultra吸引了众多目光。它是努比亚联合字节豆包打造的二代“豆包手机”,与一代努比亚M153相比,外观设计变化较大。其机身背部搭载横向排布的大尺寸影像模…

2026/7/20 0:00:34 阅读更多 →
C# 将逗号分割的字符串转换为long,并添加到List<long>

C# 将逗号分割的字符串转换为long,并添加到List<long>

目录 方法1:使用Split和Convert.ToInt64 方法2:使用LINQ的Select和ToList 方法3:使用TryParse进行异常安全转换(推荐) 如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天…

2026/7/20 0:00:34 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/20 5:57:49 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/20 4:31:26 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/20 5:56:42 阅读更多 →

月新闻