文章目录

  • 一、WS2812B资料
    • 1.硬件参数
    • 2.硬件连接
  • 二、驱动方式选择
    • 1.数据传输与组成
    • 2.数据编码
  • 三、软件驱动编写
  • 四、测试效果
  • 总结

一、WS2812B资料

  很多博客都有中文的DATASHEET,自行搜索,本文为英文DATASHEET截图。

1.硬件参数

绝对最大额定值与电气特性

  根据参数,使用5V电压供电即可;输入的电流1uA,IO口可以直接驱动,但若想驱动多个灯珠,最好还是独立供电。高电平最小值2.7V,低电平最大值0.7V,使用IO口直接做控制输入可以满足。

2.硬件连接

下图为DataSheet中的应用电路

  请将“DIN”信号连接到单片机上有定时器通道的引脚。前一个WS2812B的“DOUT”连接到下一个WS2812B的“DIN”。

二、驱动方式选择

1.数据传输与组成

首先看一眼数据传输方式,D1、D2、D3、D4代表按顺序排列的WS2812B灯珠。
可以看到,第三个WS2812B灯珠接收到了第三个24bit的数据,
故推断第一个WS2812B灯珠先收到第一个24bit的数据后,做出响应(发光);
再收到第二个24bit的数据后,直接转发给第二个WS2812B灯珠,由第二个WS2812B灯珠做出响应;
依次类推。
做不同颜色的灯光显示时,注意数据传输的先后。

  那么这24bit的数据内容是什么呢?下图为该24bit数据的组成

  根据描述,数据发送的顺序是GRB,高位先发。
  每8bit代表了WS2812B灯珠对此种颜色光发光的强度,故耀眼的绿、红、蓝纯色光对应十六进制的编码分别为:0xff0000(绿)、0x00ff00(红)、0x0000ff(蓝)。

2.数据编码

  有了编码信息,再看看WS2812B灯珠对0/1码的接收方式是什么。下图为WS2812B的编码时序图

  其中的数据传输时间如下图所示。

  可以看到,WS2812B编码所需的时间精度为ns级别,定时器中断不合适了;stm32的时钟周期在72mhz的系统时钟下为13.89ns,使用systemtick做delay也可以实现,但时间不会很精确,且需要考虑不同的指令周期对编码的影响,粗糙又繁琐。
  WS2812B的0/1编码很像一个周期内不同占空比的波形,可以考虑PWM控制,但此时又出现一个问题:
  如何在不改变频率的情况下在几个周期内连续改变占空比?
  通常我们是用库函数中TIM_SetCompare Y(TIM_TypeDef* TIM X, uint16_t Compare Z)来改变,但由于该函数执行时间很长,影响了编码时序,故不能采用此方法。
  另一种方式是直接改变定时器CCRx寄存器的值,此时又出现一个问题:
  如何在不到1us的时间内改变CCRx寄存器值?
  知识点来了:STM32中,有影子寄存器的预装载寄存器有三个,分别是:分频寄存器PSC,自动重装载ARR,自动捕获CCRx。我们读写访问的是这几个寄存器的值,往CCRx寄存器内写入值时,会写在影子寄存器的下一半字(uint16_t)中。当我们使能了预装载寄存器后(TIMx_CCMR1寄存器的OC1PE位为0 ),当PWM执行完一个周期后,将影子寄存器中下一半字(uint16_t)中的值赋给CCRx预装载寄存器。参考:STM32xx参考手册的"捕获/比较模式寄存器 1(TIMx_CCMR1)"章节。
  知道了这点后,我们可以在确定24bit的颜色编码后,得到对应位的0/1编码比较值,用数组传输给CCRx。然后启动PWM,即可得到占空比不断变化的PWM波。
  注意到STM32在定时器部分有DMA功能后,便可以尝试将数据传输部分交给DMA了。

三、软件驱动编写

  我测试使用的MCU为STM32f103c8t6,定时器使用是TIM2_CH2,移植时需要注意函数及实参使用相对应的。附图通道对应的DMA,作为移植参考。

  其他废话不多说,直接上代码,注释我写的很清楚。

头文件:HDL_WS2812B.h

/******************************************************************************** @file  HDL_WS2812B.h* @author  huiyang* @version  V1.0* @date  01/21/2022******************************************************************************/
#ifndef _HDL_WS2812B_H
#define _HDL_WS2812B_H#include "stm32f10x.h"#define WS2812B_PIN    GPIO_Pin_1
#define WS2812B_PORT   GPIOA
#define WS2812B_HIGH   GPIO_SetBits(WS2812B_PORT,WS2812B_PIN)
#define WS2812B_LOW    GPIO_ResetBits(WS2812B_PORT,WS2812B_PIN)
/*  根据DataSheet定义0/1编码对应的高电平比较值,因为高电平在前,定义高电平的即可
**  高低电平的比例约为2:1
*/
#define WS2812B_ARR 90      //TIM2的自动重装载值
#define T0H 30                      //0编码高电平时间占1/3
#define T1H 60                      //1编码高电平时间占2/3/* 灯珠亮的个数 */
#define LED_NUM  100        //底层驱动未用,为应用方便,先加上
#define DATA_SIZE  24       //WS2812B一个编码的bit数,3*8void PWM_WS2812B_Init(uint16_t arr);
void WS2812B_Reset(void);
void PWM_WS2812B_Write_24Bits(uint16_t num,uint32_t GRB_Data);
void PWM_WS2812B_Show(uint16_t num);
void PWM_WS2812B_Red(uint16_t num);
void PWM_WS2812B_Green(uint16_t num);
void PWM_WS2812B_Blue(uint16_t num);#endif

c文件:HDL_WS2812B.c

/******************************************************************************** @file  HDL_WS2812B.c* @author  huiyang* @version  V1.0* @date  01/21/2022******************************************************************************/
#include "HDL_WS2812B.h"
#include "delay.h"    //延时函数加自己对应的头文件,在复位处使用/* 单个灯珠的编码对应的比较值数组 */
uint16_t Single_LED_Buffer[DATA_SIZE*LED_NUM];/*
*作用:通用定时器2 TIM2_CH2 PWM输出+DMA初始化
*参数:arr:自动重装值
*说明:定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
*      Ft=定时器工作频率,单位:Mhz
*/
void PWM_WS2812B_Init(uint16_t arr)
{//结构体变量GPIO_InitTypeDef  GPIO_InitStructure;TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;DMA_InitTypeDef DMA_InitStructure;//使能RCC时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);     //使能PORTA时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);         //使能TIM2时钟    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);             //使能DMA1时钟    //初始化GPIO口GPIO_InitStructure.GPIO_Pin = WS2812B_PIN;                 //GPIO口GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       //速度50MHzGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;           //复用推挽输出                               GPIO_Init(WS2812B_PORT,&GPIO_InitStructure);             //根据指定的参数初始化GPIO口//初始化TIM2TIM_TimeBaseStructure.TIM_Prescaler=0;                                           //定时器分频:(0+1)=1,不分频TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;      //向上计数模式TIM_TimeBaseStructure.TIM_Period=arr;                                              //自动重装载值TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;              //时钟分割TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);                            //根据指定的参数初始化TIM2//初始化TIM2 Channel2 PWM模式TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                      //选择定时器模式:TIM脉冲宽度调制模式1TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能//TIM_OCInitStructure.TIM_Pulse = 0;                                                  //待装入捕获比较寄存器的脉冲值(此程序不用加初值)TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;        //输出极性:TIM输出比较极性高TIM_OC2Init(TIM2, &TIM_OCInitStructure);                                       //根据指定的参数初始化外设TIM2 Channel2/* 此处为知识点所描述处的对应代码,一定要有。移植时注意0C2代表定时器通道2*/TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);                       //使能TIM2在CCR2上的预装载寄存器TIM_Cmd(TIM2, DISABLE);                                    //失能TIM2,防止第一个脉冲异常TIM_DMACmd(TIM2, TIM_DMA_CC2, ENABLE);     //使能TIM2_CH2的DMA功能(CC2对应通道2)DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(TIM2->CCR2);                 //设置DMA目的地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Single_LED_Buffer;                 //设置DMA源地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                                                   //方向:从内存SendBuff到内存ReceiveBuff  DMA_InitStructure.DMA_BufferSize = DATA_SIZE;                                                           //一次传输大小DMA_BufferSize=SendBuffSize   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                        //ReceiveBuff地址不增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                           //SendBuff地址自增DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //ReceiveBuff数据单位,16bitDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;                 //SENDBUFF_SIZE数据单位,16bitDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                                                             //DMA模式:正常模式(传输一次)DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                                             //优先级:中 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                                                //内存到内存的传输DMA_Init(DMA1_Channel7, &DMA_InitStructure);                                                              //配置DMA1的7通道(不同定时器的通道不一样)DMA_Cmd(DMA1_Channel7, DISABLE);                   //失能DMA1的7通道,因为一旦使能就开始传输
}
//复位灯带
void WS2812B_Reset(void)
{TIM_Cmd(TIM2, DISABLE);WS2812B_LOW;delay_ms(1);    //此处延时时间最小值大于50us即可
}
//写数据编码
void PWM_WS2812B_Write_24Bits(uint16_t num,uint32_t GRB_Data)
{uint8_t i,j;for(j = 0; j < num; j++){for(i = 0; i < DATA_SIZE; i++){/*因为数据发送的顺序是GRB,高位先发,所以从高位开始判断,判断后比较值先放入缓存数组*/Single_LED_Buffer[i] = ((GRB_Data << i) & 0x800000) ? T1H : T0H;}}
}//点亮灯珠
void PWM_WS2812B_Show(uint16_t num)
{/* 移植时此处对应的通道和中断标志都需要更改 */DMA_SetCurrDataCounter(DMA1_Channel7, num*DATA_SIZE);DMA_Cmd(DMA1_Channel7, ENABLE);TIM_Cmd(TIM2, ENABLE);while(DMA_GetFlagStatus(DMA1_FLAG_TC7) != SET);DMA_Cmd(DMA1_Channel7, DISABLE);DMA_ClearFlag(DMA1_FLAG_TC7);TIM_Cmd(TIM2, DISABLE);
}//N个灯珠发红光
void PWM_WS2812B_Red(uint16_t num)
{PWM_WS2812B_Write_24Bits(num,0x00ff00);PWM_WS2812B_Show(num);
}
//N个灯珠发绿光
void PWM_WS2812B_Green(uint16_t num)
{PWM_WS2812B_Write_24Bits(num,0xff0000);PWM_WS2812B_Show(num);
}
//N个灯珠发蓝光
void PWM_WS2812B_Blue(uint16_t num)
{PWM_WS2812B_Write_24Bits(num,0x0000ff);PWM_WS2812B_Show(num);
}

测试主函数

#include "stm32f10x.h"
#include "delay.h"
#include "HDL_WS2812B.h"
int main(void)
{SystemInit();delay_init(72);PWM_WS2812B_Init(WS2812B_ARR);while(1){WS2812B_Reset();PWM_WS2812B_Red(1);     //灯珠个数可修改PWM_WS2812B_Green(1);PWM_WS2812B_Blue(1);}
}

四、测试效果

总结

  至此,WS2812B灯珠的简单驱动就编写完成了,其他颜色以及亮灯效果可以在此基础上任意发挥。

【STM32】WS2812B灯珠的PWM+DMA控制(库函数)相关推荐

  1. STM32F427库函数PWM+DMA控制ws2812b灯带

    STM32F427IIHx库函数PWM+DMA控制ws2812b灯带 一.参考资料 查看ws2812b用户手册可知: 二.代码部分 添加文件ws2812b.c,ws2812b.h 配置的F427IIH ...

  2. 国民技术 N32G031F8S7 PWM+DMA 控制 WS2812

    一.技术背景 之前移植使用STM32来做MCU,但是这两年STM32离谱的价格和供货情况直接劝退大部分互联网企业.国产的MCU厂家也抓住机会,迅猛抢占市场,国民技术是较为典型的一个厂家. 在很多时候, ...

  3. STM32 HAL WS2812B PWM+DMA控制

    目录 1.配置PWM+DMA 2.占空比设置 由上图可知 T0H+T0L 和 T1H+T1L 的时间周期为 800ns ~ 1380ns 取一个中间值 1.25us 那么0码/1码的周期就是 1s / ...

  4. 关于STM32利用TIM+PWM+DMA控制WS2812

    开发环境 MCU:STM32F103c8t6 开发工具:STM32CubeMX 使用板子参考原理图:STM32F103C8T6最小系统板开源链接 PWM 脉宽调制(PWM)基本原理:控制方式就是对逆变 ...

  5. 借WS2812 PWM DMA驱动调试浅谈STM32调试思路

    借WS2812 PWM DMA驱动调试浅谈STM32调试思路 ​ 接触STM32差不多有4年了,在学校参加比赛时也需要进行各种调试工作,团队的配合也需要个人具有领域内快速定位与解决BUG的能力,前些日 ...

  6. 安信可ESP-12K模组学习心得 ③ 安信可 ESP-12K 驱动 WS2812B 灯板,实现五彩斑斓的灯光效果;

    一 前言 今天为大家带来的是 安信可 ESP-12K 驱动WS2812B 灯板: 二 材料 安信可 ESP-12K 开发板 x1 WS2812B 灯板一个, 杜邦线若干 接线图: ESP-12K WS ...

  7. STM32使用PWM+DMA方式驱动WS2812灯珠

    一. 关于WS2812 WS2812 内部集成了处理芯片和3颗不同颜色的led灯(红,绿,蓝),通过单总线协议分别控制三个灯的亮度强弱,达到全彩的效果. WS2812B Datasheet 二. WS ...

  8. WS2812灯珠(三)-- STM32 PWM+DMA方式驱动

    WS2812灯珠(三)-- STM32 PWM+DMA方式驱动 文章目录 WS2812灯珠(三)-- STM32 PWM+DMA方式驱动 一.理论 二.代码实践 一.理论 PWM输出就是对外输出脉宽( ...

  9. stm32呼吸灯c语言程序,STM32使用PWM控制LED呼吸灯效果

    实验的STM32型号: STM32F103C8T6   48引脚 涉及到的知识: RCC,GPIO,TIMER,PWM 设置 *RCC需打开外部时钟 *GPIO查表得知 PA3 默认复用引脚 TIM2 ...

  10. WS2812B彩灯 STM32HAL库开发:PWM+DMA(stm32f103c8t6)

    目录 一.摘要 二.WS2812B介绍 三.CUBEMX配置 四.程序介绍(KEIL编译器) 五.数据手册 一.摘要 1.本文使用示例单片机型号为stm32f103c8t6,RGB型号为WS2812B ...

最新文章

  1. centos 安装VSFTP
  2. Java高级工程师学习路径
  3. python识别人脸多种属性_人脸检测及识别python实现系列(4)——卷积神经网络(CNN)入门...
  4. USB、UART、SPI等总线速率
  5. 一文入门 Kafka
  6. java socket参数详解:OOBInline和UrgentData .
  7. AtCoder Beginner Contest 234
  8. Linux单用户能做什么,Linux单用户模式详解 及应用场景
  9. 自注意力机制Self-attention(2)
  10. arm中断保护和恢复_浅谈ARM处理器的七种异常处理
  11. 工信部:不得利用“携号转网”实施恶性竞争行为
  12. mac下打开多个相同应用程序
  13. 计算机网络体系结构中的环节,ppt课件-第二章计算机网络体系结构.ppt
  14. c语言输出99乘法表的思路,C语言输出99乘法表
  15. dnfdpl服务器维护了,扶持青年学者 商务印书馆将推出“日新文库”
  16. Flutter 苹果原生拼音键盘在TextField上输入异常 | 拼音输入过程回调问题
  17. 魔方教程--很多教程都不对,整理并实践了一下
  18. phpstudy启动不了mysql数据库_phpstudy的数据库服务不能正常启动该怎么办
  19. 微博2面:微信朋友圈是怎么实现的?
  20. 关于证明PAC可学习

热门文章

  1. java我行我素购物系统_用java怎么编写 我行我素shopping购物管理体统
  2. php和jsp有什么区别,php和jsp之间的区别是什么
  3. paip.一千 常用汉字 高频汉字 覆盖率90%
  4. 奥的斯服务器故障查询及使用
  5. 人工智能、机器学习、深度学习和神经网络的关系
  6. Linux:什么是 i386、i586、 i686、noarch?
  7. jvisualvm监控本地jvm
  8. Android Studio Gradle下载慢解决方法
  9. Redis 官方可视化工具,高颜值,功能太强大!
  10. 快速在越狱iphone手机下定位app安装包并且导出的方法