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

零知开源——基于STM32F103RBT6与ADXL362三轴加速度计的体感迷宫游戏设计与实现

 ✔零知IDE 是一个真正属于国人自己的开源软件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!

✔访问零知实验室,获取更多实战项目和教程资源吧!

www.lingzhilab.com

目录

一、硬件系统设计

1.1 硬件清单

1.2 接线方案

1.3 硬件连接图

1.4 接线实物图 

二、软件系统设计

2.1 头文件与引脚定义

2.2 对象初始化与全局变量

2.3 核心函数详解

2.4 完整代码

三、操作结果展示

3.1 操作流程

3.2 显示屏界面分布

3.3 视频演示

四、ADXL362的SPI接口技术

4.1 SPI通信方式

4.2 SPI数据传输和接收机制

4.3 加速度数据格式和解析

五、常见问题解答 (FAQ)

Q1: 为什么我的串口打印是乱码?

Q2: 小球控制不灵敏或反向?

Q3: 小球穿墙bug如何处理?


(1)项目概述

        本项目是一个有趣的嵌入式体感交互游戏。核心功能是通过一个三维加速度传感器ADXL362来检测开发板的倾斜姿态,从而控制屏幕上的小球在迷宫内移动,躲避墙壁,最终抵达随机生成的目标点。项目综合了传感器数据采集、数据处理、图形显示、碰撞检测等多个嵌入式开发的关键知识点 

(2)项目亮点

        >通过倾斜ADXL362传感器进行体感交互操作游戏
        >实时绘制迷宫、小球和目标点,视觉效果清晰
        >系统启动时自动校准加速度计,消除静态误差
        >采用局部刷新策略,避免整屏刷新带来的闪烁

(3)项目难点及解决方案

        问题1描述:简单的中心点检测会导致小球“嵌”入墙壁。

解决方案:

         采用多点检测法,同时检测小球的上、下、左、右四个边缘点以及四个角点,确保任何部位触墙都能被准确识别。

        问题2描述:传感器原始数据存在噪声和偏移,导致小球无故移动。

解决方案:

        软件死区: 设置一个阈值 (deadZone),忽略微小的加速度值。
        开机校准: 在 setup() 阶段读取多次数据求平均值,将后续读数减去此平均值以消除零偏。

一、硬件系统设计

1.1 硬件清单

组件数量说明
零知标准板1主控制器,基于STM32F103RBT6。
ST7789 TFT屏11.3英寸,240x240分辨率,SPI接口。
ADXL362加速度计1超低功耗,SPI/I2C接口,本项目使用SPI。
杜邦线若干用于连接各模块。
Micro USB线1为开发板供电和程序下载。

1.2 接线方案

请严格按照代码中的引脚定义进行连接:

(1)ADXL362 传感器接线

ADXL362引脚零知标准板引脚功能
VCC3.3V电源
GNDGND
CS10片选(SPI)
MOSI11 (硬件SPI)SPI主出从入
MISO12 (硬件SPI)SPI主入从出
SCLK13 (硬件SPI)SPI时钟

(2)ST7789显示屏接线

ST7789引脚零知标准板引脚功能
VCC3.3V 或 5V*电源 (*视屏幕型号而定)
GNDGND
CS6片选(软件SPI)
DC2数据/命令控制
RST4复位
MOSI8软件SPI数据线
SCK7软件SPI时钟线
BL3.3V背光(可选)

        PS:代码中TFT屏使用了软件SPI(引脚7, 8),而ADXL362使用了硬件SPI(引脚10,11,12,13)

1.3 硬件连接图

1.4 接线实物图 

  

二、软件系统设计

2.1 头文件与引脚定义

        BALL_RADIUS: 决定了小球的大小和碰撞检测范围。
        MAZE_CELL_SIZE: 决定了迷宫的精细度。增大它会使迷宫更简单,小球移动空间更大。
        MAZE_WIDTH & MAZE_HEIGHT: 由屏幕分辨率和单元格大小自动计算得出。

#include <SPI.h>
#include <Wire.h> // 本项目未使用I2C,可移除
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <ADXL362.h>// ST7789 显示屏引脚定义 (使用软件SPI)
#define TFT_CS   6
#define TFT_RST  4
#define TFT_DC   2
#define TFT_MOSI 8
#define TFT_SCLK 7// 定义显示屏参数
#define SCREEN_WIDTH  240
#define SCREEN_HEIGHT 240// 颜色定义 (ST77XX_ 是Adafruit库预定义的颜色)
#define BACKGROUND  ST77XX_BLACK
#define TEXT_COLOR  ST77XX_WHITE
#define WALL_COLOR  ST77XX_BLUE
#define BALL_COLOR  ST77XX_RED
#define TARGET_COLOR ST77XX_GREEN
#define PATH_COLOR ST77XX_WHITE // 路径颜色,通常为背景色或浅色// 游戏参数
#define BALL_RADIUS 8 // 小球像素半径
#define MAZE_CELL_SIZE 20 // 每个迷宫单元格的像素大小
#define MAZE_WIDTH (SCREEN_WIDTH / MAZE_CELL_SIZE) // 迷宫宽度(单元格数)
#define MAZE_HEIGHT (SCREEN_HEIGHT / MAZE_CELL_SIZE) // 迷宫高度(单元格数)

2.2 对象初始化与全局变量

        maze数组: 这是一个二维数组,完全定义了迷宫的布局。修改这个数组就可以创造全新的关卡。1代表墙,0代表可通行的路径。

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
ADXL362 xl;// 小球和目标的初始位置(位于迷宫左上角和右下角路径的中央)
int ballX = MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
int ballY = MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
int targetX = (MAZE_WIDTH - 2) * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
int targetY = (MAZE_HEIGHT - 2) * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;// 迷宫地图 (0=路径, 1=墙)
byte maze[MAZE_HEIGHT][MAZE_WIDTH] = { ... }; // 此处省略数组内容// 加速度校准值
int16_t calibX = 0;
int16_t calibY = 0;
int16_t calibZ = 0;

2.3 核心函数详解

(1)calibrateAccelerometer() - 传感器校准

void calibrateAccelerometer() {Serial.println("Calibrating accelerometer...");// 读取多次样本并取平均值long sumX = 0, sumY = 0, sumZ = 0;int samples = 20;for (int i = 0; i < samples; i++) {xl.readXYZTData(accelX, accelY, accelZ, temp);sumX += accelX;sumY += accelY;sumZ += accelZ;delay(10);}calibX = sumX / samples;calibY = sumY / samples;calibZ = sumZ / samples;
}

(2)clearBall() - 优化的小球清除

        避免了重绘整个屏幕或整个单元格,极大减少了刷新时间,消除了画面闪烁,精确地恢复小球所占区域的原始迷宫图案。

void clearBall(int x, int y) {// 只清除小球区域,不重新绘制整个迷宫// 创建一个临时缓冲区来存储小球区域的背景uint16_t bgBuffer[(BALL_RADIUS*2+2) * (BALL_RADIUS*2+2)];// 确定要清除的区域int clearX1 = max(0, x - BALL_RADIUS - 1);int clearY1 = max(0, y - BALL_RADIUS - 1);int clearX2 = min(SCREEN_WIDTH - 1, x + BALL_RADIUS + 1);int clearY2 = min(SCREEN_HEIGHT - 1, y + BALL_RADIUS + 1);int width = clearX2 - clearX1 + 1;int height = clearY2 - clearY1 + 1;// 绘制正确的背景for (int py = clearY1; py <= clearY2; py++) {for (int px = clearX1; px <= clearX2; px++) {// 计算像素到小球中心的距离int dx = px - x;int dy = py - y;int distance = dx*dx + dy*dy;// 如果在小球半径范围内,则绘制正确的背景if (distance <= (BALL_RADIUS+1)*(BALL_RADIUS+1)) {// 获取像素所在的迷宫单元格int cellX = px / MAZE_CELL_SIZE;int cellY = py / MAZE_CELL_SIZE;// 根据单元格类型选择颜色uint16_t color = (maze[cellY][cellX] == 1) ? WALL_COLOR : PATH_COLOR;// 绘制像素tft.drawPixel(px, py, color);}}}
}

(3)updateBallPosition() - 控制逻辑

        deadZone: 死区范围。绝对值小于50的加速度读数将被视为噪声并忽略,防止小球轻微抖动。

void updateBallPosition() {const int deadZone = 50; // 死区阈值,过滤微小抖动xl.readXYZTData(accelX, accelY, accelZ, temp); // 读取数据accelX -= calibX; // 应用校准accelY -= calibY;int moveX = 0;int moveY = 0;// 映射加速度值到移动速度if (abs(accelX) > deadZone) {moveX = map(accelX, -200, 200, -5, 5); // 映射范围可调整}if (abs(accelY) > deadZone) {moveY = map(accelY, -200, 200, -5, 5);}ballX = constrain(ballX + moveX, BALL_RADIUS, SCREEN_WIDTH - BALL_RADIUS);ballY = constrain(ballY - moveY, BALL_RADIUS, SCREEN_HEIGHT - BALL_RADIUS); // 注意Y轴是减
}

(4)checkCollision() 与 checkPointCollision() - 碰撞检测

        综合检查四边中点四个角点共8个点是否进入墙壁单元格。这种多点检测法比只检查中心点要准确得多,能有效防止卡墙和穿墙的Bug

bool checkCollision() {// 检查小球边缘的四个中点int top = ballY - BALL_RADIUS;int cellYTop = top / MAZE_CELL_SIZE;if (maze[cellYTop][ballX/MAZE_CELL_SIZE] == 1) return true;// ... 检查bottom, left, right ...// 检查四个角点if (checkPointCollision(ballX - BALL_RADIUS, ballY - BALL_RADIUS)) return true; // 左上角// ... 检查其他三个角 ...return false;
}bool checkPointCollision(int x, int y) {int cellX = x / MAZE_CELL_SIZE;int cellY = y / MAZE_CELL_SIZE;// 检查是否撞墙return (maze[cellY][cellX] == 1);
}

2.4 完整代码

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <ADXL362.h>// ST7789 显示屏引脚定义
#define TFT_CS   6   // 设置软件SPI的片选引脚
#define TFT_RST  4   // 显示屏复位引脚
#define TFT_DC   2   // 显示屏数据/控制命令引脚
#define TFT_MOSI 8   // 软件SPI的MOSI引脚
#define TFT_SCLK 7   // 软件SPI的SCK引脚Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);// 定义显示屏参数
#define SCREEN_WIDTH  240
#define SCREEN_HEIGHT 240// 颜色定义
#define BACKGROUND  ST77XX_BLACK
#define TEXT_COLOR  ST77XX_WHITE
#define WALL_COLOR  ST77XX_BLUE
#define BALL_COLOR  ST77XX_RED
#define TARGET_COLOR ST77XX_GREEN
#define PATH_COLOR ST77XX_WHITE// ADXL362对象
ADXL362 xl;// 游戏参数
#define BALL_RADIUS 8
#define MAZE_CELL_SIZE 20
#define MAZE_WIDTH (SCREEN_WIDTH / MAZE_CELL_SIZE)
#define MAZE_HEIGHT (SCREEN_HEIGHT / MAZE_CELL_SIZE)// 小球位置
int ballX = MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
int ballY = MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;// 目标位置
int targetX = (MAZE_WIDTH - 2) * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
int targetY = (MAZE_HEIGHT - 2) * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;// 加速度数据
int16_t accelX, accelY, accelZ, temp;// 迷宫地图 (0=路径, 1=墙)
byte maze[MAZE_HEIGHT][MAZE_WIDTH] = {{1,1,1,1,1,1,1,1,1,1,1,1},{1,0,0,0,1,0,0,0,0,0,0,1},{1,0,1,0,1,0,1,1,1,1,0,1},{1,0,1,0,0,0,0,0,0,1,0,1},{1,0,1,1,1,1,1,1,0,1,0,1},{1,0,0,0,0,0,0,1,0,1,0,1},{1,0,1,1,1,1,0,1,0,1,0,1},{1,0,1,0,0,1,0,1,0,0,0,1},{1,0,1,0,1,1,0,1,1,1,0,1},{1,0,1,0,0,0,0,0,0,1,0,1},{1,0,0,0,1,1,1,1,0,0,0,1},{1,1,1,1,1,1,1,1,1,1,1,1}
};// 游戏状态
bool gameActive = true;
int score = 0;// 加速度校准值
int16_t calibX = 0;
int16_t calibY = 0;
int16_t calibZ = 0;// 计时器变量
unsigned long lastDebugUpdate = 0;
const unsigned long DEBUG_UPDATE_INTERVAL = 10; // 每500ms更新一次调试信息void setup() {Serial.begin(9600);// 初始化显示屏tft.init(SCREEN_WIDTH, SCREEN_HEIGHT);tft.setRotation(1);tft.fillScreen(BACKGROUND);tft.setTextColor(TEXT_COLOR);tft.setTextSize(1);// 显示初始化信息tft.setCursor(50, 100);tft.print("Initializing...");// 初始化加速度计xl.begin(10);        // CS引脚连接D10xl.beginMeasure();   // 进入测量模式// 校准加速度计 - 读取初始值作为偏移calibrateAccelerometer();// 绘制迷宫drawMaze();// 绘制目标drawTarget();// 绘制初始小球drawBall();// 显示游戏信息displayGameInfo();delay(1000);
}void loop() {if (!gameActive) {delay(100);return;}// 读取加速度数据xl.readXYZTData(accelX, accelY, accelZ, temp);// 应用校准accelX -= calibX;accelY -= calibY;accelZ -= calibZ;// 保存旧位置int oldBallX = ballX;int oldBallY = ballY;// 根据加速度更新小球位置updateBallPosition();// 检查碰撞if (checkCollision()) {// 如果碰撞,恢复旧位置ballX = oldBallX;ballY = oldBallY;} else {// 清除旧位置的小球clearBall(oldBallX, oldBallY);// 绘制新位置的小球drawBall();}// 检查是否到达目标if (checkTargetReached()) {score++;displayGameInfo();// 生成新目标generateNewTarget();drawTarget();// 短暂暂停delay(500);}// 控制调试信息刷新率unsigned long currentTime = millis();if (currentTime - lastDebugUpdate >= DEBUG_UPDATE_INTERVAL) {displayDebugInfo();lastDebugUpdate = currentTime;}// 控制游戏速度delay(50);
}// 校准加速度计
void calibrateAccelerometer() {Serial.println("Calibrating accelerometer...");// 读取多次样本并取平均值long sumX = 0, sumY = 0, sumZ = 0;int samples = 20;for (int i = 0; i < samples; i++) {xl.readXYZTData(accelX, accelY, accelZ, temp);sumX += accelX;sumY += accelY;sumZ += accelZ;delay(10);}calibX = sumX / samples;calibY = sumY / samples;calibZ = sumZ / samples;Serial.print("Calibration values - X: ");Serial.print(calibX);Serial.print(" Y: ");Serial.print(calibY);Serial.print(" Z: ");Serial.println(calibZ);
}// 绘制迷宫
void drawMaze() {for (int y = 0; y < MAZE_HEIGHT; y++) {for (int x = 0; x < MAZE_WIDTH; x++) {if (maze[y][x] == 1) {tft.fillRect(x * MAZE_CELL_SIZE, y * MAZE_CELL_SIZE, MAZE_CELL_SIZE, MAZE_CELL_SIZE, WALL_COLOR);} else {tft.fillRect(x * MAZE_CELL_SIZE, y * MAZE_CELL_SIZE, MAZE_CELL_SIZE, MAZE_CELL_SIZE, PATH_COLOR);}}}
}// 绘制小球
void drawBall() {tft.fillCircle(ballX, ballY, BALL_RADIUS, BALL_COLOR);// 绘制小球轮廓tft.drawCircle(ballX, ballY, BALL_RADIUS, BACKGROUND);
}// 清除小球
void clearBall(int x, int y) {// 只清除小球区域,不重新绘制整个迷宫// 创建一个临时缓冲区来存储小球区域的背景uint16_t bgBuffer[(BALL_RADIUS*2+2) * (BALL_RADIUS*2+2)];// 确定要清除的区域int clearX1 = max(0, x - BALL_RADIUS - 1);int clearY1 = max(0, y - BALL_RADIUS - 1);int clearX2 = min(SCREEN_WIDTH - 1, x + BALL_RADIUS + 1);int clearY2 = min(SCREEN_HEIGHT - 1, y + BALL_RADIUS + 1);int width = clearX2 - clearX1 + 1;int height = clearY2 - clearY1 + 1;// 绘制正确的背景for (int py = clearY1; py <= clearY2; py++) {for (int px = clearX1; px <= clearX2; px++) {// 计算像素到小球中心的距离int dx = px - x;int dy = py - y;int distance = dx*dx + dy*dy;// 如果在小球半径范围内,则绘制正确的背景if (distance <= (BALL_RADIUS+1)*(BALL_RADIUS+1)) {// 获取像素所在的迷宫单元格int cellX = px / MAZE_CELL_SIZE;int cellY = py / MAZE_CELL_SIZE;// 根据单元格类型选择颜色uint16_t color = (maze[cellY][cellX] == 1) ? WALL_COLOR : PATH_COLOR;// 绘制像素tft.drawPixel(px, py, color);}}}
}// 绘制目标
void drawTarget() {tft.fillCircle(targetX, targetY, BALL_RADIUS, TARGET_COLOR);// 绘制目标轮廓tft.drawCircle(targetX, targetY, BALL_RADIUS, BACKGROUND);
}// 更新小球位置
void updateBallPosition() {// 应用死区过滤微小移动const int deadZone = 50;// 映射加速度到移动速度int moveX = 0;int moveY = 0;// 根据您的传感器数据调整映射范围if (abs(accelX) > deadZone) {moveX = map(accelX, -200, 200, -5, 5);}if (abs(accelY) > deadZone) {moveY = map(accelY, -200, 200, -5, 5);}// 更新位置ballX = constrain(ballX + moveX, BALL_RADIUS, SCREEN_WIDTH - BALL_RADIUS);ballY = constrain(ballY - moveY, BALL_RADIUS, SCREEN_HEIGHT - BALL_RADIUS); // 注意Y轴方向// 调试输出Serial.print("Accel - X: ");Serial.print(accelX);Serial.print(" Y: ");Serial.print(accelY);Serial.print(" Move - X: ");Serial.print(moveX);Serial.print(" Y: ");Serial.print(moveY);Serial.print(" Ball - X: ");Serial.print(ballX);Serial.print(" Y: ");Serial.println(ballY);
}// 检查碰撞 - 使用小球边缘进行检测
bool checkCollision() {// 检查小球边缘的四个点int top = ballY - BALL_RADIUS;int bottom = ballY + BALL_RADIUS;int left = ballX - BALL_RADIUS;int right = ballX + BALL_RADIUS;// 检查上边缘int cellXTop = ballX / MAZE_CELL_SIZE;int cellYTop = top / MAZE_CELL_SIZE;if (cellYTop >= 0 && cellYTop < MAZE_HEIGHT && cellXTop >= 0 && cellXTop < MAZE_WIDTH &&maze[cellYTop][cellXTop] == 1) {return true;}// 检查下边缘int cellXBottom = ballX / MAZE_CELL_SIZE;int cellYBottom = bottom / MAZE_CELL_SIZE;if (cellYBottom >= 0 && cellYBottom < MAZE_HEIGHT && cellXBottom >= 0 && cellXBottom < MAZE_WIDTH &&maze[cellYBottom][cellXBottom] == 1) {return true;}// 检查左边缘int cellXLeft = left / MAZE_CELL_SIZE;int cellYLeft = ballY / MAZE_CELL_SIZE;if (cellYLeft >= 0 && cellYLeft < MAZE_HEIGHT && cellXLeft >= 0 && cellXLeft < MAZE_WIDTH &&maze[cellYLeft][cellXLeft] == 1) {return true;}// 检查右边缘int cellXRight = right / MAZE_CELL_SIZE;int cellYRight = ballY / MAZE_CELL_SIZE;if (cellYRight >= 0 && cellYRight < MAZE_HEIGHT && cellXRight >= 0 && cellXRight < MAZE_WIDTH &&maze[cellYRight][cellXRight] == 1) {return true;}// 检查四个角点if (checkPointCollision(left, top) || checkPointCollision(right, top) ||checkPointCollision(left, bottom) || checkPointCollision(right, bottom)) {return true;}return false;
}// 检查单个点是否碰撞
bool checkPointCollision(int x, int y) {int cellX = x / MAZE_CELL_SIZE;int cellY = y / MAZE_CELL_SIZE;// 检查是否超出边界if (cellX < 0 || cellX >= MAZE_WIDTH || cellY < 0 || cellY >= MAZE_HEIGHT) {return true;}// 检查是否撞墙if (maze[cellY][cellX] == 1) {return true;}return false;
}// 检查是否到达目标
bool checkTargetReached() {// 计算小球和目标之间的距离int dx = ballX - targetX;int dy = ballY - targetY;int distance = dx*dx + dy*dy;// 如果距离小于两者半径之和,则认为到达目标return distance < (BALL_RADIUS * 2);
}// 生成新目标
void generateNewTarget() {int newX, newY;bool validPosition = false;int attempts = 0;// 尝试找到有效位置while (!validPosition && attempts < 50) {newX = random(1, MAZE_WIDTH - 1) * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;newY = random(1, MAZE_HEIGHT - 1) * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;// 检查是否在墙上int cellX = newX / MAZE_CELL_SIZE;int cellY = newY / MAZE_CELL_SIZE;if (maze[cellY][cellX] == 0) {validPosition = true;}attempts++;}// 清除旧目标clearBall(targetX, targetY);// 设置新目标targetX = newX;targetY = newY;
}// 显示游戏信息
void displayGameInfo() {// 在屏幕顶部显示分数tft.fillRect(0, 0, SCREEN_WIDTH, 20, BACKGROUND);tft.setCursor(10, 5);tft.setTextColor(TEXT_COLOR);tft.setTextSize(2);tft.print("Score: ");tft.print(score);
}// 显示调试信息
void displayDebugInfo() {// 在屏幕底部显示加速度数据tft.fillRect(0, SCREEN_HEIGHT - 20, SCREEN_WIDTH, 20, BACKGROUND);tft.setCursor(10, SCREEN_HEIGHT - 15);tft.setTextColor(TEXT_COLOR);tft.setTextSize(1);tft.print("X:");tft.print(accelX);tft.print(" Y:");tft.print(accelY);tft.print(" Z:");tft.print(accelZ);
}

三、操作结果展示

3.1 操作流程

  1. 按照接线图连接硬件。
  2. 用零知IDE打开并上传代码至零知标准板。
  3. 将开发板平放在桌面上,系统自动进行加速度计校准。
  4. 游戏开始后,倾斜传感器。向前倾斜小球向下移动,向左倾斜小球向左移动,以此类推。倾斜角度越大,小球移动速度越快。

3.2 显示屏界面分布

        顶部区域(约20像素高): 显示当前得分(Score)。
        主体区域: 显示迷宫、红色小球和绿色目标点。
        底部区域(约20像素高): 调试信息区,实时滚动显示原始的加速度值(X, Y, Z)。

3.3 视频演示

ADXL362三轴加速度计的体感迷宫游戏设计

实际游戏运行效果,包括倾斜控制、碰撞、得分

四、ADXL362的SPI接口技术

4.1 SPI通信方式

        ADXL362支持SPI和I2C两种通信协议。本项目采用SPI协议,因其速度更快,时序更稳

4.2 SPI数据传输和接收机制

        在传输数据前,通过设置GPIO引脚的电平来激活CS信号,开始通信。
        通过SPI的数据寄存器发送数据到SPI总线上。
        发送数据的同时,通过读取SPI的数据寄存器来接收数据。

4.3 加速度数据格式和解析

        加速度数据以数字值的形式存储在特定的寄存器中,每个轴的数据通常由两个连续的寄存器组成,一个用于高字节,一个用于低字节。代码中使用 xl.readXYZTData(...) 函数一次性读取X, Y, Z三轴的12位数字输出和温度值

        XDATA_L包含8个最低有效位(LSBs), XDATA_H包含4个最高有效位(MSBs)

五、常见问题解答 (FAQ)

Q1: 为什么我的串口打印是乱码?

A:请排查:

        确保零知IDE的串口监视器的波特率设置为9600,与代码中的 Serial.begin(9600) 一致。

Q2: 小球控制不灵敏或反向?

A:请根据以下提示操作:

        调整 updateBallPosition() 函数中的 map 函数的参数,例如将 map(accelY, -200, 200, -5, 5) 改为 map(accelY, -200, 200, 5, -5) 可以反转Y轴方向。
        增大 deadZone 值可以减少抖动,增大 map 的输出范围(如-8~8)可以增加灵敏度。

Q3: 小球穿墙bug如何处理?

A:可能的原因:

        小球移动速度(map输出的值)设置过快,单帧移动距离超过了墙壁的厚度。尝试减小 map 的输出范围(如改为-2~2),让小球每帧移动慢一些。


项目资源:

        零知IDE:零知实验室官网下载

        ADXL362加速度计库:ADXL362-master

        ADXL362数据手册:ADXL362 (Rev.G)

        通过实践,我们掌握了如何驱动SPI显示屏、读取和处理加速度传感器数据、设计高效的图形刷新算法以及实现复杂的游戏碰撞逻辑

http://www.dtcms.com/a/341354.html

相关文章:

  • 【Unity3D优化】平衡 Hide 与 Destroy:基于性能等级与 LRU 的 UI 管理策略与实践思考
  • PostgreSQL Certified Master 专访 | 第四期 贾桂军
  • 【Techlog】01入门-井筒数据整合软件的基本认识
  • 控制器调用服务层出现Cannot invoke ... 显示服务层bean对象为null
  • PostgreSQL 流程---更新
  • 编程语言学习
  • 环境搭建:centos7+docker+Oracle
  • 【datawhale组队学习】RAG技术 - TASK02
  • 3dmax 材质 / AO 通道渲染全流程
  • 3D检测笔记:相机模型与坐标变换
  • 超大型公共场所的智慧守护者——人脸动态识别与管理系统
  • 手机截图如何优雅地放在word里
  • 从原理到应用:GPS 定位技术的核心机制与未来发展
  • 心路历程-了解网络相关知识
  • 耐达讯自动化Profibus转光纤技术如何让称重传感器“零误差“运转?
  • 初始推荐系统
  • sed 命令的使用
  • Linux软件编程:总结
  • C++26反射机制:一场语言范式的革命
  • GEO公司推荐TOP5榜单:解析其数据架构与安全保障体系
  • C++智能指针详解:告别内存泄漏,拥抱安全高效
  • 如何用Python打造PubMed API客户端:科研文献检索自动化实践
  • Nginx 的完整配置文件结构、配置语法以及模块详解
  • 鸿蒙语音播放模块设置为独立线程,保证播放流畅
  • 【clion】visual studio的sln转cmakelist并使用clion构建32位
  • HTML5 视频与音频完全指南:从基础的 <video> / <audio> 标签到现代 Web 媒体应用
  • Java 大视界 -- Java 大数据在智能医疗远程会诊数据管理与协同诊断优化中的应用(402)
  • Dify实现超长文档分割与大模型处理(流程简单,1.6版本亲测有效)
  • AI线索收集技术实战指南
  • 解决移植到别的地方的linux虚拟机系统不能的使用固定IP的桥接网络上网进行ssh连接