esp32cmini SK6812 2个方式

news2025/5/27 10:14:10

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个字节表示

  }

}


#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();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2385965.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据集】30 m地表温度LST数据集

目录 数据概述🔧研究目标与意义🧠 算法核心组成1. 地表比辐射率(LSE)估算2. 大气校正(Atmospheric Correction)LST反演流程图📊 精度验证与评估结果参考《Generating the 30-m land surface temperature product over continental China and USA from Landsat 5/7/8 …

【CATIA的二次开发07】草图编辑器对象结构及应用

【CATIA的二次开发07】草图编辑器对象结构及应用 草图编辑器(SketchEditor)是用于创建和编辑2D草图的核心对象。其对象结构遵循CATIA的层级关系,以下是详细说明及代码示例: 一、核心对象结构图 Application │ └─ Documents│└─ Document (.CATPart)│└─ Part│└─…

IT | 词汇科普手册Ⅱ

目录 1.报文(Message) 2.Token(令牌) Token vs. Cookie Token vs. Key "碰一碰"支付 3.NFC 4.Nginx 5.JSON 6.前置机 前置机vs.Nginx反向代理 以PDA、WMS举例前置机场景 7.RabbitMQ 核心功能 1.报文(Message) 报文&#xff08;Message&#xff09;​​是系统或组件之…

【 java 基础问题 第一篇 】

目录 1.概念 1.1.java的特定有哪些&#xff1f; 1.2.java有哪些优势哪些劣势&#xff1f; 1.3.java为什么可以跨平台&#xff1f; 1.4JVM,JDK,JRE它们有什么区别&#xff1f; 1.5.编译型语言与解释型语言的区别&#xff1f; 2.数据类型 2.1.long与int类型可以互转吗&…

自用git记录

像重复做自己在网上找的练习题&#xff0c;这种类型的git仓库管理&#xff0c;一般会用到以下命令&#xff1a; git revert a1b2c3 很复杂的git历史变成简单git历史 能用git rebase -i HEAD~5^这种命令解决&#xff0c;就最好&#xff08;IDEA还带GUI&#xff0c;很方便&…

本地环境下 前端突然端口占用问题 针对vscode

1.问题背景 本地运行前端代码&#xff0c;虚拟机中使用nginx反向代理。两者都使用vscode进行开发。后端使用vscode远程连接。在前端发起一次接口请求后&#xff0c;后端会产生新的监听端口&#xff0c;出现如下图的提示情况。随后前端刷新&#xff0c;甚至无法正常显示界面。 …

C++ - 仿 RabbitMQ 实现消息队列(3)(详解使用muduo库)

C - 仿 RabbitMQ 实现消息队列&#xff08;3&#xff09;&#xff08;详解使用muduo库&#xff09; muduo库的基层原理核心概念总结&#xff1a;通俗例子&#xff1a;餐厅模型优势体现典型场景 muduo库中的主要类EventloopMuduo 的 EventLoop 核心解析1. 核心机制&#xff1a;事…

docker部署XTdrone

目录 一、前置准备 二、依赖安装 三、ros安装 四、gazebo安装 五、mavros安装 六、PX4的配置 七、Xtdrone源码下载 八、xtdrone与gazebo&#xff08;实际上应该是第四步之后做这件事&#xff09; 九、键盘控制 参考链接&#xff1a;仿真平台基础配置 语雀 一、前置准…

图解 | 大模型智能体LLM Agents

文章目录 正文1. 存储 Memory1.1 短期记忆 Short-Term Memory1.1.1 模型的上下文窗口1.1.2 对话历史1.1.3 总结对话历史 1.2 长期记忆Long-term Memory 2. 工具Tools2.1 工具的类型2.2 function calling2.3 Toolformer2.3.1 大模型调研工具的过程2.3.2 生成工具调用数据集 2.4 …

echarts设置标线和最大值最小值

echarts设置标线和最大值最小值 基本ECharts图表初始化配置 设置动态的y轴范围&#xff08;min/max值&#xff09; 通过markPoint标记最大值和最小值点 使用markLine添加水平参考线 配置双y轴图表 自定义标记点和线的样式&#xff08;颜色、符号等&#xff09; 响应式调整图表大…

Maven 中央仓库操作指南

Maven 中央仓库操作指南 登录注册 在 Maven Central 登录&#xff08;注册&#xff09;账号。 添加命名空间 注册 通过右上角用户菜单跳转到命名空间管理页面&#xff1a; 注册命名空间&#xff1a; 填入你拥有的域名并注册&#xff1a; 刚提交的命名空间状态是Unverified…

BUUCTF——RCE ME

BUUCTF——RCE ME 进入靶场 <?php error_reporting(0); if(isset($_GET[code])){$code$_GET[code];if(strlen($code)>40){die("This is too Long.");}if(preg_match("/[A-Za-z0-9]/",$code)){die("NO.");}eval($code); } else{highlight…

FreeRTOS--消息队列

一、简介 消息队列是FreeRTOS中用于任务与任务或任务与中断之间数据交换的一种机制&#xff0c;采用FIFO&#xff08;先进先出&#xff09;方式管理数据&#xff0c;也可以采用LIFO&#xff08;后进先出&#xff09;方式。有点类似全局变量。 1.1 那为什么不直接使用全局变量&a…

三步快速部署一个本地Windows/Linux大语言模型ChatGLM(环境配置+权重下载+运行)

前言&#xff1a; 最近刚拿到实验室一个装了3张3090显卡的服务器账号&#xff0c;感觉不用来霍霍有点浪费&#xff0c;于是有了部署一个大语言模型的想法&#xff0c;除去下载权重和传文件到服务器上可能也就用了十分钟不到&#xff08;这下看懂为啥python受众现在这么广了&…

DeepSeek联网Google搜索引擎

目录&#xff1a; 1、使用背景2、实现代码3、Gradio 的 yield 机制 1、使用背景 比如所有易建联是什么时候退役的&#xff1f;使用大模型对这种实事回答不准确&#xff0c;需要通过联网搜索处理。 正确答案应该是2023年8月29日退役。 2、实现代码 # import gradio as gr# d…

奈雪小程序任务脚本

功能概述 该脚本用于自动完成奈雪点单小程序的每日任务&#xff0c;包括&#xff1a; 自动检测 Token 有效性自动签到&#xff08;如果未签到&#xff09;获取用户基础信息&#xff08;昵称、手机号&#xff09;查询当前奈雪币余额记录连续签到天数支持多账号执行&#xff0c…

上海医日健集团物联网专利技术领跑智慧药房赛道

在智慧医疗蓬勃发展的浪潮中&#xff0c;上海医日健集团凭借其卓越的创新能力与强大的技术实力&#xff0c;在智慧药房领域崭露头角。集团自主研发的物联网专利技术&#xff0c;正以前所未有的优势&#xff0c;重塑智慧药房运营模式&#xff0c;引领行业迈向新的发展高度。 上…

基于Java+MySQL实现(Web)图书借阅管理系统

图书借阅管理系统(前后台) 1 需求分析 图书借阅管理系统是模拟学校图书馆实现的一个具有前后台的 Web 系统.对于读者,能够提供全文检索,个性化推荐,借阅等功能.对于管理员,能够提供可视化数据分析,信息管理等功能. 2 技术栈 前端: Layui,jQuery,echarts 后端:Spring Boot,…

SAR ADC的功耗设计

SAR ADC 由比较器、逻辑和DAC组成,功耗比可能是3:6:1,对于低功耗设计来说,我们需要尽量让DAC的功耗最小,这里来探讨一下CDAC的功耗计算方法。 CDAC从状态1切换到状态2时,需要从Vref buffer上抽拉电荷。C是状态2时连接Vref的总电容,V2就是状态2时接Vref的电容上的电压…

PP-OCRv5

目录 PP-OCRv5官方效果如下 C封装、C#调用效果 项目 代码 下载 PP-OCRv5官方效果如下 C封装、C#调用效果 项目 代码 using Newtonsoft.Json; using OpenCvSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; usi…