开发板实战篇3 6位数码管动态显示

总结:

        模块例化思想: 例化2个定时器模块 + 数码管驱动

        根据自己思路编写代码,调试仿真代码,同时熟悉环境.加深细节理解 

         刚开始写得很乱,代码完全就是用modelsim仿真调试出来的     

注意:

       

一. 实现的功能

       使用FPGA开发板上的6位数码管以动态方式从0开始计数,每100ms计数值
增加一,当计数值从0增加到999999后重新从0开始计数。
 

二. 功能框图

        定时器模块1:加计数器,计数100ms到了产生overflow信号

        定时器模块2:加计数器,计数1ms到了产生overflow信号

        segled控制模块:

                根据en使能信号,增加显示值0~999999;

                根据en2使能信号,切换sel位选信号,并输出段选信号

        打开方法:  tools -> netlist viewers -> rtl viewer

 三. RTL代码

        segled_dy_top.v

        与segled_dynamic_top.v基本相同。

        COUNT_OVERFLOW = 26'd5_000_000,即闪烁周期为100ms。

module segled_dy_top (
    input clk,
    input reset,
    output [5:0] sel,
    output [7:0] seg_led
);

parameter COUNT_OVERFLOW = 26'd5_000_000;				//1s
parameter COUNT_OVERFLOW2 = 26'd50_000_000/10'd1000;	//1ms

wire [1:0] overflow;                 

timer #(
  .COUNT_OVERFLOW (COUNT_OVERFLOW)
  )
  u_timer(
  .clk      (clk),
  .reset    (reset),
  .overflow (overflow[0])
);

timer #(
  .COUNT_OVERFLOW (COUNT_OVERFLOW2)
  )
  u2_timer(
  .clk      (clk),
  .reset    (reset),
  .overflow (overflow[1])
);

segled_dy_driver u_segled_dy_driver(clk,reset,overflow[0],overflow[1],sel,seg_led);

    
endmodule

        timer.v

        除将overflow <=修改为非阻塞赋值外,其它与led点灯工程的相同。

/*
********************************************************************************
  *Copyright ©, 2018, CunXin_ All Rights Reserved
  *文 件 名: key_led.v
  *说    明: 
            程序功能无按键按下时, LED灯全灭; 
            按键1按下时, LED灯显示自右向左的流水效果;
            按键2按下时, LED灯显示自左向右的流水效果;
            按键3按下时,四个LED灯同时闪烁;
            按键4按下时, LED灯全亮
  *版    本: V000B00D00
  *创建日期: 2018年4月3日		下午12:16:49
  *创 建 人: wansaiyon
  *修改信息:  
================================================================================
  *修 改 人    		  修改日期       	  修改内容 
  *<作者/修改者>      <YYYY/MM/DD>		  <修改内容>
********************************************************************************
*/

module timer #(
    parameter COUNT_OVERFLOW = 26'd10_000_000
) (
    input clk,
    input reset,
    output reg overflow
);

localparam period = COUNT_OVERFLOW;

reg [25:0] cnt;

always @(posedge clk,negedge reset) begin
    if(!reset) begin
      cnt <= 26'd0;
      overflow <= 1'b0;
    end        
    else if(cnt < period) begin
      cnt <= cnt + 1'd1;
      overflow <= 1'b0;
    end        
    else begin
      cnt <= 26'd0;
      overflow <= 1'b1;
    end
        
end

endmodule

        segled_dy_driver.v

        主要功能点:

        定义16个字符、

        0~999999计数,执行周期100ms

        连续赋值计算个位、十位、百位....

        计算待显示数字的位数,执行周期100ms

        位选控制 sel_ctrl 切换,执行周期1ms

        输出位选信号sel(采用独热码)和段选信号 seg_led, 执行周期1ms

/*
	6位数码管			
	sel[0]	output	N16	第一个数码管位选信号
	sel[1]	output	N15	第二个数码管位选信号
	sel[2]	output	P16	第三个数码管位选信号
	sel[3]	output	P15	第四个数码管位选信号
	sel[4]	output	R16	第五个数码管位选信号
	sel[5]	output	T15	第六个数码管位选信号
	seg_led[0]	output	M11	数码管段选a
	seg_led[1]	output	N12	数码管段选b
	seg_led[2]	output	C9	数码管段选c
	seg_led[3]	output	N13	数码管段选d
	seg_led[4]	output	M10	数码管段选e
	seg_led[5]	output	N11	数码管段选f
	seg_led[6]	output	P11	数码管段选g
	seg_led[7]	output	D9	数码管段选h
*/

module segled_dy_driver (
    input clk,
    input reset,
    input en,
    input en2,
    output reg [5:0] sel,
    output reg [7:0] seg_led
);

// localparam define  
/*
//显示.号
localparam CHAR_0 = 8'b0100_0000;
localparam CHAR_1 = 8'b0111_1001;
localparam CHAR_2 = 8'b0010_0100;
localparam CHAR_3 = 8'b0011_0000;
localparam CHAR_4 = 8'b0001_1001;
localparam CHAR_5 = 8'b0001_0010;
localparam CHAR_6 = 8'b0000_0010;
localparam CHAR_7 = 8'b0111_1000;
localparam CHAR_8 = 8'b0000_0000;
localparam CHAR_9 = 8'b0001_0000;
localparam CHAR_a = 8'b0000_1000;
localparam CHAR_b = 8'b0000_0011;
localparam CHAR_c = 8'b0100_0110;
localparam CHAR_d = 8'b0010_0001;
localparam CHAR_e = 8'b0000_0110;
localparam CHAR_f = 8'b0000_1110;
*/
localparam CHAR_0 = 8'b1100_0000;
localparam CHAR_1 = 8'b1111_1001;
localparam CHAR_2 = 8'b1010_0100;
localparam CHAR_3 = 8'b1011_0000;
localparam CHAR_4 = 8'b1001_1001;
localparam CHAR_5 = 8'b1001_0010;
localparam CHAR_6 = 8'b1000_0010;
localparam CHAR_7 = 8'b1111_1000;
localparam CHAR_8 = 8'b1000_0000;
localparam CHAR_9 = 8'b1001_0000;
localparam CHAR_a = 8'b1000_1000;
localparam CHAR_b = 8'b1000_0011;
localparam CHAR_c = 8'b1100_0110;
localparam CHAR_d = 8'b1010_0001;
localparam CHAR_e = 8'b1000_0110;
localparam CHAR_f = 8'b1000_1110;

// reg define     
reg [19:0] disp_num;	 /* 0~999999 */
reg [2:0]  disp_num_bit; /* disp_num的位数 */
reg [2:0]  sel_ctrl; 	 /* sel[0]~sel[5] */

// wire define
wire [3:0] data0;		/* 个位 */
wire [3:0] data1;		/* 十位 */
wire [3:0] data2;		/* 百位 */
wire [3:0] data3;		/* 千位 */
wire [3:0] data4;		/* 万位 */
wire [3:0] data5;		/* 十万位 */

// continues assignment
assign data0 =  disp_num%4'd10;				
assign data1 = (disp_num/4'd10)%4'd10;		
assign data2 = (disp_num/7'd100)%4'd10;		
assign data3 = (disp_num/10'd1000)%4'd10;	
assign data4 = (disp_num/14'd10000)%4'd10;	
assign data5 = (disp_num/17'd100000)%4'd10;	

/*
	循环计数 0~999999,en产生执行周期1s
*/
always @(posedge clk,negedge reset ) begin
    if(!reset) begin
        disp_num <= 20'd0;
    end
    else if(en && (disp_num < 20'd999999)) begin
        disp_num <= disp_num + 1'b1;
    end
    else if(en && (disp_num == 20'd999999))begin
        disp_num <= 20'd0;
    end 
	else begin
        disp_num <= disp_num;
    end 	
end

/*
	计算待显示数字的位数
*/
always @(posedge clk,negedge reset ) begin
    if(!reset) begin
        disp_num_bit <= 3'd0;
    end
    else if(en && (disp_num < 4'd10)) begin
        disp_num_bit <= 3'd0;
    end
	else if(en && (disp_num < 7'd100)) begin
        disp_num_bit <= 3'd1;
    end
	else if(en && (disp_num < 10'd1000)) begin
        disp_num_bit <= 3'd2;
    end
	else if(en && (disp_num < 14'd10000)) begin
        disp_num_bit <= 3'd3;
    end
	else if(en && (disp_num < 17'd100000)) begin
        disp_num_bit <= 3'd4;
    end
	else if(en && (disp_num < 20'd1000000)) begin
        disp_num_bit <= 3'd5;
    end
    else if(en && (disp_num == 20'd999999))begin
        disp_num_bit <= 3'd0;
    end 
	else begin
        disp_num_bit <= disp_num_bit;
    end 	
end

/* 
	位选控制 sel_ctrl 切换,en2产生周期1ms
	
	sel_ctrl = 0, 位选信号 = 6'b11_1110,即选择第1个数码管
	sel_ctrl = 1, 位选信号 = 6'b11_1101,即选择第2个数码管
	sel_ctrl = 2, 位选信号 = 6'b11_1011,即选择第3个数码管
	sel_ctrl = 3, 位选信号 = 6'b11_0111,即选择第4个数码管
	sel_ctrl = 4, 位选信号 = 6'b10_1111,即选择第5个数码管
	sel_ctrl = 5, 位选信号 = 6'b01_1111,即选择第6个数码管
*/
/*
always @(posedge clk,negedge reset ) begin
    if(!reset) begin
        sel_ctrl <= 3'd0;
    end
    else if(en2 && (sel_ctrl < 3'd5)) begin
        sel_ctrl <= sel_ctrl + 1'b1;
    end
	else if(en2 && (sel_ctrl == 3'd5)) begin
        sel_ctrl <= 3'd0;
    end
    else begin
        sel_ctrl <= sel_ctrl;
    end    
end
*/
always @(posedge clk,negedge reset ) begin
    if(!reset) begin
        sel_ctrl <= 3'd0;
    end
    else if(en2 && (sel_ctrl < disp_num_bit)) begin
        sel_ctrl <= sel_ctrl + 1'b1;
    end
	else if(en2 && (sel_ctrl == disp_num_bit)) begin
        sel_ctrl <= 3'd0;
    end
    else begin
        sel_ctrl <= sel_ctrl;
    end    
end

/* 
	输出位选信号sel(采用独热码)和段选信号 seg_led, 执行周期1ms	
	
	设计思路
	举例:如何显示数字 “654321”	
		第1ms,显示第1个数码管“1”
		第2ms,显示第2个数码管“2”
		第3ms,显示第3个数码管“3”
		第4ms,显示第4个数码管“4”
		第5ms,显示第5个数码管“5”
		第6ms,显示第6个数码管“6”	
	由于切换周期1ms,即6ms可以将6个数字 “654321”依次显示出来,
	同时由于视觉暂留的影响,显示数字“6”时,数字“1”、“2”、“3”、“4”、“5”仍然还能看到
	我们人眼看到的效果跟6个数字同时显示出来的效果是一样的。
	
	因此我们需要做的就是:
	
	第0ms,显示第1个数码管“1”, 位选信号 sel = 6'b11_1110,段选信号 seg_led = “data0对应的字符”
	第1ms,显示第2个数码管“2”, 位选信号 sel = 6'b11_1101,段选信号 seg_led = “data1对应的字符”
	第2ms,显示第3个数码管“3”, 位选信号 sel = 6'b11_1011,段选信号 seg_led = “data2对应的字符”
	第3ms,显示第4个数码管“4”, 位选信号 sel = 6'b11_0111,段选信号 seg_led = “data3对应的字符”
	第4ms,显示第5个数码管“5”, 位选信号 sel = 6'b10_1111,段选信号 seg_led = “data4对应的字符”
	第5ms,显示第6个数码管“6”, 位选信号 sel = 6'b01_1111,段选信号 seg_led = “data5对应的字符”
	
*/
always @(posedge clk ,negedge reset) begin
    if(!reset) begin	
        sel <= 6'b11_1110;
		seg_led <= CHAR_0;
    end
    else if(en2) begin		
		casex (sel_ctrl)
            3'd0: begin 
				sel <= 6'b11_1110;					
				if(data0==0)
					seg_led <= CHAR_0;
				else if(data0==1)
					seg_led <= CHAR_1;
				else if(data0==2)
					seg_led <= CHAR_2;
				else if(data0==3)
					seg_led <= CHAR_3;
				else if(data0==4)
					seg_led <= CHAR_4;	
				else if(data0==5)
					seg_led <= CHAR_5;
				else if(data0==6)
					seg_led <= CHAR_6;
				else if(data0==7)
					seg_led <= CHAR_7;
				else if(data0==8)
					seg_led <= CHAR_8;	
				else if(data0==9)
					seg_led <= CHAR_9;	
				else
					seg_led <= CHAR_0;
            end
            3'd1: begin 
				sel <= 6'b11_1101;
				if(data1==0)
					seg_led <= CHAR_0;
				else if(data1==1)
					seg_led <= CHAR_1;
				else if(data1==2)
					seg_led <= CHAR_2;
				else if(data1==3)
					seg_led <= CHAR_3;
				else if(data1==4)
					seg_led <= CHAR_4;	
				else if(data1==5)
					seg_led <= CHAR_5;
				else if(data1==6)
					seg_led <= CHAR_6;
				else if(data1==7)
					seg_led <= CHAR_7;
				else if(data1==8)
					seg_led <= CHAR_8;	
				else if(data1==9)
					seg_led <= CHAR_9;	
				else
					seg_led <= CHAR_0;
            end
            3'd2: begin 
				sel <= 6'b11_1011;
				if(data2==0)
					seg_led <= CHAR_0;
				else if(data2==1)
					seg_led <= CHAR_1;
				else if(data2==2)
					seg_led <= CHAR_2;
				else if(data2==3)
					seg_led <= CHAR_3;
				else if(data2==4)
					seg_led <= CHAR_4;	
				else if(data2==5)
					seg_led <= CHAR_5;
				else if(data2==6)
					seg_led <= CHAR_6;
				else if(data2==7)
					seg_led <= CHAR_7;
				else if(data2==8)
					seg_led <= CHAR_8;	
				else if(data2==9)
					seg_led <= CHAR_9;	
				else
					seg_led <= CHAR_0;
            end
            3'd3: begin 
				sel <= 6'b11_0111;
				if(data3==0)
					seg_led <= CHAR_0;
				else if(data3==1)
					seg_led <= CHAR_1;
				else if(data3==2)
					seg_led <= CHAR_2;
				else if(data3==3)
					seg_led <= CHAR_3;
				else if(data3==4)
					seg_led <= CHAR_4;	
				else if(data3==5)
					seg_led <= CHAR_5;
				else if(data3==6)
					seg_led <= CHAR_6;
				else if(data3==7)
					seg_led <= CHAR_7;
				else if(data3==8)
					seg_led <= CHAR_8;	
				else if(data3==9)
					seg_led <= CHAR_9;	
				else
					seg_led <= CHAR_0;
				
            end
            3'd4: begin 
				sel <= 6'b10_1111;
				if(data4==0)
					seg_led <= CHAR_0;
				else if(data4==1)
					seg_led <= CHAR_1;
				else if(data4==2)
					seg_led <= CHAR_2;
				else if(data4==3)
					seg_led <= CHAR_3;
				else if(data4==4)
					seg_led <= CHAR_4;	
				else if(data4==5)
					seg_led <= CHAR_5;
				else if(data4==6)
					seg_led <= CHAR_6;
				else if(data4==7)
					seg_led <= CHAR_7;
				else if(data4==8)
					seg_led <= CHAR_8;	
				else if(data4==9)
					seg_led <= CHAR_9;	
				else
					seg_led <= CHAR_0;
            end
            3'd5: begin 
				sel <= 6'b01_1111;
				if(data5==0)
					seg_led <= CHAR_0;
				else if(data5==1)
					seg_led <= CHAR_1;
				else if(data5==2)
					seg_led <= CHAR_2;
				else if(data5==3)
					seg_led <= CHAR_3;
				else if(data5==4)
					seg_led <= CHAR_4;	
				else if(data5==5)
					seg_led <= CHAR_5;
				else if(data5==6)
					seg_led <= CHAR_6;
				else if(data5==7)
					seg_led <= CHAR_7;
				else if(data5==8)
					seg_led <= CHAR_8;	
				else if(data5==9)
					seg_led <= CHAR_9;	
				else
					seg_led <= CHAR_0;
            end 
            default: begin
				sel <= 6'b11_1110;
				if(data0==0)
					seg_led <= CHAR_0;
				else if(data0==1)
					seg_led <= CHAR_1;
				else if(data0==2)
					seg_led <= CHAR_2;
				else if(data0==3)
					seg_led <= CHAR_3;
				else if(data0==4)
					seg_led <= CHAR_4;	
				else if(data0==5)
					seg_led <= CHAR_5;
				else if(data0==6)
					seg_led <= CHAR_6;
				else if(data0==7)
					seg_led <= CHAR_7;
				else if(data0==8)
					seg_led <= CHAR_8;	
				else if(data0==9)
					seg_led <= CHAR_9;	
				else
					seg_led <= CHAR_0;
            end
        endcase    
    end
    else begin
        sel <= sel;
		seg_led <= seg_led;
    end
end

    
endmodule

四.modelsim前仿真(功能仿真)

        每次打开modelsim时需要切换工作空间,否则可能创建工程添加的源码会是上一个仿真工程的代码,导致出错 。

        使用vscode打开segled_static_top.v, ctrl+shift+p,输入testbench,在terminal生成了segled_static_top_tb.v代码,需要安装插件verilog_testbench, 初始生成时可能提示失败,需要安装 python 库(根据错误提示自行百度)。

        生成的代码有点错误,需要自己再做点修改。

        tb_segled_dy_top.v,

        为了方便减小了计数器周期为4'd10,2'd2。

`timescale  1ns / 1ns

module tb_segled_dy_top;

// segled_static_top Parameters
parameter PERIOD          = 10            ;     
parameter COUNT_OVERFLOW  = 4'd10;     
parameter COUNT_OVERFLOW2  = 2'd2;     

// segled_static_top Inputs
reg   clk                                  = 0 ;
reg   reset                                = 0 ;

// segled_static_top Outputs
wire  [5:0]  sel                           ;    
wire  [7:0]  seg_led                       ;    


initial
begin
    forever #(PERIOD/2)  clk=~clk;
end

initial
begin
    #(PERIOD*2) reset  =  1;
end

segled_dy_top #(
    .COUNT_OVERFLOW ( COUNT_OVERFLOW ),
    .COUNT_OVERFLOW2 ( COUNT_OVERFLOW2 ))
 u_segled_dy_top (
    .clk                     ( clk            ),
    .reset                   ( reset          ),
	
    .sel                     ( sel      [5:0] ),
    .seg_led                 ( seg_led  [7:0] )
);


endmodule

        仿真波形

 

五.下载验证

        FPGA开发板上的6位数码管以动态方式从0开始计数,每100ms计数值增加一,当计数值从0增加到999999后重新从0开始计数。

        但是发现4个LED有微弱的亮度,需要配置好未使用管脚的状态为高阻输入,同时将nCEO管脚修改为Use as regular I/0,修改后LED变为熄灭状态了。

        在 Quartus 软件中默认未使用管脚的状态为弱上拉输入,所以未使用到的管脚上也是有电压的,只是驱动能力很弱,这往往会导致一些不安全的隐患,所以我们需要将未使用管脚的状态设置为三态输入。        

        在左侧Category一栏中选择Dual-Purpose Pin。对于需要使用EPCS器件的引脚时,需要将下图页面中所有的引脚都改成Use as regular IO,如果大家不确定工程中是否用到EPCS器件时,可以全部修改。本次实验只修改了nCEO一栏中, 将Use as programming pin修改为Use as regular I/0。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值