ZC-CLS381RGB颜色识别+8x8点阵指示——WS2812点阵驱动(中)


前言

  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
  • 12
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值