1
#include <SPI.h>
// ESP32-C系列的SPI引腳
#define MOSI_PIN ? ?7 ? ? ? // ESP32-C3/C6的SPI MOSI引腳
#define NUM_LEDS ? ?30 ? ? ?// LED燈帶實際LED數量 - 確保與實際數量匹配!
#define SPI_CLOCK ? 10000000 ?// SPI時鐘頻率
// 顏色結構體
struct CRGB {
? uint8_t r;
? uint8_t g;
? uint8_t b;
};
// 定義常用顏色
namespace Colors {
? const CRGB Black = {0, 0, 0};
? const CRGB White = {255, 255, 255};
? const CRGB Red = {255, 0, 0};
? const CRGB Green = {0, 255, 0};
? const CRGB Blue = {0, 0, 255};
}
CRGB leds[NUM_LEDS];
uint8_t brightness = 20; ?// 亮度 (0-255)
// SK6812協議需要精確的時序
// T0H: 0碼高電平時間 ~0.3us
// T0L: 0碼低電平時間 ~0.9us
// T1H: 1碼高電平時間 ~0.6us
// T1L: 1碼低電平時間 ~0.6us
// RES: 重置時間 >80us
// 根據SPI時鐘調整字節模式
// 每個位需要至少3個字節來正確表示時序
#define BYTES_PER_BIT 3
#define BYTES_PER_LED (3 * 8 * BYTES_PER_BIT) ?// 3顏色 * 8位/顏色 * 字節/位
uint8_t spiBuffer[NUM_LEDS * BYTES_PER_LED + 100]; ?// 添加一些額外的緩沖空間
// 在ESP32上初始化SPI
SPIClass * vspi = NULL;
void setup() {
? // 初始化串口調試
? Serial.begin(115200);
? Serial.println("ESP32-C SPI SK6812控制初始化開始");
? // 初始化SPI
? vspi = new SPIClass(SPI);
? vspi->begin(MOSI_PIN);
? // 設置SPI屬性
? vspi->beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
? // 測試模式 - 打印燈帶配置
? Serial.print("控制 ");
? Serial.print(NUM_LEDS);
? Serial.println(" 個LED");
? // 初始化LED為關閉狀態
? clearAll();
? delay(500);
? // 測試所有LED - 確認連接
? testAllLeds();
}
void loop() {
? // 從頭到尾逐個點亮
? sequentialLightUp();
? // 從尾到頭逐個熄滅
? sequentialLightDown();
}
// 測試所有LED - 確認連接和燈帶長度
void testAllLeds() {
? // 全部設為紅色
? for(int i = 0; i < NUM_LEDS; i++) {
? ? leds[i] = Colors::Red;
? }
? show();
? delay(500);
? // 全部設為綠色
? for(int i = 0; i < NUM_LEDS; i++) {
? ? leds[i] = Colors::Green;
? }
? show();
? delay(500);
? // 全部設為藍色
? for(int i = 0; i < NUM_LEDS; i++) {
? ? leds[i] = Colors::Blue;
? }
? show();
? delay(500);
? // 全部熄滅
? clearAll();
? delay(500);
}
// 將HSV顏色轉換為RGB
CRGB hsv2rgb(uint8_t h, uint8_t s, uint8_t v) {
? CRGB rgb;
? // 實現HSV到RGB的轉換
? uint8_t region, remainder, p, q, t;
? if (s == 0) {
? ? rgb.r = v;
? ? rgb.g = v;
? ? rgb.b = v;
? ? return rgb;
? }
? region = h / 43;
? remainder = (h - (region * 43)) * 6;
? p = (v * (255 - s)) >> 8;
? q = (v * (255 - ((s * remainder) >> 8))) >> 8;
? t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
? switch (region) {
? ? case 0:
? ? ? rgb.r = v; rgb.g = t; rgb.b = p;
? ? ? break;
? ? case 1:
? ? ? rgb.r = q; rgb.g = v; rgb.b = p;
? ? ? break;
? ? case 2:
? ? ? rgb.r = p; rgb.g = v; rgb.b = t;
? ? ? break;
? ? case 3:
? ? ? rgb.r = p; rgb.g = q; rgb.b = v;
? ? ? break;
? ? case 4:
? ? ? rgb.r = t; rgb.g = p; rgb.b = v;
? ? ? break;
? ? default:
? ? ? rgb.r = v; rgb.g = p; rgb.b = q;
? ? ? break;
? }
? return rgb;
}
void sequentialLightUp() {
? // 清除所有LED
? clearAll();
? // 從第一個LED開始逐個點亮
? for(int i = 0; i < NUM_LEDS; i++) {
? ? // 選擇不同的顏色效果
? ? leds[i] = hsv2rgb(i * 10, 255, 255); ?// 漸變色相
? ?
? ? // 更新燈帶顯示
? ? show();
? ? delay(50); ?// 控制點亮速度
? }
}
void sequentialLightDown() {
? // 從最后一個LED開始逐個熄滅
? for(int i = NUM_LEDS - 1; i >= 0; i--) {
? ? leds[i] = Colors::Black; ?// 熄滅
? ?
? ? // 更新燈帶顯示
? ? show();
? ? delay(50); ?// 控制熄滅速度
? }
}
// 清除所有LED
void clearAll() {
? for(int i = 0; i < NUM_LEDS; i++) {
? ? leds[i] = Colors::Black;
? }
? show();
}
// 重新優化的代碼,確保正確的位時序
void show() {
? // 準備SPI數據緩沖區
? memset(spiBuffer, 0, sizeof(spiBuffer)); ?// 先清空緩沖區
? prepareSPIBuffer();
? // 發送數據前輸出調試信息
? Serial.println("發送LED數據...");
? // 通過SPI發送數據
? //vspi->transferBytes(spiBuffer, nullptr, NUM_LEDS * BYTES_PER_LED);
? vspi->transferBytes(spiBuffer, nullptr, NUM_LEDS * 3 * 8);
?// vspi->transferBytes(spiBuffer, nullptr, NUM_LEDS);
? // 添加重置時間
? delayMicroseconds(300); ?// >280μs的LED重置時間
}
// 更精確的SPI緩沖區準備
void prepareSPIBuffer() {
? int bufferPos = 0;
? // 處理每個LED的數據
? for(int i = 0; i < NUM_LEDS; i++) {
? ? // 應用亮度
? ? uint8_t r = (leds[i].r * brightness) / 255;
? ? uint8_t g = (leds[i].g * brightness) / 255;
? ? uint8_t b = (leds[i].b * brightness) / 255;
? ?
? ? // SK6812使用GRB順序
? ? // 轉換每個顏色分量為SPI數據
? ? convertByte(bufferPos, g); ?// 綠色
? ? bufferPos += 8 * BYTES_PER_BIT;
? ?
? ? convertByte(bufferPos, r); ?// 紅色
? ? bufferPos += 8 * BYTES_PER_BIT;
? ?
? ? convertByte(bufferPos, b); ?// 藍色
? ? bufferPos += 8 * BYTES_PER_BIT;
? }
}
// 優化的位編碼 - 使用更少的字節
void convertByte(int offset, uint8_t byte) {
? for(int i = 0; i < 8; i++) { ?// 每個字節8位
? ? // 獲取最高位
? ? bool bit = byte & 0x80;
? ? byte <<= 1;
? ?
? ? // 根據SK6812的協議,為SPI準備數據
? ? // 調整以下值以匹配您的SPI時鐘和SK6812的時序需求
? ? if(bit) {
? ? ? // 1碼 (T1H ~ 0.6us, T1L ~ 0.6us)
? ? ? spiBuffer[offset + 0] = 0xF8; ?// 11111000
? ? ? spiBuffer[offset + 1] = 0x00; ?// 00000000
? ? ? spiBuffer[offset + 2] = 0x00; ?// 00000000
? ? } else {
? ? ? // 0碼 (T0H ~ 0.3us, T0L ~ 0.9us)
? ? ? spiBuffer[offset + 0] = 0xC0; ?// 11000000
? ? ? spiBuffer[offset + 1] = 0x00; ?// 00000000
? ? ? spiBuffer[offset + 2] = 0x00; ?// 00000000
? ? }
? ?
? ? offset += BYTES_PER_BIT; ?// 每位使用BYTES_PER_BIT個字節表示
? }
}
2?
#include <FastLED.h>
#define LED_PIN ? ? 6 ? ? ?// 數據引腳
#define NUM_LEDS ? ?30 ? ? // LED數量
#define LED_TYPE ? ?SK6812 // LED類型
#define COLOR_ORDER GRB ? ?// 顏色順序
CRGB leds[NUM_LEDS];
void setup() {
? FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
? FastLED.setBrightness(50); ?// 設置亮度
}
void loop() {
? // 從頭到尾逐個點亮
? sequentialLightUp();
??
? // 從尾到頭逐個熄滅
? sequentialLightDown();
}
void sequentialLightUp() {
? // 清除所有LED
? FastLED.clear();
??
? // 從第一個LED開始逐個點亮
? for(int i = 0; i < NUM_LEDS; i++) {
? ? // 選擇不同的顏色效果
? ? leds[i] = CHSV(i * 10, 255, 255); ?// 漸變色相
? ? FastLED.show();
? ? delay(50); ?// 控制點亮速度
? }
}
void sequentialLightDown() {
? // 從最后一個LED開始逐個熄滅
? for(int i = NUM_LEDS - 1; i >= 0; i--) {
? ? leds[i] = CRGB::Black; ?// 熄滅
? ? FastLED.show();
? ? delay(50); ?// 控制熄滅速度
? }
}
// 可選:添加更多炫酷效果
void rainbowEffect() {
? static uint8_t hue = 0;
? for(int i = 0; i < NUM_LEDS; i++) {
? ? leds[i] = CHSV(hue + (i * 10), 255, 255);
? }
? EVERY_N_MILLISECONDS(100) {?
? ? hue++;?
? }
? FastLED.show();
}