云南工程建设投标网上报名网站,谷德设计网打不开,浏览器无法打开住房和建设网站,wordpress图片和相册文章目录 一、WS2812手册分析1.1 WS2812灯源特性及概述1.2 手册重点内容分析1.2.1 产品概述1.2.2 码型及24bit数据设计 二、系统设计2.1 模块设计2.2 模块分析2.2.1 驱动模块2.2.1 数据控制模块 三、IP核设置及项目源码3.1 MIF文件设计3.2 ROM IP核调用3.3 FIFO IP核调用3.4 项… 文章目录 一、WS2812手册分析1.1 WS2812灯源特性及概述1.2 手册重点内容分析1.2.1 产品概述1.2.2 码型及24bit数据设计 二、系统设计2.1 模块设计2.2 模块分析2.2.1 驱动模块2.2.1 数据控制模块 三、IP核设置及项目源码3.1 MIF文件设计3.2 ROM IP核调用3.3 FIFO IP核调用3.4 项目各模块源码 四、最终显示效果五、总结 一、WS2812手册分析
1.1 WS2812灯源特性及概述 1.2 手册重点内容分析
1.2.1 产品概述 由产品概述可以得到的重要信息有
该灯源数据协议采用单线归零码的通讯方式一个像素点需要24bit数据才能正常工作该灯板共有8×8 64个像素点复位时间需要至少280us传输数据每经过一个像素点便会被锁存24bit数据因此数据会逐级减少数据发送速度最高为800Kbps
1.2.2 码型及24bit数据设计 由上述手册截图可以看出该光源0码和1码占空比并不相同同时一个码元持续时间也可以不同因此在设计码型时一个码元持续时间需要同时满足0码和1码。
数据传输方法与前文概述所说一样没经过一个像素点便会被锁存24bit数据然后继续逐级传输同时每一次24bit数据传输结束需要经过经过至少280us的复位才能继续传输下一个24bit数据。
同时该光源所需24bit数据结构为GRB顺序在设计时需要将RGB数据进行重新拼接该拼接过程可以在传入数据时也可以在传出数据时。
二、系统设计
2.1 模块设计
设计本项目时建议将模块划分为驱动模块控制模块以及顶层模块。
在控制模块中设计不同数据的输入在驱动模块中设计码型以及输入数据的拼接及输出顶层模块仅进行逻辑连线不建议在顶层模块进行任何逻辑编写
2.2 模块分析
2.2.1 驱动模块
在驱动模块中首先我们主要考虑的是64个24bit数据的传输。 由上述分析可知每24bit数据传输间隔中我们需要至少280us的复位复位结束才能传输下一个24bit数据因此我们可以设计一个状态机实现数据传输与复位的状态切换。 同时我们可以设计一个ready使能信号将该信号传输给控制模块该信号拉高后代表驱动模块处于空闲状态可以进入复位状态。 同时我们可以在控制模块设计一个data_vld信号将该信号输入驱动模块该信号拉高驱动模块应该立即进入复位状态并在复位结束后即刻准备接受数据。 再由手册的介绍可知在该模块我们至少需要四个计数器分别为 24bit数据计数器计数从控制模块传来的数据量。 64个像素点计数器该灯板共有8×8 64个像素点每个像素点均需要24bit数据。 一个码元持续时间计数器由于该光源1码和0码要求时间可以不同因此我们需要取一个能同时满足0码和1码的值作为一个码元的传输时间。博主的一个码元所需传输时间设置为1200ns既能满足0码220ns-380ns的高电平时间以及580ns-1us的低电平时间也能满足1码高低电平时间均为580ns-1us。 复位时间计数器至少为280us博主设计为400us。 同时由于该光源数据格式为GRB因此我们还需要在驱动模块对传入的RGB数据重新进行拼接。 此外由于时钟数据采用频率为50MHz而该光源发送速率只有800Kbps传入数据的速度远大于传出数据的速度因此博主调用了一个FIFO核用来临时存储传入驱动模块的数据避免造成数据丢失。
该模块状态机设计如下 其中end_cnt_rst为复位计数器结束信号end_cnt_pix为64个像素点计数器结束信号。
2.2.1 数据控制模块
在该模块我们所需要的考虑仅为所需数据的存储以及如何传入驱动模块。
为了存储我们设计好的数据博主在该模块调用了一个ROM IP核。同时博主在该模块设计了XY两个计数器计数最大值均为8。将XY作为光源像素点的坐标从而确定ROM中所要取得的数据。为了方便设计并存储数据在调用ROM时需要设计MIF文件初始化ROM在后文博主会将自己设计MIF文件的方式和工具分享给大家。
三、IP核设置及项目源码
3.1 MIF文件设计
打开系统自带的画图工具点击左上角文件选择图像属性 在图像属性中设置图像为 32×8大家可以根据自己所需自由设计宽度如果自定义宽度就需要在ROM核设置的时候进行更改同时需要在博主的数据控制模块进行一个小细节的更改博主会在后文标明但高度固定因为动态显示相当于滚动移动宽度无法移动高度 然后在画图中设计自己想要显示的图案即可 设计完成后点击右上角文件另存为24位宽BMP文件注意必须为24位宽 然后利用格式转换工具将该BMP文件转化为mif文件 然后将生成的文件存放在自己创建的quartus prj文件夹下 3.2 ROM IP核调用
在quartus IP Catalog中搜索ROM并选择单端口ROM
按照下图进行设置
输出数据设置为24bit宽深度设置为256若BMP图像高度不是博主设置的32各位需要自行更改所需深度将其改为你所设置的宽度×8 点击next按下图进行如下设置 将输出q寄存一拍勾选则在后续读使能需要打两拍博主个人习惯可以不选后续只需打一拍勾选异步复位信号勾选读使能信号
点击next按照下图进行如下设置
点击Browse选择前文所设置的MIF文件对ROM进行初始化
一路next至总结界面 勾选例化模板
最终设置如下 点击finish完成ROM IP核配置
3.3 FIFO IP核调用
在IP Catalog中搜索FIFO进入配置选项卡 数据宽度同样为24bit深度同ROM由于本项目为跨时钟域因此FIFO读写选择单时钟即可
点击next按下图进行设置 空、满信号可用于确定读写请求usew可在仿真时看到FIFO中的数据个数此处不勾选不仿真用不到勾选异步复位信号
点击next按下图进行配置: 勾选前显模式
一路next至总结界面 勾选例化模板
FIFO总体配置如下 3.4 项目各模块源码
驱动模块
module ws2812_driver (input wire clk ,input wire rst_n ,input wire [23:0] pix_data ,//输入数据input wire pix_data_vld,//输入数据有效信号output wire ready ,output reg ws2812_io //输出码元信号
);//内部参数定义//状态参数定义parameter IDLE 3b001 ,RST 3b010 ,DATA 3b100 ;//码元电平参数定义parameter T0H 300/20 ,T0L 900/20 ,T1H 600/20 ,T1L 600/20 ;//复位码元计数参数parameter RST_MAX 14d15_000;//内部信号定义//现态、次态寄存器reg [2:0] cstate ;reg [2:0] nstate ;//跳转条件定义wire idle2rst ;wire rst2data ;wire data2idle ;//一个码元时间计数器reg [5:0] cnt_time ;wire add_cnt_time ;wire end_cnt_time ;//24bit计数器reg [4:0] cnt_bit ;wire add_cnt_bit ;wire end_cnt_bit ;//64个数据计数器reg [5:0] cnt_pix ;wire add_cnt_pix ;wire end_cnt_pix ;//复位码元计数器reg [13:0] cnt_reset ;wire add_cnt_reset ;wire end_cnt_reset ;//FIFO信号定义wire [23:0] fifo_wr_data ;wire fifo_rd_req ;wire fifo_wr_req ;wire fifo_empty ;wire fifo_full ;wire [23:0] fifo_rd_data ;wire [7:0] fifo_usedw ;//FIFO例化fifo fifo_inst (.aclr ( ~rst_n ),.clock ( clk ),.data ( fifo_wr_data ),.rdreq ( fifo_rd_req ),.wrreq ( fifo_wr_req ),.empty ( fifo_empty ),.full ( fifo_full ),.q ( fifo_rd_data ),.usedw ( fifo_usedw ));assign fifo_wr_data {pix_data[15:8],pix_data[23:16],pix_data[7:0]};//将传入的24bit RGB数据转为GRB顺序//24hff0000;assign fifo_wr_req pix_data_vld ~fifo_full;//1b1 ~fifo_full;//assign fifo_rd_req end_cnt_bit ~fifo_empty;//三段式状态机//第一段always (posedge clk or negedge rst_n) beginif(!rst_n)begincstate IDLE;endelse begincstate nstate;endend//第二段组合逻辑always(*)begincase(cstate)IDLE : beginif(idle2rst)beginnstate RST;endelse beginnstate cstate;endendRST : beginif(rst2data)beginnstate DATA;endelse beginnstate cstate;endendDATA : beginif(data2idle)beginnstate IDLE;endelse beginnstate cstate;endenddefault : nstate cstate;endcaseendassign idle2rst cstate IDLE pix_data_vld ;//1b1;//assign rst2data cstate RST end_cnt_reset ;assign data2idle cstate DATA end_cnt_pix ;//码元最低持续时间计数器always (posedge clk or negedge rst_n)begin if(!rst_n)begincnt_time d0;end else if(add_cnt_time)begin if(end_cnt_time)begin cnt_time d0;endelse begin cnt_time cnt_time 1b1;end endend assign add_cnt_time cstate DATA;assign end_cnt_time add_cnt_time cnt_time 6d59;//一位数据的24bit数计数器always (posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit d0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit d0;endelse begin cnt_bit cnt_bit 1b1;end endend assign add_cnt_bit end_cnt_time;assign end_cnt_bit add_cnt_bit cnt_bit 5d23;//64个数据计数器always (posedge clk or negedge rst_n)begin if(!rst_n)begincnt_pix d0;end else if(add_cnt_pix)begin if(end_cnt_pix)begin cnt_pix d0;endelse begin cnt_pix cnt_pix 1b1;end endend assign add_cnt_pix end_cnt_bit;assign end_cnt_pix add_cnt_pix cnt_pix 6d63;//复位计数器always (posedge clk or negedge rst_n)begin if(!rst_n)begincnt_reset d0;end else if(add_cnt_reset)begin if(end_cnt_reset)begin cnt_reset d0;endelse begin cnt_reset cnt_reset 1b1;end endend assign add_cnt_reset cstate RST;assign end_cnt_reset add_cnt_reset cnt_reset RST_MAX - 1b1;//第三段 状态机输出always(*)begincase(cstate)IDLE : ws2812_io 1b0;RST : ws2812_io 1b0;DATA : beginif(fifo_rd_data[23 - cnt_bit])beginif(cnt_time T1H)beginws2812_io 1b1;endelse beginws2812_io 1b0;endendelse beginif(cnt_time T0H)beginws2812_io 1b1;endelse beginws2812_io 1b0;endendenddefault : ws2812_io 1b0;endcaseend //ready使能信号赋值
assign ready cstate IDLE;endmodule数据控制模块
/**************************************************************
File : ws2812_control.v
Time : 2023/08/11 15:09:52
Author : majiko
EditTool: VS Code
Font : UTF-8
Function: 提供64个RGB像素数据给到ws2812接口模块用于验证接口模块
**************************************************************/
module ws2812_ctrl2(input clk ,input rst_n ,input ready ,//可以接收图像数据了output [23:0] pix_data ,output pix_data_vld );parameter IDLE 0,DATA 1,DELAY 2;parameter DELAY_TIME 25d25_000_000;wire rom_rd_req ;reg rom_rd_req1 ;reg rom_rd_req2 ;wire rom_rd_data ;reg [2:0] state ;reg [5:0] cnt_x ;wire add_x_cnt;wire end_x_cnt; reg [4:0] cnt_y ;wire add_y_cnt;wire end_y_cnt;reg [4:0] cnt_offset ;wire add_cnt_offset ;wire end_cnt_offset ;reg [24:0] cnt_delay ;wire add_cnt_delay ;wire end_cnt_delay ; // localparam RED 24hFF0000, //红色// ORANGE 24hFF8000, //橙色// YELLOW 24hFFFF00, //黄色// GREEN 24h00FF00, //绿色// CYAN 24h00FFFF, //青色// BLUE 24h0000FF, //蓝色// PURPPLE 24h8000FF, //紫色// BLACK 24h000000, //黑色// WHITE 24hFFFFFF, //白色// GRAY 24hC0C0C0; //灰色/**************************************************************状态机
**************************************************************/always(posedge clk or negedge rst_n)if(!rst_n)state IDLE;else case(state)IDLE : if(ready)state DATA;DATA : if(end_y_cnt)state DELAY;DELAY : if(end_cnt_delay)state IDLE;default : state IDLE;endcase//帧间隔计数器always (posedge clk or negedge rst_n)begin if(!rst_n)begincnt_delay d0;end else if(add_cnt_delay)begin if(end_cnt_delay)begin cnt_delay d0;endelse begin cnt_delay cnt_delay 1b1;end endend assign add_cnt_delay state DELAY;assign end_cnt_delay add_cnt_delay cnt_delay DELAY_TIME - 1;/**************************************************************图像数据个数计数器
**************************************************************/ always(posedge clk or negedge rst_n) if(!rst_n) cnt_x d0; else if(add_x_cnt) begin if(end_x_cnt) cnt_x d0; else cnt_x cnt_x 1b1; end assign add_x_cnt state DATA;assign end_x_cnt add_x_cnt cnt_x 8 - 1;always(posedge clk or negedge rst_n) if(!rst_n) cnt_y d0; else if(add_y_cnt) begin if(end_y_cnt) cnt_y d0; else cnt_y cnt_y 1b1; end assign add_y_cnt end_x_cnt;assign end_y_cnt add_y_cnt cnt_y 8 - 1;//偏移计数器always (posedge clk or negedge rst_n)begin if(!rst_n)begincnt_offset d0;end else if(add_cnt_offset)begin if(end_cnt_offset)begin cnt_offset d0;endelse begin cnt_offset cnt_offset 1b1;end endend assign add_cnt_offset end_cnt_delay;assign end_cnt_offset add_cnt_offset cnt_offset 5d23;// assign pix_data_vld add_x_cnt;// always(*)// case(cnt_y)// 0 : pix_data RED ;// 1 : pix_data ORANGE ;// 2 : pix_data YELLOW ;// 3 : pix_data GREEN ;// 4 : pix_data CYAN ;// 5 : pix_data BLUE ;// 6 : pix_data PURPPLE ;// 7 : pix_data GRAY ;// default : pix_data RED ;// endcasewire [4:0] real_row ;assign real_row cnt_x cnt_offset;rom rom_inst (.aclr ( ~rst_n ),.address ( cnt_y*32 real_row),.clock ( clk ),.rden ( rom_rd_req ),.q ( pix_data ));//ROM读请求打两拍always(posedge clk or negedge rst_n)beginif(!rst_n)beginrom_rd_req1 1b0;rom_rd_req2 1b0;endelse beginrom_rd_req1 rom_rd_req;rom_rd_req2 rom_rd_req1;endendassign rom_rd_req state DATA;assign rom_rd_data rom_rd_req2 ;assign pix_data_vld rom_rd_data ;endmodule顶层模块
module ws2812_top(input wire clk ,input wire rst_n ,output wire ws2812_io
);wire [23:0] pix_data ;
wire ready ;
wire pix_data_vld;//模块例化
//显示图片
// ws2812_ctrl u_ws2812_ctrl(// .clk (clk ),// .rst_n (rst_n ),// .pix_data (pix_data ),// .pix_data_vld (pix_data_vld ),// .ready (ready )
// );//动态显示图片
ws2812_ctrl2 u_ws2812_ctrl2(.clk (clk ),.rst_n (rst_n ),.pix_data (pix_data ),.pix_data_vld (pix_data_vld ),.ready (ready )
);interface u_interface(.clk (clk ),.rst_n (rst_n ),.pix_data (pix_data ),.pix_data_vld (pix_data_vld),.ready (ready ),.ws2812_io (ws2812_io )
);endmodule四、最终显示效果
引脚绑定如下 实现效果 五、总结
博主本人为FPGA初学者因此才会写下此篇博客作为自己对WS2812的一个总结如果有和博主一样的初学者希望此博文能够帮助到你。
这也是博主第一次利用FPGA驱动外设第一次设计一个较为简单的单总线通讯协议尽管很大程度博主都是依靠着博主老师的代码才完成的。
如果此篇博文有任何错误还请各位提出