首页 > 技术知识 > 正文

1.实验简述

使用 PWM+DMA 的方式驱动 WS2812B,并每隔 800ms 随机显示不同颜色。

2.实验硬件

兆易创新GD32F310 MCU开发板套件

16*16 WS2812B

3.开发环境

keil 5.24.1

4.WS2812B讲解

WS2812B 是 WorldSemi 公司推出的外控集成 RGB LED 光源。

WS2812B 则是将控制 IC 和传统 RGB 结合到了一起,我们使用一条数据线即可驱动所有 RGB,另外也使得颜色控制的精度更高,同时也支持 RGB 灯组的单个 RGB 颜色控制,因此WS2812B 除了可以用于照明外,还能应用到办公楼外墙来作为屏幕进行广告宣传,此时每个WS2812B 就对应的是一个像素点。

WS2812B 数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN 端接受从控制器传输过来的数据,首先送过来的 24bit 数据(红绿蓝各 8 位)被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整形放大后通过 DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少 24bit。

像素点采用自动整形转发技术,使得该像素点的级联个数不受信号传送的限制,仅仅受限信号传输速度要求。WS2812B 传输协议时序图如下:

使用PWM+DMA的方式驱动WS2812B-pwm驱动模块电路

从时序图可以看到,二进制的 0 和 1 用周期相同的不同占空比的方波来表示(1 对应 68%占空比,0 对应 32%占空比),因此我们可以通过改变 PWM 的占空比来模拟出要传输的数据。因为WS2812B 协议对传输速度要求非常高,所以使用了 DMA+PWM 这种方法,在比较事件发生时,DMA 立即响应并将对应数据传输到比较寄存器中。

WS2812 的传输过程如下图:

使用PWM+DMA的方式驱动WS2812B-pwm驱动模块电路1

每经过一个 WS2812B,数据就被截走24bit。

WS2812B 的 24 位数据如下:

使用PWM+DMA的方式驱动WS2812B-pwm驱动模块电路2

数据按照高位在前的顺序分别输出绿色,红色和蓝色控制数据。在这里我们顺便说一下RGB 的取色原理。RGB 由三种基本色构成,分别是红,绿,蓝,也叫加法三原色,通过这三种颜色的不同比例可以组合出各种颜色,而不同比例可以通过 PWM 的占空比来实现。如果想要特定颜色,可以使用调色板取色,如下图:

使用PWM+DMA的方式驱动WS2812B-pwm驱动模块电路3

5.实验步骤

1、获取demo工程,任意选中一个工程用来修改实现PWM+DMA控制WS2812B 功能

使用PWM+DMA的方式驱动WS2812B-pwm驱动模块电路4

2、添加需要的头文件和宏定义

使用PWM+DMA的方式驱动WS2812B-pwm驱动模块电路5

TIMER0_CH0CC为定时器1的CH0通道比较输出的地址。

num为要控制ws2812B的数量,这里我们只控制1个。

3、定义需要的变量和声明需要的初化函数

使用PWM+DMA的方式驱动WS2812B-pwm驱动模块电路6

RGB_buffer数组用来存放占空比数值。

4、配置PWM输出引脚

使用PWM+DMA的方式驱动WS2812B-pwm驱动模块电路7

5、配置定时器0通道0输出PWM

`void timer_config(void)

{ timer_oc_parameter_struct timer_ocintpara; timer_parameter_struct timer_initpara; rcu_periph_clock_enable(RCU_TIMER0); timer_deinit(TIMER0); /* TIMER0 configuration */ timer_initpara.prescaler = 0; timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_initpara.counterdirection = TIMER_COUNTER_UP; timer_initpara.period = 89; timer_initpara.clockdivision = TIMER_CKDIV_DIV1; timer_initpara.repetitioncounter = 0; timer_init(TIMER0, &timer_initpara); /* CH0 configuration in PWM0 mode */ timer_ocintpara.outputstate = TIMER_CCX_ENABLE; timer_ocintpara.outputnstate = TIMER_CCXN_ENABLE; timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH; timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH; timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_HIGH; timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; timer_channel_output_config(TIMER0, TIMER_CH_0, &timer_ocintpara); timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0,0); timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0); timer_channel_output_shadow_config(TIMER0, TIMER_CH_0, TIMER_OC_SHADOW_ENABLE); /* TIMER0 primary output enable */ timer_primary_output_config(TIMER0, ENABLE); /* TIMER0 CH0D DMA request enable */ timer_dma_enable(TIMER0, TIMER_DMA_CH0D); /* auto-reload preload enable */ //timer_auto_reload_shadow_enable(TIMER0); timer_auto_reload_shadow_disable(TIMER0); /* TIMER0 counter enable */ timer_enable(TIMER0);

}`

6、DMA配置

`void dma_config(void)

{ dma_parameter_struct dma_init_struct; /* enable DMA clock */ rcu_periph_clock_enable(RCU_DMA); /* initialize DMA channel1 */ dma_deinit(DMA_CH1); /* DMA channel1 initialize */ dma_deinit(DMA_CH1); dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL; dma_init_struct.memory_addr = (uint32_t)RGB_buffer; dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT; dma_init_struct.number =sizeof(RGB_buffer); dma_init_struct.periph_addr = (uint32_t)TIMER0_CH0CC; dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; dma_init_struct.priority = DMA_PRIORITY_HIGH; dma_init(DMA_CH1, &dma_init_struct); /* configure DMA mode */ dma_circulation_disable(DMA_CH1); dma_memory_to_memory_disable(DMA_CH1); /* enable DMA channel1 */ dma_channel_enable(DMA_CH1);

}`

7、通过PWM模拟WS2812B数据协议实现颜色设置

`void setRGB(uint8_t red,uint8_t green,uint8_t blue)

{

uint8_t i = 0,j =0;

uint32_t rgb_value = green<<16 | red<<8 | blue; while(dma_flag_get(DMA_CH1, DMA_INTF_FTFIF)==RESET); dma_flag_clear(DMA_CH1,DMA_INTC_FTFIFC); dma_channel_disable(DMA_CH1); dma_transfer_number_config(DMA_CH1,sizeof(RGB_buffer)); for(j=1;j<=num;++j) { for(i=0;i<24;++i) { if((rgb_value<

}`

8、在主函数实现每隔 800ms 随机显示不同颜色功能。

`int main(void)

{ systick_config(); gpio_config(); dma_config(); timer_config(); usart0_gpio_config(); usart0_config(); /* print out */ printf(“Hello world!\n\r”); while(1) { setRGB(rand()%256,rand()%256,rand()%256); delay_1ms(800); };

}`

审核编辑:刘清

猜你喜欢