WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源。其外型与一个5050LED灯珠相同,每
个元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路,还包含有高精度的内
部振荡器和12V高压可编程定电流控制部分,有效保证了像素点光的颜色高度一致。
数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先
送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路
整形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素
点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅仅受限信号传输速度要求。
LED具有低电压驱动,环保节能,亮度高,散射角度大,一致性好,超低功率,超长寿命等优点。将控制电
路集成于LED上面,电路变得更加简单,体积小,安装更加简便。
● IC控制电路与LED点光源公用一个电源。
● 控制电路与RGB芯片集成在一个5050封装的元器件中,构成一个完整的外控像素点。
● 内置信号整形电路,任何一个像素点收到信号后经过波形整形再输出,保证线路波形畸变不会累加。
● 内置上电复位和掉电复位电路。
● 每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示,扫描频率不低于
400Hz/s。
● 串行级联接口,能通过一根信号线完成数据的接收与解码。
● 任意两点传传输距离在不超过3米时无需增加任何电路。
● 当刷新速率30帧/秒时,级联数不小于1024点。
● 数据发送速度可达800Kbps。
● 光的颜色高度一致,性价比高。
● LED全彩发光字灯串,LED全彩软灯条硬灯条,LED护栏管。
● LED点光源,LED像素屏,LED异形屏,各种电子产品,电器设备跑马灯。
ws2812b如果要形成灯带,是串连在一起的,即DIN是连接在DO(或者DOU)引脚上的,连接如下图,模块内部有控制芯片的,会将芯片控制的颜色按位进行传输到对应的LED上。
模块引脚说明
每一个LED都需要24bit数据,形成RGB,RGB的数据格式如下
在这里需要注意的是发送时发送GRB,所以到时在写程序时特别要注意的了。
颜色的RGB码,大家都懂吧,不懂的请看下面操作
每一个LED都需要24bit数据,那么一位要么是1,要么是0了,如何表示数据0或者1的呢,请看官方资料
详细分析,可知,如果要发送1bit的数据0,那么要发送高电平并持续220ns420ns之间再发送低电平750ns1.6us之间。如果要发送1bit的数据1,那么要发送高电平并持续220ns420ns之间再发送低电平750ns1.6us之间。ws2812b内部有控制芯片,能够识别不同的电平宽度,这点很赞。
根据下面的图得知,W2812B全彩灯在传输时必须是连续的,如果中间有超过300us的间隔,则会复位,即再传数据就不会跟之前的数据进行连接在一起了,而是重新开始点亮灯了。
DMA:DMA(Direct Memory Access,直接存储器访问) ,STM32的DMA使用技术在这里就不多讲解了,在这里使用的是**存储(SRAM)到外设(SPI)**的方式进行数据搬运。
SPI:SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线。这个是不是感觉很难与W2812B全彩灯挂勾呢。理由如下:
1、SPI总线传输过程只传输数据,IIC总线使用外设库的方式需要发送启动信号、结束等信号;USART也是要发送启动与结束信号;不适合W2812B灯时序开发。
1、SPI是高速总线,能够快速传输数据,W2812B一位数据只需要(1.6us+420ns) = 1.62us; W2812B一个灯所需要的最多时间:(1.6us+420ns)*24 = 38.88us。对传输速度比较高。
2、SPI是STM32外设,不需要CPU去参考数据处理,直接稳定可以输出数据。
那么如何通过SPI的数据来表示位数据0与1呢,以STM32F407ZET6的SPI2为使用来分析。SPI2挂在APB1总线下,也就是说明SPI2的时钟频率为42MHZ,学习SPI的同学都知道,可以对SPI2进行分频的,这个分频得有一定的技巧。这里我以8分频为例子。42/8 = 5.25MHZ, 那么它的时钟周期:1/5.25MHZ = 190ns(约等)。表示传输一个字节的时间大概是:190ns8=1.52us(而W2812B灯一位数据所需要时间:970ns~1.62us)。这样一看,是不是这个分频是满足的啊。
设置好分频之后。来看看如果用SPI表示W2812B的位。其实要记住的原理是SPI发送的字节来表示W2812B的位。怎么做的,请看图。
这回看懂了吧,W2812B的一位数据,SPI输出一个字节来表示即可,那么如果某个W2812B灯显示为白色,即RGB为:0xFFFFF(24位),需要的时间范围:23.280us(970ns24)~38.88us(1.62us24);那么SPI需要输出的字节数:24个(248 = 192位数据,大约需要190ns*192 = 36.480us),这样一算,完成符合W2812B传输时序。
ws2812.h
#ifndef __WS2812_H
#define __WS2812_H
/* Includes ------------------------------------------------------------------*/
#include "sys.h"
#include "delay.h"
#include "usart1_printf.h"
typedef struct
{
u8 R;
u8 G;
u8 B;
} RGBColor_TypeDef;
//extern u8 pixelBuffer[][24];
extern const RGBColor_TypeDef RED ;
extern const RGBColor_TypeDef GREEN;
extern const RGBColor_TypeDef BLUE;
extern const RGBColor_TypeDef SKY;
extern const RGBColor_TypeDef MAGENTA ;
extern const RGBColor_TypeDef YELLOW ;
extern const RGBColor_TypeDef ORANGE;
extern const RGBColor_TypeDef BLACK;
extern const RGBColor_TypeDef WHITE;
extern const RGBColor_TypeDef PURPLE;
/* Exported constants --------------------------------------------------------*/
#define Pixel_S1_NUM 6 //灯珠 RGB数量
/**************************************
说明:
WS2812B编码协议(单位:ns):
min typ max
bit 0
T0H: 220 - 420
T0L: 750 - 1600
bit 1
T1H: 750 - 1600
T1L: 220 - 420
RESET: time > 300us
8分频APB1,42MHz/8 = 5.25MHz
时钟周期为:1/5.25/1e6 = 1.90e-7=190ns
**************************************/
#define CODE0 0xC0 // 0码, 发送的时间 1100 0000 根据不同的SCK适当调整
#define CODE1 0xFC // 1码, 发送的时间 1111 1100
void WS2812b_Configuration(void);
/* Basic Color Effects -------------------------------------------------------*/
void RGB_RED(u16 Pixel_LEN);
void RGB_GREEN(u16 Pixel_LEN);
void RGB_BLUE(u16 Pixel_LEN);
void RGB_YELLOW(u16 Pixel_LEN);
void RGB_MAGENTA(u16 Pixel_LEN);
void RGB_BLACK(u16 Pixel_LEN);
void RGB_WHITE(u16 Pixel_LEN);
void RGB_SKY(u16 Pixel_LEN);
void RGB_ORANGE(u16 Pixel_LEN);
void RGB_PURPLE(u16 Pixel_LEN);
/* Complicated Color Effects -------------------------------------------------*/
void rainbowCycle(u16 Pixel_LEN);
#endif /* __WS2812_H */
ws2812.c
#include "WS2812.h"
// Some Static Colors
const RGBColor_TypeDef RED = {
255,0,0};
const RGBColor_TypeDef GREEN = {
0,255,0};
const RGBColor_TypeDef BLUE = {
0,0,255};
const RGBColor_TypeDef SKY = {
0,255,255};
const RGBColor_TypeDef MAGENTA = {
255,0,255};
const RGBColor_TypeDef YELLOW = {
255,255,0};
const RGBColor_TypeDef ORANGE = {
127,106,0};
const RGBColor_TypeDef BLACK = {
0,0,0};
const RGBColor_TypeDef WHITE = {
255,255,255};
const RGBColor_TypeDef PURPLE = {
65,105,225};
static u8 pixelBuffer[Pixel_S1_NUM][24]; //灯珠
/***********************************************************************************************
** name: WS2812b_Configuration
** function: WS2812B SPI DMA总线初始化
**parameter: void
************************************************************************************************/
/*****************************************
说明:
SPI2:
引脚:使用的是PB15引脚,在TFTLCD下的LCD BL
时钟:根据总线图,SPI2由APB1(42MHz)分频而来
****************************************/
void WS2812b_Configuration(void){
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //使能SPI2时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE); //DMA1时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; //PB15复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化
GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2); //PB15复用为 SPI2
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置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; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //42M/8=5.25M
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
DMA_DeInit(DMA1_Stream4);
while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE){
} //等待DMA可配置
DMA_InitStructure.DMA_Channel = DMA_Channel_0; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR; //DMA外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)pixelBuffer; //DMA 存储器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //存储器到外设模式
DMA_InitStructure.DMA_BufferSize = Pixel_S1_NUM * 24; //数据传输量
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_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输
DMA_Init(DMA1_Stream4, &DMA_InitStructure); //初始化DMA Stream
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE); // 使能SPI2的DMA发送
DMA_Cmd(DMA1_Stream4, DISABLE); //关闭DMA传输
while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE){
} //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA1_Stream4,Pixel_S1_NUM * 24); //数据传输量
DMA_Cmd(DMA1_Stream4, ENABLE);
delay_ms(1);
RGB_BLACK(Pixel_S1_NUM); //RGB RESET
delay_ms(1);
}
/***********************************************************************************************
** name: rgb_SetColor
** function: 设定某个RGB LED的颜色
**parameter: void
** return: void
************************************************************************************************/
void rgb_SetColor(u16 LedId, RGBColor_TypeDef Color){
u16 i;
if( LedId > ( Pixel_S1_NUM ) ){
printf("Error:Out of Range!\r\n");
return; //to avoid overflow
}
for(i=0;i<=7;i++){
pixelBuffer[LedId][i]= ( (Color.G & (1 << (7 -i)) )? (CODE1):CODE0 );
}
for(i=8;i<=15;i++){
pixelBuffer[LedId][i]= ( (Color.R & (1 << (15-i)) )? (CODE1):CODE0 );
}
for(i=16;i<=23;i++){
pixelBuffer[LedId][i]= ( (Color.B & (1 << (23-i)) )? (CODE1):CODE0 );
}
}
/***********************************************************************************************
** name: rgb_SendArray
** function: Configure colors to RGB pixel series.
RGBColor_TypeDef: pointer to a RGBColor_TypeDef structure that contains the color configuration information for the RGB pixel.
**parameter: void
** return: void
************************************************************************************************/
void rgb_SendArray(void){
if(DMA_GetFlagStatus(DMA1_Stream4,DMA_FLAG_TCIF4) != RESET){
//等待DMA1_Steam5传输完成
DMA_ClearFlag(DMA1_Stream4,DMA_FLAG_TCIF4); //清除DMA1_Steam5传输完成标志,先预想DMA_FLAG_TCIF0的零,代表的是Channel
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE); // 使能SPI3的DMA发送
DMA_Cmd(DMA1_Stream4, DISABLE); //关闭DMA传输
while (DMA_GetCmdStatus(DMA1_Stream4) != DISABLE){
} //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA1_Stream4,Pixel_S1_NUM * 24); //数据传输量
DMA_Cmd(DMA1_Stream4, ENABLE); //开启DMA传输
}
}
/***********************************************************************************************
** name: RGB_RED
** function: 设定颜色为RED
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_RED(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,RED);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: RGB_PURPLE
** function: 设定颜色为PURPLE
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_PURPLE(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,PURPLE);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: RGB_SKY
** function: 设定颜色为SKY
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_SKY(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,SKY);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: RGB_MAGENTA
** function: 设定颜色为MAGENTA
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_MAGENTA(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,MAGENTA);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: RGB_ORANGE
** function: 设定颜色为ORANGE
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_ORANGE(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,ORANGE);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: RGB_GREEN
** function: 设定颜色为GREEN
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_GREEN(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,GREEN);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: RGB_BLUE
** function: 设定颜色为BLUE
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_BLUE(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,BLUE);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: RGB_YELLOW
** function: 设定颜色为YELLOW
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_YELLOW(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,YELLOW);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: RGB_BLACK
** function: 设定颜色为all-off
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_BLACK(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,BLACK);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: RGB_WHITE
** function: 设定颜色为WHITE
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void RGB_WHITE(u16 Pixel_LEN){
u16 i;
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,WHITE);
}
rgb_SendArray();
}
/***********************************************************************************************
** name: Colourful_Wheel
** function: 将颜色转换为GRB
**parameter: WheelPos 颜色数值
** return: RGBColor_TypeDef 颜色GRB
************************************************************************************************/
RGBColor_TypeDef Colourful_Wheel(u8 WheelPos){
RGBColor_TypeDef color;
WheelPos = 255 - WheelPos;
if(WheelPos < 85){
color.R = 255 - WheelPos * 3;
color.G = 0;
color.B = WheelPos * 3;
return color;
}
if(WheelPos < 170){
WheelPos -= 85;
color.R = 0;
color.G = WheelPos * 3;
color.B = 255 - WheelPos * 3;
return color;
}
WheelPos -= 170;
color.R = WheelPos * 3;
color.G = 255 - WheelPos * 3;
color.B = 0;
return color;
}
/***********************************************************************************************
** name: rainbowCycle
** function: 呼吸灯功能
**parameter: Pixel_LEN 灯珠数
** return: void
************************************************************************************************/
void rainbowCycle(u16 Pixel_LEN){
u16 i, j = 0;
for(j = 0; j < 1023; j++){
// 1 cycles of all colors on wheel
for(i = 0; i < Pixel_LEN; i++){
rgb_SetColor(i,Colourful_Wheel(((i * 256 / Pixel_LEN) + j)&255));
}
rgb_SendArray();
delay_ms(20);
}
}
main.c
int main(void)
{
Delay_Init();
Usart1_Init(115200);
WS2812b_Configuration();
delay_ms(20);
RGB_GREEN(8); //8个灯全绿
delay_s(2);
RGB_YELLOW(8); //8个灯全黄
delay_s(2);
RGB_RED(8); //8个灯全红
delay_s(2);
RGB_BLUE(8); //8个灯全蓝
while (1)
{
//printf("Activation completed!\r\n");
}
}