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.pswp.cn/bicheng/82350.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/82350.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/82350.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

互联网大厂Java求职面试:Spring Cloud微服务架构设计中的挑战与解决方案

互联网大厂Java求职面试&#xff1a;Spring Cloud微服务架构设计中的挑战与解决方案 面试场景设定 郑薪苦是一位拥有丰富实战经验的Java开发者&#xff0c;他正在参加一场由某知名互联网大厂的技术总监主持的面试。这场面试将围绕Spring Cloud微服务架构展开&#xff0c;涵盖…

品鉴JS的魅力之防抖与节流【JS】

前言 小水一波&#xff0c;函数的防抖与节流。 文章目录 前言介绍实现方式防抖节流 介绍 防抖与节流的优化逻辑&#xff0c;在我们的日常开发中&#xff0c;有着一定的地位。 防抖和节流是两种常用的性能优化技术&#xff0c;用于限制某个函数在一定时间内被触发的次数,减少不…

# 使用 Hugging Face Transformers 和 PyTorch 实现信息抽取

使用 Hugging Face Transformers 和 PyTorch 实现信息抽取 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;信息抽取是一种常见的任务&#xff0c;其目标是从文本中提取特定类型的结构化信息。本文将介绍如何使用 Hugging Face Transformers 和 PyTorch 实现基于大…

Firecrawl MCP Server 深度使用指南

无论是市场分析师洞察行业动态、研究者收集学术资料&#xff0c;还是开发者为智能应用采集数据&#xff0c;都对网络数据采集工具提出了极高的要求。Firecrawl MCP Server 应运而生&#xff0c;它宛如一把犀利的 “数字手术刀”&#xff0c;能够精准地剖析网页&#xff0c;为用…

OceanBase数据库全面指南(基础入门篇)

文章目录 一、OceanBase 简介与安装配置指南1.1 OceanBase 核心特点1.2 架构解析1.3 安装部署实战1.3.1 硬件要求1.3.2 安装步骤详解1.3.3 配置验证二、OceanBase 基础 SQL 语法入门2.1 数据查询(SELECT)2.1.1 基础查询语法2.1.2 实际案例演示2.2 数据操作(INSERT/UPDATE/DE…

几种环境下的Postgres数据库安装

1. Postgres 数据库介绍 PostgreSQL&#xff08;又称 Postgres&#xff09;是一种强大、开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它具备高度的可靠性、稳定性和可扩展性&#xff0c;主要特点如下&#xff1a; 开源&#xff1a;PostgreSQL 是基于开…

函数[x]和{x}在数论中的应用

函数[x]和{x}在数论中的应用 函数[x]和{x}的定义与基本性质&#xff08;定义1&#xff0c;命题1&#xff09;定义1例1命题1 函数[x]和{x}的应用&#xff08;定理1&#xff0c;推论1-推论3&#xff09;例2定理1注解5推论1例3例4推论2推论3命题2 函数[x]和{x}的定义与基本性质&am…

Python爬虫(32)Python爬虫高阶:动态页面处理与Scrapy+Selenium+BeautifulSoup分布式架构深度解析实战

目录 引言一、动态页面爬取的技术背景1.1 动态页面的核心特征1.2 传统爬虫的局限性 二、技术选型与架构设计2.1 核心组件分析2.2 架构设计思路1. 分层处理2. 数据流 三、代码实现与关键技术3.1 Selenium与Scrapy的中间件集成3.2 BeautifulSoup与Scrapy Item的整合3.3 分布式爬取…

FreeSWITCH rtcp-mux 测试

rtcp 跟 rtp 占用同一个端口&#xff0c;这就是 rtcp 复用 Fs 呼出是这样的&#xff1a; originate [rtcp_muxtrue][rtcp_audio_interval_msec5000]user/1001 &echo 需要同时指定 rtcp_audio_interval_msec&#xff0c;否则 rtcp_mux 不能生效 Fs 呼入不需要配置&#xf…

day019-特殊符号、正则表达式与三剑客

文章目录 1. 磁盘空间不足-排查流程2. 李导推荐书籍2.1 大话存储2.2 性能之巅 3. 特殊符号3.1 引号系列&#xff08;面试题&#xff09;3.2 重定向符号3.2.1 cat与重定向3.2.2 tr命令&#xff1a;替换字符3.2.3 xargs&#xff1a;参数转换3.2.4 标准全量追加重定向 4. 正则表达…

Vue3 watch 使用与注意事项

watch 的第一个参数可以是不同形式的“数据源”&#xff1a;它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组&#xff1a; 1&#xff1a;reactive监听对象 <template><div><h1>情况二&#xff1a;watchEffect…

医学写作供应商管理全流程优化

1. 供应商筛选与评估 1.1 资质审核 1.1.1 行业认证核查 核查供应商的行业认证,如AMWA医学写作认证、EMWA会员资格、ISO 9001等,确保其专业资质。 1.1.2 团队背景评估 评估团队成员专业背景,包括医学/药学学位、临床试验经验、发表记录,保障专业能力。 1.1.3 国际规范熟悉…

固态硬盘颗粒类型、选型与应用场景深度解析

一、固态硬盘颗粒类型的技术演进与特性 固态硬盘&#xff08;SSD&#xff09;的性能核心在于存储单元结构的设计&#xff0c;这种设计直接决定了数据的存储密度、读写速度、耐久度及成本效益。当前主流的闪存颗粒类型呈现从单层到多层架构的梯度演进&#xff0c;其技术特征与应…

CAPL自动化-诊断Demo工程

文章目录 前言一、诊断控制面板二、诊断定义三、发送诊断通过类.方法的方式req.SetParameterdiagSetParameter四、SendRequestAndWaitForResponse前言 本文将介绍CANoe的诊断自动化测试,工程可以从CANoe的 Sample Configruration 界面打开,也可以参考下面的路径中打开(以实…

嵌入式预处理链接脚本lds和map文件

在嵌入式开发中&#xff0c;.lds.S 文件是一个 预处理后的链接脚本&#xff08;Linker Script&#xff09;&#xff0c;它结合了 C 预处理器&#xff08;Preprocessor&#xff09; 的功能和链接脚本的语法。它的核心作用仍然是 定义内存布局和链接规则&#xff0c;但通过预处理…

PT5F2307触摸A/D型8-Bit MCU

1. 产品概述 ● PT5F2307是一款51内核的触控A/D型8位MCU&#xff0c;内置16K*8bit FLASH、内部256*8bit SRAM、外部512*8bit SRAM、触控检测、12位高精度ADC、RTC、PWM等功能&#xff0c;抗干扰能力强&#xff0c;适用于滑条遥控器、智能门锁、消费类电子产品等电子应用领域。 …

RabbitMQ——消息确认

一、消息确认机制 生产者发送的消息&#xff0c;可能有以下两种情况&#xff1a; 1> 消息消费成功 2> 消息消费失败 为了保证消息可靠的到达消费者&#xff08;&#xff01;&#xff01;&#xff01;注意&#xff1a;消息确认机制和前面的工作模式中的publisher confi…

C++异步(1)

什么是异步? 异步就是多个线程是同时执行的&#xff0c;与之相对的就是线程同步&#xff0c;二者都应用在并发的场景上。 异步的特点 异步执行的任务无需等待其他任务完成&#xff0c;其本身是通过非阻塞的方式执行的&#xff0c;不依赖前驱任务&#xff0c;通常用于IO密集…

向量数据库Milvus03-高级功能与性能调优

Milvus高级功能与性能调优 目录 高级特性详解性能调优技巧生产环境部署最佳实践总结与展望 1. 高级特性详解 1.1 多索引兼容 Milvus 支持多种索引类型&#xff08;如 HNSW、IVF_PQ、IVF_FLAT&#xff09;的混合使用&#xff0c;以适应不同场景的需求。 HNSW&#xff08;Hier…

5月24日day35打卡

模型可视化与推理 知识点回顾&#xff1a; 三种不同的模型可视化方法&#xff1a;推荐torchinfo打印summary权重分布可视化进度条功能&#xff1a;手动和自动写法&#xff0c;让打印结果更加美观推理的写法&#xff1a;评估模式 作业&#xff1a;调整模型定义时的超参数&#x…