零知派ESP32-ADXL362体感迷宫游戏
✔零知派(零知开源)是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台,在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录,让开发重心从 “配置环境” 转移到 “创意实现”,极大降低了技术门槛。零知开源编程软件,内置上千个覆盖多场景的示例代码,支持项目源码一键下载,项目文章在线浏览。零知派(零知开源)平台通过软硬件协同创新,让你的创意快速转化为实物,来动手试试吧!
✔访问零知实验室,获取更多实战项目和教程资源吧!
www.lingzhilab.com
项目概述
你是否想过用自己的身体倾斜来控制一个小球,在一个迷宫里探索并抵达终点?本项目利用零知派ESP32开发板、ADXL362三轴加速度计和ST7789彩色显示屏,制作了一个体感控制的迷宫游戏。当你倾斜开发板时,小球会向相反方向滚动,你需要灵活控制倾斜角度,避开深灰色的墙壁,最终抵达绿色的终点。游戏采用局部刷新机制,运行流畅无拖尾,非常适合学习嵌入式图形界面、传感器数据处理和物理模拟。
项目亮点
体感控制:使用ADXL362加速度计检测倾斜方向,小球运动方向与倾斜方向相反,操控直观有趣。
平滑物理引擎:加入速度、阻尼、加速度和最大速度限制,小球滚动真实自然。
局部刷新:仅更新小球经过的格子,大幅减少屏幕绘制开销,无闪烁拖尾。
精准碰撞检测:基于圆形边界和网格遍历,确保小球不会穿墙。
死区校准:通过偏移量和死区阈值消除静止时的抖动,操作稳定。
项目难点及解决方案
难点解决方案
加速度计数据抖动导致小球乱动加入软件校准偏移量和死区阈值(DEADZONE),静止时忽略微小加速度
快速倾斜时小球穿墙分轴移动(先X后Y),碰撞时回退并置零速度;限制最大速度MAX_SPEED
屏幕刷新拖尾现象局部擦除:小球移动后,仅重绘旧位置覆盖的格子,再在新位置绘制小球
方向反直觉(倾斜左边小球却向右)在代码中对加速度值取反:accX = -rawX * SENSITIVITY,让小球与倾斜方向相反
SPI总线冲突(加速度计与屏幕共用)每次读取加速度计时使用SPI事务(beginTransaction/endTransaction),确保时序正确
一、硬件系统部分1.1 硬件清单
组件型号/规格数量
主控板零知派ESP321
扩展板零知派ESP32扩展板
加速度计ADXL362(三轴数字输出)1
显示屏ST7789(240×240分辨率,SPI接口)1
连接线杜邦线(母对母)若干
面包板通用型1
1.2 接线方案 本项目使用两个SPI设备:ADXL362加速度计和ST7789显示屏。为了不冲突,将它们连接到不同的CS片选引脚,而共用SCK、MOSI、MISO(ADXL362需要MISO,ST7789通常不需要MISO)。参考接线如下:
ESP32引脚连接ADXL362说明
3.3VVCC供电
GNDGND共地
GPIO18SCKSPI时钟线
GPIO23MOSISPI数据线(主机输出)
GPIO19MISO仅ADXL362需要读取数据
GPIO5CSADXL362片选(可自定义)
注意:因为有零知派扩展板,ST7789不需要接线,只需要把屏幕插到零知派扩展板上就可以了。
1.3 硬件连接图https://file1.elecfans.com/web3/M00/61/B1/wKgZO2oVlIOALhaMAAVP6s9nWz0780.png1.4 实物连接图https://file1.elecfans.com/web3/M00/61/B1/wKgZPGoVlG6ASZ6ZAADmizJl-2A420.png二、软件架构设计2.1 系统初始化setup() 函数中按顺序执行:
串口初始化(用于调试)。
自动寻找终点(最后一个路径格子作为绿色终点)。
初始化TFT屏幕,设置旋转方向,清屏为黑色。
初始化ADXL362加速度计,设置为测量模式。
绘制完整的静态迷宫(墙壁、路径、终点)。
绘制初始红色小球。
2.2 主循环逻辑loop() 中无限循环执行以下步骤(仅在非胜利状态):
读取加速度计数据:通过SPI事务获取原始X、Y轴值。
校准与死区处理:加上偏移量,小于阈值则置零。
方向取反并计算加速度:accX = -rawX * SENSITIVITY,accY = rawY * SENSITIVITY。
更新速度:velX = velX * DAMPING + accX,并限制最大速度。
移动并碰撞检测:先尝试X方向移动,若碰撞则回退并清零速度;再尝试Y方向。
胜利检测:若小球所在格子等于终点坐标,显示胜利文字,停止更新。
局部刷新:若整数位置改变,则擦除旧小球背景并绘制新小球。
延时:delay(50) 控制帧率约20FPS。
2.3 运动检测与碰撞算法运动检测:通过对加速度值的积分(累加到速度)模拟惯性,阻尼项模拟空气阻力。
碰撞检测函数 collidesWithWall():
根据小球圆心坐标和半径,计算出小球外接正方形的四个边界。
求出正方形覆盖的网格行列范围。
遍历这些格子,若任一格子为墙壁(值1)或超出迷宫边界,则判定碰撞。
分轴移动:先移动X轴,若碰撞则回退;再移动Y轴。这样可以避免同时移动时卡入墙壁角落。
2.4 系统校准ADXL362平放时理论输出为(0,0,1g)。但实际电路存在零偏,因此需要软校准:
偏移量:CALIB_X_OFFSET = 85,CALIB_Y_OFFSET = 55。可以通过读取平放时的数值,取平均值后填入,根据自己的实际情况来调整。
死区阈值:DEADZONE = 40,当校准后的绝对值小于40时视为0,消除微小抖动引起的误触发,根据自己的实际情况来调整。
调试技巧:在串口监视器中打印 rawX, rawY,将开发板水平静止,记录读数,将其相反数填入偏移量。例如静止时X输出为-85,则偏移量设为+85。
2.5 加速度数据采集ADXL362通过SPI读取三轴数据(16位有符号整数)。代码中调用 accel.readXYZTData(rawX, rawY, rawZ, rawTemp) 一次性读取。注意每次读取前使用 SPI.beginTransaction 确保SPI总线不被其他设备(如屏幕)干扰,读取后立即 endTransaction。
三、代码拆分讲解3.1 头文件与全局定义#include <SPI.h>#include <ADXL362.h>#include <TFT_eSPI.h>https://file1.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.pngTFT_eSPI 库需要提前配置 User_Setup.h 指定屏幕引脚和驱动。
迷宫尺寸为12×12,每个格子20×20像素。小球半径8像素,略小于半格,以便在通道中顺畅移动。
3.2 迷宫地图使用二维数组 maze 定义墙壁(1)和路径(0)。起点固定在(1,1)格子,终点自动搜索最后一个0格子。你可以自由修改迷宫地图,只要保持四周为墙壁即可。
3.3 碰撞检测函数bool collidesWithWall() {int left = ballX - BALL_RADIUS;int right= ballX + BALL_RADIUS;int top = ballY - BALL_RADIUS;int bottom = ballY + BALL_RADIUS;int startX = left / CELL_SIZE;int endX = right / CELL_SIZE;// ... 遍历这些格子if (maze == 1) return true;}https://file1.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png使用整数除法获取格子索引,避免了复杂的浮点运算。
对于圆形小球,使用矩形边界会略微严格,但在格子大小为20、半径为8的情况下,效果良好且简单。
3.4 局部刷新机制传统做法是每帧清屏重绘,效率低且有闪烁。本代码采用:
drawBall() 绘制圆形红色小球。
restoreBallBackground() 恢复旧位置被覆盖的格子:根据旧坐标范围,调用 drawCell() 重绘每个格子(墙壁、终点或黑色路径)。
只有当小球整像素位置改变时才执行擦除和重绘,减少了不必要的绘制。
3.5 速度阻尼与最大速度限制velX = velX * DAMPING + accX;velX = constrain(velX, -MAX_SPEED, MAX_SPEED);https://file1.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.pngDAMPING = 0.97 使得速度每帧衰减3%,没有加速度时小球会逐渐停下。
MAX_SPEED = 5.0 限制单帧最大移动5像素,避免速度过快一次穿透多个格子。
3.6 方向取反的用意为了让操作更符合直觉:向右倾斜开发板,小球应该向左滚动(模拟重力作用)。因此代码中 accX = -rawX * SENSITIVITY,而Y轴未取反是因为屏幕坐标系Y轴向下为正,倾斜向前(Y负方向)让小球向下滚动,符合直觉。
四、操作过程及数据展示4.1 操作步骤烧录程序:将零知派ESP32通过USB连接电脑,在Arduino IDE中选择正确的开发板和端口,点击上传。
放置水平:将开发板平放在桌面上,此时小球应静止在起点位置。
倾斜控制:
向前(远离你)倾斜 → 小球向下(屏幕下方)滚动
向后(靠近你)倾斜 → 小球向上滚动
向左倾斜 → 小球向右滚动
向右倾斜 → 小球向左滚动
目标:在不触碰灰色墙壁的情况下,将红色小球滚到绿色终点区域。抵达后屏幕显示“YOU WIN!”并停止。
重置游戏:按开发板上的RST按钮重新开始。
4.2 数据展示(调试信息)在 setup() 中开启 Serial.begin(115200),上传后打开串口监视器可以看到加速度计原始值(需取消注释相应 Serial.print)。示例输出(平放时):
rawX = 85, rawY = 55https://file1.elecfans.com/web2/M00/03/FB/poYBAGDYdXCAWkKMAAAAK8RNs4s030.png若倾斜30度左右,rawX可能变化到300~500。通过调整 SENSITIVITY 可以改变灵敏度,数值越大小球对倾斜越敏感。
4.3 演示视频
五、ADXL362 加速度计技术原理5.1 工作原理 ADXL362是一款三轴MEMS加速度计,它内部有一个微小的质量块,当发生加速度时,质量块会位移,导致电容变化,通过电路转换为数字量输出。与传统加速度计不同,ADXL362具有超低功耗(测量模式下仅1.8μA),非常适合电池供电的可穿戴或手持设备。它通过SPI接口通信,输出16位二进制补码值,量程可配置为±2g/±4g/±8g,本项目使用默认±2g。
5.2 工作模式配置代码中通过以下步骤配置ADXL362:
accel.begin(CS_PIN):初始化SPI,设置片选引脚。
accel.beginMeasure():将芯片从待机模式切换到测量模式,开始连续采集数据。
读取数据时调用 accel.readXYZTData(rawX, rawY, rawZ, rawTemp),它会自动发送SPI命令读取四个寄存器(X、Y、Z、温度)。
常用寄存器配置:
滤波器控制寄存器(0x2C):默认配置即可,输出数据速率100Hz。
活动/非活动阈值寄存器:本项目未使用,可忽略。
如果你想改变量程,可以调用 accel.setRange(ADXL362_RANGE_4G) 等。
六、常见问题指引Q1: 编译报错 “TFT_eSPI.h: No such file or directory”解决:需要先安装TFT_eSPI库。在Arduino IDE中打开“项目” → “加载库” → “管理库”,搜索“TFT_eSPI”并安装。然后需要配置 User_Setup.h 文件,根据你屏幕的实际引脚修改(例如设置 TFT_CS、TFT_DC、TFT_RST 等)。
Q2: 小球完全不动或乱动检查ADXL362的接线,尤其CS引脚是否正确。
打开串口监视器,查看 rawX, rawY 数值是否随倾斜变化。若无变化,检查SPI接线或尝试更换ADXL362库。
调整 CALIB_X_OFFSET 和 CALIB_Y_OFFSET,使静止时输出接近0。
Q3: 小球穿墙或卡在墙壁里减小 MAX_SPEED 值(例如改为3.0),使每帧移动距离更小。
检查碰撞检测中 BALL_RADIUS 是否过大(应小于半格10像素)。
确保分轴移动逻辑正确,不要同时移动XY。
Q4: 屏幕显示残影/拖尾检查 restoreBallBackground() 是否被正确调用。可以在移动条件中加入串口打印,确认每次位置改变时确实执行了擦除。
确认 drawCell() 中路径填充的是 TFT_BLACK,终点填充绿色,墙壁填充深灰色。
如果仍出现问题,可以尝试在 restoreBallBackground() 中扩大擦除范围(例如边界多扩展一个像素)。
Q5: 游戏胜利后不显示“YOU WIN!”检查终点坐标是否正确被自动找到。可在 setup() 结束后串口打印 goalX, goalY。
确认胜利判断条件 cellX == goalX && cellY == goalY 中,cellX 和 cellY 是通过整数除法获得,由于小球半径存在,可能圆心落在终点格子内但边缘超出?一般不会,因为终点格子也是20×20,圆心只要在格子内即可。
可尝试将胜利条件改为:if (maze == 0 && cellX == goalX && cellY == goalY)。
Q6: 如何修改迷宫地图?直接修改 maze 数组中的数字:0为路,1为墙。注意保持起点(1,1)和终点(自动找最后一个0)是通路。也可以手动指定终点坐标。
Q7: 方向感相反(倾斜左边小球向左)怎么办?如果希望倾斜方向与小球运动方向相同,去掉 accX 前面的负号:accX = rawX * SENSITIVITY。但通常推荐相反方向,因为模拟真实重力的感觉。
页:
[1]