前言
RGB888点阵是一个由64个WS2812 RGB LED灯珠组成的点阵显示屏,可以用于艺术装饰、玩具和游戏、时钟计时器和状态指示器等各种场景。
本文将向各位读者展示,如何利用FPGA驱动该8x8点阵显示屏,使得点阵显示特定的字符信息。
一、WS2812简介
WS2812 RGB LED灯珠广泛应用于LED灯带、室内和户外装饰照明、信号指示灯、车载设备等方面,由于它可以通过简单的控制器控制多个LED灯珠的颜色和亮度,因此具有非常广泛的市场前景。
二、WS2812配置说明
根据WS2812特性可知,要实现8x8图像的显示,就必须要配置64x24个数据,在设计中可以把配置64个数据和配置24个数据这两部分分开,在设计时,可以分成三个模块来实现此功能:配置模块、控制模块和顶层模块。
配置模块用来指示需要配置的是64个RGB LED灯中的哪一个,并提供开始配置信号和待显示的24位图像信息,将上述信号提供给控制模块完成对LED灯显示与色彩信息的配置。
控制模块用来配置配置模块发送过来的24位图像信息,对每一位进行解码然后按照特定格式发送“0”或者发送“1”,当配置完成24x64个数据并持续一段时间的低电平复位,就能够驱动8x8点阵显示了。
顶层模块用来级联配置模块和控制模块,此处不再过多叙述。
发送的“0”码或者“1”码配置规则如下图所示:
在本设计中,设定发送“0”码的总时间为1180ns,其中高电平占据300ns;发送“1”码的总时间为1280ns,其中高电平占据640ns;复位总时间为300us。
三、波形图绘制
1.配置模块
配置模块信号波形图如下图所示:
如图所示,cfg_start信号为控制模块每完成一个24bit数据的配置,就发送拉高信号告知配置模块可以开始配置下一个24bit数据了。需要注意的是,cnt_wait信号为上电等待信号,这里人为设定等待20ms等待电源电压稳定、器件初始化和内部元件自适应调节,等待完成之后拉高start_en,指示器件开始工作。cfg_num是指示配置了多少个LED灯,cfg_data是指示需要向该LED灯里面写入什么数据去配置它,ws2812_start是控制模块的开始配置信号,与cfg_num和cfg_data是保持同步的。
2.控制模块
控制模块状态转移图如下图所示:
如图所示,初始时控制模块状态为空闲IDLE状态,即不进行任何数据的配置,当检测到配置模块发送的开始信号时,空闲状态就会跳转到仲裁ARBIT状态,仲裁状态仲裁发送的24位数据,每一位应该是发“0”还是发“1”,每一次发“0”或者发“1”完成后,又重新回到仲裁状态仲裁下一个数据的发送状态(“0”或“1”).当发送完成64x24个数据时,表明一副8x8图像数据配置完成,此时跳转都复位RST状态,数据持续发送一段时间低电平,结束后又重新回到空闲IDLE状态等待配置模块的开始信号到来,再继续开始配置。
控制模块信号波形图如下图所示:
如图所示,cfg_data为待配置的24位数据,这里为了描述状态的跳转,假设配置的前两位数据分别位“0”和“1”,cfg_num初始为不定态,表示配置的是哪一个LED灯是不关心的,ws2812_start为该模块的开始工作信号。
skip_en_0为仲裁状态跳转到发“0”码状态的跳转信号,或者是发“0”码状态跳转到仲裁状态的跳转信号;skip_en_1为仲裁状态跳转到发“1”码状态的跳转信号,或者是发“1”码状态跳转到仲裁状态的跳转信号;skip_en_rst为发“0”码或者“1”码状态跳转到复位状态的跳转信号,或者是复位状态跳转到空闲状态的跳转信号。
n_state为次态,c_state为现态,两者具有一个时钟周期的差异。cnt_wait为全局计数器,计数仲裁、发“0”码或者“1”码、复位状态的时间,data为解码得到的24bit待配置数据的每一位数据,cnt_num位配置的数据个数,最多配置24个数据。
led_data为输出信号,根据led_data高低电平持续时间不同,用来指示配置的LED灯不同的色彩信息。如图所示,发送“0”码总时间为SEND_ZERO_time+ARBIT_time=1180ns,高电平占据300ns;发送“1”码总时间为SEND_ONE_time+ARBIT_time=1280ns,高电平占据640ns。
cfg_start为输出给配置模块的开始配置信号,该信号在控制模块每配置完成24bit图像数据时会拉高。
总结
本文介绍了如何利用FPGA驱动RGB888点阵显示屏显示特定的字符信息,详细阐述了配置模块和控制模块之间的协作关系,以及两个模块之间的信号传递图示以及每个信号的作用。下一章演示颜色识别与RGB88点阵关联效果。
参考代码
1.配置模块
module ws2812_cfg_ctrl
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire cfg_start , //配置模块开始工作指示的单脉冲信号,由控制模块产生
input wire r_valid , //红色分量有效信号,为持续拉高的电平信号
input wire g_valid , //绿色分量有效信号,为持续拉高的电平信号
input wire b_valid , //蓝色分量有效信号,为持续拉高的电平信号
output reg ws2812_start , //控制模块开始工作指示的单脉冲信号,由配置模块产生
output reg [5:0] cfg_num , //配置的8x8点阵个数,最大值64-1
output reg [23:0] cfg_data //待显示的颜色数据
);
localparam CNT_WAIT_MAX = 20'd1_000_000 ; //上电等待20ms,自行设定
wire [23:0] data_none[63:0] ; //显示白色字母“N”
wire [23:0] data_r[63:0] ; //显示红色字母“R”
wire [23:0] data_g[63:0] ; //显示绿色字母“G”
wire [23:0] data_b[63:0] ; //显示蓝色字母“B”
reg [19:0] cnt_wait ; //上电等待计数器,等待20ms后一直保持最大值
reg start_en ; //上电等待结束开始工作信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_wait <= 20'd0 ;
else if(cnt_wait >= CNT_WAIT_MAX - 1'b1)
cnt_wait <= CNT_WAIT_MAX ;
else
cnt_wait <= cnt_wait + 1'b1 ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
start_en <= 1'b0 ;
else if(cnt_wait == CNT_WAIT_MAX - 1'b1)
start_en <= 1'b1 ;
else
start_en <= 1'b0 ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
ws2812_start <= 1'b0 ;
else if((start_en == 1'b1)||((cfg_start == 1'b1)&&(cfg_num == 6'd63)))
ws2812_start <= 1'b1 ;
else
ws2812_start <= 1'b0 ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cfg_num <= 6'd0 ;
else if(cfg_start == 1'b1)
cfg_num <= cfg_num + 1'b1 ;
else
cfg_num <= cfg_num ;
//选择显示的字母和颜色类型
//r_valid有效显示红色“R”
//g_valid有效显示绿色"G"
//b_valid有效显示蓝色"B"
//三种信号均无效显示白色"N"
//RGB每一位向右移5位是在不改变显示颜色条件下减小显示亮度,否则发光太刺眼
always@(*)
case({r_valid,g_valid,b_valid})
3'b100 : cfg_data = {(data_r[cfg_num][23:16] >> 5),(data_r[cfg_num][15:8] >> 5),(data_r[cfg_num][7:0] >> 5)} ;
3'b010 : cfg_data = {(data_g[cfg_num][23:16] >> 5),(data_g[cfg_num][15:8] >> 5),(data_g[cfg_num][7:0] >> 5)} ;
3'b001 : cfg_data = {(data_b[cfg_num][23:16] >> 5),(data_b[cfg_num][15:8] >> 5),(data_b[cfg_num][7:0] >> 5)} ;
default : cfg_data = {(data_none[cfg_num][23:16] >> 5),(data_none[cfg_num][15:8] >> 5),(data_none[cfg_num][7:0] >> 5)} ;
endcase
//默认显示字母“N”
assign data_none[00] = {8'hff,8'hff,8'hff} ;
assign data_none[01] = {8'h00,8'h00,8'h00} ;
assign data_none[02] = {8'h00,8'h00,8'h00} ;
assign data_none[03] = {8'h00,8'h00,8'h00} ;
assign data_none[04] = {8'h00,8'h00,8'h00} ;
assign data_none[05] = {8'h00,8'h00,8'h00} ;
assign data_none[06] = {8'h00,8'h00,8'h00} ;
assign data_none[07] = {8'hff,8'hff,8'hff} ;
assign data_none[08] = {8'hff,8'hff,8'hff} ;
assign data_none[09] = {8'hff,8'hff,8'hff} ;
assign data_none[10] = {8'h00,8'h00,8'h00} ;
assign data_none[11] = {8'h00,8'h00,8'h00} ;
assign data_none[12] = {8'h00,8'h00,8'h00} ;
assign data_none[13] = {8'h00,8'h00,8'h00} ;
assign data_none[14] = {8'h00,8'h00,8'h00} ;
assign data_none[15] = {8'hff,8'hff,8'hff} ;
assign data_none[16] = {8'hff,8'hff,8'hff} ;
assign data_none[17] = {8'h00,8'h00,8'h00} ;
assign data_none[18] = {8'hff,8'hff,8'hff} ;
assign data_none[19] = {8'h00,8'h00,8'h00} ;
assign data_none[20] = {8'h00,8'h00,8'h00} ;
assign data_none[21] = {8'h00,8'h00,8'h00} ;
assign data_none[22] = {8'h00,8'h00,8'h00} ;
assign data_none[23] = {8'hff,8'hff,8'hff} ;
assign data_none[24] = {8'hff,8'hff,8'hff} ;
assign data_none[25] = {8'h00,8'h00,8'h00} ;
assign data_none[26] = {8'h00,8'h00,8'h00} ;
assign data_none[27] = {8'hff,8'hff,8'hff} ;
assign data_none[28] = {8'h00,8'h00,8'h00} ;
assign data_none[29] = {8'h00,8'h00,8'h00} ;
assign data_none[30] = {8'h00,8'h00,8'h00} ;
assign data_none[31] = {8'hff,8'hff,8'hff} ;
assign data_none[32] = {8'hff,8'hff,8'hff} ;
assign data_none[33] = {8'h00,8'h00,8'h00} ;
assign data_none[34] = {8'h00,8'h00,8'h00} ;
assign data_none[35] = {8'h00,8'h00,8'h00} ;
assign data_none[36] = {8'hff,8'hff,8'hff} ;
assign data_none[37] = {8'h00,8'h00,8'h00} ;
assign data_none[38] = {8'h00,8'h00,8'h00} ;
assign data_none[39] = {8'hff,8'hff,8'hff} ;
assign data_none[40] = {8'hff,8'hff,8'hff} ;
assign data_none[41] = {8'h00,8'h00,8'h00} ;
assign data_none[42] = {8'h00,8'h00,8'h00} ;
assign data_none[43] = {8'h00,8'h00,8'h00} ;
assign data_none[44] = {8'h00,8'h00,8'h00} ;
assign data_none[45] = {8'hff,8'hff,8'hff} ;
assign data_none[46] = {8'h00,8'h00,8'h00} ;
assign data_none[47] = {8'hff,8'hff,8'hff} ;
assign data_none[48] = {8'hff,8'hff,8'hff} ;
assign data_none[49] = {8'h00,8'h00,8'h00} ;
assign data_none[50] = {8'h00,8'h00,8'h00} ;
assign data_none[51] = {8'h00,8'h00,8'h00} ;
assign data_none[52] = {8'h00,8'h00,8'h00} ;
assign data_none[53] = {8'h00,8'h00,8'h00} ;
assign data_none[54] = {8'hff,8'hff,8'hff} ;
assign data_none[55] = {8'hff,8'hff,8'hff} ;
assign data_none[56] = {8'hff,8'hff,8'hff} ;
assign data_none[57] = {8'h00,8'h00,8'h00} ;
assign data_none[58] = {8'h00,8'h00,8'h00} ;
assign data_none[59] = {8'h00,8'h00,8'h00} ;
assign data_none[60] = {8'h00,8'h00,8'h00} ;
assign data_none[61] = {8'h00,8'h00,8'h00} ;
assign data_none[62] = {8'h00,8'h00,8'h00} ;
assign data_none[63] = {8'hff,8'hff,8'hff} ;
//检测到红色显示字母“R”
assign data_r[00] = {8'h00,8'h00,8'h00} ;
assign data_r[01] = {8'h00,8'hff,8'h00} ;
assign data_r[02] = {8'h00,8'hff,8'h00} ;
assign data_r[03] = {8'h00,8'hff,8'h00} ;
assign data_r[04] = {8'h00,8'hff,8'h00} ;
assign data_r[05] = {8'h00,8'hff,8'h00} ;
assign data_r[06] = {8'h00,8'h00,8'h00} ;
assign data_r[07] = {8'h00,8'h00,8'h00} ;
assign data_r[08] = {8'h00,8'h00,8'h00} ;
assign data_r[09] = {8'h00,8'hff,8'h00} ;
assign data_r[10] = {8'h00,8'h00,8'h00} ;
assign data_r[11] = {8'h00,8'h00,8'h00} ;
assign data_r[12] = {8'h00,8'h00,8'h00} ;
assign data_r[13] = {8'h00,8'h00,8'h00} ;
assign data_r[14] = {8'h00,8'hff,8'h00} ;
assign data_r[15] = {8'h00,8'h00,8'h00} ;
assign data_r[16] = {8'h00,8'h00,8'h00} ;
assign data_r[17] = {8'h00,8'hff,8'h00} ;
assign data_r[18] = {8'h00,8'h00,8'h00} ;
assign data_r[19] = {8'h00,8'h00,8'h00} ;
assign data_r[20] = {8'h00,8'h00,8'h00} ;
assign data_r[21] = {8'h00,8'h00,8'h00} ;
assign data_r[22] = {8'h00,8'hff,8'h00} ;
assign data_r[23] = {8'h00,8'h00,8'h00} ;
assign data_r[24] = {8'h00,8'h00,8'h00} ;
assign data_r[25] = {8'h00,8'hff,8'h00} ;
assign data_r[26] = {8'h00,8'hff,8'h00} ;
assign data_r[27] = {8'h00,8'hff,8'h00} ;
assign data_r[28] = {8'h00,8'hff,8'h00} ;
assign data_r[29] = {8'h00,8'hff,8'h00} ;
assign data_r[30] = {8'h00,8'h00,8'h00} ;
assign data_r[31] = {8'h00,8'h00,8'h00} ;
assign data_r[32] = {8'h00,8'h00,8'h00} ;
assign data_r[33] = {8'h00,8'hff,8'h00} ;
assign data_r[34] = {8'h00,8'h00,8'h00} ;
assign data_r[35] = {8'h00,8'hff,8'h00} ;
assign data_r[36] = {8'h00,8'h00,8'h00} ;
assign data_r[37] = {8'h00,8'h00,8'h00} ;
assign data_r[38] = {8'h00,8'h00,8'h00} ;
assign data_r[39] = {8'h00,8'h00,8'h00} ;
assign data_r[40] = {8'h00,8'h00,8'h00} ;
assign data_r[41] = {8'h00,8'hff,8'h00} ;
assign data_r[42] = {8'h00,8'h00,8'h00} ;
assign data_r[43] = {8'h00,8'h00,8'h00} ;
assign data_r[44] = {8'h00,8'hff,8'h00} ;
assign data_r[45] = {8'h00,8'h00,8'h00} ;
assign data_r[46] = {8'h00,8'h00,8'h00} ;
assign data_r[47] = {8'h00,8'h00,8'h00} ;
assign data_r[48] = {8'h00,8'h00,8'h00} ;
assign data_r[49] = {8'h00,8'hff,8'h00} ;
assign data_r[50] = {8'h00,8'h00,8'h00} ;
assign data_r[51] = {8'h00,8'h00,8'h00} ;
assign data_r[52] = {8'h00,8'h00,8'h00} ;
assign data_r[53] = {8'h00,8'hff,8'h00} ;
assign data_r[54] = {8'h00,8'h00,8'h00} ;
assign data_r[55] = {8'h00,8'h00,8'h00} ;
assign data_r[56] = {8'h00,8'h00,8'h00} ;
assign data_r[57] = {8'h00,8'hff,8'h00} ;
assign data_r[58] = {8'h00,8'h00,8'h00} ;
assign data_r[59] = {8'h00,8'h00,8'h00} ;
assign data_r[60] = {8'h00,8'h00,8'h00} ;
assign data_r[61] = {8'h00,8'h00,8'h00} ;
assign data_r[62] = {8'h00,8'hff,8'h00} ;
assign data_r[63] = {8'h00,8'h00,8'h00} ;
//检测到绿色显示字母“G”
assign data_g[00] = {8'h00,8'h00,8'h00} ;
assign data_g[01] = {8'hff,8'h00,8'h00} ;
assign data_g[02] = {8'hff,8'h00,8'h00} ;
assign data_g[03] = {8'hff,8'h00,8'h00} ;
assign data_g[04] = {8'hff,8'h00,8'h00} ;
assign data_g[05] = {8'hff,8'h00,8'h00} ;
assign data_g[06] = {8'h00,8'h00,8'h00} ;
assign data_g[07] = {8'h00,8'h00,8'h00} ;
assign data_g[08] = {8'hff,8'h00,8'h00} ;
assign data_g[09] = {8'h00,8'h00,8'h00} ;
assign data_g[10] = {8'h00,8'h00,8'h00} ;
assign data_g[11] = {8'h00,8'h00,8'h00} ;
assign data_g[12] = {8'h00,8'h00,8'h00} ;
assign data_g[13] = {8'h00,8'h00,8'h00} ;
assign data_g[14] = {8'hff,8'h00,8'h00} ;
assign data_g[15] = {8'h00,8'h00,8'h00} ;
assign data_g[16] = {8'hff,8'h00,8'h00} ;
assign data_g[17] = {8'h00,8'h00,8'h00} ;
assign data_g[18] = {8'h00,8'h00,8'h00} ;
assign data_g[19] = {8'h00,8'h00,8'h00} ;
assign data_g[20] = {8'h00,8'h00,8'h00} ;
assign data_g[21] = {8'h00,8'h00,8'h00} ;
assign data_g[22] = {8'h00,8'h00,8'h00} ;
assign data_g[23] = {8'h00,8'h00,8'h00} ;
assign data_g[24] = {8'hff,8'h00,8'h00} ;
assign data_g[25] = {8'h00,8'h00,8'h00} ;
assign data_g[26] = {8'h00,8'h00,8'h00} ;
assign data_g[27] = {8'h00,8'h00,8'h00} ;
assign data_g[28] = {8'h00,8'h00,8'h00} ;
assign data_g[29] = {8'h00,8'h00,8'h00} ;
assign data_g[30] = {8'h00,8'h00,8'h00} ;
assign data_g[31] = {8'h00,8'h00,8'h00} ;
assign data_g[32] = {8'hff,8'h00,8'h00} ;
assign data_g[33] = {8'h00,8'h00,8'h00} ;
assign data_g[34] = {8'h00,8'h00,8'h00} ;
assign data_g[35] = {8'h00,8'h00,8'h00} ;
assign data_g[36] = {8'hff,8'h00,8'h00} ;
assign data_g[37] = {8'hff,8'h00,8'h00} ;
assign data_g[38] = {8'hff,8'h00,8'h00} ;
assign data_g[39] = {8'h00,8'h00,8'h00} ;
assign data_g[40] = {8'hff,8'h00,8'h00} ;
assign data_g[41] = {8'h00,8'h00,8'h00} ;
assign data_g[42] = {8'h00,8'h00,8'h00} ;
assign data_g[43] = {8'h00,8'h00,8'h00} ;
assign data_g[44] = {8'h00,8'h00,8'h00} ;
assign data_g[45] = {8'h00,8'h00,8'h00} ;
assign data_g[46] = {8'hff,8'h00,8'h00} ;
assign data_g[47] = {8'h00,8'h00,8'h00} ;
assign data_g[48] = {8'h00,8'h00,8'h00} ;
assign data_g[49] = {8'hff,8'h00,8'h00} ;
assign data_g[50] = {8'h00,8'h00,8'h00} ;
assign data_g[51] = {8'h00,8'h00,8'h00} ;
assign data_g[52] = {8'h00,8'h00,8'h00} ;
assign data_g[53] = {8'h00,8'h00,8'h00} ;
assign data_g[54] = {8'hff,8'h00,8'h00} ;
assign data_g[55] = {8'h00,8'h00,8'h00} ;
assign data_g[56] = {8'h00,8'h00,8'h00} ;
assign data_g[57] = {8'h00,8'h00,8'h00} ;
assign data_g[58] = {8'hff,8'h00,8'h00} ;
assign data_g[59] = {8'hff,8'h00,8'h00} ;
assign data_g[60] = {8'hff,8'h00,8'h00} ;
assign data_g[61] = {8'hff,8'h00,8'h00} ;
assign data_g[62] = {8'h00,8'h00,8'h00} ;
assign data_g[63] = {8'h00,8'h00,8'h00} ;
//检测到绿色显示字母“B”
assign data_b[00] = {8'h00,8'h00,8'hff} ;
assign data_b[01] = {8'h00,8'h00,8'hff} ;
assign data_b[02] = {8'h00,8'h00,8'hff} ;
assign data_b[03] = {8'h00,8'h00,8'hff} ;
assign data_b[04] = {8'h00,8'h00,8'h00} ;
assign data_b[05] = {8'h00,8'h00,8'h00} ;
assign data_b[06] = {8'h00,8'h00,8'h00} ;
assign data_b[07] = {8'h00,8'h00,8'h00} ;
assign data_b[08] = {8'h00,8'h00,8'hff} ;
assign data_b[09] = {8'h00,8'h00,8'h00} ;
assign data_b[10] = {8'h00,8'h00,8'h00} ;
assign data_b[11] = {8'h00,8'h00,8'h00} ;
assign data_b[12] = {8'h00,8'h00,8'hff} ;
assign data_b[13] = {8'h00,8'h00,8'h00} ;
assign data_b[14] = {8'h00,8'h00,8'h00} ;
assign data_b[15] = {8'h00,8'h00,8'h00} ;
assign data_b[16] = {8'h00,8'h00,8'hff} ;
assign data_b[17] = {8'h00,8'h00,8'h00} ;
assign data_b[18] = {8'h00,8'h00,8'h00} ;
assign data_b[19] = {8'h00,8'h00,8'h00} ;
assign data_b[20] = {8'h00,8'h00,8'hff} ;
assign data_b[21] = {8'h00,8'h00,8'h00} ;
assign data_b[22] = {8'h00,8'h00,8'h00} ;
assign data_b[23] = {8'h00,8'h00,8'h00} ;
assign data_b[24] = {8'h00,8'h00,8'hff} ;
assign data_b[25] = {8'h00,8'h00,8'hff} ;
assign data_b[26] = {8'h00,8'h00,8'hff} ;
assign data_b[27] = {8'h00,8'h00,8'hff} ;
assign data_b[28] = {8'h00,8'h00,8'h00} ;
assign data_b[29] = {8'h00,8'h00,8'h00} ;
assign data_b[30] = {8'h00,8'h00,8'h00} ;
assign data_b[31] = {8'h00,8'h00,8'h00} ;
assign data_b[32] = {8'h00,8'h00,8'hff} ;
assign data_b[33] = {8'h00,8'h00,8'h00} ;
assign data_b[34] = {8'h00,8'h00,8'h00} ;
assign data_b[35] = {8'h00,8'h00,8'h00} ;
assign data_b[36] = {8'h00,8'h00,8'hff} ;
assign data_b[37] = {8'h00,8'h00,8'h00} ;
assign data_b[38] = {8'h00,8'h00,8'h00} ;
assign data_b[39] = {8'h00,8'h00,8'h00} ;
assign data_b[40] = {8'h00,8'h00,8'hff} ;
assign data_b[41] = {8'h00,8'h00,8'h00} ;
assign data_b[42] = {8'h00,8'h00,8'h00} ;
assign data_b[43] = {8'h00,8'h00,8'h00} ;
assign data_b[44] = {8'h00,8'h00,8'hff} ;
assign data_b[45] = {8'h00,8'h00,8'h00} ;
assign data_b[46] = {8'h00,8'h00,8'h00} ;
assign data_b[47] = {8'h00,8'h00,8'h00} ;
assign data_b[48] = {8'h00,8'h00,8'hff} ;
assign data_b[49] = {8'h00,8'h00,8'h00} ;
assign data_b[50] = {8'h00,8'h00,8'h00} ;
assign data_b[51] = {8'h00,8'h00,8'h00} ;
assign data_b[52] = {8'h00,8'h00,8'hff} ;
assign data_b[53] = {8'h00,8'h00,8'h00} ;
assign data_b[54] = {8'h00,8'h00,8'h00} ;
assign data_b[55] = {8'h00,8'h00,8'h00} ;
assign data_b[56] = {8'h00,8'h00,8'h00} ;
assign data_b[57] = {8'h00,8'h00,8'hff} ;
assign data_b[58] = {8'h00,8'h00,8'hff} ;
assign data_b[59] = {8'h00,8'h00,8'hff} ;
assign data_b[60] = {8'h00,8'h00,8'h00} ;
assign data_b[61] = {8'h00,8'h00,8'h00} ;
assign data_b[62] = {8'h00,8'h00,8'h00} ;
assign data_b[63] = {8'h00,8'h00,8'h00} ;
endmodule
2.控制模块
module ws2812_ctrl
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire ws2812_start , //控制模块开始工作指示的单脉冲信号,由配置模块产生
input wire [23:0] cfg_data , //待配置的RGB888颜色数据
input wire [5:0] cfg_num , //配置的RGB灯个数
output reg cfg_start , //配置模块开始工作指示的单脉冲信号,由控制模块产生
output reg led_data
);
localparam IDLE = 3'd0 , //空闲状态,跳转条件是配置模块产生的ws2812_start
ARBIT = 3'd1 , //仲裁状态,判断每一位RGB888是0还是1,是0跳转到SEND_SERO,是1跳转到SEND_ONE
SEND_ZERO = 3'd2 , //发送数据0状态,发送24x64个数据完毕跳转到RST_N状态,发送完成0码重新跳转到仲裁状态
SEND_ONE = 3'd3 , //发送数据1状态,发送24x64个数据完毕跳转到RST_N状态,发送完成1码重新跳转到仲裁状态
RST_N = 3'd4 ; //复位状态,发送完成24x64个数据后,复位状态会持续一段时间低电平
localparam CNT_WAIT_0 = 14'd55 , //发送数据0等待时间,1100ns,总时间为CNT_WAIT_0 + ARBIT_time = 1180ns
CNT_WAIT_H0 = 14'd15 , //发送数据0高电平时间,300ns,低电平时间为CNT_WAIT_0 - CNT_WAIT_H0 + ARBIT_time = 880ns
CNT_WAIT_1 = 14'd64 , //发送数据1等待时间,1280ns,总时间为CNT_WAIT_1 + ARBIT_time = 1360ns
CNT_WAIT_H1 = 14'd32 , //发送数据1高电平时间,640ns,低电平时间为CNT_WAIT_1 - CNT_WAIT_H1 + ARBIT_time = 720ns
CNT_WAIT_RST= 14'd15000 ; //发送24x64个数据完成,复位状态等待时间,总时间为300us
reg skip_en_0 ; // ①仲裁状态跳转到发送数据0状态跳转信号;②发送数据0状态跳转到仲裁状态跳转信号
reg skip_en_1 ; // ①仲裁状态跳转到发送数据1状态跳转信号;②发送数据1状态跳转到仲裁状态跳转信号
reg skip_en_rst ; // ①发送数据0/1状态跳转到复位状态跳转信号;②复位状态跳转到空闲状态跳转信号
reg [2:0] n_state ; //次态
reg [2:0] c_state ; //现态
reg [13:0] cnt_wait ; //全局等待计数器
reg data ; //数据0或者1,解码cfg_data每一位得到
reg [4:0] cnt_num ; //对配置的RGB888比特个数计数,最多配置24-1个
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
c_state <= IDLE ;
else
c_state <= n_state ;
always@(*)
case(c_state)
IDLE : if(ws2812_start == 1'b1)
n_state = ARBIT ;
else
n_state = IDLE ;
ARBIT : if(skip_en_0 == 1'b1)
n_state = SEND_ZERO ;
else if(skip_en_1 == 1'b1)
n_state = SEND_ONE ;
else
n_state = ARBIT ;
SEND_ZERO : if(skip_en_0 == 1'b1)
n_state = ARBIT ;
else if(skip_en_rst == 1'b1)
n_state = RST_N ;
else
n_state = SEND_ZERO ;
SEND_ONE : if(skip_en_1 == 1'b1)
n_state = ARBIT ;
else if(skip_en_rst == 1'b1)
n_state = RST_N ;
else
n_state = SEND_ONE ;
RST_N : if(skip_en_rst == 1'b1)
n_state = IDLE ;
else
n_state = RST_N ;
default : n_state = IDLE ;
endcase
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
skip_en_0 <= 1'b0 ;
skip_en_1 <= 1'b0 ;
skip_en_rst <= 1'b0 ;
cnt_wait <= 14'd0 ;
data <= 1'b0 ;
cnt_num <= 5'd0 ;
cfg_start <= 1'b0 ;
led_data <= 1'b0 ;
end
else
case(c_state)
ARBIT :begin
if(cnt_wait == 14'd3)
cnt_wait <= 14'd0 ;
else
cnt_wait <= cnt_wait + 1'b1 ;
data <= cfg_data[23 - cnt_num] ;
cfg_start <= 1'b0 ;
if((data == 1'b0)&&(cnt_wait == 14'd2))
skip_en_0 <= 1'b1 ;
else
skip_en_0 <= 1'b0 ;
if((data == 1'b1)&&(cnt_wait == 14'd2))
skip_en_1 <= 1'b1 ;
else
skip_en_1 <= 1'b0 ;
end
SEND_ZERO :begin
if(cnt_wait == CNT_WAIT_0 - 1'b1)
cnt_wait <= 14'd0 ;
else
cnt_wait <= cnt_wait + 1'b1 ;
if((cnt_wait == CNT_WAIT_0 - 1'b1)&&(cnt_num == 5'd23)&&(cfg_num != 6'd63))
cfg_start <= 1'b1 ;
else
cfg_start <= 1'b0 ;
if((cnt_num == 5'd23)&&(cnt_wait == CNT_WAIT_0 - 1'b1))
cnt_num <= 6'd0 ;
else if(cnt_wait == CNT_WAIT_0 - 1'b1)
cnt_num <= cnt_num + 1'b1 ;
else
cnt_num <= cnt_num ;
if((cnt_wait == CNT_WAIT_0 - 2'd2)&&(cnt_num == 5'd23)&&(cfg_num == 6'd63))
skip_en_rst <= 1'b1 ;
else if(cnt_wait == CNT_WAIT_0 - 2'd2)
skip_en_0 <= 1'b1 ;
else
begin
skip_en_rst <= 1'b0 ;
skip_en_0 <= 1'b0 ;
end
if(cnt_wait <= CNT_WAIT_H0 - 1'b1)
led_data <= 1'b1 ;
else
led_data <= 1'b0 ;
end
SEND_ONE :begin
if(cnt_wait == CNT_WAIT_1 - 1'b1)
cnt_wait <= 14'd0 ;
else
cnt_wait <= cnt_wait + 1'b1 ;
if((cnt_wait == CNT_WAIT_1 - 1'b1)&&(cnt_num == 5'd23)&&(cfg_num != 6'd63))
cfg_start <= 1'b1 ;
else
cfg_start <= 1'b0 ;
if((cnt_num == 5'd23)&&(cnt_wait == CNT_WAIT_1 - 1'b1))
cnt_num <= 6'd0 ;
else if(cnt_wait == CNT_WAIT_1 - 1'b1)
cnt_num <= cnt_num + 1'b1 ;
else
cnt_num <= cnt_num ;
if((cnt_wait == CNT_WAIT_1 - 2'd2)&&(cnt_num == 5'd23)&&(cfg_num == 6'd63))
skip_en_rst <= 1'b1 ;
else if(cnt_wait == CNT_WAIT_1 - 2'd2)
skip_en_1 <= 1'b1 ;
else
begin
skip_en_rst <= 1'b0 ;
skip_en_1 <= 1'b0 ;
end
if(cnt_wait <= CNT_WAIT_H1 - 1'b1)
led_data <= 1'b1 ;
else
led_data <= 1'b0 ;
end
RST_N :begin
if(cnt_wait == CNT_WAIT_RST - 1'b1)
cnt_wait <= 14'd0 ;
else
cnt_wait <= cnt_wait + 1'b1 ;
if(cnt_wait == CNT_WAIT_RST - 1'b1)
cfg_start <= 1'b1 ;
else
cfg_start <= 1'b0 ;
if(cnt_wait == CNT_WAIT_RST - 2'd2)
skip_en_rst <= 1'b1 ;
else
skip_en_rst <= 1'b0 ;
led_data <= 1'b0 ;
end
default :begin
skip_en_0 <= 1'b0 ;
skip_en_1 <= 1'b0 ;
skip_en_rst <= 1'b0 ;
cnt_wait <= 14'd0 ;
data <= 1'b0 ;
cnt_num <= 5'd0 ;
led_data <= 1'b0 ;
cfg_start <= 1'b0 ;
end
endcase
endmodule
3.顶层模块
module ws2812_top
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire r_valid ,
input wire g_valid ,
input wire b_valid ,
output wire led_data
);
wire cfg_start ;
wire ws2812_start;
wire [5:0] cfg_num ;
wire [23:0] cfg_data ;
ws2812_cfg_ctrl ws2812_cfg_ctrl_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.cfg_start (cfg_start ),
.r_valid (r_valid ),
.g_valid (g_valid ),
.b_valid (b_valid ),
.ws2812_start (ws2812_start ),
.cfg_num (cfg_num ),
.cfg_data (cfg_data )
);
ws2812_ctrl ws2812_ctrl_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.ws2812_start (ws2812_start ),
.cfg_data (cfg_data ),
.cfg_num (cfg_num ),
.cfg_start (cfg_start ),
.led_data (led_data )
);
endmodule