WS2812b彩灯(DMA+PWM)

WS2812b彩灯(DMA+PWM)1 级联电路 2 数据传输该芯片是通过数据传输的时间来判断数据是 0 码还是 1 码的

大家好,欢迎来到IT知识分享网。

一.WS2812B简介

        WS2812B是一种数字可编程的LED灯条,可以使用单个数据线进行通讯控制LED灯的颜色和亮度。每个WS2812B都有一个唯一的地址,可以通过单个数据线进行级联。

WS2812b彩灯(DMA+PWM)

二.WS2812B参数简介 

WS2812b彩灯(DMA+PWM)

WS2812b彩灯(DMA+PWM)

三. WS2812B数据通讯简介

 1.级联电路

WS2812b彩灯(DMA+PWM)

WS2812b彩灯(DMA+PWM)

2.数据传输

        该芯片是通过数据传输的时间来判断数据是0码还是1码的。

WS2812b彩灯(DMA+PWM)

WS2812b彩灯(DMA+PWM)

 此时就会有PWM的频率计算:

WS2812b彩灯(DMA+PWM)

3.级联的数据传输

第一个数据缓存

        ·第一个24位有第一个模块接收并缓存

        ·第二个24位会被第一个模块转发到第二个模块上并缓存

        ·第三个24位会被第一个和第二个转发到第三个模块上并缓存

        ·第四个24位……

        ·第N个24位……

复位信号

第二个数据缓存

        ·第一个24位有第一个模块接收并缓存

        ·第二个24位会被第一个模块转发到第二个模块上并缓存

        ·第三个24位会被第一个和第二个转发到第三个模块上并缓存

        ·第四个24位……

        ·第N个24位……

复位信号

…………

WS2812b彩灯(DMA+PWM)

四.WS2812B驱动程序简介 

江协科技的程序

        这里我们使用PWM+DMA的方式驱动WS2812B。这里我们用的江协科技的程序。(但是本人测试的时候出现错乱的情况不知道怎么回事)

DMA驱动程序:

#include "stm32f10x.h" void (*DMA1_Handler)(void); //也要开中断 数据转运+DMA 没用ADC void DMA1_Init(uint32_t MemoryBaseAddr)//MemoryBaseAddr是WS2812B_Bit数组地址 { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel2); DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&TIM2->CCR1);//起始地址 DMA_InitStructure.DMA_MemoryBaseAddr = MemoryBaseAddr;//存放地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//传输方向:双向 存储点到外设 DMA_InitStructure.DMA_BufferSize = 0;//缓存区大小,传输计数器大于0,触发源有触发信号,DMA使能 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设是否自增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器是否自增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//是否自增 以半字的形式传输16位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//不使用自动重装 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//硬件触发 DMA_Init(DMA1_Channel2, &DMA_InitStructure); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA1_Channel2, DMA_IT_TC, ENABLE);//DMA中断 DMA_Cmd(DMA1_Channel2, ENABLE); } void DMA1_SetIRQHandler(void (*IRQHandler)(void)) { DMA1_Handler=IRQHandler; } void DMA1_Start(uint16_t DataNumber) //调运函数,连续转运 { DMA_Cmd(DMA1_Channel2,DISABLE); DMA_SetCurrDataCounter(DMA1_Channel2,DataNumber); DMA_Cmd(DMA1_Channel2,ENABLE); } void DMA1_Channel2_IRQHandler(void) //DMA开中断12 { if(DMA_GetFlagStatus(DMA1_FLAG_TC2)) { DMA1_Handler(); DMA_ClearFlag(DMA1_FLAG_TC2); } } 

这里我们通过定时器中断驱动pwm 

PWN TIM2驱动:

#include "stm32f10x.h" //哪个函数不认识看芯片手册 void TIM2_Init(void) //TIM2开PWM的发送到DMA PA0输出 { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启定时器2时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启复用引脚时钟 GPIO_InitTypeDef GPIO_InitStructure; //GPIO GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 定时器控制引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); TIM_DeInit(TIM2); //初始化时基单元 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period = 90-1; //ARR周期 32视频里定时中断讲过,搞懂 90公式试出来的,然后一算 TIM_TimeBaseStructure.TIM_Prescaler = 0; //PSC预分频器 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_OCInitTypeDef TIM_OCInitStructure; //初始化输出比较单元 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; //CCR捕获比较器 运行中设置 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_CtrlPWMOutputs(TIM2, ENABLE); TIM_DMAConfig(TIM2,TIM_DMABase_CCR1,TIM_DMABurstLength_1Transfer); //DMA请求传输 TIM_DMACmd(TIM2,TIM_DMA_Update,ENABLE); //寄存器位传输对象 TIM_Cmd(TIM2, DISABLE); } void TIM2_Cmd(FunctionalState NewState) { TIM_Cmd(TIM2, NewState); } void TIM2_SetCompare1(uint16_t Value) //主输出使能,TM2为高级定时器,所以这里加上 { TIM_SetCompare1(TIM2,Value); } 

WS2812B驱动:

#include "stm32f10x.h" #include "TIM2.h" #include "DMA1.h" //#define WS2812B_LED_QUANTITY 32 //灯珠数量 #define WS2812B_LED_QUANTITY 30 //灯珠数量 //定义数组 类型符 数组名[常量] uint32_t WS2812B_Buf[WS2812B_LED_QUANTITY]; //0xGGRRBB 正常的数据口输入,然后配置就行,绿红蓝总共24位,只有32 uint16_t WS2812B_Bit[24*WS2812B_LED_QUANTITY+1];//灯珠数量*24bit+1 颜色位数+复位信号 根据数据手册可知还要加复位信号0(正常的0),因此需要写入 num24+reset(reset=?自己调) 位。 uint8_t WS2812B_Flag; void WS2812B_IRQHandler(void); void WS2812B_Init(void) { DMA1_SetIRQHandler(WS2812B_IRQHandler);//把WS2812B_IRQHandler赋给DMA1_Handler DMA1_Init((uint32_t) (&WS2812B_Bit));//灯位量的地址存放到DMA 这时DMA就是灯珠的量 强制类型转换 不足的高位补0 TIM2_Init();//定时器 } //用TIM2开PWM 送给DMA void WS2812B_ClearBuf(void)//把LED的RGB数组值清零,全部输出0码,变成黑色,即不发光 { uint8_t i; for(i=0;i<WS2812B_LED_QUANTITY;i++) { WS2812B_Buf[i]=0x000000;//24位的二进制数 6位的16进制数 } } void WS2812B_SetBuf(uint32_t Color) //设置显示相同的颜色 { uint8_t i; for(i=0;i<WS2812B_LED_QUANTITY;i++) { WS2812B_Buf[i]=Color;//BUF是颜色,设置成同一个颜色,初始统一设置成不发光 } } void WS2812B_UpdateBuf(void) //30和60对应不同的高电平时间也对应了时序的0码和1码 占空比控制亮灭 CCR和AAR+1共同决定的 { uint8_t i,j; for(j=0;j<WS2812B_LED_QUANTITY;j++) //遍历每个LED { for(i=0;i<24;i++) //遍历每个LED的颜色数据的每一位  00000000 00000000 { if(WS2812B_Buf[j]&(0x>>i)){WS2812B_Bit[j*24+i+1]=60;}//0x为二进制的1加23个0,>>:右移 else{WS2812B_Bit[j*24+i+1]=30;} //每一位,取值范围是0到23,所以再加1 } } DMA1_Start(24*WS2812B_LED_QUANTITY+1); TIM2_Cmd(ENABLE); while(WS2812B_Flag==0); WS2812B_Flag=0; } void WS2812B_IRQHandler(void) //定时器2 开PWM 直接就是根据时序占空比设置控制灯条 { TIM2_SetCompare1(0);//pwm设置 TIM2_Cmd(DISABLE);//关闭定时器 WS2812B_Flag=1; } 

 幻影的程序

        定时器驱动

#include "timer.h" #include "led.h" //通用定时器中断初始化 //这里时钟选择为APB1的2倍,而APB1为36M //arr:自动重装值。 //psc:时钟预分频数 //这里使用的是定时器3! void TIM3_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 TIM_ITConfig( //使能或者失能指定的TIM中断 TIM3, //TIM2 TIM_IT_Update , ENABLE //使能 ); NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 TIM_Cmd(TIM3, ENABLE); //使能TIMx外设 } 

        DMA驱动程序

#include "dma.h" u16 SendBuff[24*16+220]={ 10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, //1 10,10,10,10,10,10,10,10,3,3,10,3,10,3,10,10,3,3,3,3,3,3,3,3, //2 10,10,10,10,10,10,10,10,3,10,3,10,10,10,3,3,3,3,3,3,3,3,3,3, //3 10,10,10,10,10,10,10,10,10,3,3,3,3,10,10,3,3,3,3,3,3,3,3,3, //4 10,10,10,10,10,10,10,10,10,3,10,10,3,10,10,3,3,3,3,3,3,3,3,3, //5 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3,//6 10,10,10,10,10,3,3,10,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, //7 10,10,3,3,10,3,3,10,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, //8 10,3,3,10,10,10,10,3,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, //9 3,10,10,3,10,10,10,3,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, //10 3,10,3,3,3,10,3,3,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, //11 3,3,3,10,3,3,10,10,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, //12 3,3,3,3,3,3,3,3,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, //13 10,10,10,10,10,10,10,10,3,3,3,10,3,3,10,10,3,3,3,3,3,3,3,3, //14 10,10,10,10,10,10,10,10,3,10,3,3,3,10,3,3,3,3,3,3,3,3,3,3, //15 10,10,10,10,10,10,10,10,3,10,10,3,10,10,10,3,3,3,3,3,3,3,3,3, //16 // 3,3,3,3,3,3,3,3,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, // 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,10,10,10,10,10,10,10,10, // 10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 3,3,3,3,3,3,3,3,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, // 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,10,10,10,10,10,10,10,10, // 10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, // 3,3,3,3,3,3,3,3,10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3, // 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,10,10,10,10,10,10,10,10, // 10,10,10,10,10,10,10,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; u16 DMA1_MEM_LEN = 24*16+220;// 保存DMA每次数据传送的长度 void DMA_Config(void) { DMA_InitTypeDef DMA_InitStructure; // 使能DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 使能DMA传输 // 初始化通道参数 DMA_DeInit(DMA1_Channel2); // 将DMA的通道2寄存器重设为缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&TIM1->CCR1); // DMA外设TIM1的CCR1基地址 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff; // DMA内存基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 数据传输方向,从内存读取发送到外设 DMA_InitStructure.DMA_BufferSize = DMA1_MEM_LEN; //DMA通道的DMA缓存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环发送 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输 DMA_Init(DMA1_Channel2, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道寄存器 // 使能TIME的DMA TIM_DMACmd(TIM1,TIM_DMA_CC1,ENABLE); // 开始DMA DMA_SetCurrDataCounter(DMA1_Channel2,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小 DMA_Cmd(DMA1_Channel2, ENABLE); //使能USART1 TX DMA1 所指示的通道 } //让LED全灭 void WS2812B_ClearBuf(void) { uint16_t i; for(i=0;i<384;i++) { SendBuff[i]=3; } } 
#ifndef __DMA_H #define __DMA_H #include "sys.h" void DMA_Config(void); void WS2812B_ClearBuf(void); #endif 

PWM驱动程序

#include "pwm.h" #include "led.h" // //本程序只供学习使用,未经作者许可,不得用于其它任何用途 //ALIENTEK Mini STM32开发板 //PWM 驱动代码 //正点原子@ALIENTEK //技术论坛:www.openedv.com //修改日期:2010/12/03 //版本:V1.0 //版权所有,盗版必究。 //Copyright(C) 正点原子 2009-2019 //All rights reserved // //PWM输出初始化 //arr:自动重装值 //psc:时钟预分频数 void delay_ms(u16 nms); void TIM1_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);// 定时器1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外设时钟使能 //设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能 TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能 TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器 TIM_Cmd(TIM1, ENABLE); //使能TIM1 } 
#ifndef __PWM_H #define __PWM_H #include "sys.h" void TIM1_PWM_Init(u16 arr,u16 psc); #endif 

 要初始化io口不初始化会出现错误

#include "led.h" //初始化PB5和PE5为输出口.并使能这两个口的时钟 //LED IO初始化 void LED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能PA端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; // LED0-->PA.8 端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz GPIO_Init(GPIOA, &GPIO_InitStructure); // 根据设定参数初始化GPIOA.8 GPIO_SetBits(GPIOA,GPIO_Pin_8); // PA.8 输出高 } 
#ifndef __LED_H #define __LED_H #include "sys.h" #define LED0 PAout(8) // PA8 #define LED1 PDout(2) // PD2 void LED_Init(void);//初始化 #endif 

 主程序

#include "led.h" #include "delay.h" #include "sys.h" #include "timer.h" #include "pwm.h" #include "dma.h" extern u16 SendBuff[20]; // extern u16 DMA1_MEM_LEN; // 保存DMA每次数据传送的长度 void TIM3_IRQHandler(void) //TIM3中断 { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 { TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源 // 用户逻辑 } } int main(void) { // 延时函数初始化 delay_init(); // IO初始化 LED_Init(); // DMA控制PWM输出 TIM1_PWM_Init(13-1,7-1); // 72/7=10.285MHz 1.26us DMA_Config(); // 定时器任务 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2 TIM3_Int_Init(10-1,72-1); // 72分频,10us while(1) { // TIM1->CCR1 = 3; // 0 // TIM1->CCR1 = 10; // 1 } } 

 五.江协科技程序文件

程序代码 链接:https://pan.baidu.com/s/12t9pRkyJYpP5MXVNhYFELg 提取码:5gkc 

原来视频 链接[WS2812B] 七彩流水灯/呼吸灯/三级调光照明 *附赠程序*_哔哩哔哩_bilibili

视频讲解 链接:2ws2812b外设配置_哔哩哔哩_bilibili

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/110535.html

(0)
上一篇 2026-01-30 21:26
下一篇 2026-01-30 21:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信