【项目篇】WS2812 炫彩LED灯驱动笔记(C51/STM32)

news2024/12/23 2:08:20

优信电子:51系列驱动WS2812


3528 幻彩雾状 贴片式发光二极管

XL-3528RGBW-WS2812B

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


C51 驱动

使用 C51 单片机(如 8051 系列)驱动 WS2812 全彩 LED 需要仔细考虑 WS2812 的时序要求,因为 WS2812 使用的是单线通信协议,而 8051 系列单片机没有硬件支持这种协议。通常,我们需要手动生成符合 WS2812 时序的信号。这可以通过精确控制延时来实现。

以下是基于 C51 单片机驱动 WS2812 的一个基本方法:

1. WS2812 的时序要求

  • WS2812 的“0”信号:高电平约 0.35 µs,低电平约 0.9 µs。
  • WS2812 的“1”信号:高电平约 0.9 µs,低电平约 0.35 µs。

2. 基本方法

使用 C51 单片机的 GPIO 引脚手动产生高低电平信号,通过精确的延时控制来模拟 WS2812 的时序。

3. 设置 GPIO 引脚

选择一个 GPIO 引脚作为数据线输出。例如,假设使用 P1.0 作为 WS2812 的数据线。

4. 时序实现

为了生成准确的信号,必须精确控制延时。可以使用简单的 NOP(No Operation)指令或定时器来实现延时。

5. 代码示例

#include <reg51.h>

sbit WS2812_PIN = P1^0;  // 选择 P1.0 作为数据引脚

void delay_ns(unsigned int n) {
    while(n--);
}

void ws2812_send_bit(unsigned char bit) {
    if (bit) {
        WS2812_PIN = 1;
        delay_ns(3);  // 高电平持续时间约 0.9 µs
        WS2812_PIN = 0;
        delay_ns(1);  // 低电平持续时间约 0.35 µs
    } else {
        WS2812_PIN = 1;
        delay_ns(1);  // 高电平持续时间约 0.35 µs
        WS2812_PIN = 0;
        delay_ns(3);  // 低电平持续时间约 0.9 µs
    }
}

void ws2812_send_byte(unsigned char byte) {
    for (int i = 0; i < 8; i++) {
        ws2812_send_bit(byte & 0x80);  // 发送最高位
        byte <<= 1;                    // 左移准备发送下一位
    }
}

void ws2812_send_rgb(unsigned char green, unsigned char red, unsigned char blue) {
    ws2812_send_byte(green);  // 发送绿色通道数据
    ws2812_send_byte(red);    // 发送红色通道数据
    ws2812_send_byte(blue);   // 发送蓝色通道数据
}

void main() {
    while(1) {
        ws2812_send_rgb(0x00, 0xFF, 0x00);  // 发送纯红色
        delay_ns(50000);  // 延时,保持显示
    }
}

6. 解释代码

  • delay_ns 函数用于控制精确的延时,但由于 8051 的时钟频率不同,可能需要调整 delay_ns 的参数以获得所需的延时。
  • ws2812_send_bit 函数根据传递的位值(1 或 0)生成相应的高低电平信号。
  • ws2812_send_byte 函数发送一个字节的颜色数据,从最高位开始发送。
  • ws2812_send_rgb 函数按照 GRB 顺序发送颜色数据。

7. 注意事项

  • 精确的时序控制是关键。需要根据单片机的时钟频率调整延时函数的参数。
  • 在使用定时器或外部晶振时,要确保频率足够高,以便能精确控制到 µs 级别。

这种方法相对简单,但由于是软件控制的方式,可能会受到单片机其他任务的影响,导致时序不准确。对于需要控制大量 WS2812 LED 的复杂项目,可能需要考虑更高效的硬件平台或者利用更精确的硬件定时器来辅助实现。


STM32 SPI+DMA 驱动

头文件


#ifndef __WS2812_H
#define __WS2812_H

#include "stm32f10x.h"

//#define PIXEL_NUM 30
#define PIXEL_NUM 7


//硬件spi模拟ws2811时序(用spi的8位数据模拟ws281x的一位数据)
//要将系统时钟设置为56M,分频数设置为8,则SPI的通信频率为7M,传输一位数据的时间约为143纳秒(ns)
//3*143 = 429ns   5*143 = 715ns  符合WS281X芯片的通信时序。
//  _____   
// |     |___|   1111 1000  high level
//  ___         
// |   |_____|   1110 0000  low level

#define WS_HIGH 0XF8
#define WS_LOW  0XE0

void ws281x_init(void);
void ws281x_closeAll(void);
void ws281x_rainbowCycle(uint8_t wait);
uint32_t ws281x_color(uint8_t red, uint8_t green, uint8_t blue);
void ws281x_setPixelColor(uint16_t n ,uint32_t GRBcolor);
void ws281x_show(void);

void ws281x_theaterChase(uint32_t c, uint16_t wait);
void ws281x_colorWipe(uint32_t c, uint16_t wait);
void ws281x_rainbow(uint8_t wait);
void ws281x_theaterChaseRainbow(uint8_t wait);

#endif /* __WS2812_H */


源文件

#include "../BOARD/ws2812/ws2812.h"
#include "usart.h"
#include "delay.h"

uint8_t pixelBuffer[PIXEL_NUM][24] ;
 

void ws281x_init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;
  DMA_InitTypeDef DMA_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //PORTA时钟使能 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //SPI1时钟使能 	
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA传输


  /* PA7  SPI1_MOSI */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PA7复用推挽输出 SPI
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA

	SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		//串行同步时钟的空闲状态为低电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//串行同步时钟的第2个跳变沿(上升或下降)数据被采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;		//定义波特率预分频的值:波特率预分频值为16
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 
	SPI_Cmd(SPI1, ENABLE); //使能SPI外设
  SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
  
  DMA_DeInit(DMA1_Channel3);   //将DMA的通道1寄存器重设为缺省值
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(SPI1 -> DR); //cpar;  //DMA外设ADC基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pixelBuffer; //cmar;  //DMA内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设
	DMA_InitStructure.DMA_BufferSize = PIXEL_NUM * 24; //cndtr;  //DMA通道的DMA缓存的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA1_Channel3, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器 
  
  ws281x_closeAll();  //关闭全部的灯
  delay_ms(100); //关闭全部的灯需要一定的时间  
}

void ws281x_closeAll(void)
{
  uint16_t i;
  uint8_t j;
  
  for(i = 0; i < PIXEL_NUM; ++i)
  {
    for(j = 0; j < 24; ++j)
    {
      pixelBuffer[i][j] = WS_LOW;
    }
  }
  ws281x_show(); 
}

uint32_t ws281x_color(uint8_t red, uint8_t green, uint8_t blue)
{
  return green << 16 | red << 8 | blue;
}

void ws281x_setPixelColor(uint16_t n ,uint32_t GRBcolor)
{
  uint8_t i;
  if(n < PIXEL_NUM)
  {
    for(i = 0; i < 24; ++i)
    {
      pixelBuffer[n][i] = (((GRBcolor << i) & 0X800000) ? WS_HIGH : WS_LOW); // 1000 0000 0000 0000 0000 0000 24位
    }
  }
}
// |     |___|   1111 1000  high level
//  ___         
// |   |_____|   1110 0000  low level

//#define WS_HIGH 0XF8
//#define WS_LOW  0XE0

void ws281x_setPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue)
{
  uint8_t i;
  
  if(n < PIXEL_NUM)
  {
    for(i = 0; i < 24; ++i)
    {
      pixelBuffer[n][i] = (((ws281x_color(red,green,blue) << i) & 0X800000) ? WS_HIGH : WS_LOW);
    }
  }
}

void ws281x_show(void)
{
    DMA_Cmd(DMA1_Channel3, DISABLE );  //关闭USART1 TX DMA1 所指示的通道 
    DMA_ClearFlag(DMA1_FLAG_TC3);    
 	  DMA_SetCurrDataCounter(DMA1_Channel3,24 * PIXEL_NUM );//DMA通道的DMA缓存的大小
 	  DMA_Cmd(DMA1_Channel3, ENABLE);  //使能USART1 TX DMA1 所指示的通道 
}



// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t ws281x_wheel(uint8_t wheelPos) {
  wheelPos = 255 - wheelPos;
  if(wheelPos < 85) {
    return ws281x_color(255 - wheelPos * 3, 0, wheelPos * 3);
  }
  if(wheelPos < 170) {
    wheelPos -= 85;
    return ws281x_color(0, wheelPos * 3, 255 - wheelPos * 3);
  }
  wheelPos -= 170;
  return ws281x_color(wheelPos * 3, 255 - wheelPos * 3, 0);
}

// Fill the dots one after the other with a color
void ws281x_colorWipe(uint32_t c, uint16_t wait) {
  for(uint16_t i=0; i<PIXEL_NUM; i++) {
    ws281x_setPixelColor(i, c);
    ws281x_show();
    delay_ms(wait);
  }
}

void ws281x_rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<PIXEL_NUM; i++) {
      ws281x_setPixelColor(i, ws281x_wheel((i+j) & 255));
    }
    ws281x_show();
    delay_ms(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void ws281x_rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< PIXEL_NUM; i++) {
      ws281x_setPixelColor(i,ws281x_wheel(((i * 256 / PIXEL_NUM) + j) & 255));
    }
    ws281x_show();
    delay_ms(wait);
  }
}

//Theatre-style crawling lights.
void ws281x_theaterChase(uint32_t c, uint16_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < PIXEL_NUM; i=i+3) {
        ws281x_setPixelColor(i+q, c);    //turn every third pixel on
      }
      ws281x_show();

      delay_ms(wait);

      for (uint16_t i=0; i < PIXEL_NUM; i=i+3) {
        ws281x_setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void ws281x_theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < PIXEL_NUM; i=i+3) {
        ws281x_setPixelColor(i+q, ws281x_wheel( (i+j) % 255));    //turn every third pixel on
      }
      ws281x_show();

      delay_ms(wait);

      for (uint16_t i=0; i < PIXEL_NUM; i=i+3) {
        ws281x_setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}








main.c

#include "stm32f10x.h"

#include "usart.h"
#include "delay.h"
#include "../BOARD/ws2812/ws2812.h"


const char s[5];
int8_t i;

int main(void)
{
    usart1_init(115200);
    delay_init();
    ws281x_init();

    while (1) {
//        // Some example procedures showing how to display to the pixels:
//        ws281x_colorWipe(ws281x_color(255, 0, 0), 1000); // Red
//        ws281x_colorWipe(ws281x_color(0, 255, 0), 1000); // Green
//        ws281x_colorWipe(ws281x_color(0, 0, 255), 1000); // Blue
//        //colorWipe(strip.Color(0, 0, 0, 255), 50); // White RGBW
//        // Send a theater pixel chase in...
//        ws281x_theaterChase(ws281x_color(127, 127, 127), 1000); // White
//        ws281x_theaterChase(ws281x_color(127, 0, 0), 1000); // Red
//        ws281x_theaterChase(ws281x_color(0, 0, 127), 1000); // Blue
        //ws281x_rainbow(20);
//        ws281x_rainbowCycle(20);
//        ws281x_theaterChaseRainbow(200);

        for (i = 0; i < PIXEL_NUM; ++i) {
            ws281x_setPixelColor(i, ws281x_color(255, 127, 127));
            ws281x_show();
            delay_ms(10);
        }
    }
}


资料下载

  • [1] 【开源】WS2812 全彩LED灯珠驱动程序

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

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

相关文章

JavaEE(2):前后端项目之间的交互

现在&#xff0c;在网页中通过超链接&#xff0c;表单就可以向后端发送请求&#xff0c;后端也可以正常响应内容。 以前通过表单访问后端的请求方式称为同步请求 同步请求 当网页与后端交互时&#xff0c;前端不能再进行其他操作 服务器端响应回来的内容&#xff0c;会把整个浏…

1895. 二分查找右侧边界

代码 #include<bits/stdc.h> using namespace std; int a[100010],n,q; int yc(int x) {int l1,rn,mid;while(l<r){mid(lr)/2;if(x<a[mid]) rmid-1;else if(x>a[mid]) lmid1;}if(a[l-1]x) return l-1;else return -1; } int main() {int i,x;cin>>n;for(…

迷雾大陆游戏辅助:VMOS云手机强势辅助宠物推荐!

在《迷雾大陆》游戏中&#xff0c;选择合适的宠物是提升战斗力的关键。通过使用VMOS云手机&#xff0c;你可以体验到更为顺畅的游戏过程。VMOS云手机为《迷雾大陆》专门定制了专属的云手机版本&#xff0c;内置游戏安装包&#xff0c;不需要重新下载安装。此外&#xff0c;VMOS…

第一次使用Visual Studio社区版

由于C代码的需要&#xff0c;我开始尝试使用VS。 下载 下载地址&#xff1a;https://visualstudio.microsoft.com/zh-hans/downloads/ 网上关于专业版的教程较少&#xff0c;所以我猜测需求不大&#xff0c;于是我也下载社区版。 安装地址&#xff1a; &#xff08;工作负荷…

从 7000 余项目脱颖而出,飞轮科技《新一代实时分析数据仓库解决方案》荣获 HICOOL 2024 全球创业大赛二等奖

HICOOL 2024 全球创业者峰会于 2024 年 8 月 23 日 -25 日 在中国国际展览中心&#xff08;顺义馆&#xff09;成功举行&#xff0c;峰会以“新质引领 创新共融”为主题&#xff0c;聚焦技术创新、产业融合、新质共享与国际合作四大要素。 在 8 月 23 日晚的峰会开幕式上&…

一文速学-零成本与数据沟通NL2SQL的概念和实现技术

前言 关于NL2SQL的技术&#xff0c;如果大家最近有关注AI圈的话&#xff0c;或多或少都有所了解。其实很多业务场景下&#xff0c;于用户而言更多的是想要获取到最终数据的呈现效果&#xff0c;关于数据是如何获取得到的学习成本&#xff0c;是尽可能越少越好。众所周知当学习…

零知识证明-椭圆曲线(四)

前言 零知识证明(Zero—Knowledge Proof)&#xff0c;是指一种密码学工具&#xff0c;允许互不信任的通信双方之间证明某个命题的有效性&#xff0c;同时不泄露任何额外信息 上章介绍了基础数字知识&#xff0c;这章主要讲 椭圆曲线 方程 2&#xff1a;椭圆曲线方程 y2axybyx3…

知乎知+和信息流广告报价,知乎推广多少钱?

知乎作为中国领先的问答社区&#xff0c;凭借其高质量的内容和庞大的用户群体&#xff0c;已成为众多品牌竞相追逐的营销高地。如何在知乎平台上精准投放广告&#xff0c;实现品牌价值的最大化&#xff0c;成为了众多企业面临的难题。云衔科技&#xff0c;作为数字化营销解决方…

基于MATLAB的涡流函数方法案例+代码

前言 这里介绍一下相关理论和代码----基于MATLAB使用伪谱离散化 三阶龙格库塔时间积分实现涡流函数方法的CFD案例 1. 代码详解 这段 MATLAB 代码实现了一个二维湍流模拟&#xff0c;使用伪谱法在周期性边界条件下解算非线性涡度-流函数方程&#xff1a; M 256; % number o…

驱动开发系列14 - Wayland 详解

目录 一:概述 二:操作系统如何支持 Wayland 三:显卡驱动如何支持 Wayland 四:Wayland 协议介绍 一:概述 Wayland 是一种通信协议,规定了显示服务器与其客户端之间的通信,以及该协议的 C 语言库实现。使用 Wayland 协议的显示服务器称为 Wayland 合成器,因…

SpringBoot项目中mybatis执行sql很慢的排查改造过程(Interceptor插件、fetchSize、隐式转换等)

刚入职公司&#xff0c;就发现公司项目跑sql特别慢&#xff0c;差不多一万条数据插入到数据库要5秒以上&#xff08;没有听错&#xff0c;就是这个速度&#xff09;&#xff0c;查询修改删除也是特别慢。直到22年年底实在是受不了了&#xff0c;我就去排查了一下。 用的是Oracl…

大模型之二十八-语音识别Whisper进阶

在上一篇博客大模型之二十七-语音识别Whisper实例浅析中遗留了几个问题&#xff0c;这里来看一下前两个问题。 1.如果不是Huggingface上可以下载的数据该怎么办&#xff1f; 2.上面的代码是可以训练了&#xff0c;但是训练的时候loss真的会和我们预期一致吗&#xff1f;比如如下…

【netty系列-08】深入Netty组件底层原理和基本实现

Netty系列整体栏目 内容链接地址【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478【三】深入理解NIO的基本原理和底层…

第一个go程序

package main import "fmt" func main(){fmt.Println("Hello World") }hello.go所在目录 运行go程序

美团代付多模版三合一源码 附教程

简介 美团代付多模版三合一源码 附教程 界面

这一轮医疗数字化,沃可趣以医疗专业人员交流成长为中心

沃可趣看见医疗行业人员需求痛点&#xff0c;量身打造数字服务平台&#xff0c;促进知识分享&#xff0c;赋能活动组织。 从电子病历的普及到远程医疗的兴起&#xff0c;从人工智能辅助诊断到大数据在医疗管理中的应用&#xff0c;科技进步正在大力推动医疗领域的发展。然而&a…

Ubuntu系统本地搭建WordPress网站并一键发布内网站点至公网实战

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

嵌入式技术文件、学习资料、在线工具、学习网站、技术论坛,非常全面的分享~~~

一、数据手册查询及下载网站 1、【ALLDATASHEET 自称是最大的在线电子元件数据的搜索引擎】ALLDATASHEETCN.COM - 电子元件和半导体及其他半导体的数据表搜索网站。电子元件和半导体, 集成电路, 二极管, 三端双向可控硅 和其他半导体的https://www.alldatasheetcn.com/ 2、【…

defineProps、defineEmits、 defineExpose的TS写法

小满视频 1 defineProps&#xff1a;父向子传递数据 作用&#xff1a;父组件向子组件传递数据 1.1 传递纯类型参数的方式来声明 父组件中的代码&#xff1a; 父组件App.vue <template><div><span>传递给子组件的响应式数据&#xff1a;</span>&l…

【循环顺序队的实现】

1.队列的逻辑结构 与 抽象数据类型定义 先进先出的线性表 在顺序队列中&#xff0c;我们使用头指针front指向队首元素&#xff1b;用尾指针rear指向队尾元素的下一个位置&#xff08;当然这里的指针是用下标模拟出来的&#xff09; 同时顺序队列中的元素当然是用数组来存储的 …