WS2812全彩RGB驱动方法

简介: 笔记

一. 简介


买了一个圆形的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搭建和管理企业级网站应用
目录
相关文章
|
10月前
来看看生词:CVBS、S-Video、YPbPr、模拟RGB、DVI和HDMI
来看看生词:CVBS、S-Video、YPbPr、模拟RGB、DVI和HDMI
292 0
|
文字识别 监控 算法
Baumer工业相机堡盟相机使用BGAPISDK联合Halcon直接进行彩色图像显示(Color)(C#)(将图像数据Buffer转为HObject)
Baumer工业相机堡盟相机使用BGAPISDK联合Halcon直接进行彩色图像显示(Color)(C#)(将图像数据Buffer转为HObject)
170 0
|
文字识别 监控 算法
Baumer工业相机堡盟相机使用BGAPISDK联合Halcon直接进行图像显示(Mono)(C#)(将图像数据Buffer转为HObject)
Baumer工业相机堡盟相机使用BGAPISDK联合Halcon直接进行图像显示(Mono)(C#)(将图像数据Buffer转为HObject)
148 0
|
算法 数据可视化 开发工具
Baumer相机BGAPI SDK Demo软件去连接JPEG-650M相机进行采图时,发现图像显示为一条灰色条状图像(C++),联合OpenCV进行图像转换显示
Baumer相机BGAPI SDK Demo软件去连接JPEG-650M相机进行采图时,发现图像显示为一条灰色条状图像(C++),联合OpenCV进行图像转换显示
115 0
|
编译器 Linux
荔枝派Zero(全志V3S)驱动开发之RGB LCD屏幕显示jpg图片
由于从上篇博文 “荔枝派Zero(全志V3S)驱动开发之RGB LCD屏幕显示bmp图片” 中只实现了显示 bmp 图片,实际上我们很常用到的图片多数是 jpg 格式图片,因此我们需要折腾一下,实现 jpg 文件的显示。
241 0
|
芯片
SPI+DMA驱动和控制WS2812彩色RGB灯
SPI+DMA驱动和控制WS2812彩色RGB灯
563 0
SPI+DMA驱动和控制WS2812彩色RGB灯
|
传感器 Ubuntu API
ESP32-C3入门教程 基础篇(五、RMT应用 — 控制SK6812全彩RGB 灯)
测试第五课,本来是准备测试一下PWM驱动 SK6812 RGB灯, 但是研究了一段时间,发现在ESP32-C3 有更好而且现成的方式 实现 SK6812 的控制, 使用PWM也不是不可以,只是对于初学者,需要多花好多时间, 所以本文还是先以ESP32-C3内置的 RMT 进行 SK6812 的控制,毕竟有现成的示例
1620 0
ESP32-C3入门教程 基础篇(五、RMT应用 — 控制SK6812全彩RGB 灯)
|
存储 编解码 Linux
嵌入式Linux下LCD应用编程: 调用giflib库解码显示GIF动态图
嵌入式Linux下LCD应用编程: 调用giflib库解码显示GIF动态图
806 0
嵌入式Linux下LCD应用编程: 调用giflib库解码显示GIF动态图
|
算法 C# 计算机视觉
Win8 Metro(C#)数字图像处理--2.46图像RGB分量增强效果
原文:Win8 Metro(C#)数字图像处理--2.46图像RGB分量增强效果  [函数名称] RGB分量调整         RGBAdjustProcess(WriteableBitmap src, i...
937 0
|
算法 C# 计算机视觉
Win8Metro(C#)数字图像处理--2.23二值图像开运算
原文:Win8Metro(C#)数字图像处理--2.23二值图像开运算  [函数名称] 二值图像开运算函数OpenOperateProcess(WriteableBitmap src) [算法说明]  开运算就是先进性一次腐蚀后进行一次膨胀。
997 0