在线咨询 切换到宽版
eetop公众号 创芯大讲堂 创芯人才网

 找回密码
 注册

手机号码,快捷登录

手机号码,快捷登录

搜帖子
查看: 3592|回复: 3

[原创] 手把手教你学FPGA设计-秒表功能

[复制链接]
发表于 2018-9-30 09:55:05 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

×
秒表功能


一、项目背景

同上一个项目。

二、设计目标

开发板或者模块是有 8 位数码管,本次设计需要使用1个数码管,即数码管0,实现类似于秒表的功能,具体要求如下:

复位后,数码管0显示数字0并持续1秒;然后显示数字1并持续2秒;然后显示数字2并持续3秒;以此类推,最后是显示数字9并持续10秒。然后再次循环

上板效果图如下图所示。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpgfile:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpgfile:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpgfile:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image008.jpg

上板的演示效果,请登陆网址查看:www.mdy-edu.com/xxxx

三、模块设计

我们要实现的功能,概括起来就是控制8个数码管,其中数码管0亮,其他数码管不亮。并让数码管0显示不同的数字。

要控制8个数码管,就需要控制位选信号,即FPGA要输出一个8位的位选信号,设为seg_sel,其中seg_sel[0]对应数码管0seg_sel[1]对应数码管1,以此类推,seg_sel[7]对应数码管7

要显示不同的数字,就需要控制段选信号,不需要用到DP,一共有7根线,即FPGA要输出一个7位的段选信号,设为seg_mentseg_ment[6]~segm_ment[0]分别对应数码管的abcdefg(注意对应顺序)。

我们还需要时钟信号和复位信号来进行工程控制。

综上所述,我们这个工程需要4个信号,时钟clk,复位rst_n,输出的位选信号seg_sel和输出的段选信号seg_ment

  

信号线

  
  

信号线

  
  

FPGA管脚

  
  

内部信号

  
  

SEG_E

  
  

SEG0

  
  

Y6

  
  

seg_ment[2]

  
  

SEG_DP

  
  

SEG1

  
  

W6

  
  

未用到

  
  

SEG_G

  
  

SEG2

  
  

Y7

  
  

seg_ment[0]

  
  

SEG_F

  
  

SEG3

  
  

W7

  
  

seg_ment[1]

  
  

SEG_D

  
  

SEG4

  
  

P3

  
  

seg_ment[3]

  
  

SEG_C

  
  

SEG5

  
  

P4

  
  

seg_ment[4]

  
  

SEG_B

  
  

SEG6

  
  

R5

  
  

seg_ment[5]

  
  

SEG_A

  
  

SEG7

  
  

T3

  
  

seg_ment[6]

  
  

DIG1

  
  

DIG_EN1

  
  

T4

  
  

seg_sel[0]

  
  

DIG2

  
  

DIG_EN2

  
  

V4

  
  

seg_sel[1]

  
  

DIG3

  
  

DIG_EN3

  
  

V3

  
  

seg_sel[2]

  
  

DIG4

  
  

DIG_EN4

  
  

Y3

  
  

seg_sel[3]

  
  

DIG5

  
  

DIG_EN5

  
  

Y8

  
  

seg_sel[4]

  
  

DIG6

  
  

DIG_EN6

  
  

W8

  
  

seg_sel[5]

  
  

DIG7

  
  

DIG_EN7

  
  

W10

  
  

seg_sel[6]

  
  

DIG8

  
  

DIG_EN8

  
  

Y10

  
  

seg_sel[7]

  



我们先分析要实现的功能,数码管0显示数字0,翻译成信号就是seg_sel的值为8’b1111_1110seg_ment的值为7’b000_0001。然后数码管0显示数字1,也就是说seg_sel的值为8’b1111_1110seg_ment的值为7’b100_1111。以此类推,数码管0显示数字9,就是seg_sel的值为8’b1111_1110seg_ment的值为7’b000_0100

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image009.png

seg_sel一直为8’hfe,不变化。seg_ment隔一段时间后会变化,而这个时间在不同时期还不相同。把时间信息补充上,得到下面的波形示意图。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image011.png         上图就是seg_selseg_seg信号的变化波形图。在显示第1个时,seg_sel=8’hfeseg_ment=7’h01并持续1秒;在第2个时,seg_sel=8’hfeseg_ment=7’h4f并持续2秒;以此类推,第8个时,seg_sel=8’hfeseg_ment=7’h04并持续10秒。然后又再次重复。

由波形图可知,我们需要1个计数器用来计算时间,如2秒、3秒等。另外,我们还需要一个计数器,用来计算在第几个阶段中。所以总共需要2个计数器。

本工程的工作时钟是50MHz,即周期为20ns,计数器计数到2_000_000_000/20=100_000_000个,我们就能知道2秒时间到了。以类类推,在第2次时,数到150_000_000个,就知道了3秒时间到。第9次时,数到500_000_000个,就表示10秒时间到。另外,由于该计数器是不停地计数,永远不停止的,可以认为加1条件一直有效,可写成:assignadd_cnt0==1。综上所述,结合变量法,该计数器的代码如下

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image013.jpg

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  
  

always @(posedge clk or negedge  rst_n)begin

  

     if(!rst_n)begin

  

         cnt0 <= 0;

  

     end

  

     else if(add_cnt0)begin

  

         if(end_cnt0)

  

            cnt0 <= 0;

  

         else

  

            cnt0 <= cnt0 + 1;

  

     end

  

end

  


  

assign add_cnt0 = 1 ;

  

assign end_cnt0 = add_cnt0 &&  cnt0== x-1 ;

  


第二个计数器用于表示第几个,很自然可以看到,每个阶段完成后,该计数器加1,因此加1条件可为end_cnt0。该计数器一共要数10次。所以代码为:

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image015.jpg

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  
  

always @(posedge clk or negedge  rst_n)begin

  

     if(!rst_n)begin

  

         cnt1 <= 0;

  

     end

  

     else if(add_cnt1)begin

  

         if(end_cnt1)

  

            cnt1 <= 0;

  

         else

  

            cnt1 <= cnt1 + 1;

  

     end

  

end

  


  

assign add_cnt1 = end_cnt0;

  

assign end_cnt1 = add_cnt1 &&  cnt1== 10-1 ;

  


接下来设计seg_sel。该信号一直为8’hfe,所以代码直接写成如下:

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image016.png

  

1

  
  

assign seg_sel = 8'hfe ;

  


我们来思考输出信号seg_ment的变化。概括起来,在第1次的时候输出值为7’h01;在第2次的时候输出值为7’h4f;以此类推,在第8次的时候输出值为7’h0f。我们用信号cnt1来代替第几次,也就是:当cnt1==0的时候,输出值为7’h01;在cnt1==1的时候输出值为7’h4f;以此类推,在cnt1==9的时候输出值为7’h04。再进一步翻译成代码,就变成如下:

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image017.png

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  

15

  

16

  

17

  

18

  

19

  

20

  

21

  

22

  

23

  

24

  

25

  

26

  

27

  

28

  

29

  

30

  

31

  

32

  

33

  

34

  

35

  
  

always   @(posedge clk or negedge rst_n)begin

  

     if(rst_n==1'b0)begin

  

         seg_ment <= 7'h01;

  

     end

  

     else if(cnt1==0)begin

  

         seg_ment <= 7'h01;

  

     end

  

     else if(cnt1==1)begin

  

         seg_ment <= 7'h4f;

  

     end

  

     else if(cnt1==2)begin

  

         seg_ment <= 7'h12;

  

     end

  

     else if(cnt1==3)begin

  

         seg_ment <= 7'h06;

  

     end

  

     else if(cnt1==4)begin

  

         seg_ment <= 7'h4c;

  

     end

  

     else if(cnt1==5)begin

  

         seg_ment <= 7'h24;

  

     end

  

     else if(cnt1==6)begin

  

         seg_ment <= 7'h20;

  

     end

  

     else if(cnt1==7)begin

  

         seg_ment <= 7'h0f;

  

     end

  

     else if(cnt1==8)begin

  

         seg_ment <= 7'h00;

  

     end

  

     else if(cnt1==9)begin

  

         seg_ment <= 7'h04;

  

     end

  

end

  

然后,用组合逻辑把x的值确定下来。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image018.png

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image019.png

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  

15

  

16

  

17

  

18

  

19

  

20

  

21

  

22

  

23

  

24

  

25

  

26

  

27

  

28

  

29

  

30

  

31

  

32

  
  

always   @(*)begin

  

     if(cnt1==0)begin

  

         x = 50_000_000;

  

     end

  

     else if(cnt1==1)begin

  

         x = 100_000_000;

  

     end

  

     else if(cnt1==2)begin

  

         x = 150_000_000;

  

     end

  

     else if(cnt1==3)begin

  

         x = 200_000_000;

  

     end

  

     else if(cnt1==4)begin

  

         x = 250_000_000;

  

     end

  

     else if(cnt1==5)begin

  

         x = 300_000_000;

  

     end

  

     else if(cnt1==6)begin

  

         x = 350_000_000;

  

     end

  

     else if(cnt1==7)begin

  

         x = 400_000_000;

  

     end

  

     else if(cnt1==8)begin

  

         x = 450_000_000;

  

     end

  

     else if(cnt1==9)begin

  

         x = 500_000_000;

  

     end

  

end

  


此次,主体程序已经完成。接下来是将module补充完整。

module的名称定义为my_time。并且我们已经知道该模块有4个信号:clkrst_nseg_selseg_ment,代码如下:

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image020.png

  

1

  

2

  

3

  

4

  

5

  

6

  
  

module my_time(

  

     clk    ,

  

     rst_n  ,

  

     seg_sel,

  

     seg_ment

  

     );

  



其中clkrst_n1位的输入信号,seg_sel8位的输出信号,seg_ment7位的输出信号,根据此,补充输入输出端口定义。代码如下:

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image021.png

  

1

  

2

  

3

  

4

  
  

input           clk       ;

  

input           rst_n     ;

  

output    [7:0]  seg_sel   ;

  

output    [6:0]  seg_ment  ;

  



接下来定义信号类型。

cnt0是用always产生的信号,因此类型为regcnt0计数的最大值为500_000_000,需要用29根线表示,即位宽是29位。add_cnt0end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者11个线表示即可。因此代码如下:

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image022.png

  

1

  

2

  

3

  
  

reg     [28:0]            cnt0        ;

  

wire                     add_cnt0    ;

  

wire                     end_cnt0    ;

  


cnt1是用always产生的信号,因此类型为regcnt1计数的最大值为9,需要用4根线表示,即位宽是4位。add_cnt1end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者11根线表示即可。因此代码如下:

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image023.png

  

1

  

2

  

3

  
  

reg     [3:0]            cnt1        ;

  

wire                     add_cnt1    ;

  

wire                     end_cnt1    ;

  


seg_sel是用assign方式设计的,因此类型为wire,其一共有8根线,即位宽为8。因此代码如下:

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image024.png

  

1

  
  

wire     [7:0]  seg_sel   ;

  


seg_ ment是用always方式设计的,因此类型为reg,其一共有7根线,即位宽为7。因此代码如下:

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image025.png

  

1

  
  

reg    [6:0]  seg_ment   ;

  

x是用always方式设计的,因此类型为reg,他的位数和cnt0是一致的。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image026.png

  

1

  
  

reg    [28:0]  x   ;

  


至此,整个代码的设计工作已经完成。整体代码如下:


  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  

15

  

16

  

17

  

18

  

19

  

20

  

21

  

22

  

23

  

24

  

25

  

26

  

27

  

28

  

29

  

30

  

31

  

32

  

33

  

34

  

35

  

36

  

37

  

38

  

39

  

40

  

41

  

42

  

43

  

44

  

45

  

46

  

47

  

48

  

49

  

50

  

51

  

52

  

53

  

54

  

55

  

56

  

57

  

58

  

59

  

60

  

61

  

62

  

63

  

64

  

65

  

66

  

67

  

68

  

69

  

70

  

71

  

72

  

73

  

74

  

75

  

76

  

77

  

78

  

79

  

80

  

81

  

82

  

83

  

84

  

85

  

86

  

87

  

88

  

89

  

90

  

91

  

92

  

93

  


  
  


  

module miaobiao(

  

     clk    ,

  

     rst_n  ,

  

     seg_sel,

  

     seg_ment

  

     );

  


  


  


  

     input               clk    ;

  

     input               rst_n  ;

  


  


  

     output    [7:0]  seg_sel    ;

  

     output   [6:0]  seg_ment    ;

  


  


  

     reg           [28:0]      cnt0;

  

     reg           [3:0]       cnt1;

  

     wire      add_cnt0;

  

     wire      end_cnt0;

  

     wire      add_cnt1;

  

     wire      end_cnt1;

  

     wire    [7:0]  seg_sel    ;

  

     reg   [6:0]  seg_ment    ;

  


  

     always @(posedge clk or negedge rst_n)begin

  

         if(!rst_n)begin

  

            cnt0 <= 0;

  

         end

  

         else if(add_cnt0)begin

  

            if(end_cnt0)

  

                cnt0 <= 0;

  

            else

  

                cnt0 <= cnt0 + 1;

  

         end

  

     end

  


  

     assign add_cnt0 = 1;

  

     assign end_cnt0 = add_cnt0 && cnt0== 50000000-1;

  


  

     always @(posedge clk or negedge rst_n)begin

  

         if(!rst_n)begin

  

            cnt1 <= 0;

  

         end

  

         else if(add_cnt1)begin

  

            if(end_cnt1)

  

                cnt1 <= 0;

  

            else

  

                cnt1 <= cnt1 + 1;

  

         end

  

     end

  


  

     assign add_cnt1 = end_cnt0;

  

     assign end_cnt1 = add_cnt1 && cnt1==10-1 ;

  

     assign seg_sel  = 8'hfe;

  

   always  @(posedge clk or negedge  rst_n)begin

  

     if(rst_n==1'b0)begin

  

         seg_ment <= 7'h01;

  

     end

  

     else if(cnt1==0)begin

  

         seg_ment <= 7'h01;

  

     end

  

     else if(cnt1==1)begin

  

         seg_ment <= 7'h4f;

  

     end

  

     else if(cnt1==2)begin

  

         seg_ment <= 7'h12;

  

     end

  

     else if(cnt1==3)begin

  

         seg_ment <= 7'h06;

  

     end

  

     else if(cnt1==4)begin

  

         seg_ment <= 7'h4c;

  

     end

  

     else if(cnt1==5)begin

  

         seg_ment <= 7'h24;

  

     end

  

     else if(cnt1==6)begin

  

         seg_ment <= 7'h20;

  

     end

  

     else if(cnt1==7)begin

  

         seg_ment <= 7'h0f;

  

     end

  

     else if(cnt1==8)begin

  

         seg_ment <= 7'h00;

  

     end

  

     else if(cnt1==9)begin

  

         seg_ment <= 7'h04;

  

     end

  

end

  

always   @(*)begin

  

     if(cnt1==0)begin

  

         x = 50_000_000;

  

     end

  

     else if(cnt1==1)begin

  

         x = 100_000_000;

  

     end

  

     else if(cnt1==2)begin

  

         x = 150_000_000;

  

     end

  

     else if(cnt1==3)begin

  

         x = 200_000_000;

  

     end

  

     else if(cnt1==4)begin

  

         x = 250_000_000;

  

     end

  

     else if(cnt1==5)begin

  

         x = 300_000_000;

  

     end

  

     else if(cnt1==6)begin

  

         x = 350_000_000;

  

     end

  

     else if(cnt1==7)begin

  

         x = 400_000_000;

  

     end

  

     else if(cnt1==8)begin

  

         x = 450_000_000;

  

     end

  

     else if(cnt1==9)begin

  

         x = 500_000_000;

  

     end

  

end

  

  endmodule

  


下一步是新建工程和上板查看现象。

四、综合工程和上板新建工程

首先在d盘中创建名为“miaobiao”的工程文件夹,将写的代码命名为“miaobiao.v,顶层模块名为“miaobiao”。


file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image028.jpg

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image029.png

然后打开Quartus Ⅱ,点击File下拉列表中的New Project Wzard...新建工程选项。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image031.jpg

3.再出现的界面中直接点击Next

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image033.jpg



4.之后出现的是工程文件夹、工程名、顶层模块名设置界面。按照之前的命名进行填写,第一栏选择工程文件夹“miaobiao”,第二栏选择工程文件“miaobiao”,最后一栏选择顶层模块名“miaobiao,然后点击”Next”,在出现的界面选择emptyproject

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image035.jpgfile:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image037.jpg

5.之后是文件添加界面。添加之前写的“miaobian.v”文件,点击右侧的“Add”按钮,之后文件还会出现在大方框里,之后点击“Next”。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image039.jpg

器件型号选择界面。选择Cyclone E,在芯片型号选择处选择EP4CE15F23C8,然后点击“Next”。


file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image041.jpg

EDA工具界面。直接点击“Next”。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image043.jpg

8.之后出现的界面是我们前面设置的总结,确认无误后点击“Finish”。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image045.jpg



综合

1.新建工程步骤完成后,就会出现以下界面。在“Project Navigator”下选中要编译的文件,点击上方工具栏中“StartCompilation”编译按钮(蓝色三角形)。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image047.jpg

2.编译成功后会出现一下界面。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image049.jpg

配置管脚

1.点击箭头所指的管脚配置按钮“Pin Planner”,进入管脚配置界面。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image050.png

2.在下图location处双击填上对应的管脚号,管脚号可参照整体代码下方的管脚配置,回车即可。


file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image052.jpg

管脚配置:

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  

15

  

16

  

17

  

18

  

19

  

1

  
  


  

set_location_assignment PIN_G1 -to clk

  

set_location_assignment PIN_AB12 -to  rst_n

  

set_location_assignment PIN_T3 -to  seg_ment[6]

  

set_location_assignment PIN_R5 -to  seg_ment[5]

  

set_location_assignment PIN_P4 -to  seg_ment[4]

  

set_location_assignment PIN_P3 -to  seg_ment[3]

  

set_location_assignment PIN_Y6 -to  seg_ment[2]

  

set_location_assignment PIN_W7 -to  seg_ment[1]

  

set_location_assignment PIN_Y7 -to  seg_ment[0]

  

set_location_assignment PIN_Y10 -to  seg_sel[7]

  

set_location_assignment PIN_W10 -to  seg_sel[6]

  

set_location_assignment PIN_W8 -to  seg_sel[5]

  

set_location_assignment PIN_Y8 -to  seg_sel[4]

  

set_location_assignment PIN_Y3 -to  seg_sel[3]

  

set_location_assignment PIN_V3 -to  seg_sel[2]

  

set_location_assignment PIN_V4 -to  seg_sel[1]

  

set_location_assignment  PIN_T4 -to seg_sel[0]

  


布局布线

管脚配置完成后,在进行一次编译。

连接开发板

图中,下载器接入电脑USB接口,电源接入电源,然后摁下蓝色开关。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image054.jpg

上板

1.双击Tasks一栏中”Program Device”

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image056.jpg

2.会出现如下界面,点击add file添加.sof文件,点击“Start”,会在“Progress”出显示进度。

file:///C:/Users/pan/AppData/Local/Temp/msohtmlclip1/01/clip_image058.jpg

3.进度条中提示成功后,即可在开发板上观察到相应的现象。



手把手教你学FPGA设计-秒表功能.pdf

1.44 MB, 下载次数: 9 , 下载积分: 资产 -2 信元, 下载支出 2 信元

发表于 2018-10-1 15:09:08 | 显示全部楼层
I am learning!
回复 支持 反对

使用道具 举报

发表于 2021-7-30 13:59:34 | 显示全部楼层
赞一个先
回复 支持 反对

使用道具 举报

发表于 2021-12-9 11:06:59 | 显示全部楼层
点赞
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /1 下一条


手机版| 小黑屋| 关于我们| 联系我们| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2025-8-22 01:06 , Processed in 0.018787 second(s), 4 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表