芯片采用单线归零码的通讯方式,芯片在上电复位以后,DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个芯片提取后,送到芯片内部的数据锁存器,剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的芯片,每经过一个芯片的传输,信号减少24bit。芯片采用自动整形转发技术,使得该芯片的级联个数不受信号传送的限制,仅仅受限信号传输速度要求。
芯片内部的数据锁存器根据接受到的24bit数据,在OUTR、OUTG、OUTB控制端产生不同的占空比控制信号, 等待DIN端输入RESET信号时,所有芯片同步将接收到的数据送到各个段,芯片将在该信号结束后重新接收的数据,在接收完开始的24bit数据后,通过DO口转发数据口,芯片在没有接收到RESET码前,OUTR、OUTG、OUTB管脚原输出保持不变,当接受到280μs以上低电平RESET码后,芯片将刚才接收到的24bit PWM数据脉宽输出到OUTR、OUTG、OUTB引脚上。


通过控制引脚拉高拉低的时间来生成0码和1码,传输RGB数据,需要注意的是由于时间周期小,如果单片机主频不高 ,单条指令的执行时间可能也会影响到通信时序,因此建议通过DMA搭配TIMER的PWM输出来实现。
下边是一份GD32F303CCT6的的参考代码,使用PA0作为控制接口,实测功能正常,供参考。
#include "gd32f30x.h"
#include <stdint.h>
#include <string.h>
/* =========================================================
* 基本配置
* ========================================================= */
#define LED_COUNT 48
/* PA0 -> TIMER4_CH0 */
#define WS_GPIO_PORT GPIOA
#define WS_GPIO_PIN GPIO_PIN_0
/* DMA 映射 */
#define WS_DMA_PERIPH DMA1
#define WS_DMA_CHANNEL DMA_CH4
#define WS_TIMER TIMER4
#define WS_TIMER_RCU RCU_TIMER4
#define WS_TIMER_CH TIMER_CH_0
/* 颜色顺序:1=GRB,0=RGB */
#define WS2811_ORDER_GRB 1
/* =========================================================
* 时钟假设
* =========================================================
* 假设 TIMER4 内核时钟 = 120MHz
* 1 tick = 8.333ns
* 1.25us / 8.333ns = 150 tick
*/
#define WS_TIMER_CLK_HZ 120000000UL
#define WS_BIT_TICKS 150U
#define WS_TIMER_PERIOD (WS_BIT_TICKS - 1U)
/* 已调通的一组占空值
* 0码: 42 tick ≈ 350ns
* 1码: 84 tick ≈ 700ns
*/
#define WS_0H_TICKS 42U
#define WS_1H_TICKS 84U
/* 只发有效 bit,不把 reset 放进 DMA buffer */
#define WS_BITS_PER_LED 24U
#define WS_DMA_BUF_LEN (LED_COUNT * WS_BITS_PER_LED)
/* show() 超时保护 */
#define WS_DMA_TIMEOUT 1000000UL
/* =========================================================
* 全局缓存
* ========================================================= */
static uint8_t g_led_buf[LED_COUNT][3];
static uint16_t g_dma_buf[WS_DMA_BUF_LEN];
static volatile uint8_t g_ws_tx_done = 0;
static volatile uint8_t g_ws_tx_error = 0;
/* =========================================================
* 简单延时(只用于 demo,不参与 WS2811 bit 时序)
* ========================================================= */
static void delay_cycles(volatile uint32_t n)
{
while (n--) {
__NOP();
}
}
static void delay_us(uint32_t us)
{
/* 粗略延时,只用于 show() 后 reset 和 demo */
uint32_t cycles = SystemCoreClock / 6000000U;
while (us--) {
delay_cycles(cycles);
}
}
static void delay_ms(uint32_t ms)
{
while (ms--) {
delay_us(1000);
}
}
/* =========================================================
* GPIO 初始化
* ========================================================= */
static void ws2811_gpio_init(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
/* PA0 -> TIMER4_CH0, 复用推挽输出 */
gpio_init(WS_GPIO_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, WS_GPIO_PIN);
gpio_bit_reset(WS_GPIO_PORT, WS_GPIO_PIN);
}
/* =========================================================
* TIMER4 初始化
* 关键点:
* 1) 用 PWM 输出 bit 波形
* 2) 用 CH0 DMA 请求
* 3) 开启 CH0 compare shadow/preload
* 4) reset 不放在 DMA buffer 里,发送结束后软件拉低 300us
* ========================================================= */
static void ws2811_timer_init(void)
{
timer_parameter_struct timer_initpara;
timer_oc_parameter_struct timer_ocpara;
rcu_periph_clock_enable(WS_TIMER_RCU);
timer_deinit(WS_TIMER);
memset(&timer_initpara,0,sizeof(timer_initpara));
timer_initpara.prescaler = 0;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = WS_TIMER_PERIOD;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(WS_TIMER, &timer_initpara);
memset(&timer_ocpara,0,sizeof(timer_ocpara));
timer_ocpara.outputstate = TIMER_CCX_ENABLE;
timer_ocpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_channel_output_config(WS_TIMER, WS_TIMER_CH, &timer_ocpara);
/* PWM0:计数器从 0 开始先高,计数到比较值后拉低 */
timer_channel_output_mode_config(WS_TIMER, WS_TIMER_CH, TIMER_OC_MODE_PWM0);
timer_channel_output_pulse_value_config(WS_TIMER, WS_TIMER_CH, 0);
/* 已调通方案:开启 compare preload/shadow */
timer_channel_output_shadow_config(WS_TIMER, WS_TIMER_CH, TIMER_OC_SHADOW_ENABLE);
/* ARR shadow 保留 */
timer_auto_reload_shadow_enable(WS_TIMER);
timer_counter_value_config(WS_TIMER, 0);
timer_disable(WS_TIMER);
}
/* =========================================================
* DMA 初始化
* ========================================================= */
static void ws2811_dma_init_once(void)
{
rcu_periph_clock_enable(RCU_DMA1);
dma_deinit(WS_DMA_PERIPH, WS_DMA_CHANNEL);
}
/* =========================================================
* 颜色接口
* ========================================================= */
static void ws2811_set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b)
{
if (index >= LED_COUNT) return;
g_led_buf[index][0] = r;
g_led_buf[index][1] = g;
g_led_buf[index][2] = b;
}
static void ws2811_fill_rgb(uint8_t r, uint8_t g, uint8_t b)
{
uint32_t i;
for (i = 0; i < LED_COUNT; i++) {
g_led_buf[i][0] = r;
g_led_buf[i][1] = g;
g_led_buf[i][2] = b;
}
}
static void ws2811_clear(void)
{
memset(g_led_buf, 0, sizeof(g_led_buf));
}
/* =========================================================
* 效果辅助函数
* ========================================================= */
static uint8_t ws2811_scale8(uint8_t value, uint8_t brightness)
{
return (uint8_t)(((uint16_t)value * (uint16_t)brightness) / 255U);
}
static void ws2811_color_wheel(uint8_t pos, uint8_t *r, uint8_t *g, uint8_t *b)
{
if (pos < 85U) {
*r = 255U - pos * 3U;
*g = pos * 3U;
*b = 0U;
} else if (pos < 170U) {
pos -= 85U;
*r = 0U;
*g = 255U - pos * 3U;
*b = pos * 3U;
} else {
pos -= 170U;
*r = pos * 3U;
*g = 0U;
*b = 255U - pos * 3U;
}
}
/* =========================================================
* 构建 DMA 占空比缓冲
* ========================================================= */
static void ws2811_build_dma_buf(void)
{
uint32_t i, j, k = 0;
for (i = 0; i < LED_COUNT; i++) {
uint8_t bytes[3];
#if WS2811_ORDER_GRB
bytes[0] = g_led_buf[i][1]; /* G */
bytes[1] = g_led_buf[i][0]; /* R */
bytes[2] = g_led_buf[i][2]; /* B */
#else
bytes[0] = g_led_buf[i][0]; /* R */
bytes[1] = g_led_buf[i][1]; /* G */
bytes[2] = g_led_buf[i][2]; /* B */
#endif
for (j = 0; j < 3; j++) {
uint8_t data = bytes[j];
uint8_t bit;
for (bit = 0; bit < 8; bit++) {
g_dma_buf[k++] = (data & 0x80U) ? WS_1H_TICKS : WS_0H_TICKS;
data <<= 1;
}
}
}
}
/* =========================================================
* 启动 DMA 发送
* 当前已验证思路:
* - 第1个 bit 手动写入 CH0CV
* - 后续 bit 由 CH0 DMA 请求依次搬运
* - reset 不进 DMA buffer,show() 末尾软件拉低
* ========================================================= */
static void ws2811_dma_start(void)
{
dma_parameter_struct dma_init_struct;
g_ws_tx_done = 0;
g_ws_tx_error = 0;
timer_disable(WS_TIMER);
timer_dma_disable(WS_TIMER, TIMER_DMA_CH0D);
dma_channel_disable(WS_DMA_PERIPH, WS_DMA_CHANNEL);
dma_deinit(WS_DMA_PERIPH, WS_DMA_CHANNEL);
memset(&dma_init_struct,0,sizeof(dma_init_struct));
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = (uint32_t)&g_dma_buf[1]; /* 从第2个 bit 开始 */
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_init_struct.number = WS_DMA_BUF_LEN - 1U;
dma_init_struct.periph_addr = (uint32_t)(&TIMER_CH0CV(WS_TIMER));
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
/* 你本地库这里是传结构体本体 */
dma_init(WS_DMA_PERIPH, WS_DMA_CHANNEL, dma_init_struct);
dma_circulation_disable(WS_DMA_PERIPH, WS_DMA_CHANNEL);
dma_memory_to_memory_disable(WS_DMA_PERIPH, WS_DMA_CHANNEL);
dma_flag_clear(WS_DMA_PERIPH, WS_DMA_CHANNEL, DMA_FLAG_G);
dma_flag_clear(WS_DMA_PERIPH, WS_DMA_CHANNEL, DMA_FLAG_FTF);
dma_flag_clear(WS_DMA_PERIPH, WS_DMA_CHANNEL, DMA_FLAG_ERR);
/* 第1个 bit 先手动装入 */
timer_counter_value_config(WS_TIMER, 0);
timer_channel_output_pulse_value_config(WS_TIMER, WS_TIMER_CH, g_dma_buf[0]);
/* 触发一次更新,把第1个 bit 装到活动寄存器 */
timer_event_software_generate(WS_TIMER, TIMER_EVENT_SRC_UPG);
dma_flag_clear(WS_DMA_PERIPH, WS_DMA_CHANNEL, DMA_FLAG_G);
dma_flag_clear(WS_DMA_PERIPH, WS_DMA_CHANNEL, DMA_FLAG_FTF);
dma_flag_clear(WS_DMA_PERIPH, WS_DMA_CHANNEL, DMA_FLAG_ERR);
/* 开 CH0 DMA 请求 */
timer_dma_enable(WS_TIMER, TIMER_DMA_CH0D);
dma_channel_enable(WS_DMA_PERIPH, WS_DMA_CHANNEL);
timer_enable(WS_TIMER);
}
/* =========================================================
* 阻塞式刷新
* ========================================================= */
static void ws2811_show(void)
{
uint32_t timeout = WS_DMA_TIMEOUT;
ws2811_build_dma_buf();
ws2811_dma_start();
while (RESET == dma_flag_get(WS_DMA_PERIPH, WS_DMA_CHANNEL, DMA_FLAG_FTF)) {
if (SET == dma_flag_get(WS_DMA_PERIPH, WS_DMA_CHANNEL, DMA_FLAG_ERR)) {
g_ws_tx_error = 1U;
break;
}
if (--timeout == 0U) {
g_ws_tx_error = 2U;
break;
}
}
if (!g_ws_tx_error) {
g_ws_tx_done = 1U;
}
timer_dma_disable(WS_TIMER, TIMER_DMA_CH0D);
dma_channel_disable(WS_DMA_PERIPH, WS_DMA_CHANNEL);
timer_disable(WS_TIMER);
/* 软件 reset:强制拉低 */
timer_channel_output_pulse_value_config(WS_TIMER, WS_TIMER_CH, 0);
gpio_bit_reset(WS_GPIO_PORT, WS_GPIO_PIN);
delay_us(300);
}
/* =========================================================
* 特效:单点流水灯
* ========================================================= */
static void ws2811_effect_running_light(uint8_t r, uint8_t g, uint8_t b,
uint16_t step_delay_ms,
uint16_t rounds)
{
uint16_t round;
uint32_t i;
for (round = 0; round < rounds; round++) {
for (i = 0; i < LED_COUNT; i++) {
ws2811_clear();
ws2811_set_rgb(i, r, g, b);
ws2811_show();
delay_ms(step_delay_ms);
}
}
}
/* =========================================================
* 特效:带尾巴流水灯
* ========================================================= */
static void ws2811_effect_running_tail(uint8_t r, uint8_t g, uint8_t b,
uint8_t tail_len,
uint16_t step_delay_ms,
uint16_t rounds)
{
uint16_t round;
int32_t head, j;
if (tail_len == 0U) {
tail_len = 1U;
}
for (round = 0; round < rounds; round++) {
for (head = 0; head < (int32_t)LED_COUNT; head++) {
ws2811_clear();
for (j = 0; j < (int32_t)tail_len; j++) {
int32_t idx = head - j;
if (idx >= 0 && idx < (int32_t)LED_COUNT) {
uint8_t brightness = (uint8_t)(255U - (uint32_t)j * 255U / tail_len);
ws2811_set_rgb((uint32_t)idx,
ws2811_scale8(r, brightness),
ws2811_scale8(g, brightness),
ws2811_scale8(b, brightness));
}
}
ws2811_show();
delay_ms(step_delay_ms);
}
}
}
/* =========================================================
* 特效:单色呼吸灯
* ========================================================= */
static void ws2811_effect_breathe(uint8_t r, uint8_t g, uint8_t b,
uint8_t step,
uint16_t step_delay_ms,
uint16_t rounds,
uint8_t min_bright,
uint8_t max_bright)
{
uint16_t round;
uint16_t bright;
uint32_t i;
uint8_t real_bright;
if (step == 0U) {
step = 5U;
}
/* 防呆:保证 min <= max */
if (min_bright > max_bright) {
uint8_t tmp = min_bright;
min_bright = max_bright;
max_bright = tmp;
}
for (round = 0; round < rounds; round++) {
/* 渐亮 */
for (bright = 0; bright <= max_bright; bright += step) {
real_bright = min_bright +
(uint8_t)(((uint16_t)(max_bright - min_bright) * bright) / max_bright);
for (i = 0; i < LED_COUNT; i++) {
ws2811_set_rgb(i,
ws2811_scale8(r, real_bright),
ws2811_scale8(g, real_bright),
ws2811_scale8(b, real_bright));
}
ws2811_show();
delay_ms(step_delay_ms);
if (bright + step > max_bright) {
break;
}
}
/* 渐暗 */
for (bright = max_bright; bright > 0U; bright = (bright > step) ? (bright - step) : 0U) {
real_bright = min_bright +
(uint8_t)(((uint16_t)(max_bright - min_bright) * bright) / max_bright);
for (i = 0; i < LED_COUNT; i++) {
ws2811_set_rgb(i,
ws2811_scale8(r, real_bright),
ws2811_scale8(g, real_bright),
ws2811_scale8(b, real_bright));
}
ws2811_show();
delay_ms(step_delay_ms);
if (bright <= step) {
break;
}
}
}
}
/* =========================================================
* 特效:呼吸变色灯
* ========================================================= */
static void ws2811_effect_breathe_rainbow(uint8_t step,
uint16_t step_delay_ms,
uint16_t color_step,
uint16_t rounds)
{
uint16_t round;
uint16_t bright;
uint16_t color_pos = 0;
uint32_t i;
uint8_t r, g, b;
if (step == 0U) {
step = 5U;
}
if (color_step == 0U) {
color_step = 1U;
}
for (round = 0; round < rounds; round++) {
for (bright = 0; bright <= 255U; bright += step) {
ws2811_color_wheel((uint8_t)(color_pos & 0xFFU), &r, &g, &b);
for (i = 0; i < LED_COUNT; i++) {
ws2811_set_rgb(i,
ws2811_scale8(r, (uint8_t)bright),
ws2811_scale8(g, (uint8_t)bright),
ws2811_scale8(b, (uint8_t)bright));
}
ws2811_show();
delay_ms(step_delay_ms);
color_pos += color_step;
if (bright + step > 255U) {
break;
}
}
for (bright = 255U; bright > 0U; bright = (bright > step) ? (bright - step) : 0U) {
ws2811_color_wheel((uint8_t)(color_pos & 0xFFU), &r, &g, &b);
for (i = 0; i < LED_COUNT; i++) {
ws2811_set_rgb(i,
ws2811_scale8(r, (uint8_t)bright),
ws2811_scale8(g, (uint8_t)bright),
ws2811_scale8(b, (uint8_t)bright));
}
ws2811_show();
delay_ms(step_delay_ms);
color_pos += color_step;
if (bright <= step) {
break;
}
}
}
ws2811_clear();
ws2811_show();
}
/* =========================================================
* 特效:彩虹流水灯
* ========================================================= */
static void ws2811_effect_rainbow_running(uint16_t step_delay_ms, uint16_t rounds)
{
uint16_t round;
uint32_t offset, i;
uint8_t r, g, b;
for (round = 0; round < rounds; round++) {
for (offset = 0; offset < 256U; offset++) {
for (i = 0; i < LED_COUNT; i++) {
uint8_t pos = (uint8_t)((i * 256U / LED_COUNT + offset) & 0xFFU);
ws2811_color_wheel(pos, &r, &g, &b);
ws2811_set_rgb(i, r, g, b);
}
ws2811_show();
delay_ms(step_delay_ms);
}
}
}
/* =========================================================
* 初始化
* ========================================================= */
static void ws2811_init(void)
{
SystemCoreClockUpdate();
ws2811_gpio_init();
ws2811_timer_init();
ws2811_dma_init_once();
ws2811_clear();
delay_us(300);
}
/* =========================================================
* demo
* ========================================================= */
int main(void)
{
ws2811_init();
while (1) {
/* 单点流水 */
ws2811_effect_running_light(25, 25, 0, 80, 3);
/* 带尾巴流水 */
ws2811_effect_running_tail(32, 8, 8, 4, 60, 3);
/* 单色呼吸 */
ws2811_effect_breathe(102, 0, 0, 1, 20, 3,1,60);
/* 呼吸变色 */
ws2811_effect_breathe_rainbow(1, 20, 1, 3);
/* 彩虹流水 */
ws2811_effect_rainbow_running(20, 2);
ws2811_clear();
ws2811_show();
delay_ms(500);
}
}