STM32五子棋对战平台开发实战:硬件选型、AI算法与排坑指南

发布时间:2026/9/9 1:08:31
STM32五子棋对战平台开发实战:硬件选型、AI算法与排坑指南 简介基于STM32F4原子探索者开发板实现的五子棋对战平台是一份可直接烧录运行的完整工程适合STM32入门与中级开发者学习嵌入式游戏开发。资源围绕触摸下子、人机对战、人人对战、悔棋、音量开关等功能展开不仅包含核心游戏逻辑还覆盖LCD显示驱动、触摸输入处理、状态机设计、AI走子算法等关键模块并涉及SD卡读写、文件系统、中文字库支持等周边应用可直接在探索者板卡上体验也可参考其模块划分移植到其他STM32系列。压缩包共274个文件其中55个C源码与50个头文件构成程序主体45个依赖文件与44个预编译输出可辅助理解MDK构建流程另有uvprojx/uvoptx工程配置、sct链接脚本、map镜像文件、axf调试文件及若干图片、说明文档整体仅5.44MB便于快速下载与结合源码逐模块阅读。目前已有2166人学习该资源适合希望以实战项目掌握嵌入式人机交互和简单棋类AI实现的学习者。 先说实话基于STM32的五子棋对战平台这个题目放到五年前可能还算个新鲜玩意放到现在它在毕设和电子设计竞赛里已经快被做烂了。但我这两年帮人审过不少类似项目发现一个很有意思的现象真正能做出能玩、好玩、运行流畅的作品的人十个里最多两三个。大部分人的代码跑起来要么屏幕刷新卡顿要么AI蠢得像随机落子要么下到一半程序直接HardFault。问题出在哪不是STM32学得不够而是把单片机开发和游戏开发这两条线强行拧在一起时资源调度、数据结构、算法复杂度的取舍全乱套了。这篇文章我就拿这个项目当例子把我实际做这个东西的完整思路、硬件选型、代码结构和踩坑记录全摊开来讲希望能帮后面做类似项目的人少走几趟弯路。1. 这个项目到底在做什么需求拆解与选型逻辑很多人拿到这个标题直接就开始写代码这是最大的坑。做任何嵌入式项目第一步不是想怎么写程序而是想清楚这套程序要跑在什么硬件上、满足哪些使用场景、谁在什么环境下操作它。1.1 项目定位与核心需求拆解五子棋对战平台可以拆成三个关键词五子棋、对战、平台。五子棋意味着核心逻辑是棋盘状态管理、落子规则、胜负判定可能还要有人机AI对战意味着至少有双人对战模式或者人机对战模式或者两者兼具平台意味着它不是一锤子买卖得有交互界面、状态显示、开始/结束/计分这些基本的游戏流程控制。从毕设或练手项目的角度看这个题目的合理定位是以STM32为主控的嵌入式人机交互娱乐终端。它拼的不是算法多高深而是把一个完整的输入-处理-输出闭环做得流畅可靠。你要在一个资源极其受限的MCU上同时跑UI刷新、逻辑判定、AI搜索还要保证不会出现肉眼可见的卡顿这才是这个项目的真正难度所在。1.2 主控芯片怎么选不是越贵越好主控选择上STM32F103C8T6是绝大多数人的首选也是我最推荐的。它64KB Flash、20KB SRAM72MHz主频做这个项目刚刚好。有人一上来就上F407甚至H743理由是性能强以后好扩展但我的看法是你要先想清楚瓶颈在哪。五子棋AI的瓶颈不是主频而是评估函数和搜索深度之间的平衡UI刷新的瓶颈也不是主频而是SPI屏幕的通信速率。如果你打算做图形界面AI搜索Flash存档WiFi联机这些全都要那建议考虑F103ZET6或者F407VET6主要是Flash空间和引脚数量更宽裕。但如果是老老实实做一个功能完备的五子棋平台F103C8T6绰绰有余它16KB的SRAM足够存下15×15的棋盘、UI缓冲区以及搜索深度为4的AI临时变量。1.3 不同实现方案的取舍对比我见过这几种方案各有利弊方案优点缺点适用场景全硬件逻辑FPGA/CPLD响应极快开发周期长、逻辑修改困难课程设计炫技STM32裸机SPI/TFT屏幕逻辑直观、调试方便UI和逻辑混在一起代码结构容易乱大部分人应该选这个STM32FreeRTOSLVGL模块清晰、界面现代RAM占用高、调试复杂板子资源充裕想做得像产品STM32Linux如全志/瑞芯微性能碾压失去单片机意义、复杂度暴增不建议我自己用的是STM32F103ZET6裸机 3.5寸SPI液晶屏ILI9488 XPT2046触摸 板载W25Q64 Flash。选择ZET6是因为手头正好有这个板子Flash有512KB可以放心把字库和UI资源都放进去同时它的引脚支持FSMC将来想升级到并口屏也方便。但核心代码逻辑和F103C8T6完全一致你用的是小容量板也没问题。2. 硬件搭建屏幕、触摸与主板选择的关键点硬件部分是这个项目里最容易被低估的环节。很多人以为接个屏、焊几根线就行等到真跑起来发现屏幕狂闪、触摸乱跳、供电不稳才开始怀疑人生。其实这些问题的根源大都在硬件设计的细节里。2.1 显示方案SPI屏还是并口屏显示方案是最影响开发体验的选择。OLED屏幕尺寸太小显示五子棋棋盘要看清落子已经很吃力更别提做复杂的交互界面了不推荐。串口屏比如淘晶驰的开发起来最省心它自带UI编辑器和协议解析主控只要通过串口发指令就能刷新界面但代价是成本高、可玩性低、而且串口屏的指令响应时间偶发不稳定做对战类游戏会有延迟感。我用的是ILI9488驱动的3.5寸SPI接口TFT屏SPI时钟拉到40MHz单点刷新的延迟可以压到10微秒以内。但SPI屏有个主要问题全屏刷新的数据量太大。320×480×16bit一屏全刷要300多KB在F103上即使SPI全速传输也要几十毫秒。所以代码里必须做局部刷新——落哪颗子就只把那一个格子刷掉千万不能每次落子都全屏重绘。2.2 触摸输入XPT2046的校准与滤波触摸方案有两种一种是电阻触摸XPT2046集成在绝大多数TFT模块背后一种是外挂电容触摸板。电容触摸的手感好但要单独走I2C或SPI协议而且布局受限制。电阻触摸胜在集成度高、便宜缺点是精度一般、需要校准。我用的XPT2046主控通过SPI读触摸坐标。这个东西最大坑是坐标漂移你点屏幕物理位置的100,100读回来可能是110,96每次还不一样。解决办法是先做两点校准就是经典的线性变换取屏幕左上和右下两个参考点算出X和Y的缩放系数和偏移量再做软件消抖——同一位置连续读到5次坐标取中值滤波差值超过阈值才认为是有效触摸。这套流程做完触摸精度可以稳定在±3个点以内对五子棋这种方格子交互来说完全够用。2.3 供电与复位你最容易忽略的两件事供电是这个项目翻车的重灾区。3.5寸TFT背光全开时峰值电流能到100mA左右加上STM32和触摸芯片整板电流轻轻松松超过300mA。如果用USB口直接给板子供电而USB线材质量差压降会导致3.3V纹波大屏幕背光会肉眼可见地闪烁。我的做法是外接5V电源适配器给板子供电板载AMS1117-3.3负责降压屏幕背光单独走一路控制——低亮度PWM时背光电流小闪烁问题就消失了。复位电路看似简单但有个细节STM32的NRST引脚对噪声比较敏感如果环境里有电机、继电器之类的干扰源可能会导致单片机莫名重启。建议在NRST引脚对地接一个100nF电容硬复位按键也并联一个能有效滤除高频干扰。别问我怎么知道的我调试一个莫名其妙的重启问题查了整整两天最后发现是旁边一个12V继电器动作时把复位脚拉低了。3. 棋盘与胜负判定MCU上最省资源的建模方式五子棋的棋盘是15×15这个规模对MCU来说非常友好。但友好的前提是你要用合理的数据结构来组织它。我见过有人用链表存棋子的也见过用二维结构体存坐标和状态的都属于能用但没必要。最简单的方式往往是最好的。3.1 棋盘数据结构一维数组的妙用我定义棋盘为#define BOARD_SIZE 15 uint8_t board[BOARD_SIZE][BOARD_SIZE]; // 0空1黑子2白子两个维度直接用索引访问判断是否为空、落子、悔棋都是O(1)操作。也许有人会纠结要不要用一维数组uint8_t board[225]然后手动换算索引——老实说完全没必要编译器会帮你做这个优化二维数组的代码可读性还更高。但有一个升级方案值得提一下位棋盘。用两个16×16的uint16_t数组分别表示黑子和白子的占位15行乘15列多出来的一行一列做边界每颗棋子只占1bit判断落子时用位运算15×15的棋盘状态仅需约60字节。这在实现AI的快速判断某位置是否为空时非常高效。我在AI搜索模块内部用了位棋盘的变体而人机交互层保留二维数组两边通过转换函数对接。3.2 胜负判定四方向扫描的两个细节五子棋胜负判定是是否有同一方棋子横、竖、斜连续5颗。最直接的实现是每次落子后从落子点出发向四个方向水平、垂直、主对角线、副对角线各延伸统计同色棋子数量任一方连续数量≥5即判胜。int check_win(int row, int col, uint8_t player) { int directions[4][2] {{0,1}, {1,0}, {1,1}, {1,-1}}; for (int i 0; i 4; i) { int count 1; for (int step 1; step 5; step) { int r row directions[i][0] * step; int c col directions[i][1] * step; if (r 0 || r BOARD_SIZE || c 0 || c BOARD_SIZE) break; if (board[r][c] ! player) break; count; } for (int step 1; step 5; step) { int r row - directions[i][0] * step; int c col - directions[i][1] * step; if (r 0 || r BOARD_SIZE || c 0 || c BOARD_SIZE) break; if (board[r][c] ! player) break; count; } if (count 5) return 1; } return 0; }这个实现有两个关键细节。第一step只走到4是因为只要统计到单侧4颗就能确认是否连续5颗不需要全盘扫描第二边界检查一定要做坐标越界直接break否则数组越界读出来的脏数据会让胜率判定变得神鬼莫测。另外如果你玩的是带禁手的规则需要在判胜之后额外检查长连和三三/四四这部分逻辑可以单独写一个函数不影响核心判定流程。3.3 死局判断棋盘满员且无人五连对战类游戏还必须处理棋盘下满的情况否则会出现棋盘满了还在等用户落子这种尴尬场景。每下一子后做一个全局扫描uint8_t is_board_full(void) { for (int r 0; r BOARD_SIZE; r) for (int c 0; c BOARD_SIZE; c) if (board[r][c] 0) return 0; return 1; }全局扫描在15×15的棋盘上也就是225次比较耗时微秒级完全不需要优化。判定顺序是先判胜、再判满、都过则继续对局。4. 五子棋AI从简易评分到能赢过新手的落子策略AI是对战平台的核心体验所在。如果你只做双人对战那本质上就是个棋盘模拟器含金量低很多。但很多人一上来就想上五子棋AI的巅峰算法——蒙特卡洛树搜索MCTS在F103上跑MCTS还要保证落子不卡顿基本上是强人所难。我的建议是按难度分层实现AI从贪心评分开始逐步加深搜索深度。4.1 为什么MCU上不适合跑重型算法蒙特卡洛树搜索和深度学习这类算法动辄需要几万次模拟对局来评估局面每次模拟都要推进若干步落子这在PC上都是不小的计算量放到72MHz的MCU上会是灾难性的体验。五子棋AI的够用方案是静态评估函数 有限深度极大极小搜索α-β剪枝。评估函数推导单个位置的价值搜索负责向前看几步对手的回应两者配合就能在MCU上达到不弱于普通人类新手的水平。我实测过F103在72MHz下搜索深度4层、每次评估候选点约20个节点时单次落子计算时间在300到800毫秒之间浮动人眼会有轻微等待感但可以接受。深度5层以上基本就卡到2秒以上了体验很差。4.2 评估函数给每个空位打分评估函数是AI的核心。它的思路是扫描棋盘上所有方向上由同一方棋子组成的棋形根据棋形的威胁程度给一个分值AI落子时选择我方收益 对方威胁综合分最高的位置。棋形分值说明活四四个连续且两端都空100000必胜形冲四四个连续但一端被堵10000对方必须堵活三三个连续且两端都空8000下一步变活四眠三三个连续但一端被堵1000威胁一般活二两个连续且两端都空500有发展潜力眠二两个连续但一端被堵100低威胁打分时需要同时计算我方每个棋形分和对方每个棋形分最终综合分是我方总分 × 1.1 对方总分。为什么我方分要乘一个略大于1的系数因为五子棋是先手占优的游戏同样的局面下进攻比防守更容易兑现优势。这个系数是我试了很多轮之后调出来的1.1意味着AI在自己成四和堵对方四的收益相近时会偏攻一点实战效果比纯防守型AI好一个档次。4.3 搜索深度优先 α-β剪枝的简化实现搜索部分用极大极小值搜索。简单说就是AI落子后假设对手也会选择对自己最有利的位置AI再基于对手的选择规划下一步交替模拟到设定的深度最后一层层往回推出当前最优落子。α-β剪枝的核心优化是如果某一步棋的评估值已经比前面走过的最差情况还差就直接砍掉这个分支不再继续深入。候选点的生成不能全盘扫描225个位置否则计算量太大。我的做法是只看已有棋子周围两格内的空位。一盘五子棋进行到中盘有棋子的区域通常只占棋盘的三分之一周围两格内的空位数量一般不超过40个每次搜索层数加深后候选点从这个集合里选。这套剪枝候选点限制能直接减少两个数量级的搜索时间。难度调节就靠搜索深度简单模式搜索2层只会看一步棋普通模式搜索3层困难模式搜索4层。开局阶段棋盘上棋子少于4颗直接走固定开局点位不需要搜索可以省一大笔时间。4.4 一个容易被忽略的细节AI要不要下天元五子棋有个共识棋盘正中心天元是全局最有价值的点先手方下天元胜率极高。AI搜索在开局阶段由于空位太多、评估函数覆盖不全经常会把天元放在候选列表里靠后的位置导致AI开局落点不够激进。我处理的办法是在开局双方无子或只有一两颗子时强制AI优先考虑距离天元最近且没有被占用的点位。这个策略听着原始但实测下来配合上面的评估函数AI的先手胜率能从60%左右拉到75%以上效果非常明显。5. 从编译烧录到Flash存档绕不开的坑与排错记录这一节是全文最有价值的部分。以下每一个坑我都实际踩过每一个问题的排查过程都消耗过至少半天时间。写出来就是希望你看到之后能省掉这些冤枉时间。5.1 延时函数Delay卡死SysTick中断优先级引发的血案我做这个项目时最诡异的一个故障是程序运行几分钟后界面突然卡住不动按复位又恢复正常然后过一会又卡。一度怀疑是硬件问题换了屏幕换了板子都无解。最后一步一步printf定位发现是卡死在HAL_Delay()里。原因是我在触摸处理的中断里调用了HAL_Delay()。SysTick定时器提供HAL_Delay时基它的中断优先级如果设置得比当前正在执行的中断低那么一旦在高级中断里调用HAL_Delay()SysTick中断无法抢占执行延时计数永远不会更新函数就会死死卡住。**解决方法是中断服务函数里绝对不调用延时函数改成标志位轮询或者用定时器延时。**如果你确实需要在中断里等待某个外设就绪用for循环的空延时或专门的硬件定时器都比HAL_Delay可靠。5.2 板载Flash存档擦除粒度与掉电保护我做了当前对局存档/读档功能把棋盘状态和当前执子方存到板载FlashSTM32内部Flash的最后一页。第一个坑是STM32内部Flash擦除的最小单位是一个扇区F103是1KB/扇区F407是16KB/扇区写入最小单位是16位半字。你不能像操作EEPROM那样一个字节一个字节地改必须先整块擦除再整体写回。所以我写的通用存档函数是读出整页→修改指定偏移→擦除整页→整页写回。第二个坑是掉电保护。如果正在写入Flash的瞬间系统断电Flash里的数据可能半新半旧读档时会读到混乱的状态。我的方案是双缓存区定义两个存储页交替使用每页开头存一个魔数和一个自增序列号读档时选序列号较大的那一页。这样即使某一页写了一半就断电另一页的数据仍然是完整可用的。嵌入式的原子性做不到操作系统那么优雅但双缓存足够应付绝大多数苛刻场景。5.3 屏幕刷新慢的假死现象与解决SPI屏幕在局部刷新做不好的时候有一个表现落子后屏幕要过将近一秒才显示新棋子。这不是程序卡死而是你的刷新方式太笨——肯定有人是落子之后全屏重绘每次重绘要传输300多KB数据出去按10MHz SPI的速度大约要半秒。解决思路前面提过局部刷新。实际落子时我计算这个棋子所在格子的屏幕坐标矩形区域比如每个格子40×40像素只把这片区域的数据通过SPI传出去一次局部刷新的数据量在3KB左右刷新时间小于5毫秒。UI界面中间的棋盘区域和两侧的信息栏分开管理互不干扰刷新体验和手机App都差不多了。5.4 ST-Link烧录报错No target found令人抓狂的Debug Authentication这个错误估计每个玩STM32的都遇到过。我的情况是正常烧录程序突然有一天开始Keil烧录时报Error: No STM32 target found! If your product embeds Debug Authentication, please...大概率不是芯片烧了而是调试接口被锁或者连接不稳定。按这个顺序排查能解决90%的问题第一步检查接线——SWDIO、SWCLK、GND三条线必须稳杜邦线接触不良的坑我踩过不下五次有条件直接焊死。第二步把ST-Link的速率降到最低不少ST-Link默认频率太高导致连接不稳定。第三步按住复位键点击烧录在擦除开始的瞬间松开这个方法对芯片跑到某个卡死状态特别有效。如果以上都不行用ST-Link Utility的Connect under reset模式连一次把芯片整片擦除再重新烧录基本能救回来。最后提醒一句千万不要把SWDIO和SWCLK两个引脚随意复用成普通GPIO一旦程序把这俩引脚占用下次烧录就会遇到这个错误必须用复位时序或者整片擦除才能恢复。6. 项目还能往哪走联机对战与玩法扩展如果你觉得双人对战人机对战已经做完了但还想让这个项目在毕设答辩或者作品展示时更有竞争力可以从下面几个方向扩展。这些扩展方向我都有实际调研过难度从低到高排了个序。6.1 加一颗ESP8266从单机走向局域网对战这个是性价比最高的扩展。ESP8266模块走UART和STM32通信板子上预留一个串口引脚和3.3V供电就行。核心逻辑是STM32把每次落子的坐标一行一列两个字节通过串口发给ESP8266ESP8266走WiFi把坐标转发到对端的设备上对端收到坐标后在屏幕上落子。这样两台运行同样固件的STM32板子就能组成局域网对战。通信协议我建议自己定越简单越好比如帧头0xAA 行坐标 列坐标 校验字节一共4个字节一帧。千万不要用TCP长连接那一套嵌入式上维护TCP栈的复杂度远超你的想象。UDP广播在局域网内足够可靠丢包了通过一个简单的ACK重传机制就能补偿。那个STM32 ESP12E 机智云的热词如果你愿意折腾也可以用机智云的现成方案做远程对战让两台板子通过云端平台互传坐标但这就涉及账号和注册流程展示时容易翻车不如局域网来得利落。6.2 做一个复盘功能让程序教你怎么赢复盘功能是我觉得在展示上最加分的扩展。实现并不复杂对局过程中把每一步落子的坐标、耗时、AI评估分值都追加写入W25Q64或者板载Flash中循环写入最多保留最近50局。对局结束后进入复盘模式用户可以一步一步回放整盘棋屏幕上显示每一步AI评估认为的当时最佳落子和你实际落子的差异。这个功能的技术含量其实不在存储而在时序对齐回放时需要同步恢复每一步的棋盘状态、执子方、评估值。我是用顺序存储位置指针来实现的存的时候不断追加放的时候一个指针从头走到尾逻辑非常清晰。答辩的时候这个功能一亮相评委通常会觉得这个项目有产品思维不会只拿点灯搬砖来评价你。6.3 体验优化音效、震动反馈与难度标识低成本提升体验感的方式是做音效。用无源蜂鸣器接在定时器PWM输出引脚上落子时播放一声短促的嗒赢了播放一小段和弦。定时器输出PWM控制蜂鸣器的频率占空比控制音量五子棋落子这种短促的反馈不需要额外音频解码几行代码就能实现。注意一个是蜂鸣器不要直连单片机引脚要加一个三极管驱动或者用有源蜂鸣器模块单片机的引脚驱动能力不够直接带蜂鸣器。关于这个项目我最后想说的做完这个五子棋平台最大的收获不是我会用STM32了而是把一件看起来没什么技术含量的事拆成了算法、显示、存储、通信、可靠性的综合工程问题。真正调试下来你会发现最难的不是五子棋的规则代码而是如何在一个资源受限的系统里把各个模块的时序和资源调度安排得明明白白。如果让我重新做一遍我会在一开始就做一张资源预算表Flash每个功能占多少、RAM每个缓冲区占多少、每个中断的执行时间、每次AI搜索的最坏耗时全部列出来再动手写代码。这个习惯帮我避开了后面至少80%的返工。你也可以试试不管你是要做毕设还是单纯想做个能拿得出手的STM32作品这套先规划后编码的思路比任何一行具体代码都更值钱。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询