【数码管】— 动态数码管
做了个啥:
数码管动态显示,最低位数码管1秒加1,一直加到999_999
基础知识:
- 什么是BCD码:一种编码,用4位2进制表示1位10进制4:2——1:10;二进制、BCD都是对十进制的一种编码
- 为什么要用BCD码:需要将一个十进制数如398用3个BCD码分别将个位、十位、百位分别表示出来,然后用来产生位选、段选信号;如果直接使用二进制表示398,无法分离出3位数字
- 如何将十进制的二进制码转换成十进制的BCD码:
- 十进制数234有3位,就在A栏填3*4个0
- 从B栏知道234用二进制表示为:1110_1010
-
将B栏中的3个BCD码与4比较:
>4:+3
<=4:不变
- 判断完成后,将A、B两栏的数据统一往左移一位
- 回到第3步
- 由于B栏原来的二进制码1110_1010的位宽=8,于是将二进制转换成BCD一共需要8次
实践:
一、设计文件
模块1:数据生成模块
设计文件:
module data_bring ( input clk, input rst_n, output reg [19:0] data, output [5:0] point, output sign, output reg en ); // 0.1s 后自加1 //parameter MAX_CNT = 23'd4999_999; // 1s 后自加1 parameter MAX_CNT = 26'd49_999_999; parameter MAX_DATA = 20'd999_999; //reg [22:0] cnt ; reg [25:0] cnt ; reg cnt_flage ; assign point = 6'b000_000 ; assign sign = 1'b0 ; always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) // cnt <= 23'b0; cnt <= 26'b0; else if(cnt == MAX_CNT) // cnt <= 23'b0; cnt <= 26'b0; else // cnt <= cnt + 23'b1; cnt <= cnt + 26'b1; end always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) cnt_flage <= 1'b0; else if(cnt == MAX_CNT - 1'b1) cnt_flage <= 1'b1; else cnt_flage <= 1'b0; end always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) data <= 20'd0; else if(data == MAX_DATA && cnt_flage == 1'b1) data <= 20'd0; else if(cnt_flage == 1'b1) data <= data + 20'd1; else data <= data; end always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) en <= 1'b0; else en <= 1'b1; end endmodule
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
模块2.1:二进制转BCD码模块
设计文件:
module dcb_8421 ( input clk , input rst_n , input [19:0] data , output reg [3:0] ge , output reg [3:0] shi , output reg [3:0] bai , output reg [3:0] qian , output reg [3:0] wan , output reg [3:0] shi_wan ); parameter MAX_CNT = 21 ; reg [4:0] cnt_shift ; reg [43:0] data_shift ; reg shift_flage ; // 999_999的二进制有20位,因此需要计数:20+1首次补0=21次 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) cnt_shift <= 5'd0; else if((cnt_shift == MAX_CNT) && (shift_flage == 1'b1)) cnt_shift <= 5'd0; else if(shift_flage == 1'b1) cnt_shift <= cnt_shift + 5'd1; else cnt_shift <= cnt_shift; end always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) shift_flage <= 1'b0; else shift_flage <= ~ shift_flage;// 隔一个时钟周期取反:因为是要等到时钟上升沿才执行 end always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) data_shift <= 44'd0; else if(cnt_shift == 5'b0) data_shift <= {24'd0,data}; else if((cnt_shift < MAX_CNT) && (shift_flage == 1'b0)) // shift_flage时低电平时,做判断运算 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 < MAX_CNT) && (shift_flage == 1'b1)) // shift_flage时高电平时,做左移位运算 data_shift = data_shift << 1; else data_shift <= data_shift ; end always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0)begin ge <= 4'd0 ; shi <= 4'd0 ; bai <= 4'd0 ; qian <= 4'd0 ; wan <= 4'd0 ; shi_wan <= 4'd0 ; end else if(cnt_shift == MAX_CNT)begin ge <= data_shift[23:20] ; shi <= data_shift[27:24] ; bai <= data_shift[31:28] ; qian <= data_shift[35:32] ; wan <= data_shift[39:36] ; shi_wan <= data_shift[43:40] ; end end endmodule
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
模块2.2:驱动模块
设计文件:
module sge_01 ( input clk , input rst_n , input [19:0] data , input [5:0] point , input sign , input en , output reg [5:0] wei , output reg [7:0] duan ); wire [3:0] ge ; wire [3:0] shi ; wire [3:0] bai ; wire [3:0] qian ; wire [3:0] wan ; wire [3:0] shi_wan ; reg [23:0] data_reg ; reg [15:0] cnt_1ms ; reg flage_1ms ; reg [2:0] cnt_wei ; //位选信号 reg [5:0] cnt_wei_reg ; //数码管位选计数器 reg [3:0] data_disp ; //当前数码管显示的数据 reg dot_disp ; //当前数码管显示的小数点 parameter MAX_CNT = 16'd49_999; always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) data_reg <= 24'd0; else if((shi_wan) || (point[5])) data_reg <= {shi_wan, wan, qian, bai, shi, ge}; //123456 else if(((wan) || (point[4])) && (sign==1'b1)) data_reg <= {4'd10, wan, qian, bai, shi, ge}; //-23456 else if(((wan) || (point[4])) && (sign==1'b0)) data_reg <= {4'd11, wan, qian, bai, shi, ge}; //23456 else if(((qian) || (point[3])) && (sign==1'b1)) data_reg <= {4'd11, 4'd10, qian, bai, shi, ge}; //-3456 else if(((qian) || (point[3])) && (sign==1'b0)) data_reg <= {4'd11, 4'd11, qian, bai, shi, ge}; //3456 else if(((bai) || (point[2])) && (sign==1'b1)) data_reg <= {4'd11, 4'd11, 4'd10, bai, shi, ge}; //-456 else if(((bai) || (point[2])) && (sign==1'b0)) data_reg <= {4'd11, 4'd11, 4'd11, bai, shi, ge}; //456 else if(((shi) || (point[1])) && (sign==1'b1)) data_reg <= {4'd11, 4'd11, 4'd11, 4'd10, shi, ge}; //-56 else if(((shi) || (point[1])) && (sign==1'b0)) data_reg <= {4'd11, 4'd11, 4'd11, 4'd11, shi, ge}; //56 else if(((ge) || (point[1])) && (sign==1'b1)) data_reg <= {4'd11, 4'd11, 4'd11, 4'd11, 4'd10, ge}; //-6 else //if(((ge) || (point[1])) && (sign==1'b0)) data_reg <= {4'd11, 4'd11, 4'd11, 4'd11, 4'd11, ge}; //6 end // 1ms计数器 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) cnt_1ms <= 16'd0; else if(cnt_1ms == MAX_CNT) cnt_1ms <= 16'd0; else cnt_1ms <= cnt_1ms + 16'd1; end // 1ms标志信号 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) flage_1ms <= 1'b0; else if(cnt_1ms == MAX_CNT - 16'd1) flage_1ms <= 1'b1; else flage_1ms <= 1'b0; end // 位选计数 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) cnt_wei <= 3'd0; else if(cnt_wei == 3'd5 && flage_1ms == 1'b1) cnt_wei <= 3'd0; else if(flage_1ms == 1'b1) cnt_wei <= cnt_wei + 3'd1; else cnt_wei <= cnt_wei; end // 位选寄存器,用于后面的打拍操作 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) cnt_wei_reg <= 6'b000_000; else if((cnt_wei == 3'd0) && (flage_1ms == 1'b1)) cnt_wei_reg <= 6'b000_001; //选中第一个数码位 else if(flage_1ms == 1'b1) cnt_wei_reg <= cnt_wei_reg << 1; //选中其他数码位 ??? 一直这样左移,移完后1会回到最低位吗 else cnt_wei_reg <= cnt_wei_reg; end //待显示的数据 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) data_disp <= 4'd0; else if((en == 1'b1) && (flage_1ms == 1'b1)) case(cnt_wei) 3'd0: data_disp <= data_reg[3:0]; 3'd1: data_disp <= data_reg[7:4]; 3'd2: data_disp <= data_reg[11:8]; 3'd3: data_disp <= data_reg[15:12]; 3'd4: data_disp <= data_reg[19:16]; 3'd5: data_disp <= data_reg[23:20]; default:data_disp <= 4'd0; endcase else data_disp <= data_disp; end // 点亮小数点 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) dot_disp <= 1'd0; else if(flage_1ms == 1'b1) dot_disp <= ~point[cnt_wei];//共阳极,低电平点亮,输入的信号是高电平,于是需要取反点亮小数点 else dot_disp <= dot_disp; end // 输出信号:段选 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) duan <= 8'b1111_1111; else case(data_disp) 4'd0 : duan <= {dot_disp,7'b100_0000}; //显示数字0 4'd1 : duan <= {dot_disp,7'b111_1001}; //显示数字1 4'd2 : duan <= {dot_disp,7'b010_0100}; //显示数字2 4'd3 : duan <= {dot_disp,7'b011_0000}; //显示数字3 4'd4 : duan <= {dot_disp,7'b001_1001}; //显示数字4 4'd5 : duan <= {dot_disp,7'b001_0010}; //显示数字5 4'd6 : duan <= {dot_disp,7'b000_0010}; //显示数字6 4'd7 : duan <= {dot_disp,7'b111_1000}; //显示数字7 4'd8 : duan <= {dot_disp,7'b000_0000}; //显示数字8 4'd9 : duan <= {dot_disp,7'b001_0000}; //显示数字9 4'd10 : duan <= 8'b1011_1111 ; //显示负号 4'd11 : duan <= 8'b1111_1111 ; //不显示任何字符 default:duan <= 8'b1100_0000; endcase end // 输出信号:位选 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) wei <= 6'd0; else wei <= cnt_wei_reg; end dcb_8421 dcb_8421_inst ( .clk(clk) , .rst_n(rst_n) , .data (data) , .ge(ge) , .shi(shi) , .bai(bai) , .qian(qian) , .wan(wan) , .shi_wan(shi_wan) ); endmodule
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
模块2的顶层文件:
module seg_595_dynamic ( input clk , input rst_n , input[19:0] data , input[5:0] point , input sign , input en , output ds , output oe , output shcp , output stcp ); wire [5:0]wei; wire [7:0]duan; sge_01 sge_01_inst ( .clk(clk) , .rst_n(rst_n) , .data(data) , .point(point) , .sign(sign) , .en(en) , .wei(wei) , .duan(duan) ); seg_595 seg_595_inst ( .clk(clk) , .rst_n(rst_n) , .wei(wei) , .duan(duan) , .ds(ds) , .shcp(shcp) , .stcp(stcp) , .oe(oe) ); endmodule
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
模块3:74HC595芯片控制模块
module seg_595 ( input clk , input rst_n , input [5:0] wei , input [7:0] duan , output reg ds , output reg shcp , output reg stcp , output oe ); wire [13:0] data ; reg [1:0] cnt_4 ; reg [3:0] cnt_bit_14 ; assign data = {duan[0],duan[1],duan[2],duan[3],duan[4],duan[5],duan[6],duan[7],wei}; assign oe = ~ rst_n; // 4分频计数器 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) cnt_4 <= 2'b0 ; else if(cnt_4 == 2'd3) cnt_4 <= 2'b0 ; else cnt_4 <= cnt_4 + 2'b1 ; end // 传输14位计数器 always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) cnt_bit_14 <= 4'b0; else if(cnt_bit_14 == 4'd13 && cnt_4 == 2'd3) cnt_bit_14 <= 4'b0; else if(cnt_4 == 2'd3) cnt_bit_14 <= cnt_bit_14 + 4'b1; else cnt_bit_14 <= cnt_bit_14; end // -------------------- 输出 ---------------------------------- always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) shcp <= 1'b0; else if(cnt_4>=2'd2) shcp <= 1'b1; else shcp <= 1'b0; end always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) stcp <= 1'b0; else if(cnt_bit_14 == 4'd0 && cnt_4 == 2'd0) stcp <= 1'b1; else stcp <= 1'b0; end always@(posedge clk or negedge rst_n)begin if(rst_n == 1'b0) ds <= 1'b0; else if(cnt_4 == 1'b0) ds <= data[cnt_bit_14]; else ds <= ds; end endmodule
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
顶层模块:
module top_seg ( input clk , input rst_n , output ds , output shcp , output stcp , output oe ); wire [19:0]data; wire [5:0]point; wire sign; wire en; data_bring data_bring_inst ( .clk(clk), .rst_n(rst_n), .data(data), .point(point), .sign(sign), .en(en) ); seg_595_dynamic seg_595_dynamic_inst ( .clk(clk) , .rst_n(rst_n) , .data(data) , .point(point) , .sign(sign) , .en(en) , .ds(ds) , .oe(oe) , .shcp(shcp) , .stcp(stcp) ); endmodule
二、测试文件
模块1:数据生成模块
`timescale 1ns/1ns module tb_data_bring; reg clk; reg rst_n; wire data; wire point; wire sign; wire en; initial begin clk = 1'b0; rst_n = 1'b0; #10; rst_n = 1'b1; end always #10 clk = ~ clk ; data_bring #( .MAX_CNT(23'd24), .MAX_DATA(20'd9) ) a1 ( .clk(clk), .rst_n(rst_n), .data(data), .point(point), .sign(sign), .en(en) ); endmodule
模块2.1:BCD码
module tb_dcb_8421; reg clk; reg rst_n; reg [19:0]data; wire ge; wire shi; wire bai; wire qian; wire wan; wire shi_wan; initial begin clk = 1'b0; rst_n = 1'b0; #10; rst_n = 1'b1; data = 20'b0101_0010_1010_1010_1011; end always #10 clk = ~ clk; dcb_8421 a1 ( . clk(clk) , . rst_n(rst_n) , . data(data) , . ge(ge) , . shi(shi) , . bai(bai) , . qian(qian) , . wan(wan) , . shi_wan(shi_wan) ); endmodule
模块2.2:位选、段选驱动
`timescale 1ns/1ns module tb_seg_01; reg clk; reg rst_n; reg [19:0] data; reg [5:0] point; reg sign; reg en; wire [5:0] wei; wire [7:0] duan; initial begin clk = 1'b0; rst_n = 1'b0; data <= 20'd0; point <= 6'd0; sign <= 1'b0; en <= 1'b0; #30; rst_n = 1'b1; data <= 20'd9876; point <= 6'd000_010; sign <= 1'b1; en <= 1'b1; end always #10 clk = ~ clk; defparam sge_01_inst.MAX_CNT = 20'd5; sge_01 sge_01_inst ( .clk(clk) , .rst_n(rst_n) , .data(data) , .point(point) , .sign(sign) , .en(en) , .wei(wei) , .duan(duan) ); endmodule
顶层测试
`timescale 1ns/1ns module tb_top_seg; reg clk; reg rst_n; wire ds; wire shcp; wire stcp; wire oe; initial begin clk = 1'b0; rst_n = 1'b0; #30; rst_n = 1'b1; end always #10 clk = ~ clk; top_seg top_seg_inst ( .clk(clk) , .rst_n(rst_n) , .ds(ds) , .shcp(shcp) , .stcp(stcp) , .oe(oe) ); endmodule
三、波形图
四、RTL图
五、上板验证
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)