FPGA基础设计之数码管显示

这篇具有很好参考价值的文章主要介绍了FPGA基础设计之数码管显示。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.数码管简介

数码管是一种半导体发光器件,其基本单元是发光二极管。一般分为七段数码管和八段数码管,多的一段是小数点。也有其他如N型、米型数码管以及16段、24段管等。本次设计的是八段数码管

1.1 数码管硬件结构

FPGA基础设计之数码管显示
公阴极数码管高电平亮,公阳极数码管低电平亮。AC620上搭载的是公阳极数码管。
数码管的显示有静态和动态两种:静态的特点是每个数码管的段必须接一个八位数据线来保持显示的字形码,输送一次字形码后,显示字形可一直保持。但是这种方法需要很多的IO口,很少采用。
FPGA基础设计之数码管显示
这就实现了另一种显示模式,动态显示。动态显示将所有的段选线连一起,由位选控制哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向数码管送出字形码和相应的位选,利用发光管的余晖和人眼视觉暂留作用,使人的感觉好像数码管都在显示。

1.2 三线制数码管电路设计

AC620上配备的是八位八段数码管,如果按照上述设计,仍然需要16个IO口。为了继续节省IO口,大多使用串行移位寄存器来节省IO口。移位寄存器,就是一端使用串行接口如SPI接口来按位接收多位数据,另一端则将接收到的数据分别使用一个独立的管脚输出,实现串并转换
具体实现就是使用一片到多片SPI接口的串行移位寄存器,例如74HC595芯片。FPGA将点亮数码管需要的16位段选和位选信号通过SPI接口传递给74HC595芯片,74HC595再将这16位的数据通过16个IO信号输出,从而驱动数码管的位选和段选。一位74HC595只能实现8位数据的串并转换,因此使用两片74HC595芯片级联实现
FPGA基础设计之数码管显示
FPGA基础设计之数码管显示
从该原理图可以看出输入的16位信号为{sel,seg},其中位选高位代表数码管右边,低位左边,段选为{a,b,c,d,e,f,g,h}
FPGA基础设计之数码管显示

2.数码管模块功能划分

由于数码管显示较为自由,本模块的划分也较多,可以根据各自不同的使用情况将模块自由的组合起来。日后想起来也可补充。
1.74HC595驱动模块
本模块接收来自其他模块的位选和段选信号,通过74HC595驱动模块发送至74HC595芯片
FPGA基础设计之数码管显示
oe端在本开发板上直接接地,不需要程序操作

2.32位转位选段选信号模块
四位代表一个数码管显示的符号,八位一共需32位。可根据需要扩展或收缩。
FPGA基础设计之数码管显示

3.二进制转十进制。
FPGA中数字都是二进制,如果累加的话上述驱动无法正常显示,因此需要二进制转十进制传给其他模块来显示。
FPGA基础设计之数码管显示

3.模块编写

3.1 74HC595驱动模块

3.1.1 原理

本模块负责将数据发送给74HC595芯片。由原理图可知:
ds/dio引脚是数据线。负责发送数据。
STCP/RCLK引脚是寄存器时钟输入线。上升沿将移位寄存器的数据取出显示在八位管脚上。平时为低电平,数据传输完成为拉高。
SHCP/SCLK引脚是移位寄存器时钟线。上升沿将数据移位寄存至移位寄存器中。
FPGA基础设计之数码管显示

3.1.2 程序

// FPGA   : 小梅哥AC620
// EDA    : Quartus II 13.0sp1 (64-bit) and ModelSim SE-64 10.5 
// Author : FPGA小白758 https://blog.csdn.net/q1594?spm=1010.2135.3001.5343
// File   : hc595_ctrl.v
// Create : 2022-05-14 01:13:06
// Revise : 2022-05-14 01:13:06
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
// 74HC595控制模块
module hc595_ctrl(
    input   wire            sys_clk             ,   //系统时钟,频率 50MHz
    input   wire            sys_rst_n           ,   //复位信号,低有效
    input   wire    [7:0]   sel                 ,   //数码管位选信号
    input   wire    [7:0]   seg                 ,   //数码管段选信号
    
    output  reg             stcp                ,   //数据存储器时钟
    output  reg             shcp                ,   //移位寄存器时钟
    output  reg             ds                      //串行数据输入
);
//reg define
reg     [1:0]       cnt_4                       ;   //分频计数器
reg     [4:0]       cnt_bit                     ;   //传输位数计数器
//wire define
wire [15:0] data ; //数码管信号寄存

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//将数码管信号寄存
assign data={sel,seg};

//分频计数器:0~3 循环计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_4 <= 2'd0;
    else if(cnt_4 == 2'd3)
        cnt_4 <= 2'd0;
    else
        cnt_4 <= cnt_4 + 1'b1;

//cnt_bit:每输入一位数据加一
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <= 4'd0;
    else if(cnt_4 == 2'd3 && cnt_bit == 4'd15)
        cnt_bit <= 4'd0;
    else if(cnt_4 == 2'd3)
        cnt_bit <= cnt_bit + 1'b1;
    else
        cnt_bit <= cnt_bit;

//stcp:16个信号传输完成之后产生一个上升沿
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        stcp <= 1'b0;
    else if(cnt_bit == 4'd15 && cnt_4 == 2'd3)
        stcp <= 1'b1;
    else
        stcp <= 1'b0;

//shcp:产生四分频移位时钟
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        shcp <= 1'b0;
    else if(cnt_4 >= 4'd2)
        shcp <= 1'b1;
    else
        shcp <= 1'b0;

//ds:将寄存器里存储的数码管信号输入即
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        ds <= 1'b0;
    else if(cnt_4 == 2'd0)
        ds <= data[cnt_bit];
    else
        ds <= ds;

endmodule

3.2 译码模块

3.2.1 原理

本模块将八位数码管上需要显示的数据用一个特定的数字表示出来,然后用位选每1ms显示一位在数码管上。相当于译码。可根据需要增加数据线或更改译码规则来显示不同的信息。本代码只显示0~9。
FPGA基础设计之数码管显示

3.2.2 程序

// FPGA   : 小梅哥AC620
// EDA 	  : Quartus II 13.0sp1 (64-bit) and ModelSim SE-64 10.5 
// Author : FPGA小白758 https://blog.csdn.net/q1594?spm=1010.2135.3001.5343
// File   : seg_dynamic.v
// Create : 2022-05-14 17:08:38
// Revise : 2022-05-14 18:32:37
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
module seg_dynamic
(
    input   wire        		sys_clk , //系统时钟,频率 50MHz
    input   wire       		 	sys_rst_n , //复位信号,低有效
    input   wire 	[31:0] 		data , //数码管要显示的值
    input   wire 	[ 7:0]  	point , //小数点显示,高电平有效
    input   wire        		seg_en , //数码管使能信号,高电平有效

    output  reg  	[7:0]  		sel , //数码管位选信号
    output  reg  	[7:0]  		seg //数码管段选信号
 );
 
parameter CNT_MAX = 16'd49_999; //数码管刷新时间计数最大值
 


reg		[ 6:0]				data_disp 	; //当前数码管的段选信号 

reg 	[15:0] 				cnt_1ms 	; //1ms 计数器
reg 						flag_1ms 	; //1ms 标志信号
reg 	[3:0] 				cnt_sel 	; //数码管位选计数器
reg 	[3:0] 				data_tmp 	; //当前数码管显示的数据
	

//cnt_1ms:1ms 循环计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1ms <= 16'd0;
    else if(cnt_1ms == CNT_MAX)
        cnt_1ms <= 16'd0;
    else
        cnt_1ms <= cnt_1ms + 1'b1;
        
//flag_1ms:1ms 标志信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        flag_1ms <= 1'b0;
    else if(cnt_1ms == CNT_MAX - 1'b1)
        flag_1ms <= 1'b1;
    else
        flag_1ms <= 1'b0;

//cnt_sel:从 1 到 8 循环数,用于选择当前显示的数码管
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sel <= 3'd0;
    else if((cnt_sel == 4'd7) && (flag_1ms == 1'b1))
        cnt_sel <= 3'd0;
    else if(flag_1ms == 1'b1)
        cnt_sel <= cnt_sel + 1'b1;
    else
        cnt_sel <= cnt_sel;

//
always @(*)begin
	case(cnt_sel)
		3'd0 	:data_tmp = data[3:0];
		3'd1 	:data_tmp = data[7:4];
		3'd2 	:data_tmp = data[11:8];
		3'd3 	:data_tmp = data[15:12];
		3'd4 	:data_tmp = data[19:15];
		3'd5 	:data_tmp = data[23:20];
		3'd6 	:data_tmp = data[27:24];
		3'd7 	:data_tmp = data[31:28];
		default:data_tmp = data[3:0];
	endcase
end
//不同信号对应的段选(a,b,c,d,e,f,g)
always @(*)begin
	case(data_tmp)
		'd0 	:	data_disp	=	7'b000_0001;	//显示0
		'd1 	:	data_disp 	=	7'b100_1111; 	//显示1
		'd2 	:	data_disp 	=	7'b001_0010;	//显示2
		'd3 	:	data_disp 	=	7'b000_0110;	//显示3
		'd4 	:	data_disp 	=	7'b100_1100;	//显示4
		'd5 	: 	data_disp	=	7'b010_0100;	//显示5
		'd6 	: 	data_disp 	=	7'b010_0000;	//显示6
		'd7 	: 	data_disp 	=	7'b000_1111;	//显示7
		'd8 	: 	data_disp 	=	7'b000_0000;	//显示8
		'd9 	: 	data_disp 	=	7'b000_0100;	//显示9
		'd10 	: 	data_disp 	=	7'b000_1000;	//显示a
		'd11 	:	data_disp 	=	7'b110_0000;	//显示b
		'd12 	: 	data_disp 	=	7'b011_0001;	//显示c
		'd13 	: 	data_disp 	=	7'b100_0010;	//显示d
		'd14 	: 	data_disp 	=	7'b011_0000;	//显示e
		'd15 	: 	data_disp 	=	7'b011_0000;	//显示f
		default	:	data_disp 	=	7'b111_1111;
	endcase
end

//控制数码管的段选信号,使八个数码管轮流显示
always@(*)begin
	if (seg_en == 1'b0) begin
		sel = 8'd0;
	end
	else    case(cnt_sel)
		        4'd0: {sel,seg} = {8'b1000_0000,data_disp,~point[0]}; //给第 1 个数码管赋值
		        4'd1: {sel,seg} = {8'b0100_0000,data_disp,~point[1]}; //给第 2 个数码管赋值
		        4'd2: {sel,seg} = {8'b0010_0000,data_disp,~point[2]}; //给第 3 个数码管赋值
		        4'd3: {sel,seg} = {8'b0001_0000,data_disp,~point[3]}; //给第 4 个数码管赋值
		        4'd4: {sel,seg} = {8'b0000_1000,data_disp,~point[4]}; //给第 5 个数码管赋值
		        4'd5: {sel,seg} = {8'b0000_0100,data_disp,~point[5]}; //给第 6 个数码管赋值
		        4'd6: {sel,seg} = {8'b0000_0010,data_disp,~point[6]}; //给第 7 个数码管赋值
		        4'd7: {sel,seg} = {8'b0000_0001,data_disp,~point[7]}; //给第 8 个数码管赋值            
	    		default:{sel,seg} = {8'b0000_0000,8'b1111_1111};
	    	endcase	
end
      
endmodule

这两个模块已经能做出基本的显示的功能了,但是对传输进来的数字有要求。必须与数码管位数一一对应。但是数据在FPGA中是二进制,而一般生活中常用的是十进制,这两个并不能在数码管中对应起来。需要经过处理。于是有了下面二进制转十进制模块。

3.3二进制转十进制模块

3.3.1 原理

FPGA基础设计之数码管显示
1.补零。对二进制数高位补零,零的个数是对应的十进制数*4。
2.移位。二进制码依次左移一位,每次移位都要判断补充的位数以四个为单位有没有大于四的,如果有,则对该四位加3。移位的次数为原二进制码的个数。文章来源地址https://www.toymoban.com/news/detail-426207.html

3.3.2 程序

module  bcd_8421(
    input           sys_clk,
    input           sys_rst_n,
    input   [26:0]  data,
    
    output  reg [3:0]   unit    ,//个位
    output  reg [3:0]   ten     ,//十位
    output  reg [3:0]   hun     ,//百位
    output  reg [3:0]   tho     ,//千位
    output  reg [3:0]   t_tho   ,//万位
    output  reg [3:0]   h_hun   ,//十万位
    output  reg [3:0]   mil     ,//百万位
    output  reg [3:0]   t_mil   //千万位
);

reg [4:0]   cnt_shift;//移位判断计数器
reg [58:0]  data_shift;//移位判断数据寄存器
reg         shift_flag;//移位判断标志位

//cnt_shift:移位判断计数器,每移一位计数器加1;
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 0)
        cnt_shift <= 5'd0;
    else    if((cnt_shift == 5'd28) && (shift_flag == 1'b1))
        cnt_shift <= 5'd0;
    else    if(shift_flag == 1'b1)
        cnt_shift <= cnt_shift + 1'b1;
    else
        cnt_shift <= cnt_shift;
        
//data_shift:计数器为0时赋初值。计数器为1~27时进行移位判断操作
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 0)
        data_shift <= 58'd0;
    else    if(cnt_shift == 5'd0)
        data_shift <= {32'd0,data};
    else    if((cnt_shift <= 27) && (shift_flag == 1'b0))
        begin
            data_shift[30:27] <= (data_shift[30:27] > 4) ? (data_shift[30:27] + 2'd3) : (data_shift[30:27]);
            data_shift[34:31] <= (data_shift[34:31] > 4) ? (data_shift[34:31] + 2'd3) : (data_shift[34:31]);
            data_shift[38:35] <= (data_shift[38:35] > 4) ? (data_shift[38:35] + 2'd3) : (data_shift[38:35]);
            data_shift[42:39] <= (data_shift[42:39] > 4) ? (data_shift[42:39] + 2'd3) : (data_shift[42:39]);
            data_shift[46:43] <= (data_shift[46:43] > 4) ? (data_shift[46:43] + 2'd3) : (data_shift[46:43]);
            data_shift[50:47] <= (data_shift[50:47] > 4) ? (data_shift[50:47] + 2'd3) : (data_shift[50:47]);
            data_shift[54:51] <= (data_shift[54:51] > 4) ? (data_shift[54:51] + 2'd3) : (data_shift[54:51]);
            data_shift[58:55] <= (data_shift[58:55] > 4) ? (data_shift[58:55] + 2'd3) : (data_shift[58:55]);
        end
    else    if((cnt_shift <= 27) && (shift_flag == 1'b1))
        data_shift <= data_shift << 1;
    else
        data_shift <= data_shift;

//shift_flag:移位判断标志信号,用于控制移位判断的先后顺序
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        shift_flag <= 1'b0;
    else
        shift_flag <= ~shift_flag;
        
//当计数器等于 20 时,移位判断操作完成,对各个位数的 BCD 码进行赋值
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            unit    <= 4'd0;
            ten     <= 4'd0;
            hun     <= 4'd0;
            tho     <= 4'd0;
            t_tho   <= 4'd0;
            h_hun   <= 4'd0;
            mil     <= 4'd0;
            t_mil   <= 4'd0;
        end
    else if(cnt_shift == 5'd28)
        begin
            unit    <= data_shift[30:27];
            ten     <= data_shift[34:31];
            hun     <= data_shift[38:35];
            tho     <= data_shift[42:39];
            t_tho   <= data_shift[46:43];
            h_hun   <= data_shift[50:47];
            mil     <= data_shift[54:51];
            t_mil   <= data_shift[58:55];
        end    
endmodule


到了这里,关于FPGA基础设计之数码管显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Intel FPGA:数码管显示

    个人说明: 限于时间紧迫以及作者水平有限,本文错误、疏漏之处恐不在少数,恳请读者批评指正。意见请留言或者发送邮件至:“Email:noahpanzzz@gmail.com” 。 本博客的工程文件均存放在:GitHub:https://github.com/panziping。 本博客的地址:CSDN:https://blog.csdn.net/ZipingPan 。 参考: 芯

    2024年04月15日
    浏览(40)
  • FPGA(5)数码管静态显示与动态显示

    目录 一、数码管静态显示 二、数码管动态显示 1、变量定义 2、定时(60us) 3、动态显示 代码 FPGA的数码管有4位,8段 。( 位和段都是共阳,即低电平有效 )     位选的4位(二进制):分别为第1、2、3、4位数码管。 段选的8位(二进制):分别为第h、g、f、e、d、c、b、a段

    2023年04月12日
    浏览(46)
  • FPGA实战------数码管(1)静态显示

    led的花样点灯差不多了吧,接下来学习另一个基础的东西,数码管。 数码管在FPGA开发板上占得位置不小,在工程开发中也必不可少,比如后边的温度传感器就会用数码管来显示温度。这里先不多介绍温度传感器,过一段时间就会发了。本篇文章先用数码管来做静态显示。也

    2024年02月04日
    浏览(47)
  • [FPGA 学习记录] 数码管动态显示

    数码管动态显示 在上一小节当中,我们对数码管的静态显示做了一个详细的讲解;但是如果单单只掌握数码管的静态显示这种显示方式是远远不够的,因为数码管的静态显示当中,被选中的数码位它们显示的内容都是相同的,这种显示方式在我们的实际应用当中显然是不合适

    2024年02月04日
    浏览(52)
  • [FPGA 学习记录] 数码管静态显示

    数码管静态显示 在我们的许多项目设计当中,我们通常需要一些显示设备来显示我们需要的信息。我们可以选择的显示设备有很多,而我们的数码管就是使用最多、最简单的显示设备之一。数码管它是一种半导体发光器件,它具有响应时间短、体积小、重量轻、寿命长的优点

    2024年02月03日
    浏览(54)
  • FPGA学习之数码管时间显示模块

    在学习完小梅哥的串口通信以及数码管显示教程后,他留下了一个课后作业,也就是本次的数码管时间显示模块。作为一个FPGA新人,这也算是第一个比较完整的练手小项目了,也推荐和我一样的新人花时间去完成一下。总体功能虽然比较简单,但是我也花了小两天的时间去编

    2024年03月10日
    浏览(63)
  • 二、19【FPGA】数码管动态显示实验

    学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。 学习视频:是根据野火FPGA视频教程——第二十二讲 https://www.bilibili.com/video/BV1nQ4y1Z7zN?p=3 “天下武功唯快不破”    “看到的不一定为真” 眼睛的视觉暂留:光信号传入大脑需要短暂时间,

    2023年04月08日
    浏览(39)
  • FPGA学习汇总(六)----数码管显示(1)

    目录 概念 单个数码管显示单个数字  操作  代码  现象 分析 四个数码管定时单个显示数字 分析 代码 四个数码管同时显示 分析 代码 现象

    2024年02月01日
    浏览(55)
  • FPGA基本实验之数码管的静态显示

    此实验基于FPGA征途pro开发板实现, 数码管是一种半导体发光器件,其基本单元是发光二极管。数码管按段数一般分为七 段数码管和八段数码管,八段数码管比七段数码管多一个发光二极管(多一个小数点显 示)。当然也还有一些其他类型的数码管如“N”形管、“米”字管

    2024年02月03日
    浏览(52)
  • FPGA基本实验之数码管的动态显示

            关于数码管的基本知识大家可以参考我上一篇文章数码管的静态显示,         使用 1ms 的刷新时间让六个数码管轮 流显示:第 1ms 点亮第一个数码管,第 2ms 点亮第二个数码管,以此类推依次点亮六个数 码管,6ms 一个轮回,也就是说每个数码管每 6ms 点亮

    2024年02月08日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包