这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 有奖活动 » 【分享开发笔记,赚取电动螺丝刀】摇杆控制WS2812B/LED的贪吃蛇游戏

共1条 1/1 1 跳转至

【分享开发笔记,赚取电动螺丝刀】摇杆控制WS2812B/LED的贪吃蛇游戏

菜鸟
2025-12-23 21:36:41     打赏

一、硬件介绍

1、产品特点

Beetle RP2350【RP2350A_QFN60】是一款基于RP2350微控制器的高性能迷你开发板,双核双架构设计(Arm Cortex-M33或Hazard3 RISC-V内核);

采用Raspberry Pi RP2350高性能微控制器,支持Arm Cortex-M33Hazard3 RISC-V内核选择;

主频高达150MHz,配备520KB RAM2MB Flash,可高效处理大量数据,满足高性能计算需求;


image-20250518182257032.png


2、功能引脚示意图 / 原理图

板载LED灯

IO25引脚控制

用户按键

使用QSPI_SS引脚控制该按钮



image-20250518211204186.png


image-20250518211204186

image.png


主要原理图

image-20250518223622079.png

image-20250518223622079

3、外部硬件

1、摇杆模块

该模块为一个双向的10K电阻器,随着摇杆方向变化,阻值也会随着变化,从而改变输出的值;

本模块可用 5V / 3.3V 供电,初始状态下X,Y读出电压为供电电压的1/2左右( 1.65V / 2.5V );

当随着X、Y方向移动,读出电压值减小(0V) / 增大(VCC); 其中X、Y方向输出的数据为模拟量,而Z轴输出的为数字量 (0 / 1);

image-20250622104658742.png


模块引脚介绍:

image.png



2、8*8 LED模块【WS2812B】

WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源其外型与一个5050LED灯珠相同, 每个元件即为一个像素点。

像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路, 还包含有高精度的内部振荡器和可编程定电流控制部分, 有效保证了像素点光的颜色高度一致。

数据协议采用单线归零码的通讯方式, 像素点在上电复位以后, DIN端接受从控制器传输过来的数据。

首先送过来的24bit数据被第一个像素点提取后, 送到像素点内部的数据锁存器, 剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的像素点, 每经过一个像素点的传输, 信号减少24bit。

image-20250626181934299.png

image-20250626181934299

主要特点:

● IC控制电路与LED点光源共用一个电源。

● 控制电路与RGB芯片集成在一个5050封装的元器件中, 构成一个完整的外控像素点。

● 内置信号整形电路, 任何一个像素点收到信号后经过波形整形再输出, 保证线路波形畸变不会累加。

● 内置上电复位和掉电复位电路。

● 每个像素点的三基色颜色可实现256级亮度显示, 完成16777216种颜色的全真色彩显示。

● 端口扫描频率2KHz/s。

● 串行级联接口, 能通过一根信号线完成数据的接收与解码。

● 任意两点传输距离在不超过5米时无需增加任何电路。

● 当刷新速率30帧/秒时, 级联数不小于1024点。

● 数据发送速度可达800Kbps。


3、喇叭模块【GSPK2307P-8R1W】

image-20250626165019254.png

image-20250626165019254

image.png


二、硬件连接

模块与开发板引脚之间的连接方式如下:


image.png


实物效果搭建如下:

image-20250626232542192.png

image-20250626232542192

三、功能实现

【摇杆控制LED贪吃蛇游戏】

实现效果:通过摇杆模块控制8*8 LED模块实现贪吃蛇游戏;

通过控制摇杆上、下、左、右分别控制LED移动方向;

按下中间按钮时,实现LED清屏复位(既游戏重启);

游玩过程中,游戏运行速度会随着蛇身长度逐渐加速; 当蛇碰到墙壁或自身时,喇叭模块发生声音,提示游戏结束(此时需按下按钮,重新开启);

开发板通过ADC功能获取摇杆模块数据;

通过PWM功能控制喇叭模块、LED模块;



四、相关代码

主要相关代码

#include <FastLED.h>
#define Sound_Pin D18

#define VRx A0
#define VRy A1
#define SW  D19
#define X_POS 498
#define Y_POS 505

#define LED_PIN D8
#define LED_COLS 8  // 列
#define LED_ROWS 8  // 行
#define NUM_LEDS (LED_COLS * LED_ROWS)  // 8*8
#define LED_TYPE WS2812B   
#define COLOR_ORDER GRB

#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3

CRGB HeadColor(255,0,0);  //蛇头颜色
CRGB BodyColor(0,0,255);  //蛇身颜色
CRGB FoodColor(0,255,0);  //食物颜色
CRGB leds[NUM_LEDS];

unsigned long lastUpdate = 0;
int gameSpeed = 500;  // 游戏运行初始速度 ms

static byte lastDir = UP; //默认方向向上
bool buttonPressed = false;
unsigned long lastTime = 0;
const int Delay = 50;
bool collisionsState = false;

// 蛇结构体
struct {
  int x, y;           // 蛇头坐标
  int tail[65][2];   // 蛇身坐标数组 (x,y)
  int length = 3;     // 初始长度
  int dir = 0;       // 运动方向(0上,1下,2左,3右) 
} snake;

// 食物结构体
struct {
  int x, y;
  bool eaten = true;  //食物状态
} food;

//移动蛇身体
void moveSnake() {
  for(int i=snake.length-1; i>0; i--){
    snake.tail[i][0] = snake.tail[i-1][0];  //x
    snake.tail[i][1] = snake.tail[i-1][1];  //y
  }
  //更新蛇头位置
  snake.tail[0][0] = snake.x;
  snake.tail[0][1] = snake.y;
  // 移动蛇头
  switch(snake.dir){
    case UP: snake.y++; break; // 上
    case DOWN: snake.y--; break; // 下
    case LEFT: snake.x++; break; // 左
    case RIGHT: snake.x--; break; // 右
  }
}
//生成食物
void spawnFood() {
  if(food.eaten){
    bool valid = false;
    while(!valid){
      //生成食物位置
      valid = true;
      food.x = random(LED_COLS);
      food.y = random(LED_ROWS);
      // 检查是否与蛇重叠
      if(food.x == snake.x && food.y == snake.y) valid = false;
      for(int i=0; i<snake.length; i++){
        if(food.x == snake.tail[i][0] && food.y == snake.tail[i][1]){
          valid = false;
          break;
        }
      }
    }
    food.eaten = false;
  }
}
//蛇碰撞检测
void checkCollisions() {
  // 吃食物检测
  if(snake.x == food.x && snake.y == food.y){
    snake.length++;
    food.eaten = true;
    gameSpeed = max(100, gameSpeed-50); // 游戏加速
    spawnFood();
  }
  // 撞墙检测
  if(snake.x<0 || snake.x>=LED_COLS || snake.y<0 || snake.y>=LED_ROWS){
    playSound();
    collisionsState = true; 
  }
  // 撞自身检测
  for(int i=0; i<snake.length; i++){
    if(snake.x == snake.tail[i][0] && snake.y == snake.tail[i][1]){
      playSound();
    }
  }
}
//刷新显示
void updateDisplay() {
  if(!collisionsState){ //发生碰撞 暂停显示
  FastLED.clear();
  // 绘制食物
  leds[food.y * LED_COLS + food.x] = FoodColor;
  // 绘制蛇头
  leds[snake.y * LED_COLS + snake.x] = HeadColor;
  // 绘制蛇身
  for(int i=0; i<snake.length; i++){
    int index = snake.tail[i][1] * LED_COLS + snake.tail[i][0];
    leds[index] = BodyColor;
  }
  FastLED.show();
  }
}
//重新开始
void resetGame(){
  collisionsState = false; 
  gameSpeed = 500;
  snake.x = LED_COLS/2;
  snake.y = LED_ROWS/2;
  snake.length = 3;
  updateDisplay();
}

//摇杆按下
void buttonSet() {
  int buttonState = digitalRead(SW);
  if (buttonState == LOW && !buttonPressed && (millis() - lastTime) > Delay) {
    buttonPressed = true;
    lastTime = millis();
    resetGame();
  }else if (buttonState == HIGH && buttonPressed) {
    buttonPressed = false;
    lastTime = millis();
  }
}
//摇杆XY数据读取
void controllerRead() {
  byte newDir = lastDir;  
  int xVal = analogRead(VRx) - X_POS;
  int yVal = analogRead(VRy) - Y_POS;
  //方向保持上一次
  if(abs(xVal)< 400 && abs(yVal)<400) 
  {
    xVal = 0;
    yVal = 0;
    snake.dir = lastDir;
  }else{
    // 计算角度方向
  float angle = atan2(yVal, xVal) * 180/PI;
  if (angle >= -45 && angle < 45) {
    newDir = LEFT;      // 左
  } else if (angle >= 45 && angle < 135) {
    newDir = UP;         // 上
  } else if (angle >= 135 || angle < -135) {
    newDir = RIGHT;       // 右
  } else if (angle >= -135 && angle < -45) {
    newDir = DOWN;       // 下
  	}
  }
  // 防止直接反向180°移动
  if (!(snake.dir == RIGHT && newDir == LEFT) &&
      !(snake.dir == LEFT && newDir == RIGHT) &&
      !(snake.dir == UP && newDir == DOWN) &&
      !(snake.dir == DOWN && newDir == UP)) {
    snake.dir = newDir;   //更新方向
    lastDir = newDir;
  }
} 
//喇叭播放声音
void playSound(){
  tone(Sound_Pin, 3000, 1000);  //3Khz 1s
}

void setup() {
  //串口初始化
  Serial.begin(115200);
  while (!Serial);
  //摇杆初始化
  pinMode(VRx, INPUT);
  pinMode(VRy, INPUT);
  pinMode(SW, INPUT_PULLUP);
  // 初始化snake LED
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(15);
  snake.x = LED_COLS/2;
  snake.y = LED_ROWS/2;
  for(int i=0; i<snake.length; i++){
    snake.tail[i][0] = snake.x - i;
    snake.tail[i][1] = snake.y;
  }
    randomSeed(millis());  // 食物随机数种子
}

void loop() {
  //控制游戏速度
  if(millis() - lastUpdate > gameSpeed){
    lastUpdate = millis();
    //摇杆数据读取
    controllerRead();
    //摇杆按下
    buttonSet();
    //移动蛇位置
    moveSnake();
    //检测碰撞
    checkCollisions();
    //更新显示
    updateDisplay();
  }
}



五、实现效果

通过控制摇杆上、下、左、右分别控制LED移动方向;按下按钮时,实现LED清屏复位(既游戏重启);

游玩过程中,游戏运行速度会随着蛇身长度逐渐加速;

当蛇碰到墙壁或自身时,喇叭模块发生声音,提示游戏结束(此时需按下按钮,重新开启);

功能.gif



















关键词: WS2812B    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]