基于STM32F407的DMA+SPI实现WS2812B全彩灯实现(可以驱动上百个灯)

基于STM32F407的DMA+SPI实现WS2812B全彩灯实现

  • 一、WS2812B概述及驱动原理
    • 1、产品概述
    • 2、主要特点
    • 3、主要应用领域
    • 4、实物与引脚
    • 5、驱动原理
  • 二、DMA+SPI原理
    • 1、DMA
    • 2、SPI
      • 2.1 SPI概述
      • 2.2 SPI与WS2812B位关系
  • 三、程序实现
  • 四、实验现象

WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源。元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路,还包含有高精度的内部振荡器和12V高压可编程定电流控制部分,有效保证了像素点光的颜色高度一致。可做成长条灯带,普通用于房屋装饰,婚庆现场装饰,舞台装饰等,所以掌握WS2812BLED的驱动还是非常重要的,这里以最基本的51内核的STC8单片机为例来驱动WS2812BLED

基于STM32F407的DMA+SPI实现WS2812B全彩灯实现(可以驱动上百个灯)_第1张图片
基于STM32F407的DMA+SPI实现WS2812B全彩灯实现(可以驱动上百个灯)_第2张图片

一、WS2812B概述及驱动原理

1、产品概述

WS2812B是一个集控制电路与发光电路于一体的智能外控LED光源。其外型与一个5050LED灯珠相同,每
个元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路,还包含有高精度的内
部振荡器和12V高压可编程定电流控制部分,有效保证了像素点光的颜色高度一致。
数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先
送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路
整形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。像素
点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅仅受限信号传输速度要求。
LED具有低电压驱动,环保节能,亮度高,散射角度大,一致性好,超低功率,超长寿命等优点。将控制电
路集成于LED上面,电路变得更加简单,体积小,安装更加简便。

2、主要特点

● IC控制电路与LED点光源公用一个电源。
● 控制电路与RGB芯片集成在一个5050封装的元器件中,构成一个完整的外控像素点。
● 内置信号整形电路,任何一个像素点收到信号后经过波形整形再输出,保证线路波形畸变不会累加。
● 内置上电复位和掉电复位电路。
● 每个像素点的三基色颜色可实现256级亮度显示,完成16777216种颜色的全真色彩显示,扫描频率不低于
400Hz/s。
● 串行级联接口,能通过一根信号线完成数据的接收与解码。
● 任意两点传传输距离在不超过3米时无需增加任何电路。
● 当刷新速率30帧/秒时,级联数不小于1024点。
● 数据发送速度可达800Kbps。
● 光的颜色高度一致,性价比高。

3、主要应用领域

● LED全彩发光字灯串,LED全彩软灯条硬灯条,LED护栏管。
● LED点光源,LED像素屏,LED异形屏,各种电子产品,电器设备跑马灯。

4、实物与引脚

在这里插入图片描述
ws2812b如果要形成灯带,是串连在一起的,即DIN是连接在DO(或者DOU)引脚上的,连接如下图,模块内部有控制芯片的,会将芯片控制的颜色按位进行传输到对应的LED上。
在这里插入图片描述
模块引脚说明
在这里插入图片描述

5、驱动原理

每一个LED都需要24bit数据,形成RGB,RGB的数据格式如下
在这里插入图片描述
在这里需要注意的是发送时发送GRB,所以到时在写程序时特别要注意的了。
颜色的RGB码,大家都懂吧,不懂的请看下面操作
在这里插入图片描述
每一个LED都需要24bit数据,那么一位要么是1,要么是0了,如何表示数据0或者1的呢,请看官方资料

基于STM32F407的DMA+SPI实现WS2812B全彩灯实现(可以驱动上百个灯)_第3张图片
详细分析,可知,如果要发送1bit的数据0,那么要发送高电平并持续220ns420ns之间再发送低电平750ns1.6us之间。如果要发送1bit的数据1,那么要发送高电平并持续220ns420ns之间再发送低电平750ns1.6us之间。ws2812b内部有控制芯片,能够识别不同的电平宽度,这点很赞。

根据下面的图得知,W2812B全彩灯在传输时必须是连续的,如果中间有超过300us的间隔,则会复位,即再传数据就不会跟之前的数据进行连接在一起了,而是重新开始点亮灯了。

基于STM32F407的DMA+SPI实现WS2812B全彩灯实现(可以驱动上百个灯)_第4张图片

二、DMA+SPI原理

1、DMA

DMA:DMA(Direct Memory Access,直接存储器访问) ,STM32的DMA使用技术在这里就不多讲解了,在这里使用的是**存储(SRAM)到外设(SPI)**的方式进行数据搬运。

2、SPI

2.1 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去参考数据处理,直接稳定可以输出数据。

2.2 SPI与WS2812B位关系

那么如何通过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的位。怎么做的,请看图。
基于STM32F407的DMA+SPI实现WS2812B全彩灯实现(可以驱动上百个灯)_第5张图片
这回看懂了吧,W2812B的一位数据,SPI输出一个字节来表示即可,那么如果某个W2812B灯显示为白色,即RGB为:0xFFFFF(24位),需要的时间范围:23.280us(970ns
24)~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");
	}
}

四、实验现象

基于STM32F407的DMA+SPI实现WS2812B全彩灯实现(可以驱动上百个灯)_第6张图片
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

你可能感兴趣的:(传感器,嵌入式,STM32,stm32,单片机,嵌入式硬件)