打印
[活动专区]

【AT-START-WB415测评】+ SPI+DMA驱动WS2812B

[复制链接]
1881|21
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 袁胜富 于 2022-9-10 23:47 编辑

偶然心血来潮,准备用AT32WB415点个WS2812灯。首先我们先来学习理论知识吧。

一、WS2812的认识
Features and Benefits
 The control circuit and the LED share the only power source.
 Control circuit and RGB chip are integrated in a package of 5050 components, to form a complete addressable
pixel.
 Built-in signal reshaping circuit, after wave reshaping to the next driver, ensure wave-form distortion not
accumulate.
 Built-in electric reset circuit and power lost reset circuit.
 Each pixel of the three primary color can achieve 256 brightness display, completed 16777216 color full color
display, and scan frequency is of 2KHz.
 Cascading port transmission signal by single line.
 Any two point the distance not more than 5m transmission signal without any increase circuit.
 When the refresh rate is 30fps, cascade number are not less than 1024 pixels.
 Send data at speeds of 800Kbps.
 The color of the light is highly consistent, cost-effective.
 No need for external electric components and even the capacitor.
以上是数据手册摘抄下来的,然后来看看WS2812的数据格式
数据传输时间

数据序列和级联法

数据传输以及数据组成方式
通过以上资料我们得知一个灯的数据由24bit组成,GRB中G占位 ,R占8位,B占8位,G代表绿色,R代表红色,B代表蓝色,通过三种颜色可以显示出我们要的颜色。
WS2812B的数据传输速率位800K,1/8000000=1.25us。通过数据手册得知,0码的T0H时间为220ns~380ns,T0L时间为580ns~1us.1码的T1H时间为580ns~1us,T1L时间为220ns~380ns。WS2812使用方法一般为级联使用,由于本身拥有锁存器所以数据流经D1然后流经D2.。。。。

二、AT32WB415的SPI和DMA理论

在此应用中我们只用到SPI的MOSI引脚,故SPI的工作模式设置为发送模式就行了,通过查阅芯片的Datasheet知道此芯片只用一个SPI为SPI2,MOSI为PB15引脚。
大家务必记住在使用SPI的时候一定要注意CS_SOFT模式是CS的内部电平应该置为高,只有这样SPI才能工作在主机模式,也就是MASTER Mode。
通过芯片手册知道SPI2挂载在APB1总线。在程序中配置AHB为144MHZ ,APB1为72MHZ。
DMA分为DMA1和DMA2,在手册中了解到AT32WB415的DMA很有意思,可以分为固定请求映射模式和灵活的弹性请求映射模式,固定请求映射模式,我实际测出SPI2的发送DMA通道在DMA1的Channel5也就是通道5上。为什么说是测出呢,因为手册没告诉我们啊!请看如下图片

有意思的是,此芯片的弹性请求映射模式是很不错,意思就是支持DMA的外设都可以任意使用任意DMA通道,我测试了一下在此模式下将SPI2的TX映射在DMA1的Channel4上是可以工作的。

三、理论总结

通过一和二的分析,我们需要利用SPI2发送数据的高低电平时间来模拟WS2812B的数据时序。通过分析计算,我得出要想模拟出合适的时序,SPI2的波特率应该为18MHZ,也就是APB1要4分频(DIV4)。1/18MHZ=55ns,55*16=880ns,880ns在WS2812B的数据传输时间内,这里的16就是SPI的数据模式是16位模式.55*5=275ns用来表示0码的高电平时间。55*11=605ns用来表示1码的高电平时间。通过以上分析0码的16位数据的二进制:1111 1000 0000 0000 ,十六进制0xF800。1码的数据的二进制:1111 1111 1110 0000 ,十六进制0xFFE0.
四、程序编写

1.头文件
#ifndef __SPI_DMA_WS2812_H
#define __SPI_DMA_WS2812_H

#include "at32wb415.h"
#include "bsp_sys.h"
#include "stdint.h"
#include "bsp_sys.h"
#include "tos_k.h"
/*
要将系统时钟设置为72M,SPI分频数设置为4,则SPI的通信频率为18M,1s/7M≈55ns 即传输一位数据的时间约为55纳秒(ns)16*55 =880ns 符合WS2812

5*55 = 277ns   11*55 = 611ns  符合WS281X芯片的通信时序。

1111 1111 1110 0000high level  (十六进制:0XFFE0)表示WS281X的1码

1111 1000 0000 0000  low level   (十六进制:0XF800)表示WS281X的0码
程序头文件部分: 通过宏的方式定义了灯珠个数和WS281X的0码和1码。
*/

//  _____   

// |     |___|    1111 1111 1110 0000 high level

//  ___         

// |   |_____|   1111 1000 0000 0000  low level
#define Delay_ms(wait)  tos_task_delay(wait)
#define WS2812B   //选择WS2812B
#define PIXEL_NUM1  2     //RGB灯的数量                                         //灯带1灯数(组数)
#define PIXEL_NUM2  2                                                          //灯带2灯数(组数)
#define PIXEL_NUM3  2                                                          //灯带3灯数(组数)
#define PIXEL_NUM4  2                                                          //灯带4灯数(组数)

#define PIXEL_NUM  (PIXEL_NUM1+PIXEL_NUM2+PIXEL_NUM3+PIXEL_NUM4)//灯数统计
#define GRB  24   //3*8 //Red8位 Green8位 Blue8位 加起来24位

#ifdef WS2812B
#define WS_HIGH 0xFFE0
#endif
#define WS_LOW  0xF800
/*   
      R      G      B
红: 255     0      0
黄: 255    255     0
绿:  0     255     0
青:  0     255    255
蓝:  0      0     255
紫: 255     0     255
白: 255    255    255
*/
typedef union _rgbPixelBuffer
{
    struct
    {
                        uint16_t PixelBuffer1[PIXEL_NUM1][GRB];
                        uint16_t PixelBuffer2[PIXEL_NUM2][GRB];
                        uint16_t PixelBuffer3[PIXEL_NUM3][GRB];
                        uint16_t PixelBuffer4[PIXEL_NUM4][GRB];
    }buff;
    uint16_t All_Buffer[PIXEL_NUM][GRB];
}RGB_PixelBuffer,*PRGB_PixelBuffer;
void WS281x_Init(void);
void WS281x_CloseAll(void);
uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue);
void WS281x_SetPixelColor(uint16_t n, uint32_t GRBColor);
void WS281x_SetPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue);
void WS281x_Show(void);
void WS281x_RainbowCycle(uint8_t wait);
void WS281x_TheaterChase(uint32_t c, uint8_t wait);
void WS281x_ColorWipe(uint32_t c, uint8_t wait);
void WS281x_Rainbow(uint8_t wait);
void WS281x_TheaterChaseRainbow(uint8_t wait);
void RGB_DEBUG_TEST(void);
#endif
源文件
#include "spi_dma_ws2812.h"
#include "string.h"
#include "bsp_sys.h"

RGB_PixelBuffer pixelBuffer;
uint32_t SPI_clock;
        
void WS281x_Init(void)
{
  spi_init_type spi_init_struct;
  gpio_init_type gpio_initstructure;
  dma_init_type dma_init_struct = {0};
  crm_clocks_freq_type clocks_structer;
  crm_clocks_freq_get(&clocks_structer);
  SPI_clock = clocks_structer.apb1_freq;//72M HZ
  crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_SPI2_PERIPH_CLOCK, TRUE);//打开SPI2的外设时钟
  gpio_initstructure.gpio_out_type       = GPIO_OUTPUT_PUSH_PULL;
  gpio_initstructure.gpio_pull           = GPIO_PULL_DOWN;
  gpio_initstructure.gpio_mode           = GPIO_MODE_MUX;
  gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_initstructure.gpio_pins           = GPIO_PINS_15;
  gpio_init(GPIOB, &gpio_initstructure);

  /* dma2 channel1 configuration */
  dma_reset(DMA1_CHANNEL4);
  dma_init_struct.buffer_size = 0;
  dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
  dma_init_struct.memory_base_addr = (uint32_t)&pixelBuffer;
  dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
  dma_init_struct.memory_inc_enable = TRUE;
  dma_init_struct.peripheral_base_addr = (uint32_t)&SPI2->dt;
  dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
  dma_init_struct.peripheral_inc_enable = FALSE;
  dma_init_struct.priority = DMA_PRIORITY_VERY_HIGH;
  dma_init_struct.loop_mode_enable = FALSE;
  dma_init(DMA1_CHANNEL4, &dma_init_struct);
  /* enable dma channel */
  dma_channel_enable(DMA1_CHANNEL4, TRUE);
  dma_flexible_config(DMA1,FLEX_CHANNEL4,DMA_FLEXIBLE_SPI2_TX);
  spi_default_para_init(&spi_init_struct);//SPI2参数默认初始化//
  spi_init_struct.transmission_mode                  = SPI_TRANSMIT_HALF_DUPLEX_TX;//仅作为发送模式
  spi_init_struct.master_slave_mode                  = SPI_MODE_MASTER;//SPI工作在主机模式
  spi_init_struct.mclk_freq_division                 = SPI_MCLK_DIV_4;//18MZ
  spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;//高位先行
  spi_init_struct.frame_bit_num          = SPI_FRAME_16BIT;//半字数据模式
  spi_init_struct.clock_polarity         = SPI_CLOCK_POLARITY_HIGH;//极性低
  spi_init_struct.clock_phase            = SPI_CLOCK_PHASE_2EDGE;//第二脉冲边沿
  spi_init_struct.cs_mode_selection      = SPI_CS_SOFTWARE_MODE;//软件选择模式
  spi_init(SPI2, &spi_init_struct);
  spi_crc_polynomial_set(SPI2, 7);
  spi_crc_enable(SPI2, TRUE);
  spi_software_cs_internal_level_set(SPI2,SPI_SWCS_INTERNAL_LEVEL_HIGHT);//主机模式 SPI_CS_SOFTWARE_MODE SPI_SWCS_INTERNAL_LEVEL_HIGHT
  spi_i2s_dma_transmitter_enable(SPI2,TRUE);//使能SPI2的DMA传输
  spi_enable(SPI2, TRUE);//使能SPI2
  WS281x_CloseAll();  //关闭全部的灯
  delay_ms(100); //关闭全部的灯需要一定的时间         
}

//更新颜色显示(在设定颜色后将颜色数据存入缓存只有执行该函数后才会进行显示)
void WS281x_Show(void)
{
        // 检查DMA发送通道内是否还有数据
        while(dma_data_number_get(DMA1_CHANNEL4)){};
        //关闭通道 才能重新写入传输值
        dma_channel_enable(DMA1_CHANNEL4, FALSE);
        dma_data_number_set(DMA1_CHANNEL4, PIXEL_NUM * GRB);
        //开启通道
        dma_channel_enable(DMA1_CHANNEL4, TRUE);
        while(dma_data_number_get(DMA1_CHANNEL4)){};
       while(dma_flag_get(DMA1_FDT4_FLAG) != SET);
       dma_flag_clear(DMA1_FDT4_FLAG);
       dma_channel_enable(DMA1_CHANNEL4, FALSE);
}
//配置完成之后便可以构思底层控制函数了,为了方便多个LED灯珠的可控制首先要定义一个缓冲区pixelBuffer[PIXEL_NUM][24]\
通过设定颜色将数据填入缓冲区再通过更新函数将数据传入到LED灯珠上。
//关闭所有灯珠
void WS281x_CloseAll(void)
{
  uint16_t i;
  uint8_t j;

  for(i = 0; i < PIXEL_NUM; ++i)
  {
    for(j = 0; j < 24; ++j)
    {
      pixelBuffer.All_Buffer[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 < GRB; i++)
    {
      pixelBuffer.All_Buffer[n] = ((GRBColor << i) & 0x800000) ? WS_HIGH : WS_LOW;
    }
  }
}

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 < GRB; ++i)
    {
      pixelBuffer.All_Buffer[n] = (((WS281x_Color(red,green,blue) << i) & 0X800000) ? WS_HIGH : WS_LOW);
    }
  }
}

// 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, uint8_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, uint8_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
      }
    }
  }
}


// Slightly different, this makes the rainbow equally distributed throughout
void WS2812_RainbowRotate(uint16_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);
        }
}
//hsv和rgb互相转
float retmax(float a, float b, float c)                            //求最大值
{
        float max = 0;
        max = a;
        if (max < b)
                max = b;
        if (max < c)
                max = c;
        return max;
}
float retmin(float a, float b, float c)                            //求最小值
{
        float min = 0;
        min = a;
        if (min > b)
                min = b;
        if (min > c)
                min = c;
        return min;
}
//R,G,B参数传入范围(0~100)
//转换结果h(0~360),s(0~100),v(0~100)
void rgb_to_hsv(uint16_t *H, uint16_t *S, uint16_t *V, uint8_t r, uint8_t g, uint8_t b) {
        float max = 0, min = 0;
        float R = (float)r;
        float G = (float)g;
        float B = (float)b;
        float h, s, v;

        R = R / 255.0;
        G = G / 255.0;
        B = B / 255.0;

        max = retmax(R, G, B);
        min = retmin(R, G, B);
        v = max;
        if (max == 0)
                s = 0;
        else
                s = 1 - (min / max);

        if (max == min)
                h = 0;
        else if (max == R && G >= B)
                h = 60 * ((G - B) / (max - min));
        else if (max == R && G < B)
                h = 60 * ((G - B) / (max - min)) + 360;
        else if (max == G)
                h = 60 * ((B - R) / (max - min)) + 120;
        else if (max == B)
                h = 60 * ((R - G) / (max - min)) + 240;

        v = v * 100;
        s = s * 100;

        *H = (int) h;
        *S = (int) s;
        *V = (int) v;
}

//参数入参范围h(0~360),s(0~100),v(0~100),这里要注意,要把s,v缩放到0~1之间
//转换结果R(0~100),G(0~100),B(0~100),如需转换到0~255,只需把后面的乘100改成乘255
void hsv_to_rgb(int h, int s, int v, uint8_t *r, uint8_t *g, uint8_t *b) {
        float C = 0, X = 0, Y = 0, Z = 0;
        int i = 0;
        float H = (float) (h);
        float S = (float) (s) / 100.0;
        float V = (float) (v) / 100.0;
        float R, G, B;
        if (S == 0)
                R = G = B = V;
        else {
                H = H / 60;
                i = (int) H;
                C = H - i;

                X = V * (1 - S);
                Y = V * (1 - S * C);
                Z = V * (1 - S * (1 - C));
                switch (i) {
                case 0:
                        R = V;
                        G = Z;
                        B = X;
                        break;
                case 1:
                        R = Y;
                        G = V;
                        B = X;
                        break;
                case 2:
                        R = X;
                        G = V;
                        B = Z;
                        break;
                case 3:
                        R = X;
                        G = Y;
                        B = V;
                        break;
                case 4:
                        R = Z;
                        G = X;
                        B = V;
                        break;
                case 5:
                        R = V;
                        G = X;
                        B = Y;
                        break;
                }
        }
        R = R * 255;
        G = G * 255;
        B = B * 255;
        *r = (int) R;
        *g = (int) G;
        *b = (int) B;
}




void RGB_DEBUG_TEST(void)
{
        int i,j;
        for(i=0;i<3;i++)
        {
               
                for(j=0;j<8;j++)
                {
                        if(i==0)
                        {
                                WS281x_ColorWipe(WS281x_Color(255, 255, 255), 10); // Red
                        }
                        else if(i==1)
                        {
                                WS281x_ColorWipe(WS281x_Color(255, 255, 255), 10); // Green
                        }
                        else
                        {
                                WS281x_ColorWipe(WS281x_Color(255, 255, 255), 10); // Blue
                        }
                }
        }
                for(i=0;i<3;i++)
        {
               
                for(j=0;j<8;j++)
                {
                        if(i==0)
                        {
                                WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
                        }
                        else if(i==1)
                        {
                                WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
                        }
                        else
                        {
                                WS281x_ColorWipe(WS281x_Color(0, 0, 255), 10); // Blue
                        }
                }
        }
                for(i=0;i<3;i++)
        {
               
                for(j=0;j<8;j++)
                {
                        if(i==0)
                        {
                                WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
                                Delay_ms(100);
                                WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
                        }
                        else if(i==1)
                        {
                                WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
                                Delay_ms(100);
                                WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
                        }
                        else
                        {
                                WS281x_ColorWipe(WS281x_Color(0, 0, 255), 10); // Blue
                                Delay_ms(100);
                                WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
                                Delay_ms(100);
                                WS281x_ColorWipe(WS281x_Color(0, 0, 255), 10); // Blue
                                Delay_ms(100);
                                WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
                        }
                }
        }
        WS2812_RainbowRotate(2);
        // Some example procedures showing how to display to the pixels:
        WS281x_ColorWipe(WS281x_Color(255, 0, 0), 10); // Red
        WS281x_ColorWipe(WS281x_Color(0, 255, 0), 10); // Green
        WS281x_ColorWipe(WS281x_Color(0, 0, 255), 10); // Blue


        WS281x_TheaterChase(WS281x_Color(127, 127, 127), 10); // White
        WS281x_TheaterChase(WS281x_Color(127, 0, 0), 10); // Red
        WS281x_TheaterChase(WS281x_Color(0, 0, 127), 10); // Blue

        WS281x_Rainbow(10);
        WS281x_RainbowCycle(10);
        WS281x_TheaterChaseRainbow(10);
}


五、实物效果展示图
亮灯照片


使用特权

评论回复
沙发
weifeng90| | 2022-9-10 22:54 | 只看该作者
有程序嘛

使用特权

评论回复
评论
袁胜富 2022-9-10 22:56 回复TA
有的 
板凳
袁胜富|  楼主 | 2022-9-10 22:57 | 只看该作者

还在编辑文章

使用特权

评论回复
地板
6552918| | 2022-9-11 11:20 | 只看该作者
用SPI有2个问题,1 SPI占用管脚比较多,2 SPI想让WS2812B跑到800K的满速,时钟很难有合适的,要么主频降速要么WS2812降速。

使用特权

评论回复
5
袁胜富|  楼主 | 2022-9-11 14:52 | 只看该作者
6552918 发表于 2022-9-11 11:20
用SPI有2个问题,1 SPI占用管脚比较多,2 SPI想让WS2812B跑到800K的满速,时钟很难有合适的,要么主频降速 ...

确实这样的

使用特权

评论回复
6
gouguoccc| | 2022-9-11 18:18 | 只看该作者
为什么要用SPI接口呢?

使用特权

评论回复
7
袁胜富|  楼主 | 2022-9-11 20:10 | 只看该作者
gouguoccc 发表于 2022-9-11 18:18
为什么要用SPI接口呢?

换一种思路点亮呗,PWM是首选。

使用特权

评论回复
8
juliestephen| | 2022-9-12 13:04 | 只看该作者
这个不错,真是巧妙。

使用特权

评论回复
9
xiaoyaodz| | 2022-9-12 13:29 | 只看该作者
之前用过stm32实现过ws2812驱动的

使用特权

评论回复
10
guijial511| | 2022-9-12 14:13 | 只看该作者
PWM是首选

使用特权

评论回复
11
Undshing| | 2022-10-5 20:57 | 只看该作者
spi也不错

使用特权

评论回复
12
pl202| | 2022-11-1 09:47 | 只看该作者
ws2812比bapa102好在哪儿?   

使用特权

评论回复
13
jimmhu| | 2022-11-1 10:02 | 只看该作者
这个dma操作是什么的代码              

使用特权

评论回复
14
gygp| | 2022-11-1 10:30 | 只看该作者
能够使用串口实现吗              

使用特权

评论回复
15
nomomy| | 2022-11-1 11:41 | 只看该作者
ws2812灯珠怎样防止烧坏  

使用特权

评论回复
16
macpherson| | 2022-11-1 15:32 | 只看该作者
7.2V电压会损坏WS2812B灯带吗?  

使用特权

评论回复
17
wengh2016| | 2022-11-1 16:19 | 只看该作者
spi的mosi的引脚操作的吧              

使用特权

评论回复
18
burgessmaggie| | 2022-11-1 17:12 | 只看该作者
arduino怎么操作WS2812B彩灯模块

使用特权

评论回复
19
cashrwood| | 2022-11-1 18:01 | 只看该作者
Ws2812 时序有要求吗               

使用特权

评论回复
20
1988020566| | 2022-11-1 18:55 | 只看该作者
WS2812B可以只用PWM实现吗  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

32

主题

159

帖子

2

粉丝