news 2026/9/1 18:45:48

STM32F103R6驱动ILI9341彩屏的黑白棋游戏Proteus仿真完整方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103R6驱动ILI9341彩屏的黑白棋游戏Proteus仿真完整方案

简介:一套基于STM32F103R6主控与ILI9341彩色液晶屏的黑白棋对战完整工程,面向嵌入式初学者和对LCD/SPI显示控制感兴趣的开发者,可在Proteus中直接仿真运行并观察交互效果。工程使用STM32CubeMX完成初始化,基于HAL库开发,覆盖标准GPIO、SPI、SysTick等底层驱动,同时包含完整游戏逻辑、中断服务函数、硬件抽象层适配以及ILI9341_SPI专用显示驱动,并配有STM32最小系统与液晶模块连接方式的电路原理图和实机演示视频。压缩包共597个文件,以C源文件、H头文件、S启动文件为主,兼有Proteus设计文件、Keil MDK-ARM工程、IOC配置文件及编译产物(hex/axf/map),整体大小25.76MB,目录结构清晰,便于定位驱动、应用和仿真相关代码。所有代码已通过编译验证,重点演示SPI外设驱动、LCD显示控制与简单人机交互的实现思路,可直接作为课程设计或入门练手项目参考,当前已有56人学习。 前阵子有学生问我:想在Proteus里跑一个带液晶显示的嵌入式小项目,用STM32F103R6驱动一块IPS彩屏做黑白棋(奥赛罗棋),还要有仿真、有源码、能直接烧录,该怎么搭。我干脆把整个工程从硬件电路到软件逻辑完整过了一遍,顺便把踩过的坑也整理出来,这篇就当是给同样在折腾这个方向的朋友一份可以直接“抄作业”的参考。项目本身不算复杂,但麻雀虽小五脏俱全——MCU选型、SPI屏驱动、游戏规则状态机、按键交互、仿真环境搭建,每个环节都有值得注意的细节。

1. 项目整体设计与方案选型

1.1 为什么是STM32F103R6 + ILI9341

STM32F103R6属于F103系列中的中等容量产品,64引脚封装,内置32KB Flash和10KB SRAM,主频最高72MHz。说实话,跑一个黑白棋游戏、驱动一个240x320分辨率的TFT屏,这个配置是绰绰有余的,甚至有些浪费。选择它而不是C8T6之类的型号,更多是出于引脚数量的考虑——R6的LQFP64封装比C8T6的LQFP48多出16个引脚,给按键、指示灯、预留调试接口留足了空间,不至于为了几个IO口去做引脚复用取舍。

ILI9341是市场占有率很高的TFT LCD控制器,支持SPI和并行两种接口方式。在这个项目里,我用的是SPI模式,原因很直接:并行接口需要至少16根数据线加若干控制线,虽然刷新速度快,但占用的GPIO数量太多;而SPI模式只需要SCK、MOSI、DC、CS、RST五根线就能完成全部控制,把宝贵的GPIO留给按键和扩展功能。黑白棋本身对刷新率要求不高,SPI接口的带宽完全够用。实际测试下来,全屏填充一帧大概需要60ms左右,用于静态画面和落子刷新游刃有余。

1.2 双人对战模式的逻辑主体

这个工程的核心是黑白棋规则,不是AI。对战的交互模型是两名玩家轮流落子,每个玩家各用一组按键操作光标移动到目标格子,按确认键落子。黑白棋规则中最关键的一点是:必须在八个方向中的至少一个方向上夹住对方的棋子,否则落子无效。

规则的实现分为三个层次:合法性判断、翻转处理、终局判定。合法性判断只需要检查当前落子位置在八个方向上是否存在“己方棋子-连续对方棋子-空位”的排列;翻转处理则是把满足条件的连续对方棋子全部翻为当前玩家颜色;终局判定在一方无棋可下时自动跳过回合,双方都无棋可下则游戏结束,统计棋盘上黑白棋子数量,多者获胜。

这套逻辑用C语言实现,清晰且直观。整个游戏状态机分为:初始化、黑方回合、白方回合、终局四个状态,按键扫描和状态迁移放在主循环里完成。

2. 硬件电路设计:最小系统到外设连接

2.1 主控最小系统电路

STM32F103R6最小系统需要的东西不多:3.3V电源、8MHz外部晶振(也可以只用内部RC,但液晶刷新和SPI时序对时钟精度有一定要求,建议用外部晶振)、复位电路、BOOT配置、SWD调试接口。

电源部分要注意:STM32的VDDA和VDD都需要接3.3V,并且每个电源引脚旁边要放一个100nF去耦电容,VDDA建议再加一个1uF以上的钽电容滤波。老生常谈的话题,但很多仿真的问题其实根源就在这——原理图看着没问题,实际跑起来偶发复位,多半是电源纹波干的。

晶振电路用8MHz主晶振,两个20pF负载电容。STM32F103的PLL最高可以倍频到72MHz,系统时钟配置为8MHz x 9 = 72MHz,这是F103最稳定的运行状态。很多人在Proteus仿真时忽略了一个细节:仿真模型里如果不设置外部晶振频率,或者设置的频率与代码初始化不一致,会导致SPI时序错乱,屏幕显示花屏。

BOOT0和BOOT1都用10K电阻下拉到GND,从主Flash启动,这是最常规的配置。SWD接口引出SWDIO和SWCLK两根线,方便用ST-Link或J-Link调试下载。

2.2 ILI9341液晶接口与按键电路

ILI9341在SPI模式下需要连接六根线到MCU,具体分配如下:

液晶引脚功能接STM32引脚说明
CS片选PA4低电平有效
SCKSPI时钟PA5SPI1_SCK
MOSI数据输入PA7SPI1_MOSI
DC数据/命令选择PA3低电平命令,高电平数据
RST复位PA2低电平复位
BL背光3.3V直接拉高,最简方案

用SPI1而不是SPI2,原因是SPI1挂载在APB2总线上,时钟频率最高可达72MHz,SPI2挂在APB1上只有36MHz。虽然实际应用中SPI波特率一般分频到18MHz以内,但挂在APB2上理论上限更高,高频性能也更稳定。DC和RST用普通GPIO控制,不占用SPI硬件引脚。

按键部分,四个方向键加一个确认键,共五个按键。设计上采用独立按键接法,一端接GPIO,另一端接GND,内部上拉使能,按下为低电平。这种接法最简单可靠。按键配置在GPIOB的低五位:PB0-左,PB1-右,PB2-上,PB3-下,PB4-确认。

3. 软件架构与核心逻辑实现

3.1 工程目录结构与代码分层

整个Keil工程的代码分为四层:系统层(系统时钟初始化、延时函数)、驱动层(ILI9341底层驱动、LCD绘图库)、应用层(游戏逻辑、按键扫描)、界面层(棋盘绘制、落子动画)。

分层的好处是代码可复用性强。驱动层的ILI9341初始化序列和绘图函数,换一个项目用OLED或者其他尺寸的TFT屏,只需要改驱动层,上层游戏逻辑完全不用动。这个习惯建议养成,哪怕是一两百行的小工程,分层做清楚了后期维护和调试都省心。

系统时钟初始化用标准外设库完成,核心代码大致如下:

void SystemClock_Config(void) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); while (RCC_WaitForHSEStartUp() != SUCCESS); RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); RCC_PLLCmd(ENABLE); while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while (RCC_GetSYSCLKSource() != 0x08); RCC_HCLKConfig(RCC_SYSCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div2); RCC_PCLK2Config(RCC_HCLK_Div1); }

3.2 ILI9341 SPI驱动要点

ILI9341的SPI驱动是本项目最基础的底层,核心是搞清楚它和MCU的通信时序。ILI9341在SPI模式下的写数据包括两步:先用DC引脚控制当前传输的是命令还是数据,然后由MCU通过SPI发送8位或16位数据。这里有个新手容易踩的坑:很多ILI9341模块上电后默认是并行模式,必须通过特定的命令序列切换到SPI模式,所以初始化序列必须严格按照数据手册来,不能随便裁剪。

SPI初始化代码:

void SPI1_Init(void) { SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); }

时钟极性CPOL和相位CPHA要格外留意。很多ILI9341模块的样例代码用的是Mode 0(CPOL=Low, CPHA=1Edge),但也有一些模块要求Mode 3。如果初始化后屏幕花屏、颜色不对,首先检查这两位的配置是否和模块匹配。

3.3 黑白棋核心算法:合法性检测与翻转

黑白棋的算法核心其实不复杂,难在把规则写清楚。我用一个8x8的二维数组存储棋盘状态,0表示空位,1表示黑子,2表示白子。八个方向的偏移量用一个二维数组定义:

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

合法性检测函数的核心思路:从落子位置向某个方向步进,记录经过的对方棋子数,如果遇到己方棋子且经过的对方棋子数大于0,则说明该方向可以翻转。八个方向按相同逻辑逐一检测,只要有一个方向满足条件,该位置就是合法落子点。

uint8_t IsLegal(int8_t row, int8_t col, uint8_t player) { uint8_t opp = (player == 1) ? 2 : 1; int8_t i, d; int8_t r, c; if (board[row][col] != 0) return 0; for (d = 0; d < 8; d++) { uint8_t count = 0; r = row + dir[d][0]; c = col + dir[d][1]; while (r >= 0 && r < 8 && c >= 0 && c < 8 && board[r][c] == opp) { count++; r += dir[d][0]; c += dir[d][1]; } if (count > 0 && r >= 0 && r < 8 && c >= 0 && c < 8 && board[r][c] == player) return 1; } return 0; }

这个写法是直接且高效的。翻转函数本质上就是合法性检测的延续——在确认合法后,对每个方向再次遍历,把路径上的对方棋子改写成己方颜色。

一个容易忽略的细节:黑白棋规定,如果当前玩家无合法落子点,必须自动跳过回合。所以每轮轮到某方时,程序要遍历整个棋盘判断是否存在合法落子点,如果没有就自动切换玩家。这一步放在按键响应之前,游戏流程才不会被卡住。

3.4 界面绘制与刷新策略

界面层用LCD绘图库实现棋盘和棋子的绘制。棋盘背景为深绿色,8x8的格子线用白色绘制,棋子是实心圆,黑子填充黑色,白子填充白色。光标用红色空心圆表示当前选中的位置。

性能上有个优化技巧:不要每次按键移动都全屏重绘,只需要在光标移动前用背景色把旧光标位置重绘,再在新位置绘制光标。落子时也只需要局部刷新——在落子位置绘制新棋子,再把翻转的棋子重新绘制颜色。这样SPI刷新压力很小,画面也没有闪烁感。全屏重绘只用在开局和游戏结束画面。

4. Proteus仿真搭建与联调要点

4.1 仿真工程配置与常见坑

Proteus仿真STM32F103R6需要特别注意电源网络。STM32在Proteus里不像51单片机那样自动获得电源,必须手动把VDD引脚连接到+3.3V电源端子,VSS连接到GND。这在热词里也有人问“proteus怎么为stm32f103c8设置电源”,原理是相同的——每个供电引脚都要显式接线。遗漏任何一个VDD/GND引脚,仿真时MCU都会表现出各种奇怪现象:不运行、运行极慢、外设无响应。

Proteus中放置ILI9341模型时要注意:需要从元件库中搜索“ILI9341”,选择正确的LCD模型。模型放置后需要确认型号支持SPI接口,以及默认的分辨率是否为240x320。如果模型分辨率设置不对,初始化序列里的窗口设置会画到屏幕范围外,导致显示偏移或者只有部分区域正常。

晶振频率的设置在仿真中也是一个关键点。双击STM32F103R6元件,在弹出的属性对话框里设置Crystal Frequency为8MHz。这个值必须和代码里的HSE配置一致。如果设置为其他频率,代码里PLL倍频后得到的系统时钟会偏离72MHz,SPI分频后得到的波特率也会偏离预期,最终表现就是屏幕刷新慢或时序不对。

4.2 将Keil生成HEX文件导入仿真

在Keil中配置仿真用的HEX文件路径,建议在工程属性中的Output选项卡勾选“Create HEX File”,然后在Target选项卡配置好芯片型号(STM32F103R6对应的是STM32F103R6)。编译完成后,在Proteus里双击MCU元件,在Program File处选择生成的.hex文件。

有个细节值得注意:Proteus对HEX文件中的调试信息不敏感,但会对代码大小敏感。32KB Flash不是很大,如果工程开着调试信息导致代码膨胀,可能在下载到Proteus模型时出现问题。建议Release模式编译,关闭调试信息,只保留优化。

4.3 仿真与实物的差异

Proteus仿真和实物有差异,主要体现在三个方面:时序精度、外设细节、实时性。仿真中SPI时序是基于理想化的模型,不会出现信号反射、走线电容、上拉强度不足等问题,所以代码在仿真上一切正常不代表实物也一定正常。反过来,如果仿真跑不通,实物大概率也不行——仿真对逻辑错误和初始化配置错误的排查效率很高。

建议的联调顺序是:先在Proteus中验证核心逻辑(游戏规则、按键响应、液晶显示),确认无误后,再烧录到实物板子上调试显示效果和按键手感。这样可以避免在硬件上反复烧录调试浪费时间的尴尬。

5. 开发环境问题与调试技巧实录

5.1 Keil常见问题速查

做这个项目过程中,Keil本身也出了不少幺蛾子,这里整理几个高频问题:

问题现象可能原因解决办法
编译慢Browse Information开启、无优化关闭Browse Information,提升优化等级
J-Link提示盗版/兼容问题固件版本过旧或非官方换用ST-Link或者更新J-Link固件
Pack Installer打不开网络问题或Pack路径异常检查Pack路径设置,重新添加在线Pack仓库
下载HEX失败下载选项配置错误检查Utilities选项卡中Flash Download配置
无法识别DAP调试器驱动未安装或版本不匹配安装对应驱动,选用兼容的CMSIS-DAP包

如果你用的是J-Link仿真器,Keil报盗版提示的解决思路是:检查J-Link的固件版本,老固件会出现兼容性问题,不一定非要换硬件,先尝试更新驱动和固件。如果还是不行,直接换ST-Link更省心——STM32的生态对ST-Link支持最完善,没有这些授权相关的烦恼。

5.2 调试器连接与烧录建议

调试这个项目时,建议优先使用ST-Link。在Keil的Options for Target -> Debug选项卡里,选择ST-Link Debugger,然后在Settings里确认能识别到芯片ID。如果识别到芯片但下载失败,大概率是SWD速率太高,把下载速率降到1MHz以下再试。

在实际调试中,我习惯把液晶驱动初始化的时间节点做一个延时标记——在LCD初始化完成后,点亮背光的同时将某个GPIO拉高。示波器或者逻辑分析仪测试点可以快速确认代码执行到了哪一步。这个方法在液晶不亮、白屏、程序跑飞等场景下定位问题非常高效。

另一个小技巧:在代码里设置一个调试用计数器,每完成一次主循环加一,通过调试器实时观察这个变量的增长速度,就能知道主循环是否正常运行、执行周期是否合理。如果循环周期异常长,重点检查是否有阻塞式的延时函数占了太多CPU时间。

5.3 踩过的坑:液晶初始化序列

ILI9341的初始化序列是直接从厂商驱动芯片手册中转出来的,但是不同批次、不同厂家的模块对初始化序列的响应略有差异。最典型的例子是0x3A命令设置像素格式,有的模块需要设为0x55(16位色),有的模块虽然是SPI接口但内部还是按18位色处理——如果颜色显示不对,优先检查这条命令。

我的建议是直接用成熟的初始化序列模板,不要自己改写,除非你已经明确知道每条命令的用途。市面上ST7789和ILI9341的初始化序列不能互换,经常有人在移植代码时犯这个错。屏幕显示不正常时,优先怀疑初始化序列,其次才是SPI时序。初始化序列不对会出现花屏、白屏、显示内容错位等诡异现象,排查起来极为费劲。

5.4 关于工具链正版与许可的说明

顺便提一句工具链的许可问题。Keil MDK的评估版对代码大小有限制(32KB),本项目代码量刚好在边缘徘徊,所以建议使用正版授权或者学校提供的教育版。Proteus也是同样的道理,需要正版许可或者学校实验室授权版本。网上流传的各种注册机、破解释放器,不仅踩了版权红线,还有可能带入恶意代码,得不偿失。工具是拿来用的,不是拿来折腾的,把时间花在项目本身更划算。

我在实际做这套工程时感受最深的一点:仿真和实物的差距最容易在SPI驱动和电源部分体现。仿真时可以跑通的代码,如果你在实物上没有处理好去耦电容、没有加电平转换、没有注意信号线长度,都会出现随机性故障。反过来,仿真时候的报错信息,尤其是逻辑层面的问题,对实物的调试有极强的指导意义。尤其是按键消抖和非法落子检测,这些逻辑在仿真里验证过一遍,实物基本就是一把过。

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

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

STM32+TB6600步进电机控制:定时器PWM脉冲生成与加减速实战

简介&#xff1a;本资源是一套面向嵌入式初学者与STM32进阶开发者的实战型电机控制案例&#xff0c;聚焦于使用STM32微控制器驱动TB6600芯片精确控制42/57系列步进电机&#xff0c;解决转速调节、步数计数与角度定位等核心问题&#xff0c;适用于智能硬件、机电一体化及自动化控…

作者头像 李华
网站建设 2026/9/1 18:45:37

技术博客写作边界:从选题到工程实践

这个输入内容是一则社会新闻事件的标题&#xff0c;标题内容涉及具体个人事件、司法处理结果和公众情绪评判&#xff0c;不属于技术类内容&#xff0c;且存在敏感信息。我无法基于这个标题撰写 CSDN 技术博文。CSDN 博客的主题范围是&#xff1a;开发技术、工具实践、架构设计、…

作者头像 李华
网站建设 2026/9/1 18:45:34

嵌入式冰箱怎么选?从散热结构到十字门容量,一篇看清选购关键

家电产品买过不少&#xff0c;但每次看到“嵌入式冰箱”这四个字&#xff0c;很多人的第一反应还是有点懵&#xff1a;嵌入式到底是不是就是“塞进柜子里”&#xff1f;十字门和对开门到底怎么选&#xff1f;促销页面上写“省 1860.29 元”&#xff0c;这个数字是真优惠还是先涨…

作者头像 李华
网站建设 2026/9/1 18:42:29

基于YOLOv8的垃圾检测实战:从数据标注到部署全流程解析

简介&#xff1a;本资源是一套面向人工智能初学者与计算机视觉实践者的垃圾图像目标检测数据集&#xff0c;聚焦真实场景下的18类常见垃圾细粒度识别任务&#xff0c;适用于YOLO系列模型训练与算法优化。数据集包含约5000张高质量图像及对应YOLO格式标注文件&#xff08;.txt&a…

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

没背景的施工员叫什么 别踩坑了,真相都在这了

没背景的施工员叫什么 别踩坑了,真相都在这了 不知道去哪报名怕被坑?你不是一个人。在建筑施工行业, 没背景的施工员 其实指的是那些没有关系、没有资源、靠自己一步步打拼的普通从业者。他们往往从最基层的岗位做起,比如劳务班组负责人、现场施工人员等,虽然起薪不高,但只要肯努力,未来还是有发展的。不过,…

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

MATLAB/Simulink四旋翼控制器工程拆解:从仿真到真机部署

简介&#xff1a;本资源是一套基于MATLAB实现的阿塞铁克壁虎四旋翼无人机控制器代码包&#xff0c;面向计算机、电子信息工程、数学等专业的本科生&#xff0c;服务于课程设计、期末大作业及毕业设计等实践环节&#xff0c;解决四旋翼姿态稳定控制与参数调优等核心问题。压缩包…

作者头像 李华