【 FPGA 】控制数码管动态扫描显示的小实验

举报
李锐博恩 发表于 2021/07/15 04:42:49 2021/07/15
5.9k+ 0 0
【摘要】   实验的功能很简单,就是让4个数码管每隔1s递增显示,使用动态扫描的方式来实现。 从这个功能的描述可以看出,我们首先要写一个计数器模块,来让计数值每隔1s增加1,暂时实现的是16进制的东西,从0到f,之后10到1f等等。 我们的实验平台的系统时钟是25MHz,不是25MHz的实验平台,可以通过PLL来分频或倍频得到25MHz的时钟。 其次,写一个模块来控制...

实验的功能很简单,就是让4个数码管每隔1s递增显示,使用动态扫描的方式来实现。

从这个功能的描述可以看出,我们首先要写一个计数器模块,来让计数值每隔1s增加1,暂时实现的是16进制的东西,从0到f,之后10到1f等等。

我们的实验平台的系统时钟是25MHz,不是25MHz的实验平台,可以通过PLL来分频或倍频得到25MHz的时钟。

其次,写一个模块来控制数码管的位选和段选。

实验平台的数码管是共阴极的,也就是片选端低电平有效。

还需要注意的一个问题是:

片选控制信号的刷新速度必须足够快才能避免闪烁感,但也不能太快,以免影响数码管的开关切换,最佳的工作频率为1000Hz左右。如果FPGA的时钟为50MHz,那么至少跑5*10^4个周期,也即50000个周期刷新一次才行,我们知道2^16=65536,2^15=32768。

当然,这里的时钟是25Mhz,所以我们需要计数大概25000个周期才能刷新一次。

上面的最佳工作频率是1000hz的说法是否权威呢?我不知道,但是实验证明能用。

先给出计数模块的Verilog描述:


      /
      //工程硬件平台: Xilinx Spartan 6 FPGA
      /
      module counter(
      			input clk,		//时钟信号,25MHz
      			input rst_n,	//复位信号,低电平有效
      			output reg[15:0] display_num	//数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百位,[7:4]--数码管十位,[3:0]--数码管个位
      		);
      //-------------------------------------------------
      //1s定时产生逻辑
      reg[24:0] timer_cnt;	//1s计数器,0-24999999
     	//1s定时计数
      always @(posedge clk or negedge rst_n)
     	if(!rst_n) timer_cnt <= 25'd0;
     	else if(timer_cnt < 25'd24_999_999) timer_cnt <= timer_cnt+1'b1;
     	else timer_cnt <= 25'd0;
      wire timer_1s_flag = (timer_cnt == 25'd24_999_999);		//1s定时到标志位,高有效一个时钟周期
      //-------------------------------------------------
      //递增数据产生逻辑
     	//显示数据每秒递增
      always @(posedge clk or negedge rst_n)
     	if(!rst_n) display_num <= 16'd0;
     	else if(timer_1s_flag) display_num <= display_num+1'b1;
      endmodule
  
 

再给出片选和段选控制的Verilog描述:


      /
      //工程硬件平台: Xilinx Spartan 6 FPGA
      /
      module seg7(
      			input clk,		//时钟信号,25MHz
      			input rst_n,	//复位信号,低电平有效
      			input[15:0] display_num,	//数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百位,[7:4]--数码管十位,[3:0]--数码管个位
      			output reg[3:0] dtube_cs_n,	//7段数码管位选信号
      			output reg[7:0] dtube_data	//7段数码管段选信号(包括小数点为8段)
      		);
      //-------------------------------------------------
      //参数定义
      //数码管显示 0~F 对应段选输出
      parameter 	NUM0 	= 8'h3f,//c0,
     			NUM1 	= 8'h06,//f9,
     			NUM2 	= 8'h5b,//a4,
     			NUM3 	= 8'h4f,//b0,
     			NUM4 	= 8'h66,//99,
     			NUM5 	= 8'h6d,//92,
     			NUM6 	= 8'h7d,//82,
     			NUM7 	= 8'h07,//F8,
     			NUM8 	= 8'h7f,//80,
     			NUM9 	= 8'h6f,//90,
     			NUMA 	= 8'h77,//88,
     			NUMB 	= 8'h7c,//83,
     			NUMC 	= 8'h39,//c6,
     			NUMD 	= 8'h5e,//a1,
     			NUME 	= 8'h79,//86,
     			NUMF 	= 8'h71,//8e;
     			NDOT	= 8'h80;	//小数点显示
      //数码管位选 0~3 对应输出
      parameter	CSN		= 4'b1111,
     			CS0		= 4'b1110,
     			CS1		= 4'b1101,
     			CS2		= 4'b1011,
     			CS3		= 4'b0111;
      parameter N = 17;//高两位控制片选,其他位用于分频
      //-------------------------------------------------
      //分时显示数据控制单元
      reg[3:0] current_display_num;	//当前显示数据
      reg[N-1:0] div_cnt;	//分时计数器
     	//分时计数器
      always @(posedge clk or negedge rst_n)
     	if(!rst_n) div_cnt <= 0;
     	else div_cnt <= div_cnt+1'b1;
     	//显示数据
      always @(posedge clk or negedge rst_n)
     	if(!rst_n) current_display_num <= 4'h0;
     	else begin
     		case(div_cnt[N-1:N-2])
     			2'b00: current_display_num <= display_num[3:0];
     			2'b01: current_display_num <= display_num[7:4];
     			2'b10: current_display_num <= display_num[11:8];
     			2'b11: current_display_num <= display_num[15:12];
     			default: ;
      		endcase
      	end
     	//段选数据译码
      always @(posedge clk or negedge rst_n)
     	if(!rst_n) dtube_data <= NUM0;
     	else begin
     		case(current_display_num)
     			4'h0: dtube_data <= NUM0;
     			4'h1: dtube_data <= NUM1;
     			4'h2: dtube_data <= NUM2;
     			4'h3: dtube_data <= NUM3;
     			4'h4: dtube_data <= NUM4;
     			4'h5: dtube_data <= NUM5;
     			4'h6: dtube_data <= NUM6;
     			4'h7: dtube_data <= NUM7;
     			4'h8: dtube_data <= NUM8;
     			4'h9: dtube_data <= NUM9;
     			4'ha: dtube_data <= NUMA;
     			4'hb: dtube_data <= NUMB;
     			4'hc: dtube_data <= NUMC;
     			4'hd: dtube_data <= NUMD;
     			4'he: dtube_data <= NUME;
     			4'hf: dtube_data <= NUMF;
     			default: ;
      		endcase
      	end
     	//位选译码
      always @(posedge clk or negedge rst_n)
     	if(!rst_n) dtube_cs_n <= CSN;
     	else begin
     		case(div_cnt[N-1:N-2])
     			2'b00: dtube_cs_n <= CS0;
     			2'b01: dtube_cs_n <= CS1;
     			2'b10: dtube_cs_n <= CS2;
     			2'b11: dtube_cs_n <= CS3;
     			default:  dtube_cs_n <= CSN;
      		endcase
      	end
      endmodule
  
 

最后给出主模块,调用上述两个模块:


      /
      //工程硬件平台: Xilinx Spartan 6 FPGA
      ///产生一个每秒递增的16bit数据以16进制方式显示在4位数码管上
      module sp6(
       input ext_clk_25m, //外部输入25MHz时钟信号
       input ext_rst_n, //外部输入复位信号,低电平有效
       output[3:0] dtube_cs_n, //7段数码管位选信号
       output[7:0] dtube_data //7段数码管段选信号(包括小数点为8段)
       );
      //-------------------------------------
      //PLL例化
      wire clk_12m5;	//PLL输出12.5MHz时钟
      wire clk_25m;	//PLL输出25MHz时钟
      wire clk_50m;	//PLL输出50MHz时钟
      wire clk_100m;	//PLL输出100MHz时钟
      wire sys_rst_n;	//PLL输出的locked信号,作为FPGA内部的复位信号,低电平复位,高电平正常工作
       pll_controller uut_pll_controller
        (// Clock in ports
       .CLK_IN1(ext_clk_25m), // IN
      // Clock out ports
       .CLK_OUT1(clk_12m5), // OUT
       .CLK_OUT2(clk_25m), // OUT
       .CLK_OUT3(clk_50m), // OUT
       .CLK_OUT4(clk_100m), // OUT
      // Status and control signals
       .RESET(~ext_rst_n),// IN
       .LOCKED(sys_rst_n)); // OUT 
      //-------------------------------------
      //25MHz时钟进行分频,产生每秒递增的16位数据
      wire[15:0] display_num;	//数码管显示数据,[15:12]--数码管千位,[11:8]--数码管百位,[7:4]--数码管十位,[3:0]--数码管个位
      counter uut_counter(
       .clk(clk_25m), //时钟信号
       .rst_n(sys_rst_n), //复位信号,低电平有效
       .display_num(display_num) //LED指示灯接口 
       );
      //-------------------------------------
      //4位数码管显示驱动 
      seg7 uut_seg7(
       .clk(clk_25m), //时钟信号
       .rst_n(sys_rst_n), //复位信号,低电平有效
       .display_num(display_num), //LED指示灯接口 
       .dtube_cs_n(dtube_cs_n), //7段数码管位选信号
       .dtube_data(dtube_data) //7段数码管段选信号(包括小数点为8段)
       );
      endmodule
  
 

里面用到了一个PLL的IP核,这里只是一个示例,由于本设计的实验平台的时钟频率本身就是25MHz,所以不用也行。

实验结果证明,很完美。

关于数码管,以前也写过两篇博文:

数码管显示实验(一)(初步明白片选、段选)

四位16进制的数码管动态显示设计

8段数码管译码表

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/86516193

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。