FPGA入门(6):数码管静态/动态显示(一)+https://developer.aliyun.com/article/1556559
data_gen.v
`timescale 1ns/1ns module data_gen #( parameter CNT_MAX = 23'd4999_999, //100ms计数值 parameter DATA_MAX= 20'd999_999 //显示的最大值 ) ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低电平有效 output reg [19:0] data , //数码管要显示的值 output wire [5:0] point , //小数点显示,高电平有效 output reg seg_en , //数码管使能信号,高电平有效 output wire sign //符号位,高电平显示负号 ); //reg define reg [22:0] cnt_100ms ; //100ms计数器 reg cnt_flag ; //100ms标志信号 //不显示小数点以及负数 assign point = 6'b000_000; assign sign = 1'b0; //cnt_100ms:用50MHz时钟从0到4999_999计数即为100ms always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_100ms <= 23'd0; else if(cnt_100ms == CNT_MAX) cnt_100ms <= 23'd0; else cnt_100ms <= cnt_100ms + 1'b1; //cnt_flag:每100ms产生一个标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_flag <= 1'b0; else if(cnt_100ms == CNT_MAX - 1'b1) cnt_flag <= 1'b1; else cnt_flag <= 1'b0; //数码管显示的数据:0-999_999 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data <= 20'd0; else if((data == DATA_MAX) && (cnt_flag == 1'b1)) data <= 20'd0; else if(cnt_flag == 1'b1) data <= data + 1'b1; else data <= data; //数码管使能信号给高即可 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) seg_en <= 1'b0; else seg_en <= 1'b1; endmodule
二进制数转BCD码设计
BCD编码模块波形图绘制
bcd_8421.v
`timescale 1ns/1ns module bcd_8421 ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低电平有效 input wire [19:0] data , //输入需要转换的数据 output reg [3:0] unit , //个位BCD码 output reg [3:0] ten , //十位BCD码 output reg [3:0] hun , //百位BCD码 output reg [3:0] tho , //千位BCD码 output reg [3:0] t_tho , //万位BCD码 output reg [3:0] h_hun //十万位BCD码 ); //reg define reg [4:0] cnt_shift ; //移位判断计数器 reg [43:0] data_shift ; //移位判断数据寄存器 reg shift_flag ; //移位判断标志信号 //cnt_shift:从0到21循环计数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_shift <= 5'd0; else if((cnt_shift == 5'd21) && (shift_flag == 1'b1)) cnt_shift <= 5'd0; else if(shift_flag == 1'b1) cnt_shift <= cnt_shift + 1'b1; else cnt_shift <= cnt_shift; //data_shift:计数器为0时赋初值,计数器为1~20时进行移位判断操作 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_shift <= 44'b0; else if(cnt_shift == 5'd0) data_shift <= {24'b0,data}; else if((cnt_shift <= 20) && (shift_flag == 1'b0)) begin data_shift[23:20] <= (data_shift[23:20] > 4) ? (data_shift[23:20] + 2'd3) : (data_shift[23:20]); data_shift[27:24] <= (data_shift[27:24] > 4) ? (data_shift[27:24] + 2'd3) : (data_shift[27:24]); data_shift[31:28] <= (data_shift[31:28] > 4) ? (data_shift[31:28] + 2'd3) : (data_shift[31:28]); data_shift[35:32] <= (data_shift[35:32] > 4) ? (data_shift[35:32] + 2'd3) : (data_shift[35:32]); data_shift[39:36] <= (data_shift[39:36] > 4) ? (data_shift[39:36] + 2'd3) : (data_shift[39:36]); data_shift[43:40] <= (data_shift[43:40] > 4) ? (data_shift[43:40] + 2'd3) : (data_shift[43:40]); end else if((cnt_shift <= 20) && (shift_flag == 1'b1)) data_shift <= data_shift << 1; else data_shift <= data_shift; //shift_flag:移位判断标志信号,用于控制移位判断的先后顺序 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) shift_flag <= 1'b0; else shift_flag <= ~shift_flag; //当计数器等于20时,移位判断操作完成,对各个位数的BCD码进行赋值 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin unit <= 4'b0; ten <= 4'b0; hun <= 4'b0; tho <= 4'b0; t_tho <= 4'b0; h_hun <= 4'b0; end else if(cnt_shift == 5'd21) begin unit <= data_shift[23:20]; ten <= data_shift[27:24]; hun <= data_shift[31:28]; tho <= data_shift[35:32]; t_tho <= data_shift[39:36]; h_hun <= data_shift[43:40]; end endmodule
动态显示驱动模块波形图绘制
seg_dynamic.v
`timescale 1ns/1ns module seg_dynamic ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低有效 input wire [19:0] data , //数码管要显示的值 input wire [5:0] point , //小数点显示,高电平有效 input wire seg_en , //数码管使能信号,高电平有效 input wire sign , //符号位,高电平显示负号 output reg [5:0] sel , //数码管位选信号 output reg [7:0] seg //数码管段选信号 ); //parameter define parameter CNT_MAX = 16'd49_999; //数码管刷新时间计数最大值 //wire define wire [3:0] unit ; //个位数 wire [3:0] ten ; //十位数 wire [3:0] hun ; //百位数 wire [3:0] tho ; //千位数 wire [3:0] t_tho ; //万位数 wire [3:0] h_hun ; //十万位数 //reg define reg [23:0] data_reg ; //待显示数据寄存器 reg [15:0] cnt_1ms ; //1ms计数器 reg flag_1ms ; //1ms标志信号 reg [2:0] cnt_sel ; //数码管位选计数器 reg [5:0] sel_reg ; //位选信号 reg [3:0] data_disp ; //当前数码管显示的数据 reg dot_disp ; //当前数码管显示的小数点 //data_reg:控制数码管显示数据 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_reg <= 24'b0; //若显示的十进制数的十万位为非零数据或需显示小数点,则六个数码管全显示 else if((h_hun) || (point[5])) data_reg <= {h_hun,t_tho,tho,hun,ten,unit}; //若显示的十进制数的万位为非零数据或需显示小数点,则值显示在5个数码管上 //打比方我们输入的十进制数据为20’d12345,我们就让数码管显示12345而不是012345 else if(((t_tho) || (point[4])) && (sign == 1'b1))//显示负号 data_reg <= {4'd10,t_tho,tho,hun,ten,unit};//4'd10我们定义为显示负号 else if(((t_tho) || (point[4])) && (sign == 1'b0)) data_reg <= {4'd11,t_tho,tho,hun,ten,unit};//4'd11我们定义为不显示 //若显示的十进制数的千位为非零数据或需显示小数点,则值显示4个数码管 else if(((tho) || (point[3])) && (sign == 1'b1)) data_reg <= {4'd11,4'd10,tho,hun,ten,unit}; else if(((tho) || (point[3])) && (sign == 1'b0)) data_reg <= {4'd11,4'd11,tho,hun,ten,unit}; //若显示的十进制数的百位为非零数据或需显示小数点,则值显示3个数码管 else if(((hun) || (point[2])) && (sign == 1'b1)) data_reg <= {4'd11,4'd11,4'd10,hun,ten,unit}; else if(((hun) || (point[2])) && (sign == 1'b0)) data_reg <= {4'd11,4'd11,4'd11,hun,ten,unit}; //若显示的十进制数的十位为非零数据或需显示小数点,则值显示2个数码管 else if(((ten) || (point[1])) && (sign == 1'b1)) data_reg <= {4'd11,4'd11,4'd11,4'd10,ten,unit}; else if(((ten) || (point[1])) && (sign == 1'b0)) data_reg <= {4'd11,4'd11,4'd11,4'd11,ten,unit}; //若显示的十进制数的个位且需显示负号 else if(((unit) || (point[0])) && (sign == 1'b1)) data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10,unit}; //若上面都不满足都只显示一位数码管 else data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,unit}; //cnt_1ms:1ms循环计数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_1ms <= 16'd0; else if(cnt_1ms == CNT_MAX) cnt_1ms <= 16'd0; else cnt_1ms <= cnt_1ms + 1'b1; //flag_1ms:1ms标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) flag_1ms <= 1'b0; else if(cnt_1ms == CNT_MAX - 1'b1) flag_1ms <= 1'b1; else flag_1ms <= 1'b0; //cnt_sel:从0到5循环数,用于选择当前显示的数码管 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_sel <= 3'd0; else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1)) cnt_sel <= 3'd0; else if(flag_1ms == 1'b1) cnt_sel <= cnt_sel + 1'b1; else cnt_sel <= cnt_sel; //数码管位选信号寄存器 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) sel_reg <= 6'b000_000; else if((cnt_sel == 3'd0) && (flag_1ms == 1'b1)) sel_reg <= 6'b000_001; else if(flag_1ms == 1'b1) sel_reg <= sel_reg << 1; else sel_reg <= sel_reg; //控制数码管的位选信号,使六个数码管轮流显示 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_disp <= 4'b0; else if((seg_en == 1'b1) && (flag_1ms == 1'b1)) case(cnt_sel) 3'd0: data_disp <= data_reg[3:0] ; //给第1个数码管赋个位值 3'd1: data_disp <= data_reg[7:4] ; //给第2个数码管赋十位值 3'd2: data_disp <= data_reg[11:8] ; //给第3个数码管赋百位值 3'd3: data_disp <= data_reg[15:12]; //给第4个数码管赋千位值 3'd4: data_disp <= data_reg[19:16]; //给第5个数码管赋万位值 3'd5: data_disp <= data_reg[23:20]; //给第6个数码管赋十万位值 default:data_disp <= 4'b0 ; endcase else data_disp <= data_disp; //dot_disp:小数点低电平点亮,需对小数点有效信号取反 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) dot_disp <= 1'b1; else if(flag_1ms == 1'b1) dot_disp <= ~point[cnt_sel]; else dot_disp <= dot_disp; //控制数码管段选信号,显示数字 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) seg <= 8'b1111_1111; else case(data_disp) 4'd0 : seg <= {dot_disp,7'b100_0000}; //显示数字0 4'd1 : seg <= {dot_disp,7'b111_1001}; //显示数字1 4'd2 : seg <= {dot_disp,7'b010_0100}; //显示数字2 4'd3 : seg <= {dot_disp,7'b011_0000}; //显示数字3 4'd4 : seg <= {dot_disp,7'b001_1001}; //显示数字4 4'd5 : seg <= {dot_disp,7'b001_0010}; //显示数字5 4'd6 : seg <= {dot_disp,7'b000_0010}; //显示数字6 4'd7 : seg <= {dot_disp,7'b111_1000}; //显示数字7 4'd8 : seg <= {dot_disp,7'b000_0000}; //显示数字8 4'd9 : seg <= {dot_disp,7'b001_0000}; //显示数字9 4'd10 : seg <= 8'b1011_1111 ; //显示负号 4'd11 : seg <= 8'b1111_1111 ; //不显示任何字符 default:seg <= 8'b1100_0000; endcase //sel:数码管位选信号赋值 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) sel <= 6'b000_000; else sel <= sel_reg; //---------- bsd_8421_inst ---------- bcd_8421 bcd_8421_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n), //复位信号,低电平有效 .data (data ), //输入需要转换的数据 .unit (unit ), //个位BCD码 .ten (ten ), //十位BCD码 .hun (hun ), //百位BCD码 .tho (tho ), //千位BCD码 .t_tho (t_tho ), //万位BCD码 .h_hun (h_hun ) //十万位BCD码 ); endmodule
hc595_ctrl.v
`timescale 1ns/1ns module hc595_ctrl ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低有效 input wire [5:0] sel , //数码管位选信号 input wire [7:0] seg , //数码管段选信号 output reg stcp , //数据存储器时钟 output reg shcp , //移位寄存器时钟 output reg ds , //串行数据输入 output wire oe //使能信号,低有效 ); //reg define reg [1:0] cnt_4 ; //分频计数器 reg [3:0] cnt_bit ; //传输位数计数器 //wire define wire [13:0] data ; //数码管信号寄存 //将数码管信号寄存 assign data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel}; //将复位取反后赋值给其即可 assign oe = ~sys_rst_n; //分频计数器:0~3循环计数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_4 <= 2'd0; else if(cnt_4 == 2'd3) cnt_4 <= 2'd0; else cnt_4 <= cnt_4 + 1'b1; //cnt_bit:每输入一位数据加一 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_bit <= 4'd0; else if(cnt_4 == 2'd3 && cnt_bit == 4'd13) cnt_bit <= 4'd0; else if(cnt_4 == 2'd3) cnt_bit <= cnt_bit + 1'b1; else cnt_bit <= cnt_bit; //stcp:14个信号传输完成之后产生一个上升沿 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) stcp <= 1'b0; else if(cnt_bit == 4'd13 && cnt_4 == 2'd3) stcp <= 1'b1; else stcp <= 1'b0; //shcp:产生四分频移位时钟 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) shcp <= 1'b0; else if(cnt_4 >= 4'd2) shcp <= 1'b1; else shcp <= 1'b0; //ds:将寄存器里存储的数码管信号输入即 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) ds <= 1'b0; else if(cnt_4 == 2'd0) ds <= data[cnt_bit]; else ds <= ds; endmodule
seg_595_dynamic.v
`timescale 1ns/1ns module seg_595_dynamic ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低有效 input wire [19:0] data , //数码管要显示的值 input wire [5:0] point , //小数点显示,高电平有效 input wire seg_en , //数码管使能信号,高电平有效 input wire sign , //符号位,高电平显示负号 output wire stcp , //数据存储器时钟 output wire shcp , //移位寄存器时钟 output wire ds , //串行数据输入 output wire oe //使能信号 ); //wire define wire [5:0] sel; //数码管位选信号 wire [7:0] seg; //数码管段选信号 //---------- seg_dynamic_inst ---------- seg_dynamic seg_dynamic_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n), //复位信号,低有效 .data (data ), //数码管要显示的值 .point (point ), //小数点显示,高电平有效 .seg_en (seg_en ), //数码管使能信号,高电平有效 .sign (sign ), //符号位,高电平显示负号 .sel (sel ), //数码管位选信号 .seg (seg ) //数码管段选信号 ); //---------- hc595_ctrl_inst ---------- hc595_ctrl hc595_ctrl_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n), //复位信号,低有效 .sel (sel ), //数码管位选信号 .seg (seg ), //数码管段选信号 .stcp (stcp ), //输出数据存储寄时钟 .shcp (shcp ), //移位寄存器的时钟输入 .ds (ds ), //串行数据输入 .oe (oe ) ); endmodule
top_seg_595.v
`timescale 1ns/1ns module top_seg_595 ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低电平有效 output wire stcp , //输出数据存储寄时钟 output wire shcp , //移位寄存器的时钟输入 output wire ds , //串行数据输入 output wire oe //输出使能信号 ); //wire define wire [19:0] data ; //数码管要显示的值 wire [5:0] point ; //小数点显示,高电平有效top_seg_595 wire seg_en ; //数码管使能信号,高电平有效 wire sign ; //符号位,高电平显示负号 //-------------data_gen_inst-------------- data_gen data_gen_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n), //复位信号,低电平有效 .data (data ), //数码管要显示的值 .point (point ), //小数点显示,高电平有效 .seg_en (seg_en ), //数码管使能信号,高电平有效 .sign (sign ) //符号位,高电平显示负号 ); //-------------seg7_dynamic_inst-------------- seg_595_dynamic seg_595_dynamic_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n ), //复位信号,低有效 .data (data ), //数码管要显示的值 .point (point ), //小数点显示,高电平有效 .seg_en (seg_en ), //数码管使能信号,高电平有效 .sign (sign ), //符号位,高电平显示负号 .stcp (stcp ), //输出数据存储寄时钟 .shcp (shcp ), //移位寄存器的时钟输入 .ds (ds ), //串行数据输入 .oe (oe ) //输出使能信号 ); endmodule
仿真:tb_top_seg_595.v
`timescale 1ns/1ns module tb_top_seg_595(); //wire define wire stcp ; //输出数据存储寄时钟 wire shcp ; //移位寄存器的时钟输入 wire ds ; //串行数据输入 wire oe ; //输出使能信号 //reg define reg sys_clk ; reg sys_rst_n ; //对sys_clk,sys_rst_n赋初始值 initial begin sys_clk = 1'b1; sys_rst_n <= 1'b0; #100 sys_rst_n <= 1'b1; end //clk:产生时钟 always #10 sys_clk <= ~sys_clk; //重新定义参数值,缩短仿真时间 defparam top_seg_595_inst.seg_595_dynamic_inst.seg_dynamic_inst.CNT_MAX=19; defparam top_seg_595_inst.data_gen_inst.CNT_MAX = 49; //------------- seg_595_static_inst ------------- top_seg_595 top_seg_595_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n ), //复位信号,低电平有效 .stcp (stcp ), //输出数据存储寄时钟 .shcp (shcp ), //移位寄存器的时钟输入 .ds (ds ), //串行数据输入 .oe (oe ) //输出使能信号 ); endmodule