WS2812全彩RGB驱动方法

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 笔记

一. 简介


买了一个圆形的WS2812模块玩玩,特来总结一下驱动方法,感觉对比于普通的RGB灯来说,还是有点不一样的。

踩了一些坑,也在此列出。


二. ws2812驱动


驱动方法其实很简单,就是发送一个24bit的数据即可,数据0和1的定义分别如下。


0.png

三. 特别提示


它没有所谓的空闲态,如果两个24bit的数据传输时间间隔相差过大,那个第二个24bit数据,不会传递到后面的ws2812灯上,而是会更新当前ws2812灯的状态。


四. FPGA实现


整个模块的实现方式如下,欢迎关注,写的比较随便了。

module ws2812_driver(
  input       sys_clk_50M,
  input       rst_n,
  output      ws2812_o,
  //外部控制
  input       ws2812_req,     //显示请求
  output      ws2812_ack,      //显示完成应答
  input       ws2812_reset,    //显示复位
  output      ws2812_reset_ack, //显示复位完成
  input[7:0]    ws2812_r,     //显示的数据
  input[7:0]    ws2812_g,
  input[7:0]    ws2812_b
);
//逻辑0高低电平持续周期数
localparam  T0_H    = 6'd16;  //320ns
localparam  T0_L    = 6'd42;  //840ns
//逻辑1高低电平持续周期数
localparam  T1_H    = 6'd42;  //840ns
localparam  T1_L    = 6'd16;  //320ns
//复位持续周期数
localparam  T_RESET = 14'd15000;//300us
localparam  S_IDLE    =   3'd0;
localparam  S_DATA    =   3'd1;
localparam  S_RESET   =   3'd2;
localparam  S_ACK     =   3'd3;
localparam  S_Sub_IDLE    =   4'd0;
localparam  S_Sub_T0_L    =   4'd1;
localparam  S_Sub_T0_H    =   4'd2;
localparam  S_Sub_T1_L    =   4'd3;
localparam  S_Sub_T1_H    =   4'd4;
localparam  S_Sub_ACK   =   4'd5;
reg[2:0]    state , next_state;
reg[3:0]    sub_state , sub_next_state;
reg[13:0] t_cnt;        //时间计数
reg[5:0]    bit_cnt;        //发送比特位计数
reg[23:0] color_rgb;
assign  ws2812_o = (sub_state == S_Sub_T0_L || sub_state == S_Sub_T1_L || state == S_RESET) ? 1'b0 : 1'b1;
assign  ws2812_ack = ( state == S_ACK ) ? 1'b1 : 1'b0;
always@(posedge sys_clk_50M or negedge rst_n)
begin
  if( rst_n == 1'b0 )
    state <= S_IDLE;
  else
    state <= next_state;
end
always@(*)
begin
  case(state)
  S_IDLE:
    if( ws2812_reset == 1'b1)
      next_state <= S_RESET;
    else if( ws2812_req == 1'b1)
      next_state <= S_DATA;
    else
      next_state <= S_IDLE;
  S_DATA:
    if( bit_cnt == 'd23)
      next_state <= S_ACK;
    else
      next_state <= S_DATA;
  S_RESET:
    if( t_cnt == T_RESET)
      next_state <= S_ACK;
    else
      next_state <= S_RESET;
  S_ACK:
    next_state <= S_IDLE;
  default: next_state <= S_IDLE;
  endcase 
end
always@(posedge sys_clk_50M or negedge rst_n)
begin
  if( rst_n == 1'b0 )
    t_cnt <= 8'd0;
  else if( state != next_state )
    t_cnt <= 8'd0;
  else if( state == S_DATA )
    if( sub_state == S_Sub_T0_L && t_cnt == T0_L)
      t_cnt <= 8'd0;
    else if( sub_state == S_Sub_T0_H && t_cnt == T0_H)
      t_cnt <= 8'd0;
    else if( sub_state == S_Sub_T1_L && t_cnt == T1_L)
      t_cnt <= 8'd0;
    else if( sub_state == S_Sub_T1_H && t_cnt == T1_H)
      t_cnt <= 8'd0;
    else
      t_cnt <= t_cnt + 1'b1;
  else if( state == S_RESET )
    t_cnt <= t_cnt + 1'b1;
  else
    t_cnt <= 'd0;
end
always@(posedge sys_clk_50M or negedge rst_n)
begin
  if( rst_n == 1'b0 )
    sub_state <= S_Sub_IDLE;
  else
    sub_state <= sub_next_state;
end
always@(*)
begin
  case(sub_state)
  S_Sub_IDLE:
    if( state == S_DATA && color_rgb[23] == 1'b1)
      sub_next_state <= S_Sub_T1_H;
    else if( state == S_DATA && color_rgb[23] == 1'b0)
      sub_next_state <= S_Sub_T0_H;
    else
      sub_next_state <= S_Sub_IDLE;
  S_Sub_T0_H:
    if( t_cnt == T0_H)
      sub_next_state <= S_Sub_T0_L;
    else if( state == S_ACK )
      sub_next_state <= S_Sub_IDLE;
    else
      sub_next_state <= S_Sub_T0_H;
  S_Sub_T0_L:
    if( t_cnt == T0_L && color_rgb[23] == 1'b0)
      sub_next_state <= S_Sub_T0_H;
    else if( t_cnt == T0_L && color_rgb[23] == 1'b1)
      sub_next_state <= S_Sub_T1_H;
    else
      sub_next_state <= S_Sub_T0_L;
  S_Sub_T1_H:
    if( t_cnt == T1_H)
      sub_next_state <= S_Sub_T1_L;
    else if( state == S_ACK )
      sub_next_state <= S_Sub_IDLE;
    else
      sub_next_state <= S_Sub_T1_H;
  S_Sub_T1_L:
    if( t_cnt == T1_L && color_rgb[23] == 1'b0)
      sub_next_state <= S_Sub_T0_H;
    else if( t_cnt == T1_L && color_rgb[23] == 1'b1)
      sub_next_state <= S_Sub_T1_H;
    else
      sub_next_state <= S_Sub_T1_L;
  default: sub_next_state <= S_Sub_IDLE;
  endcase
end
always@(posedge sys_clk_50M or negedge rst_n)
begin
  if( rst_n == 1'b0)
    color_rgb <= 24'd0;
  else if(state == S_DATA && sub_state == S_Sub_IDLE)
    color_rgb <= color_rgb << 1;
  else if( sub_state == S_Sub_T0_L && t_cnt == T0_L)
    color_rgb <= color_rgb << 1;
  else if( sub_state == S_Sub_T1_L && t_cnt == T1_L)
    color_rgb <= color_rgb << 1;
  else if( state == S_DATA)
    color_rgb <= color_rgb;
  else
    color_rgb <= {ws2812_g,ws2812_r,ws2812_b};
end
always@(posedge sys_clk_50M or negedge rst_n)
begin
  if( rst_n == 1'b0 )
    bit_cnt <= 6'd0;
  else if( sub_state == S_Sub_T0_L && t_cnt == T0_L )
    bit_cnt <= bit_cnt + 1'b1;
  else if( sub_state == S_Sub_T1_L && t_cnt == T1_L)
    bit_cnt <= bit_cnt + 1'b1;
  else if( state == S_ACK)
    bit_cnt <= 6'd0;
  else
    bit_cnt <= bit_cnt;
end
endmodule 

公众号:FPGA之旅

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
编解码 API 开发工具
|
6月前
|
Linux Windows
imx6ull开发板之qt应用编程读取AP3216c(光照,距离)数据。
imx6ull开发板之qt应用编程读取AP3216c(光照,距离)数据。
102 0
|
6月前
来看看生词:CVBS、S-Video、YPbPr、模拟RGB、DVI和HDMI
来看看生词:CVBS、S-Video、YPbPr、模拟RGB、DVI和HDMI
194 0
|
前端开发 芯片
【杂七杂八】excel中根据RTL信号位宽生成拼接取位
【杂七杂八】excel中根据RTL信号位宽生成拼接取位
170 0
【杂七杂八】excel中根据RTL信号位宽生成拼接取位
|
芯片
SPI+DMA驱动和控制WS2812彩色RGB灯
SPI+DMA驱动和控制WS2812彩色RGB灯
513 0
SPI+DMA驱动和控制WS2812彩色RGB灯
|
数据格式
基于STM32+Qt上位机的RGB调色器,全开源!
基于STM32+Qt上位机的RGB调色器,全开源!
298 0
基于STM32+Qt上位机的RGB调色器,全开源!
|
Java
摄像头NV21格式转RGB的JAVA代码,测试正确
摄像头NV21格式转RGB的JAVA代码,测试正确
195 0
|
编解码
嵌入式实践教程--FFmpeg4.1中YUV原像素编码为H264
嵌入式实践教程--FFmpeg4.1中YUV原像素编码为H264
嵌入式实践教程--FFmpeg4.1中YUV原像素编码为H264
FPGA-VGA驱动Color Bar显示
FPGA-VGA驱动Color Bar显示
260 0
FPGA-VGA驱动Color Bar显示
|
传感器 Ubuntu API
ESP32-C3入门教程 基础篇(五、RMT应用 — 控制SK6812全彩RGB 灯)
测试第五课,本来是准备测试一下PWM驱动 SK6812 RGB灯, 但是研究了一段时间,发现在ESP32-C3 有更好而且现成的方式 实现 SK6812 的控制, 使用PWM也不是不可以,只是对于初学者,需要多花好多时间, 所以本文还是先以ESP32-C3内置的 RMT 进行 SK6812 的控制,毕竟有现成的示例
1535 0
ESP32-C3入门教程 基础篇(五、RMT应用 — 控制SK6812全彩RGB 灯)