当前位置: 首页 > news >正文

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

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

http://www.cnnetsun.cn/news/4358004.html

相关文章:

  • STM32+TB6600步进电机控制:定时器PWM脉冲生成与加减速实战
  • 技术博客写作边界:从选题到工程实践
  • 嵌入式冰箱怎么选?从散热结构到十字门容量,一篇看清选购关键
  • 基于YOLOv8的垃圾检测实战:从数据标注到部署全流程解析
  • 没背景的施工员叫什么 别踩坑了,真相都在这了
  • MATLAB/Simulink四旋翼控制器工程拆解:从仿真到真机部署
  • 没背景的施工员叫什么证良心建议全在这了
  • 没背景的施工员叫什么 2026河南最新政策怎么应对
  • 断联期情绪反扑怎么办?用认知失真检测与情绪日志重建稳定
  • Python电商数据分析平台:Django+爬虫+机器学习完整实战
  • C# 工业级集成 YOLOv12 完整方案:从 ONNX 推理封装到 WPF 实时检测界面全流程
  • 【计算机毕业设计单片机案例】基于 STM32 或 51 单片机的阈值触发式环境报警控制系统设计 基于 STM32 或 51 单片机的光敏采集与 LED 补光控制系统设计(024405)
  • 借条再规范也难获利息:信用卡套现转贷合同无效解析
  • 4小时挣130美元的网约车司机,副业写诗背后的可复制方法
  • 入团资料员证怎么转?考前押题帮你稳过!
  • 危化安全标准化评审员真实经历:学历不够怕报不上名?这招能让你稳过
  • 入团资料员考前押题全攻略,工地太忙根本没时间复习?别慌!
  • 正版施工员证书图片值不值得考?别让钱打了水漂
  • 正版施工员证书图片工作内容与日常职责
  • 正版施工员证书图片怎么拿?官方报名入口在这儿找
  • 施工员自学保姆级教程:过期证书怎么复审?手把手教你搞定
  • 入团资料员怕考不过白交钱?全流程办理全知道,多少钱都值了
  • 八大员施工员自学需要什么材料多少钱
  • 安全员安全检查汇报在哪考,怕考不过白交钱?这篇讲透了
  • 机械员考试实务怎么考?怕考不过白交钱?官方报名入口全在这
  • 机械员考试实务值不值得考?学历不够怕报不上名怎么办
  • 机械员考试实务多久拿证?学历不够也能报上名的干货来了
  • 安全员安全检查汇报怎么写?官方报名入口在哪?证书挂靠找工作必须知道
  • 安全员安全检查汇报千万别踩坑这些雷区你得知道
  • 施工员自学怎么考 官方回应来了,别再被坑