news 2026/9/1 7:29:43

STM32驱动ILI9341实现黑白棋对战:SPI屏幕驱动、游戏逻辑与Proteus仿真详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32驱动ILI9341实现黑白棋对战:SPI屏幕驱动、游戏逻辑与Proteus仿真详解

简介:面向嵌入式初学者的STM32黑白棋对战完整工程,主控采用STM32F103R6,通过SPI接口驱动ILI9341彩色液晶屏实时显示棋盘与落子效果,并支持在Proteus中直接仿真运行。工程基于STM32CubeMX初始化配置,使用HAL库开发,源码覆盖GPIO、SPI、SysTick等底层驱动,并包含完整游戏逻辑、中断处理、硬件抽象层适配及ILI9341专用显示驱动等模块,能够清晰看到应用层与驱动层的拆分,目录分工明确。资源包共597个文件、约25.76MB,除大量C/H源文件与启动汇编文件外,还带有.uvprojx/.uvoptx等Keil MDK-ARM工程配置、.pdsprj/.pdsbak等Proteus仿真设计、.hex/.axf等编译产物、电路原理图、.ioc初始化配置,以及实机演示视频(STM32_BWGame.wmv),其中Proteus仿真文件可直接加载运行,硬件接线方式在原理图中一目了然,方便对照理解。全部代码已验证可编译通过,配合Proteus仿真可实时观察黑白棋对弈流程、落子刷新与屏幕交互效果,适合嵌入式初学者系统掌握SPI外设驱动、LCD显示控制与人机交互逻辑。目前已有56人学习。 先说一个我个人的感受:做嵌入式小项目,最期待、也最有成就感的一刻,通常不是程序写完整,而是代码烧进去、屏幕“啪”地一下亮起来,光标能移动、棋子能落下去。这篇文章要拆解的就是这样一个工程:STM32F103R6驱动ILI9341液晶屏实现黑白棋对战,附带Proteus仿真、Keil源码和硬件电路设计。如果你想用一个小而完整的项目把单片机外设、屏幕驱动、游戏逻辑和仿真调试串起来,这篇文章可以给你一个从零到一的完整参考。

黑白棋这个题材选得好。它的规则够简单,但落子合法判定、吃子翻转、AI策略、无子可下结束这些逻辑又足够有含金量,比单纯跑个流水灯或者驱动个传感器更能训练系统思维。工程本身也不复杂,适合做课设、毕设的练手项目,或者作为嵌入式GUI开发的第一块跳板。

1. 项目全貌:一颗64脚芯片怎么装下一整局黑白棋

1.1 黑白棋规则与产品形态

黑白棋英文名Reversi,也叫Othello,8x8棋盘,黑先白后。每回合必须在空位上落一颗子,落子的八个方向中,至少有一个方向能把对方的连续棋子“夹住”,然后把这些被夹住的棋子翻过来变成己方颜色。如果当前玩家所有空位都无法合法落子,就自动跳过,由对方继续。双方都无棋可下时对局结束,棋盘上棋子数多的一方获胜。

这套规则写代码时不复杂,但很容易出错,尤其是“夹住”的判定和翻转的边界处理。实际上黑白棋应用在这个项目里还有一个额外好处:它几乎没有连续动画和实时数据,对MCU性能要求不高,整个游戏逻辑用纯C就能跑得很流畅,非常适合作为STM32驱动TFT屏幕的入门应用形态。

我做的工程形态是:STM32F103R6作为主控,通过SPI接口驱动ILI9341屏幕,用独立按键完成光标移动、落子、模式选择等操作。屏幕上实时显示8x8棋盘、当前光标位置、当前轮次、比分,以及提示文字。对战模式包括人机对弈和双人对战两种,人机模式下MCU扮演AI,用评分策略落子。

1.2 STM32F103R6的资源账本与系统分层

先算一笔资源账,因为这个决定了后面所有设计边界。STM32F103R6是Cortex-M3内核,72MHz主频,64KB Flash、20KB SRAM,LQFP64封装。20KB的RAM对一块240x320的屏幕来说完全够用,因为ILI9341内部自带GRAM,我们不需要在MCU内存里开整帧的显存,只需要维护8x8棋盘数组和少量状态变量。压力主要在Flash上:ILI9341初始化序列、字模、游戏逻辑和驱动代码加起来,在标准库下一般能控制在20KB以内,剩余容量依然充足。

系统架构我分成三层:

  • 底层驱动:SPI初始化、ILI9341复位和初始化、画点/填充/绘图API;
  • 游戏逻辑:棋盘状态、落子合法性判断、翻转、胜负统计、AI选择;
  • 界面层:棋盘绘制、光标绘制、文字显示、按键扫描和状态机切换。

这个分层的实际好处是调试方便。我在写AI逻辑时,可以在main函数里直接跳过屏幕初始化,只调用游戏逻辑接口,用串口把每一步棋盘打印出来验证规则;屏幕显示有问题时,又可以拿固定棋盘数据喂给界面层。分层清晰对后面排错帮助非常大。

2. 点亮屏幕:ILI9341的SPI驱动与初始化细节

2.1 四线SPI接线:省引脚不是唯一理由

ILI9341是一款非常常见的TFT液晶驱动芯片,支持8080并口和SPI接口。8080并口速度快,但要占掉十几个IO,这在64脚的F103R6上很不划算,所以我选择四线SPI模式。需要的信号一共六根:

  • SCK:SPI时钟
  • MOSI:主出从进数据线
  • CS:片选
  • DC:数据/命令选择线
  • RST:硬件复位
  • BL:背光控制

这里最容易忽略的是DC线的含义:发送命令字节时DC必须为低,发送数据字节时必须为高。整个时序过程就是:先设置DC电平,拉低CS,然后通过SPI发送一个字节。我用的引脚分配是PA5=SCK、PA7=MOSI、PA4=CS、PA1=DC、PA0=RST、PB0=BL。这个映射完全可改,但建议把屏幕信号放在SPI1而不是SPI2上。

为什么优先SPI1?因为SPI1挂在APB2总线上,外设时钟最高36MHz;SPI2挂在APB1上,最高18MHz。虽然屏幕刷新未必总是满载跑,但SPI1的余量更大,同样的分频数下实际吞吐更高,刷屏流畅度有差异。像素格式选RGB565,16位色,一个像素两个字节,理论上SPI 16MHz时钟带宽足够撑起流畅的局部刷新。

2.2 初始化序列:这块屏真正的“脾气”

ILI9341的初始化序列是出了名的“玄学”。不同厂家模组的初始化向量不同,同一条命令在不同屏幕上效果可能完全不同。最典型的现象是:同一套初始化代码,在A模组上正常,到B模组上就偏色、花屏甚至白屏。所以初始化序列一定要保留成独立的命令表,方便换屏时调整。

我工程里的初始化流程大概是这样的:

  1. 硬件复位:RST拉低至少10ms,再拉高,延时120ms以上等内部状态稳定;
  2. 发送软件复位命令0x01;
  3. 发送退出睡眠命令0x11,延时足够;
  4. 配置电源、VCOM电压、显示时序相关寄存器,常见有0xC0、0xC1、0xC5等;
  5. 设置像素格式0x3A为0x55,即16位色;
  6. 设置内存访问控制0x36,这一步决定扫描方向和坐标原点;
  7. 配置Gamma校正寄存器(0xE0、0xE1);
  8. 最后发送0x29开启显示。

第6步的0x36是方向相关的关键,如果屏幕显示方向不对,比如坐标翻转、上下倒置,多半是这里没配好。同一个屏在Proteus仿真里和实物上也可能需要不同的MADCTL值,因为两者的坐标映射方式不完全一致。这个坑我在移植实物时踩过,后来老老实实在初始化代码旁边写了注释,提醒自己换屏时优先检查这里。

初始化期间每条命令之间要留足延时。很多网上代码为了“快”,把延时压得特别短,结果屏幕偶发不亮或者开机花屏。我建议把延时函数封装成宏,统一调参,而不是散落在各处。

2.3 绘图封装:窗口模式是性能核心

屏幕驱动最底层是画点函数。ILI9341的GRAM在写入像素时是有“窗口”概念的:先发送0x2A设置列地址范围,0x2B设置行地址范围,然后连续写0x2C命令并发送像素数据,GRAM会按设定自动填充,不需要每个像素单独设置地址。

基于这个原理,我把核心API设计成三个:

  • LCD_SetWindow(x0, y0, x1, y1):设置矩形窗口;
  • LCD_FillRect(x0, y0, x1, y1, color):窗口模式下整块填充;
  • LCD_DrawPoint(x, y, color):单个像素写入。

画棋盘时,我整屏先填背景色,再按格子坐标画9条横线和9条竖线形成网格;初始4颗棋子用实心圆函数画。实心圆最省事的方式不是Bresenham圆+种子填充,而是两层循环直接判断每个像素是否满足“到圆心距离小于半径”,代码短、不容易错,对屏幕这种像素级绘图来说也够用。

文字显示我用了字模取模,只取“黑白棋”“黑”“白”“胜”“负”“思考中”“轮到你”这些必要汉字,16x16点阵,一个字32字节,全部字模加起来不到1KB,Flash完全没压力。取模软件我用的是PCtoLCD2002,取模时要勾选逐行提取和字节倒置,否则显示会镜像或者错位。

屏幕刷新最核心的经验是局部刷新。Proteus仿真里SPI刷屏本来就比实物慢,如果每次按键都全屏重绘,光标移动都会卡成幻灯片。我实现时落子后只重绘当前格子和受影响区域,光标移动只擦除旧光标位置并绘制新位置。这个优化对体验提升非常显著。

3. 黑白棋逻辑与AI:让MCU学会“思考”

3.1 棋盘建模与8方向搜索算法

棋盘数据结构我用最简单的uint8_t board[8][8],值0表示空,1表示黑子,2表示白子。为什么不用位棋盘?位棋盘运算更“高级”,但对20KB RAM的MCU来说没必要,8x8数组更直观,Keil调试时能直接看到每个元素,排错效率高。

方向数组是整个黑白棋逻辑的灵魂:

const int8_t dir[8][2] = { {-1, -1}, {-1, 0}, {-1, 1}, { 0, -1}, { 0, 1}, { 1, -1}, { 1, 0}, { 1, 1} };

所有规则逻辑都可以统一成一句话:从落点出发,沿八个方向寻找被夹住的对方棋子。落子合法性判断的完整流程如下:

  1. 落点必须在棋盘内,且当前位置为空;
  2. 从落点沿某方向迈出一步,如果越界或踩到空格,这个方向直接失效;
  3. 继续沿该方向走,只要还是对方棋子就继续向前;
  4. 如果最终遇到自己棋子,那么这个方向上的所有对方棋子都可以被翻转;
  5. 只要存在一个满足条件的方向,这个落点就是合法的。

实现时最容易犯的错误是只判断“下一步是对方棋子”,没有追踪到“再往后能遇到自己棋子”。另一个常见问题是合法判定和翻转逻辑不一致,导致判断结果和实际棋盘变化对不上。我的做法是拆成两个函数:isValidMove只看合法性,doMove负责翻转,两者复用同一套方向扫描逻辑,保证判定和翻转的结果始终一致。

3.2 落子翻转、跳过回合与胜负判定

翻转逻辑上,我的做法是对每个合法方向,从落点沿该方向往回扫描,把中途所有对方棋子翻成当前落子方。这个“反方向扫描”比从落点往外走更稳妥,因为不需要额外记录坐标数组。

然后是“无子可下”的处理。黑白棋规则允许无合法位置时跳过一回合,但跳过后要把回合交给对方。如果双方都无子可下,对局才真正结束。很多实现里只判断自己无子可下就结束,结果对手还有棋可下时流程就断了。我维护了一个检查函数,每次都要数清楚黑白双方当前的合法落子总数,都为0才结束游戏。

胜负判定最简单,遍历棋盘统计黑、白棋子数,多者胜,一样多算平局。游戏结束后的界面我做成“再来一局”选项,确认后重新初始化棋盘。

3.3 AI策略:评分表而不是搜索树

人机对战的AI我没有用深搜,而是用局面评分加权,因为STM32虽然跑得动两层搜索,但对这种小工程而言性价比不高。黑白棋的胜负关键更多体现在占角和控边,而不是算得深。与其让AI“深谋远虑”,不如让它“懂棋理”。

评分表设计成8x8权重矩阵,核心思路是:

  • 角落权重最高(100分),占住角就能稳稳控制一整条对角线方向;
  • 边的权重次之(10分);
  • 角落周围的位置是高风险区,给负分(-20到-50),防止AI主动给对手留角;
  • 中间区域给低分。

AI的落子算法是:遍历所有合法落点,在临时棋盘上模拟落子并翻转,统计落子后己方与对方棋子数量的差值,再叠加该位置的权重分数,取总分最高的位置落子。

这个简单AI已经能占到角、避开高风险区,作为对战对手不会显得太笨,但也不至于强到让人抓狂。如果想调整难度,我有两个建议:降低难度时,可以让AI在开局前几个回合随机走;提高难度时,可以在评分中加入“行动力”因子,也就是落子后己方合法可落点数减去对手合法可落点数,数值越高越有利。这个因子对黑白棋中后期判断很有价值。

我实际测试时发现AI不宜做得太强。课设演示或者自己娱乐时,AI太强反而没有交互乐趣,而且不利于展示“这个AI到底怎么思考的”。这个项目的核心展示价值在于规则实现正确、界面操作流畅,AI能占到角不犯明显错误,就已经是相当合格的教学级AI了。

4. Proteus仿真:搭建、接线与避开三个大坑

4.1 仿真工程搭建与最小系统接线

Proteus仿真是这套工程快速复现的关键,我用到的元件大致有:

  • U1:STM32F103R6,Category里直接搜索型号;
  • U2:ILI9341屏幕模型,不同Proteus版本库里名称可能略有差异,搜索ILI9341或TFT;
  • 8MHz晶振,22pF电容x2;
  • 复位按键、10k上拉电阻、100nF电容;
  • 3.3V电源终端和GND终端;
  • 若干按键模拟方向键、确认键。

接线时最容易出问题的是STM32的电源引脚。F103R6有VDDA、VSSA、VDD、VSS,仿真中要把所有电源脚接3.3V,所有地脚接GND。还有一个专门的稳压器引脚,不同库模型里标注可能是VCAP或者VREF,需要接一个1μF电容到地,否则仿真可能报错或者MCU不运行。实物上这个引脚同样要接电容,不是仿真特有的。

复位电路在仿真中不接外部电阻也能跑,但我还是按实物画了上拉+电容的链路,这样能同时验证复位时序对开机显示是否有影响。晶振部分我直接放8MHz晶振并接22pF电容。这里有个细节:MCU元件属性里如果设置了默认时钟频率,和外接晶振冲突会导致仿真时钟异常,建议检查一下属性页,别让两处配置打架。

4.2 仿真调试最容易踩的三个坑

第一个坑:屏幕不亮或者白屏。按我的经验,Proteus白屏问题90%不是接线错,而是初始化序列和仿真模型不兼容。Proteus里的ILI9341模型对某些命令的响应和实物有差异,我换过一套初始化序列之后立刻正常。定位思路是:在初始化代码的每个关键命令之后加一个GPIO翻转,用虚拟示波器观察运行到哪一步卡住,逐步缩小问题范围。

第二个坑:仿真速度太慢。Proteus对SPI刷屏的模拟确实吃力,如果初始化后立刻全屏刷一张大图,仿真能卡到一秒刷新几帧。我的办法是SPI预分频调到较低的档位(提升SPI时钟),同时坚持界面层局部刷新。如果还是慢,就把Proteus右下角的仿真帧率调高,默认的一

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 7:23:56

YOLO26实测:低光增强、轻量解耦与端侧部署全解析

简介:面向计算机视觉开发者、算法工程师及边缘设备部署者,YOLO26发布项目源码压缩包为需要抢先体验新一代检测、分割、姿态估计等能力的团队提供轻量入口。压缩包为zip格式,共3个文件,包含inscode在线开发环境配置、HTML预览页面和…

作者头像 李华
网站建设 2026/9/1 7:23:51

电商用户行为分析实战:Python+Pandas实现RFM分层与转化漏斗

简介:电商用户行为分析是一份面向大数据分析方向毕业设计或相关课题研究的可运行源码包,围绕2017年11月25日至12月3日淘宝用户超1亿条行为记录,梳理了数据导入、清洗、异常值处理、Hive分析及可视化全流程,重点呈现用户流量、行为…

作者头像 李华
网站建设 2026/9/1 7:23:40

代码管理软件的进化:从版本控制到AI赋能的研发协作枢纽

据 pmarketresearch 测算,全球源代码管理软件市场 2025 年已达 94.5 亿美元,预计 2032 年突破 332.5 亿美元,年复合增长率约 19.7%。与此同时,GitHub Copilot 用户已超 2000 万、GitLab 全年营收突破 9.5 亿美元——这些数字背后&…

作者头像 李华
网站建设 2026/9/1 7:23:17

基于SpringBoot的焕智老旧小区改造信息管理系统毕业设计项目源码文档

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

作者头像 李华
网站建设 2026/9/1 7:21:58

魔兽世界插件+客户端监控+智能体:事件驱动架构实战指南

这次我们来看一个把三件看起来不相关的东西强行焊在一起的实战项目:魔兽世界插件、客户端监控、智能体。听起来像游戏外挂,但它实际上是一个偏向编程实践和技术验证的综合项目,更适合描述成“基于魔兽世界客户端事件流的数据采集与智能体决策…

作者头像 李华
网站建设 2026/9/1 7:21:53

福彩3D和值统计:Python实现频率分布与置信区间分析

先说结论:福彩3D开奖号码是独立随机事件,任何算法都不能做到“精准预测”。这篇文章要讲的,不是玄学选号,而是把“和值范围”这件事做成一套可计算、可验证的统计与算法工程。我们会用 Python 写一个和值频率分析器,基…

作者头像 李华