一个问题:六位八段数码管(Verilog)
【基本信息】
需求:verilog程序,显示任意六位字符或数值,包含点号,且能够按需点亮位数。(学习篇)
芯片型号:cyclone Ⅳ EP4CE10F17C8
数码管属性:六位、八段
【最终成果图】
经过多轮测试,最后代码程序满足设计要求,但结合仿真发现了一个问题,仿真和上机不匹配,当然还是要以上机为准。
【模块例化图】
这里就是简单地赋个初始值,来测试digital模块,最终是要seg_led和seg_sel的显示变化。
【verilog程序】
1、digital模块
模块化设计,六个位上的值直接拆开做处理,分析起来清晰。sel_cnt表示目前要显示的位数(从右往左),即sel_cnt = 6代表全亮。dp_cnt表示点号所在位数,若dp_cnt=0则代表没有点号。clk_2khz为数码管的刷新信号。
module digital ( input sys_clk , input sys_rst , input clk_2khz , input [3:0] num6, input [3:0] num5, input [3:0] num4, input [3:0] num3, input [3:0] num2, input [3:0] num1, input [2:0] sel_cnt , input [2:0] dp_cnt , output reg[5:0] seg_sel , output reg[7:0] seg_led ); reg[2:0] sel_cnt_tran; reg[3:0] num; reg[2:0] tran1; //endmodule
定义了几个变量,sel_cnt_tran用来根据数码管刷新信号更替数码管的位选,而num用来接引各位上的值,数码管根据num编号段选信号。tran1在过程讨论部分做解释。
always @(posedge sys_clk or negedge sys_rst) begin if(!sys_rst) sel_cnt_tran = 3'd0; else if(clk_2khz)begin if(sel_cnt_tran < sel_cnt)begin tran1 = sel_cnt_tran; sel_cnt_tran = sel_cnt_tran + 1'b1; end else begin tran1 = sel_cnt_tran; sel_cnt_tran = 3'd0; end end end
上述代码就是位选更替,可以直观得到,tran1比sel_cnt_tran要晚上一个数码管刷新周期。
always @(posedge sys_clk or negedge sys_rst) begin if(!sys_rst) seg_sel <= 6'b111_111; else if(clk_2khz)begin case(sel_cnt_tran) 3'd1:begin seg_sel <= 6'b111_110; num <= num1; end 3'd2:begin seg_sel <= 6'b111_101; num <= num2; end 3'd3:begin seg_sel <= 6'b111_011; num <= num3; end 3'd4:begin seg_sel <= 6'b110_111; num <= num4; end 3'd5:begin seg_sel <= 6'b101_111; num <= num5; end 3'd6:begin seg_sel <= 6'b011_111; num <= num6; end default:begin seg_sel <= 6'b111_111; end endcase end end
根据原理图,数码管是共阳极,并且位选拉低有效,这里测试是没有问题的。
always @(posedge sys_clk or negedge sys_rst)begin if (!sys_rst) seg_led = 8'b0; else begin case (num) 4'h0 : seg_led = 8'b1100_0000; 4'h1 : seg_led = 8'b1111_1001; 4'h2 : seg_led = 8'b1010_0100; 4'h3 : seg_led = 8'b1011_0000; 4'h4 : seg_led = 8'b1001_1001; 4'h5 : seg_led = 8'b1001_0010; 4'h6 : seg_led = 8'b1000_0010; 4'h7 : seg_led = 8'b1111_1000; 4'h8 : seg_led = 8'b1000_0000; 4'h9 : seg_led = 8'b1001_0000; 4'ha : seg_led = 8'b1000_1000; 4'hb : seg_led = 8'b1000_0011; 4'hc : seg_led = 8'b1100_0110; 4'hd : seg_led = 8'b1010_0001; 4'he : seg_led = 8'b1000_0110; 4'hf : seg_led = 8'b1000_1110; default : seg_led = 8'b1100_0000;//多余 endcase if(dp_cnt == (tran1))begin //确定点号,这里不是sel_cnt_tran seg_led = seg_led & 8'b0111_1111; end end end
2、clk_2khz模块
module clk_2khz ( input sys_clk , input sys_rst , output reg clk_out ); parameter cnt_2khz_max = 25_000; reg[14:0] cnt;
clk_2khz时钟产生很简单,cnt_2khz_max = 1/2_000x50_000_000,在这就放下代码。注意两个问题:一个是不要直接posedge clk_2khz方式来读上升沿,另外尽量不要采用取反方式产生分频时钟信号,如果是取反,可以采用wire en;assign en = d1 & d0来读边沿信号。
always @(posedge sys_clk or negedge sys_rst)begin if(!sys_rst)begin cnt <= 15'd0; clk_out <= 1'b0; end else if(cnt <(cnt_2khz_max-1'b1))begin cnt <= cnt + 1'b1; clk_out <= 1'b0; end else begin cnt <= 15'd0;verilog clk_out <= 1'b1; end end endmodule
3、顶部例化测试
module digital_top ( input sys_clk , input sys_rst , output [5:0] seg_sel , output [7:0] seg_led ); wire clk_2khz_tran; reg[3:0] num1 = 4'd2; reg[3:0] num2 = 4'h1; reg[3:0] num3 = 4'd5; reg[2:0] sel_cnt = 3'd6; reg[2:0] dp_cnt = 3'd6; digital digital_inst(); //例化省略 clk_2khz clk_generator1(); endmodule
4、Modelsim仿真
`timescale 1ns/1ns module digital_tb(); parameter T1 =20 ; parameter T2 =200; parameter max = 14; reg sys_clk; reg clk_2khz; reg sys_rst; reg[3:0] num1 = 4'h1; reg[3:0] num2 = 4'h1; reg[3:0] num3 = 4'h1; reg[2:0] sel_cnt = 3'd6; reg[2:0] dp_cnt = 3'd6; reg[3:0] cnt; wire[7:0] seg_led; wire[5:0] seg_sel; always # (T1/2) sys_clk <= ~sys_clk; always @(posedge sys_clk or negedge sys_rst)begin if(!sys_rst) cnt <= 4'd0; else begin if(cnt < max)begin cnt <= cnt + 1'b1; clk_2khz <= 1'b0; end else begin cnt <= 4'd0; clk_2khz <= 1'b1; end end end initial begin sys_clk <=1'b0; sys_rst <=1'b0; #50 sys_rst <=1'b1; end digital digital_inst();//例化省略 endmodule
【过程讨论】
首先由于自己疏忽,数码管位选刷新位置采用的是非阻塞赋值的方法,通过用sel_cnt_tran判断为真的话,通过仿真(下图)观察到,位选和段选不匹配,就是说,点号的位置不正确,现象是位选落后了。
然后想着将赋值方法改为阻塞赋值,还是有问题。强制更改,设定判断条件为if(dp_cnt == (sel_cnt_tran-1’b1))看看,显然,这样虽然有效(图在下面),但只能保证最终的点号位置有限,比如dp_cnt = 6就有问题,因为sel_cnt_tran在6后赋值是0,达不到条件。后面借助变量tran1在sel_cnt_tran刷新处赋值使其落后一个周期,仿真显示不正确,但上机测试有效。
本文作者:Handat
本文链接:https://www.cnblogs.com/handat/p/18227025
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步