我的另一个项目中已经对WS2812点阵进行了应用,并可以显示字符串和常用图标:《项目》 之 ESP8266 心知天气 + 时钟 + WS2812点阵屏 + B站粉丝计数,这也非常感谢 我的舍友 帮助我移植驱动库。
目录
1.WS2812B灯珠简介
可以看到,WS2812灯珠支持级联,我们只需要一个引脚即可驱动 1024 个灯珠,这无疑给我们省了很多IO口,驱动方式也是异常的简单,只需要按照格式去改变IO口电平就可以了,这让任何一款单片机都可以使用这款灯珠;
接下来,让我带领大家从点亮一个灯开始吧~
2.直接改变IO口电平来驱动WS2812灯珠
本次使用的硬件:ESP32 + 8*8 WS2812点阵
开发环境:Arduino IDE
2.1.点亮一个灯
我们看下 WS2812 要求的 0 和 1 是什么样的格式:
用IO口电平改变去模拟 0码 和 1码:
//模拟的ns,不准确!!!
void delay_ns(float a)
{
for(int j;j<a;j++)
NOP();
}
//0码
void GPIO_0()
{
digitalWrite(RGB_PIN,HIGH);
delay_ns(300);
digitalWrite(RGB_PIN,LOW);
delayMicroseconds(1);
}
//1码
void GPIO_1()
{
digitalWrite(RGB_PIN,HIGH);
delayMicroseconds(1);
digitalWrite(RGB_PIN,LOW);
delay_ns(300);
}
接下来,我们按照格式去改变IO口电平:
常用的 WS2812 灯珠有两种数据结构,一种为 RGB 顺序,一种为 GRB 顺序,我手中的便是 RGB 格式,接下来我就用RGB顺序来演示,如果你的是GRB格式,你只需要改变发送顺序即可,这并不难。
假如我们要让一个灯亮红色,即要发送11111111 00000000 00000000 ,前面的11111111 即对应了255亮度的红色,我们发送8个GPIO_1()和16个GPIO_0()即可:
颜色 | 二进制 | 16进制 |
---|---|---|
红色 | 11111111 00000000 00000000 | 0xFF0000 |
绿色 | 00000000 11111111 00000000 | 0x00FF00 |
蓝色 | 00000000 00000000 11111111 | 0x0000FF |
黑色 | 00000000 00000000 00000000 | 0x000000 |
白色 | 11111111 11111111 11111111 | 0xFFFFFF |
for (int i = 0; i < 8; i++)
GPIO_1();
for (int i = 0; i < 8; i++)
GPIO_0();
for (int i = 0; i < 8; i++)
GPIO_0();
接下来,你便可以尝试点亮各种颜色:
#include <WiFi.h>
//定义WS2812B引脚
#define RGB_PIN 27
//模拟纳秒延时,不准确
void delay_ns(float a)
{
for(int j;j<a;j++)
NOP();
}
//0码
void GPIO_0()
{
digitalWrite(RGB_PIN,HIGH);
delay_ns(300);
digitalWrite(RGB_PIN,LOW);
delayMicroseconds(1);
}
//1码
void GPIO_1()
{
digitalWrite(RGB_PIN,HIGH);
delayMicroseconds(1);
digitalWrite(RGB_PIN,LOW);
delay_ns(300);
}
//红色
void RGB_RED()
{
for (int i = 0; i < 8; i++)
GPIO_1();
for (int i = 0; i < 8; i++)
GPIO_0();
for (int i = 0; i < 8; i++)
GPIO_0();
}
//绿色
void RGB_GREEN()
{
for (int i = 0; i < 8; i++)
GPIO_0();
for (int i = 0; i < 8; i++)
GPIO_1();
for (int i = 0; i < 8; i++)
GPIO_0();
}
//蓝色
void RGB_BLUE()
{
for (int i = 0; i < 8; i++)
GPIO_0();
for (int i = 0; i < 8; i++)
GPIO_0();
for (int i = 0; i < 8; i++)
GPIO_1();
}
//黑色,即熄灭
void RGB_BLACK()
{
for (int i = 0; i < 8; i++)
GPIO_0();
for (int i = 0; i < 8; i++)
GPIO_0();
for (int i = 0; i < 8; i++)
GPIO_0();
}
void setup(){
pinMode(RGB_PIN,OUTPUT);
digitalWrite(RGB_PIN,LOW);
}
void loop(){
RGB_RED();//红
delay(1000);
RGB_GREEN();//绿色
delay(1000);
RGB_BLUE();//蓝色
delay(1000);
RGB_BLACK();//黑色,即熄灭
delay(1000);
}
2.2.设置WS2812灯为任意颜色
我们可以写个函数,形参为你想要发送的颜色,例如:你想要的发送红色(0xFF0000),你只需要调用WS2812_Send_Date(0xFF0000)即可,是不是简单了很多。
//获取的某一位的值
#define getbit(x,y) ((x) >> (y)&1)
void WS2812_Send_Date(int32_t date)
{
for(int i= 1;i<25;i++)
{
if(getbit(date,24-i))
GPIO_1();
else
GPIO_0();
}
}
接下来点亮各种颜色试试吧:
#include <WiFi.h>
#include <math.h>
//定义WS2812B引脚
#define RGB_PIN 27
//指定的某一位数置1
#define setbit(x,y) x|=(1<<y)
//指定的某一位数置0
#define clrbit(x,y) x&=~(1<<y)
//指定的某一位数取反
#define reversebit(x,y) x^=(1<<y)
//获取的某一位的值
#define getbit(x,y) ((x) >> (y)&1)
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
#define BLACK 0x000000
#define WHITE 0xFFFFFF
void delay_ns(float a)
{
for(int j;j<(a/2);j++)
NOP();
}
void GPIO_0()
{
digitalWrite(RGB_PIN,HIGH);
delay_ns(300);
digitalWrite(RGB_PIN,LOW);
delayMicroseconds(1);
}
void GPIO_1()
{
digitalWrite(RGB_PIN,HIGH);
delayMicroseconds(1);
digitalWrite(RGB_PIN,LOW);
delay_ns(300);
}
void WS2812_Send_Date(int32_t date)
{
for(int i= 1;i<25;i++)
{
if(getbit(date,24-i))
GPIO_1();
else
GPIO_0();
Serial.print(getbit(date,24-i));
}
Serial.println("");
}
void setup(){
Serial.begin(115200);
pinMode(RGB_PIN,OUTPUT);
digitalWrite(RGB_PIN,LOW);
}
void loop(){
WS2812_Send_Date(RED);
delay(1000);
WS2812_Send_Date(GREEN);
delay(1000);
WS2812_Send_Date(BLUE);
delay(1000);
WS2812_Send_Date(BLACK);
delay(1000);
WS2812_Send_Date(WHITE);
delay(1000);
}
2.3.点亮多个灯珠,并设置不同颜色
按照数据格式,有几个灯珠,我们只需要发送几次数据就行,注意多个灯珠数据发送不可以停顿,要一次性发送出去,不然灯珠会认为你发了多个帧的数据。
#include <WiFi.h>
#include <math.h>
//定义WS2812B引脚
#define RGB_PIN 27
//指定的某一位数置1
#define setbit(x,y) x|=(1<<y)
//指定的某一位数置0
#define clrbit(x,y) x&=~(1<<y)
//指定的某一位数取反
#define reversebit(x,y) x^=(1<<y)
//获取的某一位的值
#define getbit(x,y) ((x) >> (y)&1)
//定义一些常用的颜色
#define RED 0xFF0000
#define GREEN 0x00FF00
#define BLUE 0x0000FF
#define BLACK 0x000000
#define WHITE 0xFFFFFF
//WS2812B灯数目
#define RGB_NUM 64
//如果是-GRB-格式
//#define R 1
//#define G 0
//#define B 2
//#define BRIGHT 4
//如果是-RGB-格式
#define R 0 //红色
#define G 1 //绿色
#define B 2 //蓝色
#define BRIGHT 4 //亮度
uint8_t WS2812[RGB_NUM][4];//灯状态数组
//模拟的ns,并不准确!!!
void delay_ns(float a)
{
for(int j;j<a;j++)
NOP();
}
//将所有灯的状态发送出去
void WS2812_send_date()
{
uint8_t i,j,k,a;
for(k=0;k<RGB_NUM;k++)
{
for(j=0;j<3;j++)
{
switch(j)
{
case R:a=WS2812[k][R];break;
case G:a=WS2812[k][G];break;
case B:a=WS2812[k][B];break;
}
for(i=1;i<9;i++)
{
if(getbit(a,8-i))//发送1码
{
digitalWrite(RGB_PIN,HIGH);
delayMicroseconds(1);
digitalWrite(RGB_PIN,LOW);
delay_ns(300);
}
else//发送0码
{
digitalWrite(RGB_PIN,HIGH);
delay_ns(300);
digitalWrite(RGB_PIN,LOW);
delayMicroseconds(1);
}
}
}
}
delayMicroseconds(600);//每帧数据相隔400us
}
//设置灯RGB颜色,格式为0~0xFFFFFF
void WS2812_colour_set(int32_t colour,uint8_t num)
{
int32_t colour_buff = colour;
WS2812[num][R] = (colour>>16)&0xFF;colour = colour_buff;
WS2812[num][G] = (colour>>8)&0xFF;colour = colour_buff;
WS2812[num][B] = colour&0xFF;
}
//设置灯R颜色,格式为0~255
void WS2812_RED_set(int8_t r_colour,int8_t num)
{
WS2812[num][R] = r_colour;
}
//设置灯G颜色,格式为0~255
void WS2812_GREEN_set(int8_t g_colour,int8_t num)
{
WS2812[num][G] = g_colour;
}
//设置灯B颜色,格式为0~255
void WS2812_BLUE_set(int8_t b_colour,int8_t num)
{
WS2812[num][B] = b_colour;
}
void setup(){
Serial.begin(115200);
pinMode(RGB_PIN,OUTPUT);
digitalWrite(RGB_PIN,LOW);
}
void loop(){
//红色测试
for(int i = 0;i<RGB_NUM;i++)
WS2812_colour_set(RED,i);
WS2812_send_date();
delay(1000);
//绿色测试
for(int i = 0;i<RGB_NUM;i++)
WS2812_colour_set(GREEN,i);
WS2812_send_date();
delay(1000);
//蓝色测试
for(int i = 0;i<RGB_NUM;i++)
WS2812_colour_set(BLUE,i);
WS2812_send_date();
delay(1000);
//红色渐变
for(int i = 0;i<RGB_NUM;i++)
WS2812_colour_set(0x000000,i);
for(int j = 0;j<255;j++)
{
for(int i = 0;i<RGB_NUM;i++)
{
WS2812_RED_set((j*i)/RGB_NUM,i);
}
WS2812_send_date();
delay(100);
}
//绿色渐变
for(int i = 0;i<RGB_NUM;i++)
WS2812_colour_set(0x000000,i);
for(int k = 0;k<255;k++)
{
for(int i = 0;i<RGB_NUM;i++)
{
WS2812_GREEN_set((k*i)/RGB_NUM,i);
}
WS2812_send_date();
delay(100);
}
//蓝色渐变
for(int i = 0;i<RGB_NUM;i++)
WS2812_colour_set(0x000000,i);
for(int h = 0;h<255;h++)
{
for(int i = 0;i<RGB_NUM;i++)
{
WS2812_BLUE_set((h*i)/RGB_NUM,i);
}
WS2812_send_date();
delay(100);
}
delay(200);
}
3.用Arduino的库去驱动(方便很多)
3.1.FastLED库
FsatLED 库下载地址:https://github.com/FastLED/FastLED
3.2.Adafruit_NeoPixel-master库
Adafruit_NeoPixel 库下载地址:https://github.com/adafruit/Adafruit_NeoPixel
未完待续~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~