一天一个设计实例-FPGA和数码管

1.1.1数码管基础知识

数码管由8个发光二极管(以下简称字段)构成,通过不同的组合可显示数字0~9、字符A~F、H、L、P、R、U、Y、符号“”及小数点“”。数码管的外型结构如图所示。数码管又分为共阴极和共阳极两种结构,分别如图所示。

图1‑94 共阴极和共阳极数码管

(a)共阴极(b)共阳极

共阳极数码管的8个发光二极管的阳极(二极管正端)连接在一起,通常接高电平(一般接电源),其它管脚接段驱动电路输出端。当某段驱动电路的输出端为低电平时,该端所连接的字段导通并点亮,根据发光字段的不同组合可显示出各种数字或字符。此时,要求段驱动电路能吸收额定的段导通电流,还需根据外接电源及额定段导通电流来确定相应的限流电阻。

共阴极数码管的8个发光二极管的阴极(二极管负端)连接在一起,通常接低电平(一般接地),其它管脚接段驱动电路输出端。当某段驱动电路的输出端为高电平时,该端所连接的字段导通并点亮,根据发光字段的不同组合可显示出各种数字或字符。此时,要求段驱动电路能提供额定的段导通电流,还需根据外接电源及额定段导通电流来确定相应的限流电阻。

要使数码管显示出相应的数字或字符,必须使段数据口输出相应的字型编码。对照下图,字型码各位定义如下:

表1‑37 数码管字型码定义

数据线

D7

D6

D5

D4

D3

D2

D1

D0

I/O口线

P0.7

P0.6

P0.5

P0.4

P0.3

P0.2

P0.1

P0.0

LED段

dp

g

f

e

d

c

b

a

数据线D0与a字段对应,D1字段与b字段对应……,依此类推。如使用共阳极数码管,则数据为0表示对应字段亮,数据为1表示对应字段暗;如使用共阴极数码管,则相反。

表1‑38 数码管字型编码表

数码管工作方式有两种:静态显示方式和动态显示方式。 

1、静态显示接口

从下图可以看出,静态显示方式的特点是各位数码管相互独立,公共端恒定接地(共阴极)或接正电源(共阳极)。每个数码管的8个字段分别与一个8位I/O口地址相连,I/O口只要有段码输出,相应字符即显示出来并保持不变,直到I/O口输出新的段码。

图1‑95 两位的LED数码管静态显示示意图

采用静态显示方式时,用较小的电流即可获得较高的亮度,且占用CPU时间少,编程简单,显示便于监测和控制,但其占用的口线多,硬件电路复杂,成本高,只适用于显示位数较少的场合。

2、动态显示

当需要显示的位数较多时,为了节省硬件接口,往往采用动态显示的方式。

动态显示的特点是将所有位数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。动态显示的亮度比静态显示要差一些,所以在选择限流电阻时应略小于静态显示电路中的。

动态显示是指一位一位地轮流点亮各位数码管,这种逐位点亮显示器的方式称为位扫描。通常,各位数码管的段选线相应并联在一起,由一个8位的I/O口控制;各位的位选线(公共阴极或公共阳极)由另外的I/O口线控制。以动态方式显示时,各数码管分时轮流选通。要使其稳定显示,必须采用扫描方式,即在某一时刻只选通一位数码管,并送出相应的段码,在另一时刻选通另一位数码管,并送出相应的段码。依此规律循环,即可使各位数码管显示将要显示的字符,虽然这些字符是在不同的时刻分别显示的,但由于人眼存在视觉暂留效应,因此只要每位显示间隔足够短就可以给人以同时显示的感觉。

图1‑96 四位的LED数码管动态显示示意图

1.1.2FPGA和数码管驱动

使用动态扫描还是静态扫描其实取决于硬件设计,并不取决于驱动程序,一旦硬件确定下来那么就需要驱动去适配硬件,本次设计使用的数码管为共阳极数码管,动态扫描加载,电路如下:

图1‑97 动态数码管设计电路

表1‑39 数码管设计资源

设计的资源如表1‑39所示,其中扫描频率是1s扫描的频率,即1/6ms≈166.67Hz。

数码管主要考虑输入调用它的输入。即如何充分减少资源以实现数码管动态显示。

图1‑98 数码管电路设计建模图

表1‑40 建模图中模块间信号定义

模块间信号

管脚

传输方向

作用/说明

Number_Data

数码管控制模块传给数码管加码模块

显示的数字0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F

时序

见上述分析

表1‑41 数码管电路设计建模图顶层模块信号定义

顶层模块

输入管脚

作用/说明

输出管脚

作用/说明

Number_Sig

每位数码管要显示的内容

SMG_data

数码管段选信号

Scan_Sig

数码管位选信号

时序

见上述时序分析

整个系统设计结构如图1‑98所示,上图的组合模块-数码管接口 smg_interface.v 中,输入信号 Number_Sig 占了 24 位宽,然而 Number_Sig 的位分配如下表:

表1‑42 Number_Sig 的位分配表

为什么每一位数字数码管,都用 4 位位宽来代表呢?

每数码管可以支持显示 0~F,正是因为如此,如果我们用每 4 位位宽来代表某一个数码管显示的信号,那么可以避免“使用除法或者求余运算符执行十进制的取位操作”。一来方便设计,而来减少资源。

举个例子:24'h123456, 亦即 0001 0010 0011 0100 0101 0110。

smg_encode_module.v 在这里的功能就是就是将数字 0~F 加码为数码管码 。然而,比较 特 别 的 是 , smg_control_module.v 和 smg_scan_module.v 有 并 行 操 作 的 性 质 。

smg_interface.v 的大致操作如下:

假设我往 Number_Sig 输入 24'h123456在 T1, smg_control_module.v 会将 Number_Sig[23:20] 送往至 smg_encode_module.v 加码并且送往数码管。在同一时间 smg_scan_module.v 会扫描第一位数码管(使能)。在 T2, smg_control_module.v 会将 Number_Sig[19:16] 送往至 smg_encode_module.v 加码并且送往数码管,在同一时间 smg_scan_module.v 会扫描第二位数码管(使能)。在 T3, smg_control_module.v 会将 Number_Sig[15:12] 送往至 smg_encode_module.v 加码并且送往数码管,在同一时间 smg_scan_module.v 会扫描第三位数码管(使能)。在 T4, smg_control_module.v 会将 Number_Sig[11:8] 送往至 smg_encode_module.v 加码并且送往数码管,在同一时间 smg_scan_module.v 会扫描第四位数码管(使能)。在 T5, smg_control_module.v 会将 Number_Sig[7:4] 送往至 smg_encode_module.v 加码并且送往数码管,在同一时间 smg_scan_module.v 会扫描第五位数码管(使能)。在 T6, smg_control_module.v 会将 Number_Sig[3:0] 送往至 smg_encode_module.v 加码并且送往数码管,在同一时间 smg_scan_module.v 会扫描第六位数码管(使能)。

在 T1 的时候第一位数码管会显示 1。在 T2 的时候第二位数码管会显示 2,其他的依此类推。最后在 T6 的时候,第六位数码管会显示 6。就这样一次性的扫描(六位数码管全扫描)就完成。啊,别忘了!每位数码管扫描停留的时间(使能的时间)大约是 1ms。所以一次性扫描所需要的时间大约是 6ms,亦即在每一秒内,一组 6 位的数码管会扫描 166 次左右。-----改成时序图。

图1‑99 加码扫描过程理想时序图·

图1‑100 数码管控制模块结构图

代码1‑13 数码管控制模块(smg_control_module)代码

1.//****************************************************************************//

2.//# @Author: 碎碎思

3.//# @Date:   2019-05-18 23:59:39

4.//# @Last Modified by:   zlk

5.//# @WeChat Official Account: OpenFPGA

6.//# @Last Modified time: 2019-05-19 01:27:11

7.//# Description:

8.//# @Modification History: 2019-05-19 01:27:11

9.//# Date                By             Version             Change Description:

10.//# ========================================================================= #

11.//# 2019-05-19 01:27:11

12.//# ========================================================================= #

13.//# |                                                                       | #

14.//# |                                OpenFPGA                               | #

15.//****************************************************************************//

16.module smg_control_module

17.(

18.    input CLOCK,

19.     input RST_n,

20.     input [23:0]Number_Sig,

21.     output [3:0]Number_Data

22.);

23.

24.    /******************************************/

25.

26.     parameter T1MS = 16'd49999;   //定义 1ms 的常量

27.

28.     /******************************************/

29.     //1ms 的定时器

30.      reg [15:0]C1;

31.

32.     always @ ( posedge CLOCK or negedge RST_n )

33.         if( !RST_n )

34.              C1 <= 16'd0;

35.          else if( C1 == T1MS )

36.              C1 <= 16'd0;

37.          else

38.              C1 <= C1 + 1'b1;

39.

40.     /******************************************/

41.

42.     reg [3:0]i;

43.     reg [3:0]rNumber;

44.

45.     always @ ( posedge CLOCK or negedge RST_n )

46.         if( !RST_n )

47.              begin

48.                  i <= 4'd0;

49.                     rNumber <= 4'd0;

50.                end

51.          else

52.              case( i )

53.

54.                    0:

55.                     if( C1 == T1MS ) i <= i + 1'b1;

56.                     else rNumber <= Number_Sig[23:20];

57.

58.                     1:

59.                     if( C1 == T1MS ) i <= i + 1'b1;

60.                     else rNumber <= Number_Sig[19:16];

61.

62.                     2:

63.                     if( C1 == T1MS ) i <= i + 1'b1;

64.                     else rNumber <= Number_Sig[15:12];

65.

66.                     3:

67.                     if( C1 == T1MS ) i <= i + 1'b1;

68.                     else rNumber <= Number_Sig[11:8];

69.

70.                     4:

71.                     if( C1 == T1MS ) i <= i + 1'b1;

72.                     else rNumber <= Number_Sig[7:4];

73.

74.                     5:

75.                     if( C1 == T1MS ) i <= 4'd0;

76.                     else rNumber <= Number_Sig[3:0];

77.

78.                endcase

79.

80.    /******************************************/

81.

82.     assign Number_Data = rNumber;

83.

84.     /******************************************/

85.

86.endmodule

rNumber 是每一位数字的暂存器(43 行)用来驱动 Number_Data(82 行)。(52~78 行)每隔 1ms 该控制模块就会将不同位的数字往 Number_Data 输出

图1‑101 数码管加码模块结构图

代码1‑14 数码管加码模块(smg_encode_module)代码

1.//****************************************************************************//

2.//# @Author: 碎碎思

3.//# @Date:   2019-05-18 23:59:39

4.//# @Last Modified by:   zlk

5.//# @WeChat Official Account: OpenFPGA

6.//# @Last Modified time: 2019-07-21 03:32:41

7.//# Description:

8.//# @Modification History: 2019-07-20 20:54:39

9.//# Date                By             Version             Change Description:

10.//# ========================================================================= #

11.//# 2019-07-20 20:54:39

12.//# ========================================================================= #

13.//# |                                                                       | #

14.//# |                                OpenFPGA                               | #

15.//****************************************************************************//

16.module smg_encode_module

17.(

18.     input CLOCK,

19.     input RST_n,

20.     input [3:0]Number_Data,

21.     output [7:0]SMG_Data

22.);

23.

24.     /***************************************/

25.

26.     parameter _0 = 8'b1100_0000, _1 = 8'b1111_1001, _2 = 8'b1010_0100,

27.               _3 = 8'b1011_0000, _4 = 8'b1001_1001, _5 = 8'b1001_0010,

28.               _6 = 8'b1000_0010, _7 = 8'b1111_1000, _8 = 8'b1000_0000,

29.               _9 = 8'b1001_0000, _A = 8'b1000_1000, _B = 8'b1000_0011,

30.               _C = 8'b1100_0110, _D = 8'b1010_0001, _E = 8'b1000_0110,

31.               _F = 8'b1000_1110;

32.

33.     /***************************************/

34.

35.     reg [7:0]rSMG;

36.

37.     always @ ( posedge CLOCK or negedge RST_n )

38.         if( !RST_n )

39.              begin

40.                    rSMG <= 8'b1111_1111;

41.                end

42.        else

43.               case( Number_Data )

44.

45.                    4'd0  :  rSMG <= _0;

46.                    4'd1  :  rSMG <= _1;

47.                    4'd2  :  rSMG <= _2;

48.                    4'd3  :  rSMG <= _3;

49.                    4'd4  :  rSMG <= _4;

50.                    4'd5  :  rSMG <= _5;

51.                    4'd6  :  rSMG <= _6;

52.                    4'd7  :  rSMG <= _7;

53.                    4'd8  :  rSMG <= _8;

54.                    4'd9  :  rSMG <= _9;

55.                    4'd10 :  rSMG <= _A;

56.                    4'd11 :  rSMG <= _B;

57.                    4'd12 :  rSMG <= _C;

58.                    4'd13 :  rSMG <= _D;

59.                    4'd14 :  rSMG <= _E;

60.                    4'd15 :  rSMG <= _F;

61.

62.                 endcase

63.

64.     /***************************************/

65.

66.     assign SMG_Data = rSMG;

67.

68.     /***************************************/

69.

70.endmodule

第 26~31 行声明了 SMG 码的常量。第 43~62 行是针对“十位数”的加码操作,但是是针对“个位数”(58 行)。第66 行是输出。

图1‑102 数码管扫描模块结构图

代码1‑15 数码管扫描模块(smg_scan_module)代码

1.//****************************************************************************//

2.//# @Author: 碎碎思

3.//# @Date:   2019-05-18 23:59:39

4.//# @Last Modified by:   zlk

5.//# @WeChat Official Account: OpenFPGA

6.//# @Last Modified time: 2019-07-21 03:37:17

7.//# Description:

8.//# @Modification History: 2019-05-19 01:41:56

9.//# Date                By             Version             Change Description:

10.//# ========================================================================= #

11.//# 2019-05-19 01:41:56

12.//# ========================================================================= #

13.//# |                                                                       | #

14.//# |                                OpenFPGA                               | #

15.//****************************************************************************//

16.module smg_scan_module

17.(

18.    input CLOCK,

19.     input RST_n,

20.     output [5:0]Scan_Sig

21.);

22.

23.     /*****************************/

24.

25.     parameter T1MS = 16'd49999;

26.

27.     /*****************************/

28.

29.     reg [15:0]C1;

30.

31.     always @ ( posedge CLOCK or negedge RST_n )

32.         if( !RST_n )

33.              C1 <= 16'd0;

34.          else if( C1 == T1MS )

35.              C1 <= 16'd0;

36.          else

37.              C1 <= C1 + 1'b1;

38.

39.     /*******************************/

40.

41.     reg [3:0]i;

42.     reg [5:0]rScan;

43.

44.     always @ ( posedge CLOCK or negedge RST_n )

45.         if( !RST_n )

46.              begin

47.                  i <= 4'd0;

48.                  rScan <= 6'b100_000;

49.               end

50.          else

51.              case( i )

52.

53.                     0:

54.                  if( C1 == T1MS ) i <= i + 1'b1;

55.                     else rScan <= 6'b011_111;

56.

57.                     1:

58.                     if( C1 == T1MS ) i <= i + 1'b1;

59.                     else rScan <= 6'b101_111;

60.

61.                     2:

62.                     if( C1 == T1MS ) i <= i + 1'b1;

63.                     else rScan <= 6'b110_111;

64.

65.                     3:

66.                     if( C1 == T1MS ) i <= i + 1'b1;

67.                     else rScan <= 6'b111_011;

68.

69.                     4:

70.                     if( C1 == T1MS ) i <= i + 1'b1;

71.                     else rScan <= 6'b111_101;

72.

73.                     5:

74.                     if( C1 == T1MS ) i <= 4'd0;

75.                     else rScan <= 6'b111_110;

76.

77.

78.                endcase

79.

80.     /******************************/

81.

82.     assign Scan_Sig = rScan;

83.

84.     /******************************/

85.

86.

87.endmodule

第 25 行是 1ms 的常量声明,在 31~37 行是 1ms 的定时器。该模块和smg_control_module.v 一样,都是每隔 1ms 都有一个动作。smg_scan_module.v 每隔 1ms就会使能不同的数码管(56~78 行)。然而数码管实际的扫描顺序是自左向右。在位操作的角度上,逻辑 0 从最高位到最低位交替移位。

接下来将上诉模块进行封装,详细的连接图见图1‑98,综合后的RTL电路图如下:

图1‑103 数码管电路综合后的RTL图

结果和图1‑98一样。

下面进行模块的调用和验证,验证结构图如下:

图1‑104 数码管电路验证结构图

验证中会建立一个名为 demo_control_module.v输出 24'h000000 ~ 24'h999999 用来驱动 smg_interface.v 的输入。具体的内容还是直接看代码:

代码1‑16 数码管电路验证代码

1.//****************************************************************************//

2.//# @Author: 碎碎思

3.//# @Date:   2019-05-18 23:59:39

4.//# @Last Modified by:   zlk

5.//# @WeChat Official Account: OpenFPGA

6.//# @Last Modified time: 2019-07-20 21:04:39

7.//# Description:

8.//# @Modification History: 2019-07-20 21:04:39

9.//# Date                By             Version             Change Description:

10.//# ========================================================================= #

11.//# 2019-07-20 21:04:39

12.//# ========================================================================= #

13.//# |                                                                       | #

14.//# |                                OpenFPGA                               | #

15.//****************************************************************************//

16.module demo_control_module

17.(

18.    input CLOCK,

19.     input RST_n,

20.     output [23:0]Number_Sig

21.);

22.

23.     /******************************/

24.

25.     parameter T100MS = 23'd4_999_999;

26.

27.     /******************************/

28.

29.     reg [22:0]C1;

30.

31.     always @ ( posedge CLOCK or negedge RST_n )

32.         if( !RST_n )

33.              C1 <= 23'd0;

34.          else if( C1 == T100MS )

35.              C1 <= 23'd0;

36.          else

37.              C1 <= C1 + 1'b1;

38.

39.      /*******************************************************/

40.

41.     reg [3:0]i;

42.     reg [23:0]rNum;

43.     reg [23:0]rNumber;

44.

45.     always @ ( posedge CLOCK or negedge RST_n )

46.          if( !RST_n )

47.                begin

48.                    i <= 4'd0;

49.                      rNum <= 24'd0;

50.                      rNumber <= 24'd0;

51.                 end

52.           else

53.                case( i )

54.

55.                     0:

56.                      if( C1 == T100MS ) begin rNum[3:0] <= rNum[3:0] + 1'b1; i <= i + 1'b1; end

57.

58.                      1:

59.                      if( rNum[3:0] > 4'd14 ) begin rNum[7:4] <= rNum[7:4] + 1'b1; rNum[3:0] <= 4'd0; i <= i + 1'b1; end

60.                      else i <= i + 1'b1;

61.

62.                      2:

63.                      if( rNum[7:4] > 4'd14 ) begin rNum[11:8] <= rNum[11:8] + 1'b1; rNum[7:4] <= 4'd0; i <= i + 1'b1; end

64.                      else i <= i + 1'b1;

65.

66.                      3:

67.                      if( rNum[11:8] > 4'd14 ) begin rNum[15:12] <= rNum[15:12] + 1'b1; rNum[11:8] <= 4'd0; i <= i + 1'b1; end

68.                      else i <= i + 1'b1;

69.

70.                      4:

71.                      if( rNum[15:12] > 4'd14 ) begin rNum[19:16] <= rNum[19:16] + 1'b1; rNum[15:12] <= 4'd0; i <= i + 1'b1; end

72.                    else i <= i + 1'b1;

73.

74.                      5:

75.                      if( rNum[15:12] > 4'd14 ) begin rNum[19:16] <= rNum[19:16] + 1'b1; rNum[15:12] <= 4'd0; i <= i + 1'b1; end

76.                      else i <= i + 1'b1;

77.

78.                      6:

79.                      if( rNum[19:16] > 4'd14 ) begin rNum[23:20] <= rNum[23:20] + 1'b1; rNum[19:16] <= 4'd0; end

80.                      else i <= i + 1'b1;

81.

82.                      7:

83.                      if( rNum[23:20] > 4'd14 ) begin rNum <= 24'd0; i <= i + 1'b1; end

84.                      else i <= i + 1'b1;

85.

86.                      8:

87.                      begin rNumber <= rNum; i <= 4'd0; end

88.

89.                 endcase

90.

91.    /*******************************************************/

92.

93.     assign Number_Sig = rNumber;

94.

95.     /*******************************************************/

96.

97.endmodule

第 20~37 行之间,包含了 100ms 定时的常量(25 行)和 100ms 的定时器(31~37 行)。

在 41~89 就是该模块的核心部分。寄存器 rNum 操作空间(42 行),然而 rNumber 是用于驱动 Number_Sig(93 行)。每隔 100ms 的定时都会是 rNum 递增(56 行), 58~84行之间就会执行“4 位宽”数字之间的“进位操作”。

我们假设一个情况,当 rNum 的值是 24'h000009,然后在下一个 100ms 的定时钟, rNum的值就会 +1 操作。在59行, if条件就会成立, rNum[3:0]就会被赋值位零,然后rNum[7:4]就会执行 +1 操作, rNum 的值成为 24'h000010。接下来的几个步骤也会执行类似的操作。

在 72~84 行表示了当 rNum 的值超过 24'h999999 的时候,就会恢复为 24'h000000。

在这里我们有一个问题?为什么不直接使用 rNum 驱动 Number_Sig 而是选择使用rNumber寄存器来驱动 Number_Sig。如果我们把 rNum 当着 Number_Sig 的驱动对象,在 i 步骤 16~22 之间,由于“进位操作”的关系,会使得 Number_Sig 的输出产生许多毛刺,因此才使用 rNumber 驱动 Number_Sig。当 rNum 完成“进位操作”以后,再赋值与 rNumber,由 rNumber 驱动 Number_Sig ( 87 行)。

然后按照图1‑104进行顶层模块的设计,代码如下:

代码1‑17 数码管电路验证代码的顶层代码

1.//****************************************************************************//

2.//# @Author: 碎碎思

3.//# @Date:   2019-05-18 23:59:39

4.//# @Last Modified by:   zlk

5.//# @WeChat Official Account: OpenFPGA

6.//# @Last Modified time: 2019-07-21 03:49:46

7.//# Description:

8.//# @Modification History: 2019-05-19 01:41:52

9.//# Date                By             Version             Change Description:

10.//# ========================================================================= #

11.//# 2019-05-19 01:41:52

12.//# ========================================================================= #

13.//# |                                                                       | #

14.//# |                                OpenFPGA                               | #

15.//****************************************************************************//

16.module smg_interface_demo

17.(

18.    input CLOCK,

19.     input RST_n,

20.     output [7:0]SMG_Data,

21.     output [5:0]Scan_Sig

22.);

23.

24.    /******************************/

25.

26.    wire [23:0]Number_Sig;

27.

28.    demo_control_module U1

29.     (

30.         .CLOCK( CLOCK ),

31.          .RST_n( RST_n ),

32.          .Number_Sig( Number_Sig ) // output - to U2

33.     );

34.

35.     /******************************/

36.

37.     smg_interface U2

38.     (

39.         .CLOCK( CLOCK ),

40.          .RST_n( RST_n ),

41.          .Number_Sig( Number_Sig ), // input - from U1

42.          .SMG_Data( SMG_Data ),     // output - to top

43.          .Scan_Sig( Scan_Sig )      // output - to top

44.     );

45.

46.    /******************************/

47.

48.endmodule

综合后的RTL如下:

图1‑105 数码管电路验证代码的顶层代码综合后RTL图

基本和图1‑104一样,将代码下载到目标板上就可以看到数码管已经变成了一个“秒表”了。

(0)

相关推荐