FPGA学习altera 系列 第二十一篇 数码管设计

2020-12-29 17:29:33 浏览数 (1)

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。

今天给大侠带来“FPGA学习系列 altera"系列,持续更新。此学习心得是本人之前所写,所用设计软件为Quartus II 13.1,现Quartus II 新版本已更新到19 ,以下仅供初学者学习参考。后续会更新其他系列,敬请关注。话不多说,上货。

对于每一个的小实验,我们都可以把它看作是一个小项目,逐步的去分析,设计,调试,最后完成功能。下面我们就开始我们的“小项目”。

项目名称:数码管

具体要求:使用数码管显示任意数字。

项目分析:

1. 什么是数码管

数码管也称LED数码管,不同行业人士对数码管的称呼不一样,其实都是同样的产品。

数码管按段数可分为七段数码管和八段数码管,八段数码管比七段数码管多一个发光二极管单元,也就是多一个小数点(DP)这个小数点可以更精确的表示数码管想要显示的内容;按能显示多少个(8)可分为1位、2位、3位、4位、5位、6位、7位等数码管。

按发光二极管单元连接方式可分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳数码管在应用时应将公共极COM接到 5V,当某一字段发光二极管的阴极为低电平时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就不亮。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管,共阴数码管在应用时应将公共极COM接到地线GND上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相应字段就不亮。

例如:如果想要显示数字“1”,则B、C段亮,其他段不亮就可以了。

2. 驱动的方式

数码管要正常显示,就要用驱动电路来驱动数码管的各个段码,从而显示出我们要的数字,因此根据数码管的驱动方式的不同,可以分为静态式和动态式两类。

静态显示驱动

静态驱动也称直流驱动。静态驱动是指每个数码管的每一个段码都由一个FPGA的I/O端口进行驱动。静态驱动的优点是编程简单,显示亮度高,缺点是占用I/O端口多,如驱动6个数码管静态显示则需要6×8=48个I/O端口来驱动。

动态显示驱动

数码管动态显示接口是单片机中应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起,另外为每个数码管的公共极COM增加位选通控制电路,位选通由各自独立的I/O线控制,当FPGA输出字形码时,所有数码管都接收到相同的字形码,但究竟是哪个数码管会显示出字形,取决于FPGA对位选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。

架构图如下:

上述架构的设计,基于笔者所使用开发板。开发板的数码管为共阳数码管,共有6个,并且位选信号也是经过3-8译码器之后连接到数码管硬件电路上。我们选择动态显示驱动。

seg7_data[23:0]:数码管的正常显示数据为0~9,二进制为4’b0000~4’b1001。由于数码管有六个,故而显示的数据应该是6x4=24(位)。

seg[7:0]:段选信号。

sel[2:0]:6个数码管应该有6个位选信号,为了节省I/O,通过3-8译码器与数码管的硬件电路相连接,故而位选信号3位。

系统设计:

1. 工程的名称:seg7

2. 实现方法:

动态显示要求扫描的频率足够快(1-2ms),笔者定义扫描的频率为1KHz(每个数码管亮1ms)。

由一个计数器产生频率为1KHz的高脉冲,利用高脉冲控制状态机的跳转,进而完成动态驱动。在每一个状态中,控制点亮的那个数码管(sel),并且输出想要显示的数字(show_data)。笔者开发板的数码管共有6个,对应的数码管显示对应位的数字,例如:sel=0:选择第一个数码管。show_data=seg7_data[23;20]:最高的四位。

使用组合逻辑将想要显示的数字翻译成段选信号。如果使用时序逻辑,位选信号“sel”和段选信号“seg”就不是同时达到数码管的硬件电路。分析:“sel”和“show_data”同时输出,而“show_data”只是想要显示的数字,并不能够直接输出给数码管硬件电路,还需要翻译成段选信号,若使用时序逻辑,那么seg也将被综合成为寄存器,导致段选信号“seg”将比段选信号“sel”晚到一个时钟周期。

3. 状态转移图

设计代码如下:

代码语言:javascript复制
/*
模块名称:seg7
模块功能:使用数码管显示任意数字
作者:郝旭帅
邮箱:746833924@qq.com
*/
module seg7 (clk, rst_n, seg7_data, seg, sel);

  input clk;//50MHz
  input rst_n;
  input [23:0] seg7_data;

  output reg [7:0] seg;
  output reg [2:0] sel;

  parameter T1ms = 50_000;//1ms的周期数

  reg [15:0] cnt;//1ms计数器

  always @ (posedge clk or negedge rst_n)
    begin
      if (!rst_n)
        begin
          cnt <= 16'd0;
        end
      else
        begin
          if (cnt < T1ms - 1)
            cnt <= cnt   1'b1;
          else
            cnt <= 16'd0;
        end
    end

  wire flag;

assign flag = (cnt == T1ms - 1) ? 1'b1 : 1'b0;//1ms的高脉冲

  localparam s0 = 3'b000,
         s1 = 3'b001,
         s2 = 3'b010,
         s3 = 3'b011,
         s4 = 3'b100,
         s5 = 3'b101;

  reg [2:0] state;
  reg [3:0] show_data;

  always @ (posedge clk or negedge rst_n)
    begin
      if (!rst_n)
        begin
          state <= s0;
          show_data <= 4'd0;
          sel <= 3'b000;
        end
      else
        begin
          case (state)
            s0 : begin
                if (!flag)
                  begin
                    state <= s0;
                    sel <= 3'b000;
                    show_data <= seg7_data[23:20];
                  end
                else
                  begin
                    state <= s1;
                  end
              end

            s1 : begin
                if (!flag)
                  begin
                    state <= s1;
                    sel <= 3'b001;
                    show_data <= seg7_data[19:16];
                  end
                else
                  begin
                    state <= s2;
                  end
                end

            s2 : begin
                if (!flag)
                  begin
                    state <= s2;
                    sel <= 3'b010;
                    show_data <= seg7_data[15:12];  
                  end
                else
                  begin
                    state <= s3;
                  end
              end

            s3 : begin
                if (!flag)
                  begin
                    state <= s3;
                    sel <= 3'b011;
                    show_data <= seg7_data[11:8];
                  end
                else
                  begin
                    state <= s4;
                  end
              end

            s4 : begin
                if (!flag)
                  begin
                    state <= s4;
                    sel <= 3'b100;
                    show_data <= seg7_data[7:4];
                  end
                else
                  begin
                    state <= s5;
                  end
              end

            s5 : begin
                if (!flag)
                  begin
                    state <= s5;
                    sel <= 3'b101;
                    show_data <= seg7_data[3:0];
                  end
                else
                  begin
                    state <= s0;
                  end
              end

            default : state <= s0;

          endcase
        end
    end

  always @ (*)
    begin
      if (!rst_n)
        begin
          seg = 8'b1111_1111;
        end
      else
         begin
          case (show_data)
            0 : seg = 8'b1100_0000;
            1 : seg = 8'b1111_1001;
            2 : seg = 8'b1010_0100;
            3 : seg = 8'b1011_0000;
            4 : seg = 8'b1001_1001;
            5 : seg = 8'b1001_0010;
            6 : seg = 8'b1000_0010;
            7 : seg = 8'b1111_1000;
            8 : seg = 8'b1000_0000;
            9 : seg = 8'b1001_0000;
            default : seg = 8'b0000_0000;
          endcase
        end
    end

endmodule

代码解析:

段选信号(seg)的最高位对应dp(小数点),依次是g,f,e,d,c,b,a。例如:数字“1”,则seg=8’b1111_1001;

激励代码如下:

代码语言:javascript复制
/*
模块名称:seg7_tb
模块功能:为seg7模块提供激励信号
作者:郝旭帅
邮箱:746833924@qq.com
*/
`timescale 1ns/1ps

module seg7_tb;

  reg clk;
  reg rst_n;
  reg [23:0] seg7_data;

  wire [7:0] seg;
  wire [2:0] sel;

  parameter T1ms = 5;//仿真时的参数

  initial begin
    clk = 1'b1;
    rst_n = 1'b0;
    seg7_data = 24'h123456;//显示数据为123456
    # 200.1
    rst_n = 1'b1;
    # 2000
    $stop;
  end

  always # 10 clk = ~clk;//50MHz

  seg7
    # (
      .T1ms(T1ms)//传递参数
    )
    seg7_dut(
      .clk(clk),
      .rst_n(rst_n), 
      .seg7_data(seg7_data),
      .seg(seg),
      .sel(sel)
  );

endmodule

仿真波形如下:

通过仿真,我们可以清楚的看到,当sel为“0”时,seg为数字“1”的段码;当sel为“1”时······,证明设计正确。

如果设计要求或者本地晶振与笔者的设计不同,请自行更改设计,以保证设计的正确性。如果还是有不明白的读者可以发邮件到我邮箱或者加群询问。

END

制作人:郝旭帅

0 人点赞