基于FPGA的呼吸灯设计-基础篇

基于FPGA的呼吸灯设计-基础篇 目录呼吸灯的设计设计需求绘制模块框图及波形图编写模块代码编写仿真代码仿真验证​编辑总结小作业设计方法绘制模块框图及波形图​编辑编写模块代码编写仿真代码仿真验证​编辑呼吸灯的设计我们知道灯的亮暗是与电流和电压有关的但是我们的FPGA是数字信号只有逻辑0和1分别代表0V和3.3V。所以我们想到可以使用PWM(脉冲宽度调制)方波去控制占空比进而去控制led的亮暗程度。当占空比拆分的越细效果就越细腻类似于平时打游戏的帧率一样。控制PWM(脉冲宽度调制)方波占空比常用于led、风扇等。设计需求控制led从灭到亮再从亮到灭的过程通过控制PWM的占空比来实现led越亮的效果先分析led从灭到亮的过程(因为从亮到灭是其逆过程)设置从灭到亮的时间为1s,将其划分为1000段即1000ms;在第1段时led亮0us;第2段时led亮1us;第3段时led亮2us;…;第999段时led亮998us;第1000段时led亮999us;绘制模块框图及波形图这里的多级计数器的设计可能比较巧妙有点难理解建议多思考多看看这个波形图理解了再去编写代码会方便很多。下面这个波形图和上面的是一个意思希望能够帮助大家去理解。编写模块代码module breath_led #( parameter CNT_1US_MAX 6d49 , parameter CNT_1MS_MAX 10d999 , parameter CNT_1S_MAX 10d999 ) ( input wire clk , input wire rst_n , output reg led1 , output reg led2 ); reg [5:0] cnt_1us ; reg [9:0] cnt_1ms ; reg [9:0] cnt_1s ; reg en ; //cnt_1us:1us计数器,计满时为1us always (posedge clk or negedge rst_n) begin if(!rst_n) cnt_1us 6d0; else if(cnt_1us CNT_1US_MAX) cnt_1us 6d0; else cnt_1us 1b1 cnt_1us; end //cnt_1ms:1ms计数器,计数1us的个数,计数1000个1us,计数满为1ms always (posedge clk or negedge rst_n) begin if(!rst_n) cnt_1ms 10d0; else if((cnt_1ms CNT_1MS_MAX) (cnt_1us CNT_1US_MAX))//计满为1ms,清零 cnt_1ms 10d0; else if(cnt_1us CNT_1US_MAX) cnt_1ms 1b1 cnt_1ms; else cnt_1ms cnt_1ms; end //cnt_1s:1s计数器,计数1ms的个数,计数1000个1ms,计数满为1s always (posedge clk or negedge rst_n) begin if(!rst_n) cnt_1s 10d0; else if((cnt_1s CNT_1S_MAX cnt_1ms CNT_1MS_MAX) (cnt_1us CNT_1US_MAX)) cnt_1s 10d0; else if(cnt_1ms CNT_1MS_MAX cnt_1us CNT_1US_MAX) cnt_1s 1b1 cnt_1s; else cnt_1s cnt_1s; end //en:呼吸状态寄存器 1:代表灯从灭到亮 0:代表其逆过程 always (posedge clk or negedge rst_n) begin if(!rst_n) en 1b1; else if((cnt_1s CNT_1S_MAX cnt_1ms CNT_1MS_MAX) (cnt_1us CNT_1US_MAX)) en ~en; else en en; end // led1波形延迟1拍 always (posedge clk or negedge rst_n) begin if(!rst_n) led1 1b1; else if((en 1b1 cnt_1ms cnt_1s) || (en 1b0 cnt_1ms cnt_1s)) led1 1b0; else led1 1b1; end // led2波形无延迟 always (*) begin if(!rst_n) led2 1b1; else if((en 1b1 cnt_1ms cnt_1s) || (en 1b0 cnt_1ms cnt_1s)) led2 1b0; else led2 1b1; end endmodule编写仿真代码timescale 1ns/1ps module breath_led_tb(); reg clk ; reg rst_n ; wire led1 ; wire led2 ; initial begin clk 1b0; rst_n 1b0; #123 rst_n 1b1; end always #10 clk ~clk; breath_led #( .CNT_1US_MAX ( 6d4 ) , .CNT_1MS_MAX ( 10d9 ) , .CNT_1S_MAX ( 10d9 ) )breath_led_inst ( .clk (clk ) , .rst_n (rst_n) , .led1 (led1 ) , .led2 (led2 ) ); endmodule仿真验证仿真验证通过下载代码上板验证即可。总结在多级计数器设计当中n级计数器计数满时的条件有n个加一操作的条件有n-1个其余时刻保持。知道结论便于代码的检查。小作业结合上面的呼吸灯实现4s吸气4s呼气的呼吸灯。设计方法方法一将4s划分为1000段每段是4ms也就是说我们可以在第1段亮0*4us第2段亮1*4us...;第999段亮998*4us第1000段亮999*4us。实现方法保持CNT_1MS_MAX999和CNT_1S_MAX999不变将CNT_1US_MAX从49改为1994μs并将cnt_1us位宽扩展为8位。这样每个阶梯时长为4ms总吸气时间为1000×4ms4s。方法二将4s吸气过程划分为2000段每段时长2ms。在第1段LED亮0μs第2段亮1×2μs……第2000段亮1999×2μs。实现方法保持CNT_1US_MAX491μs不变将CNT_1MS_MAX改为19992msCNT_1S_MAX改为19992000个阶梯并相应将cnt_1ms和cnt_1s的位宽扩展为11位。这样每个阶梯时长为2ms总吸气时间为2000×2ms4秒且PWM频率更高500Hz、分辨率更细0.05%。方法三将4s吸气过程划分为4000段每段时长1ms。在第1段LED亮0*1μs第2段亮1×1μs……第4000段亮3999×1μs。但是要求的最大点亮时间3999 μs超过了分配的段时长1000 μs所以舍去。下面将以方法二完成设计。绘制模块框图及波形图编写模块代码module breath_led #( parameter CNT_MAX_1US 6d49 , parameter CNT_MAX_2MS 11d1999 , parameter CNT_MAX_4S 11d1999 ) ( input wire clk , input wire rst_n , output reg led ); reg [5:0] cnt_1us; reg [10:0] cnt_2ms; reg [10:0] cnt_4s; reg led_en; always (posedge clk or negedge rst_n) if(!rst_n) cnt_1us 6d0; else if(cnt_1us CNT_MAX_1US) cnt_1us 6d0; else cnt_1us cnt_1us 1b1; always (posedge clk or negedge rst_n) if(!rst_n) cnt_2ms 11d0; else if(cnt_2ms CNT_MAX_2MS cnt_1us CNT_MAX_1US) cnt_2ms 11d0; else if(cnt_1us CNT_MAX_1US) cnt_2ms cnt_2ms 1b1; else cnt_2ms cnt_2ms; always (posedge clk or negedge rst_n) if(!rst_n) cnt_4s 11d0; else if(cnt_4s CNT_MAX_4S (cnt_2ms CNT_MAX_2MS cnt_1us CNT_MAX_1US)) cnt_4s 11d0; else if(cnt_2ms CNT_MAX_2MS cnt_1us CNT_MAX_1US) cnt_4s cnt_4s 1b1; else cnt_4s cnt_4s; always (posedge clk or negedge rst_n) if(!rst_n) led_en 1b0; else if(cnt_4s CNT_MAX_4S (cnt_2ms CNT_MAX_2MS cnt_1us CNT_MAX_1US)) led_en ~led_en; else led_en led_en; // led波形会延迟1拍(绘制的波形图没有打拍) always (posedge clk or negedge rst_n) if(!rst_n) led 1b1; else if((led_en 1b0 cnt_2ms cnt_4s) || (led_en 1b1 cnt_2ms cnt_4s)) led 1b0; else led 1b1; endmodule编写仿真代码timescale 1ns/1ps module breath_led_tb(); reg clk ; reg rst_n ; wire led ; initial begin clk 1b0; rst_n 1b0; #123 rst_n 1b1; end always #10 clk ~clk; breath_led #( .CNT_MAX_1US ( 6d4 ) , .CNT_MAX_2MS ( 11d99 ) , .CNT_MAX_4S ( 11d99 ) )breath_led_inst ( .clk (clk ) , .rst_n (rst_n) , .led (led ) ); endmodule仿真验证仿真验证通过下载代码进行上板验证即可。