keer_zu 发表于 2025-5-21 12:15

很好的LED显示例子

代码:

// TETRIS
#include <FastLED.h>
#include <LEDMatrix.h>
#include <LEDSprites.h>
#include <LEDText.h>
#include <FontMatrise.h>
#include "BluetoothSerial.h"

#define LED_PIN      15
#define COLOR_ORDER    GRB
#define CHIPSET      WS2812B
#define MATRIX_WIDTH   30
#define MATRIX_HEIGHT15
#define MATRIX_TYPE    HORIZONTAL_MATRIX

// NOTE the '-' sign before the width, this is due to my leds matrix origin being on the right hand side
cLEDMatrix<MATRIX_WIDTH, -MATRIX_HEIGHT, MATRIX_TYPE> leds;

BluetoothSerial SerialBT;

#define TARGET_FRAME_TIME    15// Desired update rate, though if too many leds it will just run as fast as it can!
#define INITIAL_DROP_FRAMES20// Start of game block drop delay in frames

// Bluetooth input
enum btnInput {NONE, ROTATE, DOWN, LEFT, RIGHT};
btnInput currentInput = NONE;

const uint8_t TetrisIData[] =
{
// Frame 1
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(11110000),
// Frame 2
B8_3BIT(10000000),
B8_3BIT(10000000),
B8_3BIT(10000000),
B8_3BIT(10000000),
// Frame 3
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(11110000),
// Frame 4
B8_3BIT(10000000),
B8_3BIT(10000000),
B8_3BIT(10000000),
B8_3BIT(10000000)
};
const uint8_t TetrisIMask[] =
{
// Frame 1
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11110000),
// Frame 2
B8_1BIT(10000000),
B8_1BIT(10000000),
B8_1BIT(10000000),
B8_1BIT(10000000),
// Frame 3
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11110000),
// Frame 4
B8_1BIT(10000000),
B8_1BIT(10000000),
B8_1BIT(10000000),
B8_1BIT(10000000)
};
const uint8_t TetrisJData[] =
{
// Frame 1
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(22200000),
B8_3BIT(00200000),
// Frame 2
B8_3BIT(00000000),
B8_3BIT(02000000),
B8_3BIT(02000000),
B8_3BIT(22000000),
// Frame 3
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(20000000),
B8_3BIT(22200000),
// Frame 4
B8_3BIT(00000000),
B8_3BIT(22000000),
B8_3BIT(20000000),
B8_3BIT(20000000)
};
const uint8_t TetrisJMask[] =
{
// Frame 1
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11100000),
B8_1BIT(00100000),
// Frame 2
B8_1BIT(00000000),
B8_1BIT(01000000),
B8_1BIT(01000000),
B8_1BIT(11000000),
// Frame 3
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(10000000),
B8_1BIT(11100000),
// Frame 4
B8_1BIT(00000000),
B8_1BIT(11000000),
B8_1BIT(10000000),
B8_1BIT(10000000)
};
const uint8_t TetrisLData[] =
{
// Frame 1
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(33300000),
B8_3BIT(30000000),
// Frame 2
B8_3BIT(00000000),
B8_3BIT(33000000),
B8_3BIT(03000000),
B8_3BIT(03000000),
// Frame 3
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(00300000),
B8_3BIT(33300000),
// Frame 4
B8_3BIT(00000000),
B8_3BIT(30000000),
B8_3BIT(30000000),
B8_3BIT(33000000)
};
const uint8_t TetrisLMask[] =
{
// Frame 1
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11100000),
B8_1BIT(10000000),
// Frame 2
B8_1BIT(00000000),
B8_1BIT(11000000),
B8_1BIT(01000000),
B8_1BIT(01000000),
// Frame 3
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(00100000),
B8_1BIT(11100000),
// Frame 4
B8_1BIT(00000000),
B8_1BIT(10000000),
B8_1BIT(10000000),
B8_1BIT(11000000)
};
const uint8_t TetrisOData[] =
{
// Frame 1
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(44000000),
B8_3BIT(44000000),
// Frame 2
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(44000000),
B8_3BIT(44000000),
// Frame 3
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(44000000),
B8_3BIT(44000000),
// Frame 4
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(44000000),
B8_3BIT(44000000)
};
const uint8_t TetrisOMask[] =
{
// Frame 1
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11000000),
B8_1BIT(11000000),
// Frame 2
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11000000),
B8_1BIT(11000000),
// Frame 3
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11000000),
B8_1BIT(11000000),
// Frame 4
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11000000),
B8_1BIT(11000000)
};
const uint8_t TetrisSData[] =
{
// Frame 1
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(05500000),
B8_3BIT(55000000),
// Frame 2
B8_3BIT(00000000),
B8_3BIT(50000000),
B8_3BIT(55000000),
B8_3BIT(05000000),
// Frame 3
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(05500000),
B8_3BIT(55000000),
// Frame 4
B8_3BIT(00000000),
B8_3BIT(50000000),
B8_3BIT(55000000),
B8_3BIT(05000000)
};
const uint8_t TetrisSMask[] =
{
// Frame 1
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(01100000),
B8_1BIT(11000000),
// Frame 2
B8_1BIT(00000000),
B8_1BIT(10000000),
B8_1BIT(11000000),
B8_1BIT(01000000),
// Frame 3
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(01100000),
B8_1BIT(11000000),
// Frame 4
B8_1BIT(00000000),
B8_1BIT(10000000),
B8_1BIT(11000000),
B8_1BIT(01000000)
};
const uint8_t TetrisTData[] =
{
// Frame 1
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(66600000),
B8_3BIT(06000000),
// Frame 2
B8_3BIT(00000000),
B8_3BIT(06000000),
B8_3BIT(66000000),
B8_3BIT(06000000),
// Frame 3
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(06000000),
B8_3BIT(66600000),
// Frame 4
B8_3BIT(00000000),
B8_3BIT(60000000),
B8_3BIT(66000000),
B8_3BIT(60000000)
};
const uint8_t TetrisTMask[] =
{
// Frame 1
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11100000),
B8_1BIT(01000000),
// Frame 2
B8_1BIT(00000000),
B8_1BIT(01000000),
B8_1BIT(11000000),
B8_1BIT(01000000),
// Frame 3
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(01000000),
B8_1BIT(11100000),
// Frame 4
B8_1BIT(00000000),
B8_1BIT(10000000),
B8_1BIT(11000000),
B8_1BIT(10000000)
};
const uint8_t TetrisZData[] =
{
// Frame 1
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(77000000),
B8_3BIT(07700000),
// Frame 2
B8_3BIT(00000000),
B8_3BIT(07000000),
B8_3BIT(77000000),
B8_3BIT(70000000),
// Frame 3
B8_3BIT(00000000),
B8_3BIT(00000000),
B8_3BIT(77000000),
B8_3BIT(07700000),
// Frame 4
B8_3BIT(00000000),
B8_3BIT(07000000),
B8_3BIT(77000000),
B8_3BIT(70000000)
};
const uint8_t TetrisZMask[] =
{
// Frame 1
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11000000),
B8_1BIT(01100000),
// Frame 2
B8_1BIT(00000000),
B8_1BIT(01000000),
B8_1BIT(11000000),
B8_1BIT(10000000),
// Frame 3
B8_1BIT(00000000),
B8_1BIT(00000000),
B8_1BIT(11000000),
B8_1BIT(01100000),
// Frame 4
B8_1BIT(00000000),
B8_1BIT(01000000),
B8_1BIT(11000000),
B8_1BIT(10000000)
};

#define TETRIS_SPR_WIDTH4
#define TETRIS_SPR_HEIGHT 4
const uint8_t *TetrisSprData[] = { TetrisIData, TetrisJData, TetrisLData, TetrisOData, TetrisSData, TetrisTData, TetrisZData };
const uint8_t *TetrisSprMask[] = { TetrisIMask, TetrisJMask, TetrisLMask, TetrisOMask, TetrisSMask, TetrisTMask, TetrisZMask};
const struct CRGB TetrisColours[] = { CRGB(0, 255, 255), CRGB(0, 0, 255), CRGB(255, 165, 0), CRGB(255, 255, 0), CRGB(50, 205, 50), CRGB(255, 0, 255), CRGB(255, 0, 0) };

uint8_t PlayfieldData;
uint8_t PlayfieldMask;
uint8_t CompletedLinesData;
const struct CRGB CompletedLinesColour[] = { CRGB(255, 255, 255) };
cSprite Playfield, CompletedLines, CurrentBlock;
cLEDSprites Sprites(&leds);

unsigned char AttractMsg, GameOverMsg;
char BlankMsg;
cLEDText TetrisMsg;

uint8_t DropDelay;
boolean AttractMode, NextBlock;
int16_t TotalLines;
unsigned int HighScore = 0, LastScore;

uint16_t PlasmaTime, PlasmaShift;
uint32_t LoopDelayMS, LastLoop;

void setup()
{
Serial.begin(115200);
SerialBT.begin("ESP32Tetris");

FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, leds.Size());
FastLED.setBrightness(200);
FastLED.clear(true);
FastLED.show();

memset(PlayfieldData, 0, sizeof(PlayfieldData));
memset(PlayfieldMask, 0, sizeof(PlayfieldMask));
Playfield.Setup(leds.Width(), leds.Height(), PlayfieldData, 1, _3BIT, TetrisColours, PlayfieldMask);
Playfield.SetPositionFrameMotionOptions(0, 0, 0, 0, 0, 0, 0, 0, 0);
Sprites.AddSprite(&Playfield);

memset(CompletedLinesData, 0, sizeof(CompletedLinesData));
CompletedLines.Setup(leds.Width(), TETRIS_SPR_HEIGHT, CompletedLinesData, 1, _1BIT, CompletedLinesColour, CompletedLinesData);
CompletedLines.SetPositionFrameMotionOptions(0, 0, 0, 0, 0, 0, 0, 0, 0);

TetrisMsg.SetFont(MatriseFontData);
sprintf((char *)BlankMsg, "%.*s", _min(((leds.Height() + TetrisMsg.FontHeight()) / (TetrisMsg.FontHeight() + 1)), (int)sizeof(BlankMsg) - 1), "                              ");
sprintf((char *)AttractMsg, "%sTETRIS%sSCORE %u%sHIGH %u%sANY BUTTON TO START%s", BlankMsg, BlankMsg, LastScore, BlankMsg, (int)HighScore, BlankMsg, BlankMsg);
TetrisMsg.Init(&leds, TetrisMsg.FontWidth() + 1, leds.Height(), (leds.Width() - TetrisMsg.FontWidth()) / 2, 0);
TetrisMsg.SetBackgroundMode(BACKGND_LEAVE);
TetrisMsg.SetScrollDirection(SCROLL_UP);
TetrisMsg.SetTextDirection(CHAR_UP);
TetrisMsg.SetFrameRate(1);
TetrisMsg.SetOptionsChangeMode(INSTANT_OPTIONS_MODE);
TetrisMsg.SetText(AttractMsg, strlen((const char *)AttractMsg));
AttractMode = true;
LoopDelayMS = TARGET_FRAME_TIME;
LastLoop = millis() - LoopDelayMS;
PlasmaShift = (random8(0, 5) * 32) + 64;
PlasmaTime = 0;
}

void loop()
{
if (abs(millis() - LastLoop) >= LoopDelayMS)
{
    LastLoop = millis();
    FastLED.clear();

    // Fill background with dim plasma
    #define PLASMA_X_FACTOR24
    #define PLASMA_Y_FACTOR24
    for (int16_t x=0; x<MATRIX_WIDTH; x++)
    {
      for (int16_t y=0; y<MATRIX_HEIGHT; y++)
      {
      int16_t r = sin16(PlasmaTime) / 256;
      int16_t h = sin16(x * r * PLASMA_X_FACTOR + PlasmaTime) + cos16(y * (-r) * PLASMA_Y_FACTOR + PlasmaTime) + sin16(y * x * (cos16(-PlasmaTime) / 256) / 2);
      leds(x, y) = CHSV((uint8_t)((h / 256) + 128), 255, 64);
      }
    }
    uint16_t OldPlasmaTime = PlasmaTime;
    PlasmaTime += PlasmaShift;
    if (OldPlasmaTime > PlasmaTime)
      PlasmaShift = (random8(0, 5) * 32) + 64;

    if (AttractMode)
    {
      if (currentInput != NONE)
      {
      AttractMode = false;
      memset(PlayfieldData, 0, sizeof(PlayfieldData));
      memset(PlayfieldMask, 0, sizeof(PlayfieldMask));
      Sprites.RemoveSprite(&CurrentBlock);
      LastScore = 0;
      TotalLines = 0;
      DropDelay = INITIAL_DROP_FRAMES;
      CurrentBlock.SetXChange(-1);
      NextBlock = true;
      currentInput = NONE;
      }
    }
    else
    {
      if (Sprites.IsSprite(&CompletedLines))// We have highlighted complete lines, delay for visual effect
      {
      if (CompletedLines.GetXCounter() > 0)
          CompletedLines.SetXCounter(CompletedLines.GetXCounter() - 1);
      else
      {
          Sprites.RemoveSprite(&CompletedLines);
          // Remove completed lines from playfield sprite
          uint8_t *Data = PlayfieldData;
          uint8_t *Mask = PlayfieldMask;
          uint16_t Mbpl = (MATRIX_WIDTH + 7) / 8;
          uint16_t Dbpl = Mbpl * _3BIT;
          int16_t k;
          for (int16_t i=(MATRIX_HEIGHT-1)*Dbpl,j=(MATRIX_HEIGHT-1)*Mbpl; i>=0; i-=Dbpl,j-=Mbpl)
          {
            for (k=0; k<MATRIX_WIDTH; k+=8)
            {
            if ((uint8_t)(0xff00 >> _min(MATRIX_WIDTH - k, 8)) != Mask)
                break;
            }
            if (k >= MATRIX_WIDTH)
            {
            memmove(&Data, &Data, i);
            memset(&Data, 0, Dbpl);
            memmove(&Mask, &Mask, j);
            memset(&Mask, 0, Mbpl);
            i+=Dbpl;
            j+=Mbpl;
            }
          }
      }
      }
      else
      {
      if (CurrentBlock.GetXChange() >= 0) // We have a current block
      {
          // Check for user input
          if ( currentInput == ROTATE )
          {
            currentInput = NONE;
            if ((CurrentBlock.GetCurrentFrame() % 2) == 1)
            {
            if (CurrentBlock.GetXChange() == 0)
                CurrentBlock.m_X = _min(CurrentBlock.m_X, MATRIX_WIDTH - TETRIS_SPR_WIDTH);
            else if ((CurrentBlock.GetXChange() != 3) && (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MAX))
                --CurrentBlock.m_X;
            }
            CurrentBlock.IncreaseFrame();
            Sprites.DetectCollisions(&CurrentBlock);
            if (CurrentBlock.GetFlags() & SPRITE_COLLISION)
            CurrentBlock.DecreaseFrame();
          }
         
          if ( currentInput == LEFT && (! (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MIN)) )
          {
            currentInput = NONE;
            CurrentBlock.m_X--;
            Sprites.DetectCollisions(&CurrentBlock);
            if (CurrentBlock.GetFlags() & SPRITE_COLLISION)
            CurrentBlock.m_X++;
          }
         
          else if ( currentInput == RIGHT && (! (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MAX)) )
          {
            currentInput = NONE;
            CurrentBlock.m_X++;
            Sprites.DetectCollisions(&CurrentBlock);
            if (CurrentBlock.GetFlags() & SPRITE_COLLISION)
            CurrentBlock.m_X--;
          }
         
          if ( currentInput == DOWN )
          {
            currentInput = NONE;
            CurrentBlock.SetYCounter(1);
          }
            
          // Do block checks for bottom or collision
          if (CurrentBlock.GetYCounter() <= 1)
          {
            if (CurrentBlock.GetFlags() & SPRITE_EDGE_Y_MIN)
            NextBlock = true;
            else
            {
            --CurrentBlock.m_Y;
            Sprites.DetectCollisions(&CurrentBlock);
            ++CurrentBlock.m_Y;
            if (CurrentBlock.GetFlags() & SPRITE_COLLISION)
            {
                // Block has collided check for game over
                int16_t MaxY = MATRIX_HEIGHT - 2;
                if ((CurrentBlock.GetCurrentFrame() % 2) == 1)
                {
                  if (CurrentBlock.GetXChange() == 0)
                  MaxY -= 2;
                  else if (CurrentBlock.GetXChange() != 3)
                  MaxY -= 1;
                }
                else if (CurrentBlock.GetXChange() == 0)
                  ++MaxY;
                if (CurrentBlock.m_Y < MaxY)
                  NextBlock = true;
                else
                {
                  // Game over
                  CurrentBlock.SetYCounter(2);// Stop last block moving down!
                  AttractMode = true;
                  if (LastScore > HighScore)
                  {
                  HighScore = LastScore;
                  sprintf((char *)GameOverMsg, "%sGAME OVER%sNEW HIGH SCORE %u%s", BlankMsg, BlankMsg, LastScore, BlankMsg);
                  }
                  else
                  sprintf((char *)GameOverMsg, "%sGAME OVER%sSCORE %u%s", BlankMsg, BlankMsg, LastScore, BlankMsg);
                  sprintf((char *)AttractMsg, "%sTETRIS%sSCORE %u%sHIGH %u%sANY BUTTON TO START%s", BlankMsg, BlankMsg, LastScore, BlankMsg, HighScore, BlankMsg, BlankMsg);
                  TetrisMsg.SetText(GameOverMsg, strlen((char *)GameOverMsg));
                  TetrisMsg.SetBackgroundMode(BACKGND_DIMMING, 0x40);
                }
            }
            }
          }
      }
      if (NextBlock)// Start new block
      {
          if (CurrentBlock.GetXChange() >= 0) // We have a current block so add to playfield before creating new block
          {
            Playfield.Combine(CurrentBlock.m_X, CurrentBlock.m_Y, &CurrentBlock);
            Sprites.RemoveSprite(&CurrentBlock);
            // Make completed lines highlight sprite & score
            memset(CompletedLinesData, 0, sizeof(CompletedLinesData));
            CompletedLines.m_Y = -1;
            uint8_t *Mask = PlayfieldMask;
            uint16_t Mbpl = (MATRIX_WIDTH + 7) / 8;
            int16_t j, numlines = 0;
            for (int16_t i=(MATRIX_HEIGHT-1)*Mbpl, y=0; i>=0; i-=Mbpl,++y)
            {
            for (j=0; j<MATRIX_WIDTH; j+=8)
            {
                if ((uint8_t)(0xff00 >> _min(MATRIX_WIDTH - j, 8)) != Mask)
                  break;
            }
            if (j >= MATRIX_WIDTH)
            {
                if (CompletedLines.m_Y == -1)
                  CompletedLines.m_Y = y;
                memset(&CompletedLinesData[((TETRIS_SPR_HEIGHT - 1) - (y - CompletedLines.m_Y)) * Mbpl], 0xff, Mbpl);
                numlines++;
            }
            }
            if (numlines > 0)
            {
            CompletedLines.SetXCounter(15);// Set delay for highlight display to 15 loops
            Sprites.AddSprite(&CompletedLines);
            }
            LastScore += 1;
            if (numlines == 1)
            LastScore += 4;
            else if (numlines == 2)
            LastScore += 12;
            else if (numlines == 3)
            LastScore += 20;
            else if (numlines == 4)
            LastScore += 40;
            TotalLines += numlines;
            DropDelay = _max(1, INITIAL_DROP_FRAMES - (TotalLines / 5));
          }
          // Start new block
          uint8_t j = random8(sizeof(TetrisSprData) / sizeof(TetrisSprData));
          CurrentBlock.Setup(TETRIS_SPR_WIDTH, TETRIS_SPR_WIDTH, TetrisSprData, 4, _3BIT, TetrisColours, TetrisSprMask);
          CurrentBlock.SetPositionFrameMotionOptions((MATRIX_WIDTH/2)-1, MATRIX_HEIGHT, 0, 0, 0, 0, -1, DropDelay, SPRITE_DETECT_COLLISION | SPRITE_DETECT_EDGE);
          CurrentBlock.SetXChange(j);
          Sprites.AddSprite(&CurrentBlock);
          NextBlock = false;
      }
      Sprites.UpdateSprites();
      }
    }
    Sprites.RenderSprites();
    if (AttractMode)
    {
      if (TetrisMsg.UpdateText() == -1)
      {
      TetrisMsg.SetText(AttractMsg, strlen((char *)AttractMsg));
      TetrisMsg.SetBackgroundMode(BACKGND_LEAVE);
      Sprites.RemoveSprite(&CurrentBlock);
      memset(PlayfieldData, 0, sizeof(PlayfieldData));
      memset(PlayfieldMask, 0, sizeof(PlayfieldMask));
      }
    }
    FastLED.show();
}
if(SerialBT.available()){
    char keyPress = (char)SerialBT.read();
    switch(keyPress) {
      case 'w':
      currentInput = ROTATE;
      break;
      case 'a':
      currentInput = LEFT;
      break;
      case 's':
      currentInput = DOWN;
      break;
      case 'd':
      currentInput = RIGHT;
      break;
    }
    Serial.println(currentInput);
}
}

keer_zu 发表于 2025-5-21 14:35

只有背景的显示效果:
// TETRIS
#include <FastLED.h>
#include <LEDMatrix.h>
#include <LEDText.h>
#include <FontMatrise.h>

#define LED_PIN      15
#define COLOR_ORDER    GRB
#define CHIPSET      WS2812B
#define MATRIX_WIDTH   30
#define MATRIX_HEIGHT15
#define MATRIX_TYPE    HORIZONTAL_MATRIX

// NOTE the '-' sign before the width, this is due to my leds matrix origin being on the right hand side
cLEDMatrix<MATRIX_WIDTH, -MATRIX_HEIGHT, MATRIX_TYPE> leds;


#define TARGET_FRAME_TIME    15// Desired update rate, though if too many leds it will just run as fast as it can!

uint16_t PlasmaTime, PlasmaShift;

void setup()
{
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, leds.Size());
FastLED.setBrightness(200);
FastLED.clear(true);
FastLED.show();

PlasmaShift = (random8(0, 5) * 32) + 64;
PlasmaTime = 0;
}

void loop()
{
    FastLED.clear();

    // Fill background with dim plasma
    #define PLASMA_X_FACTOR24
    #define PLASMA_Y_FACTOR24
    for (int16_t x=0; x<MATRIX_WIDTH; x++)
    {
      for (int16_t y=0; y<MATRIX_HEIGHT; y++)
      {
      int16_t r = sin16(PlasmaTime) / 256;
      int16_t h = sin16(x * r * PLASMA_X_FACTOR + PlasmaTime) + cos16(y * (-r) * PLASMA_Y_FACTOR + PlasmaTime) + sin16(y * x * (cos16(-PlasmaTime) / 256) / 2);
      leds(x, y) = CHSV((uint8_t)((h / 256) + 128), 255, 64);
      }
    }

    uint16_t OldPlasmaTime = PlasmaTime;
    PlasmaTime += PlasmaShift;
    if (OldPlasmaTime > PlasmaTime)
      PlasmaShift = (random8(0, 5) * 32) + 64;

    FastLED.show();
    delay(10);

}

keer_zu 发表于 2025-5-21 21:19

// TETRIS
#include <FastLED.h>
#include <LEDMatrix.h>


#define LED_PIN      15
#define COLOR_ORDER    GRB
#define CHIPSET      WS2812B
#define MATRIX_WIDTH   30
#define MATRIX_HEIGHT15
#define MATRIX_TYPE    HORIZONTAL_MATRIX

// NOTE the '-' sign before the width, this is due to my leds matrix origin being on the right hand side
cLEDMatrix<MATRIX_WIDTH, -MATRIX_HEIGHT, MATRIX_TYPE> leds;


#define TARGET_FRAME_TIME    15// Desired update rate, though if too many leds it will just run as fast as it can!

uint16_t PlasmaTime, PlasmaShift;

void setup()
{
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, leds.Size());
FastLED.setBrightness(200);
FastLED.clear(true);
FastLED.show();

PlasmaShift = (random8(0, 5) * 32) + 64;
PlasmaTime = 0;
}

void loop()
{
    // Fill background with dim plasma
    #define PLASMA_X_FACTOR24
    #define PLASMA_Y_FACTOR24
    for (int16_t x=0; x<MATRIX_WIDTH; x++)
    {
      for (int16_t y=0; y<MATRIX_HEIGHT; y++)
      {
      int16_t r = sin16(PlasmaTime) / 256;
      int16_t h = sin16(x * r * PLASMA_X_FACTOR + PlasmaTime) + cos16(y * (-r) * PLASMA_Y_FACTOR + PlasmaTime) + sin16(y * x * (cos16(-PlasmaTime) / 256) / 2);
      leds(x, y) = CHSV((uint8_t)((h / 256) + 128), 255, 64);
      }
    }

    uint16_t OldPlasmaTime = PlasmaTime;
    PlasmaTime += PlasmaShift;
    if (OldPlasmaTime > PlasmaTime)
      PlasmaShift = (random8(0, 5) * 32) + 64;

    FastLED.show();
    delay(50);

}

keer_zu 发表于 2025-5-22 08:19

限制颜色和平滑的效果(不理想)
// TETRIS
#include <FastLED.h>
#include <LEDMatrix.h>

#define LED_PIN      15
#define COLOR_ORDER    GRB
#define CHIPSET      WS2812B
#define MATRIX_WIDTH   30
#define MATRIX_HEIGHT15
#define MATRIX_TYPE    HORIZONTAL_MATRIX

cLEDMatrix<MATRIX_WIDTH, -MATRIX_HEIGHT, MATRIX_TYPE> leds;

#define TARGET_FRAME_TIME    15
uint16_t PlasmaTime, PlasmaShift;

void setup() {
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, leds.Size());
FastLED.setBrightness(200);
FastLED.clear(true);
PlasmaShift = (random8(0, 5) * 16) + 32;// 降低变化速度
PlasmaTime = 0;
}

void loop() {
static uint8_t lastHue = 128;// 用于色相平滑过渡
FastLED.clear();

// 更平滑的波形参数
#define PLASMA_X_FACTOR16// 降低X/Y方向波形频率
#define PLASMA_Y_FACTOR16
#define HUE_START96       // 青色
#define HUE_END    220       // 紫色

for (int16_t x=0; x<MATRIX_WIDTH; x++) {
    for (int16_t y=0; y<MATRIX_HEIGHT; y++) {
      // 使用更平滑的波形组合
      int16_t r = sin16(PlasmaTime * 0.7) / 512;// 降低振幅变化速度
      int16_t h = sin16(x * PLASMA_X_FACTOR + PlasmaTime * 0.5)
                + cos16(y * PLASMA_Y_FACTOR + PlasmaTime * 0.3)
                + sin16((x + y) * 64 + PlasmaTime * 0.4);// 简化第三项波形

      // 非线性映射 + 低通滤波平滑色相
      uint8_t targetHue = map(h, -32768, 32767, HUE_START, HUE_END);
      uint8_t hue = (targetHue * 0.3) + (lastHue * 0.7);// 指数平滑
      lastHue = hue;

      leds(x, y) = CHSV(hue, 200, 100);
    }
}

// 更缓慢的时间推进
PlasmaTime += PlasmaShift;
if (PlasmaTime % 512 == 0)// 定期重置避免溢出
    PlasmaShift = random8(0, 5) * 16 + 32;
   
FastLED.show();
delay(20);
}

keer_zu 发表于 2025-5-22 10:50

随机变化的效果:

// 风场参数
const uint8_t scale = 25;       // 缩小缩放因子扩大波纹范围
const uint8_t speed = 2;      // 降低运动速度
const int8_t windDirection = 1;
const uint8_t blurAmount = 48;// 新增模糊系数
uint16_t z = 0;
// 优化的噪声生成函数
void generateNoise(uint8_t *noiseData, uint16_t zOffset) {
for(uint8_t y=0; y<MATRIX_HEIGHT; y++) {
    for(uint8_t x=0; x<MATRIX_WIDTH; x++) {
      uint8_t xOffset = windDirection * beatsin8(
      3,                  // 降低波浪频率
      0, 15,               // 减小振幅范围
      0,
      (x * 2 + y * 3)      // 优化相位关系
      );
      
      noiseData = inoise8(
      (x + xOffset) * scale,
      y * scale * 1.2,   // Y轴增加缩放差异
      zOffset
      );
    }
}
}


void wind_vision()
{
static uint32_t lastUpdate = 0;
static uint8_t blendVal = 0;

if(millis() - lastUpdate > 30) {
    lastUpdate = millis();
   
    // 双缓冲噪声生成
    uint8_t noiseData1;
    uint8_t noiseData2;
   
    // 生成两帧噪声数据
    generateNoise(noiseData1, z);
    generateNoise(noiseData2, z + 50); // 50个时间单位偏移
   
    // 混合两帧数据
    for(int i=0; i<NUM_LEDS; i++) {
      uint8_t finalNoise = blend8(noiseData1, noiseData2, blendVal);
      
      // 扩展颜色映射范围
      uint8_t hue = map(finalNoise, 40, 210, 180, 255); // 颜色范围180-255
      uint8_t val = map(finalNoise, 60, 190, 50, 220);// 保持亮度动态
      
      leds = CHSV(hue, 245, val); // 略微降低饱和度
    }
   
    // 应用二维模糊
    blur2d(leds, MATRIX_WIDTH, MATRIX_HEIGHT, blurAmount);
   
    z += speed;
    blendVal += 3; // 控制混合速度
    FastLED.show();
}
}


keer_zu 发表于 2025-5-23 22:29


#include <FastLED.h>

// ========== 硬件配置 ==========
#define LED_PIN       15
#define LED_TYPE      WS2812B
#define COLOR_ORDER   GRB
#define MATRIX_WIDTH30
#define MATRIX_HEIGHT 15
#define NUM_LEDS      (MATRIX_WIDTH * MATRIX_HEIGHT)
CRGB leds;

// ========== 动画参数 ==========
#define FRAME_RATE    30
#define NOISE_SCALE   0.1f
#define FLOW_SPEED    0.8f
#define HUE_SPEED   0.02f
#define BRIGHTNESS    80

// ========== 全局变量 ==========
float z;
uint8_t hue_base;

// ========== 完整的perm数组 ==========
const uint8_t perm = {
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225,
140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148,
247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32,
57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122,
60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54,
65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169,
200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64,
52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212,
207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213,
119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104,
218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241,
81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157,
184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93,
222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
};

// ========== 修改后的噪声函数(重命名避免冲突)==========
uint8_t custom_noise(uint16_t x, uint16_t y, uint16_t z) {
uint8_t a= perm[(x>>8)&0xFF] + (y>>8);
uint8_t b= perm[(y>>8)&0xFF] + (z>>8);
uint8_t aa = perm + (z>>8);
uint8_t ba = perm + (x>>8);

uint8_t u = x & 0xFF;
uint8_t v = y & 0xFF;

return lerp8by8(lerp8by8(perm, perm, u),
                lerp8by8(perm, perm, u), v);
}

// ========== 坐标映射函数 ==========
uint16_t XY(uint8_t x, uint8_t y) {
if(y % 2 == 0) {
    return y * MATRIX_WIDTH + x;
} else {
    return (y + 1) * MATRIX_WIDTH - x - 1;
}
}

// ========== 主渲染函数(参数类型修正)==========
void renderFrame() {
static float xOffset = 0;
static float yOffset = 0;

xOffset += FLOW_SPEED * 0.7;
yOffset += FLOW_SPEED * 0.5;
hue_base += HUE_SPEED * 255;
z += 0.05;

for(uint8_t y = 0; y < MATRIX_HEIGHT; y++) {
    for(uint8_t x = 0; x < MATRIX_WIDTH; x++) {
      // 转换为整数参数
      uint16_t nx = (uint16_t)(x * NOISE_SCALE * 256 + xOffset * 256);
      uint16_t ny = (uint16_t)(y * NOISE_SCALE * 256 + yOffset * 256);
      uint16_t nz = (uint16_t)(z * 256);
      
      uint8_t noise = (
          custom_noise(nx, ny, nz) * 0.6 +
          custom_noise(nx*2, ny*2, nz) * 0.4
      );
      
      CRGB color = CHSV(noise + hue_base, 255, noise);
      leds = color;
    }
}
}

void setup() {
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
FastLED.clear();
randomSeed(analogRead(0));
z = random(65536);
}

void loop() {
static uint32_t lastFrame = 0;
uint32_t frameInterval = 1000 / FRAME_RATE;

if(millis() - lastFrame >= frameInterval) {
    renderFrame();
    FastLED.show();
    lastFrame = millis();
}
yield();
}

keer_zu 发表于 2025-5-23 22:35

keer_zu 发表于 2025-5-23 22:29


颜色受到限制的版本:
#include <FastLED.h>

// ========== 硬件配置 ==========
#define LED_PIN      15
#define LED_TYPE      WS2812B
#define COLOR_ORDER   GRB
#define MATRIX_WIDTH30
#define MATRIX_HEIGHT 15
#define NUM_LEDS      (MATRIX_WIDTH * MATRIX_HEIGHT)
CRGB leds;

// ========== 动画参数 ==========
#define FRAME_RATE    30
#define NOISE_SCALE   0.05f    // 更细腻的噪声图案
#define FLOW_SPEED    0.3f   // 慢速流动
#define HUE_SPEED   0.005f   // 缓慢色调变化
#define BRIGHTNESS    80       // 中等亮度

// ========== 全局变量 ==========
float z;
uint8_t hue_base;

// ========== perm数组(保持不变)==========
const uint8_t perm = {
151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,
134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,
18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,
189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,
172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,
228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,
49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,
236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};

// ========== 自定义噪声函数 ==========
uint8_t custom_noise(uint16_t x, uint16_t y, uint16_t z) {
uint8_t a= perm[(x>>8)&0xFF] + (y>>8);
uint8_t b= perm[(y>>8)&0xFF] + (z>>8);
uint8_t aa = perm + (z>>8);
uint8_t ba = perm + (x>>8);

uint8_t u = x & 0xFF;
uint8_t v = y & 0xFF;

return lerp8by8(lerp8by8(perm, perm, u),
                lerp8by8(perm, perm, u), v);
}

// ========== 蛇形映射函数 ==========
uint16_t XY(uint8_t x, uint8_t y) {
if(y % 2 == 0) {
    return y * MATRIX_WIDTH + x;
} else {
    return (y + 1) * MATRIX_WIDTH - x - 1;
}
}

// ========== 主渲染函数 ==========
void renderFrame() {
static float xOffset = 0;
static float yOffset = 0;

// 缓慢更新位置参数
xOffset += FLOW_SPEED * 0.7;
yOffset += FLOW_SPEED * 0.5;
hue_base += HUE_SPEED * 255;
z += 0.02;

for(uint8_t y = 0; y < MATRIX_HEIGHT; y++) {
    for(uint8_t x = 0; x < MATRIX_WIDTH; x++) {
      // 生成噪声参数
      uint16_t nx = (uint16_t)(x * NOISE_SCALE * 256 + xOffset * 256);
      uint16_t ny = (uint16_t)(y * NOISE_SCALE * 256 + yOffset * 256);
      uint16_t nz = (uint16_t)(z * 256);
      
      // 混合噪声层
      uint8_t noise = (
          custom_noise(nx, ny, nz) * 0.6 +
          custom_noise(nx*2, ny*2, nz) * 0.4
      );
      
      // 颜色处理(限制在100-220 Hue范围)
      uint8_t rawHue = noise + hue_base;
      uint8_t scaledHue = scale8(rawHue, 120) + 100; // 120 = 60*2
      CRGB color = CHSV(scaledHue, 200, noise);      // 适当降低饱和度
      
      leds = color;
    }
}
}

// ========== 初始化 ==========
void setup() {
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
FastLED.clear();
randomSeed(analogRead(0));
z = random(65536);// 随机初始Z值
}

// ========== 主循环 ==========
void loop() {
static uint32_t lastFrame = 0;
uint32_t frameInterval = 1000 / FRAME_RATE;

if(millis() - lastFrame >= frameInterval) {
    renderFrame();
    FastLED.show();
    lastFrame = millis();
}
yield();
}
页: [1]
查看完整版本: 很好的LED显示例子