【FPGA——Cyclone Ⅳ学习笔记】六.数码管动态扫描显示(上)(EP4CE6F17C8)

一.原理图

在这里插入图片描述
原理图与前面静态显示相同。不过在动态显示时尤其需要注意,位选的低位控制左边,高位控制右边

二.代码和注释

此代码通过数码管的动态扫描方式显示计数时间。
顶层结构图:
在这里插入图片描述

1.time_count.v

定时器模块,用于产生0.1s的时钟信号,使数码管的显示每0.1s加1。

module time_count
(
	input             sys_clk,
	input	    	  sys_rst_n,
	
	output reg [19:0] data,		//记录数码管需要显示的数字,
								//6位数码管最大能显示的十进制数为999999,因此需要20位
	output reg [5:0]  point,	//6个位的小数点显示控制,相当于把每个位的小数点段选单独提出来
	output reg        en,		//数码管使能标志
	output reg 		  sign		//正负符号显示标志
);

parameter max_count = 23'd5_000_000;	//计数器定时0.1s,5000000*0.02us=0.1s

reg[22:0] count;	//定时器计数器
reg		  change_flag;	//数码管改变信号

//0.1秒计数
always @(posedge sys_clk or negedge sys_rst_n)
begin
	if(sys_rst_n==0)
	begin
		change_flag <= 1'b0;
		count <= 24'b0;
	end
	
	else if(count<max_count-1'b1)
	begin
		count <= count + 1'b1;	//自加1
		change_flag <= 1'b0;
	end
	
	else
	begin
		count <= 24'b0;
		change_flag <= 1'b1;	//计到0.1s,给出改变信号
	end
end

//计数到0.1s,显示值加1
always @(posedge sys_clk or negedge sys_rst_n)
begin
	if(sys_rst_n==0)
	begin
		data  <= 20'b0;			//显示值复位
		point <= 6'b000000;		//关闭所有小数点显示,在数码管显示模块中会对其取反,因此此处低电平为关闭
		en 	<= 1'b0;	
		sign  <= 1'b0;
	end
	else
	begin
		point <= 6'b000000;
		en    <= 1'b1;
		sign  <= 1'b0;
		if(change_flag)
		begin
			if(data<20'd999_999)
				data <= data + 1'b1;	//显示值加1
			else
				data <= 20'b0;
		end
	end
end

endmodule 

2.smg_display.v

数码管动态扫描显示模块,动态扫描的原理与学单片机时相同,此处不在赘述。

module smg_display
(
	input 		 sys_clk,
	input 		 sys_rst_n,
	
	input [19:0] data,		//此处四个输入均是定时器模块的输出
	input [5:0]  point,
	input 		 en,
	input 		 sign,
	
	output reg [5:0] smg_wei,	//位选信号输出,低位控制左边,高位控制右边
	output reg [7:0] smg_duan	//段选信号输出
);

//这两个常量与动态扫描所用的定时器有关,类似于单片机学习时把数码管显示放入的定时器中断
localparam CLK_DIVIDE = 4'd10;	//时钟分频系数,
localparam MAX_NUM	 = 13'd5000;	//时钟计数值,定时1ms,详见语句块代码

reg [3:0]  clk_cnt;		//用于预分频语句块,详见语句块代码
reg        dri_clk;		//分频后的时钟输出,50MHz/10=5MHz
reg [23:0] num;			//每4位存储1个数码管位的数值(bcd码),例:num[3:0]存储个位的数字(0~9)
reg [12:0] cnt0;		//动态扫描定时器的计数器
reg        flag;		//动态扫描定时器的标志信号,每1ms产生一个高电平信号
reg [2:0]  cnt_wei;		//当前显示的位
reg [3:0]  num_display;	//当前数码管显示的数字
reg 	   dot_display;	//当前数码管小数点的显示

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

assign  data0 = data % 4'd10;				// 个位数
assign  data1 = data / 4'd10 % 4'd10   ;    // 十位数
assign  data2 = data / 7'd100 % 4'd10  ;    // 百位数
assign  data3 = data / 10'd1000 % 4'd10 ;   // 千位数
assign  data4 = data / 14'd10000 % 4'd10;   // 万位数
assign  data5 = data / 17'd100000;  		// 十万位数

//动态扫描定时器的时钟分频语句块,10分频即5MHz
//分频的原因:上述六个assign语句由于有除法和取余运算,占用时间较长,
//因此,在一个50MHz的系统时钟周期内,无法计算完毕,
//如果后面的数码管显示语句块仍用系统时钟触发,则会产生时序冲突(即没计算完就要显示)
//所以将系统时钟10分频后,产生一个5MHz的时钟来触发显示语句块。经过10个系统时钟周期,已经计算完毕。
always @(posedge sys_clk or negedge sys_rst_n)
begin
	if(sys_rst_n==0)
	begin
		clk_cnt <= 4'd0;
		dri_clk <= 1'b1;
	end
	else if(clk_cnt == CLK_DIVIDE/2 - 1'd1)		//每5个系统时钟脉冲反转一次状态,即每10个系统时钟为一个周期
	begin
		clk_cnt <= 4'd0;
		dri_clk <= ~dri_clk;	//定时器时钟输出
	end
	else
	begin
		clk_cnt <= clk_cnt + 1'b1;	//每个系统时钟脉冲自加1
		dri_clk <= dri_clk;
	end
end

//动态扫描定时器的计数语句块,定时1ms
always @(posedge dri_clk or negedge sys_rst_n)	//注意此处的触发时钟为dri_clk,而不是系统时钟
begin
	if(sys_rst_n==0)
	begin
		cnt0 <= 13'b0;
		flag <= 1'b0;
	end
	else if(cnt0 < MAX_NUM - 1'b1)	//定时1ms,5000*0.2us=1ms
	begin
		cnt0 <= cnt0 + 1'b1;
		flag <= 1'b0;
	end
	else 
	begin
		cnt0 <= 13'b0;
		flag <= 1'b1;	//到达1ms,给出高电平信号
	end
end

//每1ms换下一个位显示,实现动态扫描。
always @(posedge dri_clk or negedge sys_rst_n)
begin
	if(sys_rst_n==0)
		cnt_wei <= 3'b0;
	else if(flag)
	begin
		if(cnt_wei < 3'd5)
			cnt_wei <= cnt_wei + 1'b1;	//每1ms,显示的位号加1
		else
			cnt_wei <= 3'b0;
	end
	else
		cnt_wei<=cnt_wei;
end

always @(posedge sys_clk or negedge sys_rst_n)
begin	
	if(sys_rst_n==0)
		num <= 24'b0;
	else 
	begin
		if(data5 || point[5])	//如果第6位有不为0的值(6位数)或第六位的小数点打开
		begin
			num[23:20] <= data5;  //则依次给6位数码管赋值
			num[19:16] <= data4;
			num[15:12] <= data3;
			num[11:8]  <= data2;
			num[ 7:4]  <= data1;
			num[ 3:0]  <= data0;
		end
		else 
		begin                         
			if (data4 || point[4]) 
			begin //如果显示数据为5位十进制数,则给低5位数码管赋值
				 num[19:0] <= {data4,data3,data2,data1,data0};
				 if(sign)                    
					  num[23:20] <= 4'd11; //如果需要显示负号,则最高位(第6位)为符号位
				 else
					  num[23:20] <= 4'd10; //不需要显示负号时,则第6位不显示任何字符
			end
         	else 
			begin //如果显示数据为4位十进制数,则给低4位数码管赋值
				if (data3 || point[3]) 
				begin
					num[15: 0] <= {data3,data2,data1,data0};
					num[23:20] <= 4'd10; //第6位不显示任何字符
					if(sign)             //如果需要显示负号,则最高位(第5位)为符号位
						num[19:16] <= 4'd11;
					else                 //不需要显示负号时,则第5位不显示任何字符
						num[19:16] <= 4'd10;
				end
				else 
				begin //如果显示数据为3位十进制数,则给低3位数码管赋值
					if (data2 || point[2]) 
					begin
						num[11: 0] <= {data2,data1,data0};
						num[23:16] <= {2{4'd10}};	//第6、5位不显示任何字符
						if(sign)         //如果需要显示负号,则最高位(第4位)为符号位
							num[15:12] <= 4'd11;
						else             //不需要显示负号时,则第4位不显示任何字符
							num[15:12] <= 4'd10;
					end
					else 
					begin //如果显示数据为2位十进制数,则给低2位数码管赋值
						if (data1 || point[1]) 
						begin
							num[ 7: 0] <= {data1,data0};
							num[23:12] <= {3{4'd10}};	//第6、5、4位不显示任何字符
							if(sign)     //如果需要显示负号,则最高位(第3位)为符号位
								num[11:8]  <= 4'd11;
							else         //不需要显示负号时,则第3位不显示任何字符
								num[11:8] <=  4'd10;
						end
						else 
						begin      		 //如果显示数据为1位十进制数,则给最低位数码管赋值
							num[3:0] <= data0;
							num[23:8] <= {4{4'd10}};//第6、5、4、3位不显示任何字符
							if(sign)     //如果需要显示负号,则最高位(第2位)为符号位
								num[7:4] <= 4'd11;
							else         //不需要显示负号时,则第2位不显示任何字符
								num[7:4] <= 4'd10;
						end
					end
				end
            end
        end
    end
end	

always @(posedge dri_clk or negedge sys_rst_n) 
begin
    if(sys_rst_n==0) 
    begin
        smg_wei  <= 6'b111111;     //关闭所有位(位选信号低电平有效)
        num_display <= 4'b0;           
        dot_display <= 1'b1;       //不显示小数点(共阳极数码管,低电平导通)
    end
    else 
    begin
        if(en) 
        begin		//确认数码管已使能
            case (cnt_wei)	//判断当前显示的位
                3'd0 :
                begin
                    smg_wei  <= 6'b011111;     //显示数码管最低位
                    num_display <= num[3:0] ;  //显示的数据
                    dot_display <= ~point[0];  //显示的小数点
                end
                3'd1 :
                begin
                    smg_wei  <= 6'b101111;     //显示数码管第1位
                    num_display <= num[7:4] ;
                    dot_display <= ~point[1];
                end
                3'd2 :
				begin
                    smg_wei  <= 6'b110111;    //显示数码管第2位
                    num_display <= num[11:8];
                    dot_display <= ~point[2];
                end
                3'd3 :
                begin
                    smg_wei  <= 6'b111011;    //显示数码管第3位
                    num_display <= num[15:12];
                    dot_display <= ~point[3];
                end
                3'd4 :
                begin
                    smg_wei  <= 6'b111101;    //显示数码管第4位
                    num_display <= num[19:16];
                    dot_display <= ~point[4];
                end
                3'd5 :
                begin
                    smg_wei  <= 6'b111110;    //显示数码管最高位
                    num_display <= num[23:20];
                    dot_display <= ~point[5];
                end
                default :
                begin
                    smg_wei  <= 6'b111111;
                    num_display <= 4'b0;
                    dot_display <= 1'b1;
                end
            endcase
        end
        else 
        begin
            smg_wei  <= 6'b111111;    //使能信号为0时,所有数码管均不显示
            num_display <= 4'b0;  
            dot_display <= 1'b1;
        end
    end
end

always @ (posedge dri_clk or negedge sys_rst_n) begin
    if (sys_rst_n==0)
        smg_duan <= 8'hc0;
    else 
    begin
        case (num_display)
            4'd0 : smg_duan <= {dot_display,7'b1000000}; //显示数字 0
            4'd1 : smg_duan <= {dot_display,7'b1111001}; //显示数字 1
            4'd2 : smg_duan <= {dot_display,7'b0100100}; //显示数字 2
            4'd3 : smg_duan <= {dot_display,7'b0110000}; //显示数字 3
            4'd4 : smg_duan <= {dot_display,7'b0011001}; //显示数字 4
            4'd5 : smg_duan <= {dot_display,7'b0010010}; //显示数字 5
            4'd6 : smg_duan <= {dot_display,7'b0000010}; //显示数字 6
            4'd7 : smg_duan <= {dot_display,7'b1111000}; //显示数字 7
            4'd8 : smg_duan <= {dot_display,7'b0000000}; //显示数字 8
            4'd9 : smg_duan <= {dot_display,7'b0010000}; //显示数字 9
            4'd10: smg_duan <= 8'b11111111;           //不显示任何字符
            4'd11: smg_duan <= 8'b10111111;           //显示负号(-)
            default: 
                   smg_duan <= {dot_display,7'b1000000};
        endcase
    end
end

endmodule			

3.smg_test.v

主模块

module smg_test
(
	input		 sys_clk,
	input		 sys_rst_n,
	
	output [5:0] smg_wei,
	output [7:0] smg_duan
);

wire [19:0] data;
wire [5:0]  point;
wire        en;
wire		sign;

time_count u_time_count
(
	.sys_clk	 (sys_clk),
	.sys_rst_n   (sys_rst_n),
	
    .data        (data     ),       // 6位数码管要显示的数值
    .point       (point    ),       // 小数点具体显示的位置,高电平有效
    .en          (en       ),       // 数码管使能信号
    .sign        (sign     )    
);

smg_display u_smg_display
(
	.sys_clk	 (sys_clk),
	.sys_rst_n   (sys_rst_n),
	.data        (data     ),       // 显示的数值
	.point       (point    ),       // 小数点具体显示的位置,高电平有效
	.en          (en       ),       // 数码管使能信号
	.sign        (sign     ),       // 符号位,高电平显示负号(-)
	
	.smg_wei	 (smg_wei),
	.smg_duan	 (smg_duan)
);

endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

默默无闻小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值