FPGA项目(5)--FPGA控制数码管动态显示的原理

        数码管是现在电子产品上常用的显示器件,它有驱动简单、显示清晰、价格低廉等优势。数码管的实物图:

FPGA项目(5)--FPGA控制数码管动态显示的原理_第1张图片

         数码管的内部结构图如下所示:

FPGA项目(5)--FPGA控制数码管动态显示的原理_第2张图片

         从图中可以看出,它由八个段组成,即A B C D E F G DP(小数点),只要将这八个段按规律组合点亮,就能显示出一定的数字。例如,对于数字1,只需要将B C两段点亮,其他全部熄灭,那么就可以在数码管上显示出数字1.数码管还有一个公共端,用于接电源或地。

        数码管又分为两种,一种是共阴极数码管,一种是共阳极数码管。对于共阴极数码管而言,它的各个段是高电平点亮,公共端接地。对于共阳极数码管,它的各个段是低电平点亮,公共端接电源。现在给出两种数码管的0-9这十个数字的段码表:

        共阴极(兼有共阳极):

        FPGA项目(5)--FPGA控制数码管动态显示的原理_第3张图片

        共阳极:

         FPGA项目(5)--FPGA控制数码管动态显示的原理_第4张图片

         至此,对于如何在数码管上显示出数字,我们就有了思路。本次我用的是共阳极数码管,不需要显示小数点,所以对我而言,我只需要控制数码管的七个段即可。用FPGA控制数码管静态显示是很容易的,直接给七个段分配七个管脚,然后再输出对应的电平,就能静态显示出数字。如果要显示数字1,只要输出电平 1111001即可。难点在于如何动态显示。下面讲解FPGA驱动四位数码管动态显示的思路:

        本次使用的四位数码管按如下方式排列:

FPGA项目(5)--FPGA控制数码管动态显示的原理_第5张图片

         每个数码管都有一个位选端,通过位选端的控制,就可以一次只点亮一个数码管,然后快速切换到另一个数码管,只要频率够快,就能达到动态显示的效果。本次我使用的4位数码管的位选端是低电平有效,那么当位选信号为1110时,标号为4的数码管被点亮,可以显示相应的数字。要使得标号为3的数码管也被点亮显示数字时,只需要将位选信号赋值为1101即可。只要以足够快的频率,依次给位选信号赋值为1110 ,1101 , 1011 , 0111,就可以同时点亮四个数码管。

        下面给出FPGA驱动数码管动态显示的代码,并对代码进行具体的分析、解释。

module seg(
input 				sys_clk,sys_rest,
input	[15:0]		data,
output	reg [3:0]	sel,
output  reg [6:0]	seg_led
);

parameter	CLK_NUM=4'd10;
parameter	MSNUM=14'd5000;
reg	[3:0]	CNT_NUM;
reg			CLK;

reg [12:0]	MSCNT;
reg			MS_flag;
reg [3:0]	num_display;

reg [15:0]	num;
reg [2:0]	sel_num; //选择哪一位数码管被点亮
//wire define
wire   [3:0]              data0    ;        // 个位数
wire   [3:0]              data1    ;        // 十位数
wire   [3:0]              data2    ;        // 百位数
wire   [3:0]              data3    ;        // 千位数


//提取显示数值所对应的十进制数的各个位
assign  data0 = data[3:0];      // 个位数
assign  data1 = data[7:4];    // 十位数
assign  data2 = data[11:8];    // 百位数
assign  data3 = data[15:12];   // 千位数


always @(posedge sys_clk or negedge sys_rest) begin
	if(!sys_rest)
		begin
		CNT_NUM<=4'd0;
		CLK<=1'd1;
		end
	else if(CNT_NUM<=CLK_NUM/2-1'b1)
		begin
		CLK<=~CLK;
		CNT_NUM<=4'd0;
		end
	else
		begin
		CNT_NUM<=CNT_NUM+1;
		CLK<=CLK;
		end
end

always @(posedge CLK or negedge sys_rest) begin
	if(!sys_rest)
		num<=16'd0;
	else 
		begin		    
            num[15:12] <= data3;	//则依次给4位数码管赋值
            num[11:8]  <= data2;
            num[ 7:4]  <= data1;
            num[ 3:0]  <= data0;
		end
end


always @(posedge CLK or negedge sys_rest) begin  //产生1ms脉冲
	if(!sys_rest)
		begin
			MSCNT<=13'd0;
			MS_flag<=1'b0;
		end
	else if(MSCNT==MSNUM-1)
		begin
			MSCNT<=13'd0;
			MS_flag<=1'b1;
		end
	else
		begin
			MSCNT<=MSCNT+1;
			MS_flag<=1'b0;
		end
end


always @(posedge CLK or negedge sys_rest) begin
	if(!sys_rest)
		sel_num<=0;
	else if(MS_flag)
		begin
			if(sel_num<3'd3)
				sel_num<=sel_num+1;
			else
				sel_num<=0;
		end
	else
		sel_num<=sel_num;
end

always @(posedge CLK or negedge sys_rest) begin
	if(!sys_rest)
		sel<=4'b1111;
	else
		begin
			case(sel_num)
				3'd0:	begin
						sel<= 4'b1110;  //显示数码管最低位
						num_display<=num[3:0];
						end
				3'd1:	begin
						sel<= 4'b1101;  //显示数码管第1位
						num_display<=num[7:4];
						end
				3'd2:	begin
						sel<= 4'b1011;  //显示数码管第2位
						num_display<=num[11:8];
						end
				3'd3:	begin
						sel<= 4'b0111;  //显示数码管第3位
						num_display<=num[15:12];
						end
				default	sel<= 4'b1111;
			endcase
		end
end

always @(posedge CLK or negedge sys_rest) begin
	if(!sys_rest)
		seg_led<=7'b100000;
	else
		begin
			case(num_display)
			4'h0 :    seg_led <= 7'b1000000;
            4'h1 :    seg_led <= 7'b1111001;
            4'h2 :    seg_led <= 7'b0100100;
            4'h3 :    seg_led <= 7'b0110000;
            4'h4 :    seg_led <= 7'b0011001;
            4'h5 :    seg_led <= 7'b0010010;
            4'h6 :    seg_led <= 7'b0000010;
            4'h7 :    seg_led <= 7'b1111000;
            4'h8 :    seg_led <= 7'b0000000;
            4'h9 :    seg_led <= 7'b0010000;
			4'd10: 	  seg_led <= 7'b1111111;           //不显示任何字符
            default : seg_led <= 7'b1000000;
        endcase
		end
end

endmodule

        首先显示的数据是以BCD码的格式传入的,例如我要显示数据1234,那么就要传入0001_0010_0011_0100给该模块,所以输入数据data的位宽为16。然后定义4个内部变量,data0到data3,用于保存个位 ,十位, 百位 ,千位上的数字,方便后面的显示。随后又分频,产生了5M的时钟CLK和1K的时钟MS_flag。位选信号的变化就是在MS_flag信号的驱动下变换的,也就是说,数码管以1KHZ的频率切换显示,人眼是分辨不出来的,所以看起来就是同时显示了四个数字。最后,根据位选信号,将对应位(个位/十位/百位/千位)的数字传给num_display,再由num_display变量控制数码管段选信号的输出。这样就实现了动态的显示!

        备注:数码管的段选信号就是数码管A B C D E F G等七个段的控制信号,数码管位选信号就是公共端的控制信号。

你可能感兴趣的:(FPGA项目,fpga开发,嵌入式硬件)