Verilog HDL

本文档详细介绍了使用FPGA开发板进行的一系列实验,包括Quartus II工具的使用、ModelSim仿真、LED灯控制、数码管显示、有限状态机设计、按键消抖、蜂鸣器控制以及数码管动态显示。实验涵盖了数字电路设计的基础知识,如计数器、状态机、时序逻辑等,并提供了详细的代码实现和仿真结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,原理

(1) Quartus

Altera Quartus II 作为一种可编程逻辑的设计环境, 由于其强大的设计能力和直观易用的接口,受到数字系统设计者的欢迎, Altera Quartus II设计软件是业界唯一提供FPGA和固定功能HardCopy器件统一设计流程的设计工具。

(2)ModelSim

 ModelSim是业界最优秀的HDL语言仿真软件,它能提供友好的仿真环境,是业界唯一的单内核支持VHDL和Verilog混合仿真的仿真器。它采用直接优化的编译技术、Tcl/Tk技术、和单一内核仿真技术,编译仿真速度快,编译的代码与平台无关,便于保护IP核,个性化的图形界面和用户接口,为用户加快调错提供强有力的手段,是FPGA/ASIC设计的首选仿真软件。

(3) led灯

led灯就是发光二极管,在正向电压作用下电阻很小,处于导通状态相当于一只接通的开关;在反向电压作用下,由于电阻很大,科研堪称一只断开的开关。发光二极管在导通的时候发光,在没有导通的时候不发光。发光二极管有点像我们初中做的物理实验。单向开关串联一个灯泡,开关闭合的时候,电流流过灯泡,灯泡发光。开关断开的时候,灯泡也就不亮了

(4) 有限状态机

有限状态机(Finite State Machine, FSM),又称有限状态自动机,简称状态机,是指在有限个状态之间按照一定规律转换的时序电路。有限状态机主要包括:米利状态机和穆尔状态机。输出与输入变量直接相关的状态机称为米利状态机

输出与输入变量无直接关系的状态机称为穆尔状态机。

(5)蜂鸣器

蜂鸣器的发声原理由振动装置和谐振装置组成,而蜂鸣器又分为无源他激型与有源自激型。区别:有缘蜂鸣器内部有震荡源,无缘蜂鸣器内部没有震荡源。

按照工作原理可分为:压电式蜂鸣器和电磁式蜂鸣器。

按照音源可分为:有源蜂鸣器和无源蜂鸣器。

有源蜂鸣器:内部有振荡源,直接通以直流电即可发出声音。

无源蜂鸣器:内部无振荡源,需要通以方波、PWM信号才能发出声音。无源蜂鸣器需要输入一定频率的方波或者脉冲宽度调制(Pulse Width Modulation,PWM)信号,蜂鸣器就可以发出声音。输入不同频率的信号,蜂鸣器可以发出不同音色的声音。

(6)按键消抖

按键抖动:机械弹性开关会出现按键抖动问题,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。当按下一次按键时,在之后的时刻会检测到不止一次低电平,那么按下一次按键,抖动可能会误以为按下多次按键。按键消抖的目的就是未来解决这种抖动给我们程序带来的影响;按键消抖解决方案有延迟采样和信号变化频率平稳后并且持续一段时间(通常取20ms)则采样。

(7)数码管

数码管分七段数码管和八段数码管。七段和八段的区别在于,是否包括小数点DP(Digital Point)。本实验中使用的是数码管是8段数码管,每段是由led组成。通过控制每段led的亮灭,来控制数码管显示不同的数字和字母。

数码管的连接方式分为共阴极和共阳极:当输入端全部在二极管的正极,二极管的负极共同接地,只有当输入端为高电平的时候,二极管才导通,然后对应的段发亮,这种连接方式称为共阴极;相反,当输入端全部在二极管的负极,二极管正极共同接高电平,只有当输入端为低电平,二极管才导通,然后对应的段发亮,这种连接方式称为共阳极

要显示不同的数字或者字母,就要选择对应的led段,真值表如下:

字形

DP

g

f

e

d

c

b

a

.

0

1

1

1

1

1

1

1

1

1

1

0

0

0

0

0

0

2

1

1

1

1

1

0

0

1

3

1

0

1

1

0

0

0

0

4

1

0

0

1

1

0

0

1

5

1

0

0

1

0

0

1

0

6

1

0

0

0

0

0

1

0

7

1

1

1

1

1

0

0

0

8

1

0

0

0

0

0

0

0

A

1

0

0

0

1

0

0

0

B

1

0

0

0

0

0

1

1

C

1

1

0

0

0

1

1

0

D

1

0

1

0

0

0

0

1

E

1

0

0

0

0

1

1

0

F

1

0

0

0

1

1

1

0

对于数码管动态显示,由于段选信号公用,所以在同一时刻只能选择一个数码管,具体选择十位还是个位由位选信号决定,用此种方式实现多种数码管就是利用视觉暂留现象。

(8)乐谱知识

音频(Audio),指人耳可以听到的声音频率在20HZ~20kHz之间的声波。乐普是由音符组成的,不同的音符拥有不同的频率。可以利用音频率算出音符振动周期(晶振50MHZ,振动一次20ns时振动周期=1000/(20*音符频率)

二,建立项目

(1) 目录结构

(2) 新建项目

 

 

项目命名:

开发板选型:

 

 

EDA设置:

 

 

3. led流水灯实验

输入信号为时钟和复位,led灯的状态没0.2s改变一次,同一时刻下只能有一只led灯亮,其余的led灯灭。最后通过移位寄存器输出信号给四个led灯。

新建verilog文件

 

工程代码:

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

module led_test(

input wire clk,

input wire rst_n,

output reg [3:0]led //4个led灯

);

reg [25:0]cnt;

//计数器

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

cnt <= 26'd0;

end

else if(cnt == 40_000_000 - 1)begin//从0开始计数,所以只记到39_999_999

cnt <= 26'd0;

end

else begin

cnt = cnt + 1'd1;

end

end

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

led <= 4'b0000;//led灯高电平有效,低电平无效,1亮0不亮

end

else if(cnt == 10 -1 )begin  

led <= 4'b0001;

end

else if(cnt == 20 -1 )begin  

led <= 4'b0010;

end

else if(cnt == 30 -1 )begin

led <= 4'b0100;

end

else if(cnt == 40 -1 )begin

led <= 4'b1000;

end

else begin

led <= led;

end

end

endmodule

注:编译前设置如下(遇到的问题:在没有设置这个时编译一直报错,经过老师讲解,明白如果不设置即使编译通过了烧录也会出问题):

 

分析综合:

 

引脚布线:

 

编译通过:

仿真路径设置(刚开始使用时一直报错“ERRO:cannot launch the modelsim-altera software because you did not specify the path...”很明显时路径出了问题,检查路径发现路径选择不全,经过修改解决问题

 

功能仿真代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

`timescale 1ns/1ns//单位/精度

module led_test_tb();

reg clk  ;//时钟信号

reg rst_n;//复位信号

wire [3:0] led  ;//4个led灯

always #10 clk = ~clk;//每10ns翻转一次时钟信号

initial begin

clk       = 0;//初始化时钟信号为0

rst_n   = 0;//初始化复位信号为0

#10 rst_n = 1;//10ns后将复位信号置1

#1000 $stop;//1000ns后停止,可以观看4个led灯的信号

end

led_test u_led_test(//实例化

.clk (clk)  ,//50MHz

.rst_n (rst_n),//reset negetive复位信号下降沿有效

.led (led)//4个led灯

);

endmodule

modelsim仿真

 

 

 

仿真结果

 

 

 

4. 点亮led灯实验

建立工程过程与上文相同。

工程代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

module led(

input clk,

input rst_n,

output reg ld

);

reg [25:0]cnt;

//计时器模块

always@(negedge rst_n or posedge clk)begin

if(!rst_n)begin

cnt <= 26'd0;//初始化计时器为0

ld <= 1'b1;//初始化led灯,高电平有效

end

else if(cnt == 26'd50_000_000-1)begin

cnt <= 26'd0;

ld <= ~ld;//1s钟led取反

end

else begin

cnt <= cnt + 26'd1;

ld <= ld;//其他时刻,led等于其自身

end

end

endmodule  

5. 有限状态机实现流水灯

建立工程项目过程与上文相同

项目代码

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

module fsm_led(

input clk,

input rst_n,

output reg[3:0] led

);

parameter TIME = 50_000_000;//间隔1s

parameter S_LED0 =2'd0;//led存在的状态

parameter S_LED1 =2'd1;

parameter S_LED2 =2'd2;

parameter S_LED3 =2'd3;

parameter LED0 = 4'b0001;//led输出

parameter LED1 = 4'b0010;

parameter LED2 = 4'b0100;

parameter LED3 = 4'b1000;

reg [25:0] cnt;

reg [1:0] stat;

always@(posedge clk or negedge rst_n)begin

if (!rst_n) begin

 cnt <= 26'd0;

end

else if (cnt ==TIME - 1'd1)begin

cnt <= 26'd0;

end

else begin

cnt <= cnt +1;

end

end

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

stat <= 2'b0;

end

else begin

case(stat)

S_LED0:begin

if (cnt ==TIME - 1)begin

led <= LED1;

stat <= S_LED1;

end

else begin

led <= LED0;

stat <= S_LED0;

end

end

S_LED1:begin

if (cnt ==TIME - 1)begin

led <= LED2;

stat <= S_LED2;

end

else begin

led <= LED1;

stat <= S_LED1;

end

end

S_LED2:begin

if (cnt ==TIME - 1)begin

led <= LED3;

stat <= S_LED3;

end

else begin

led <= LED2;

stat <= S_LED2;

end

end

S_LED3:begin

if (cnt ==TIME - 1)begin

led <= LED0;

stat <= S_LED0;

end

else begin

led <= LED3;

stat <= S_LED3;

end

end

default:;

endcase

end

end

endmodule

引脚:

 

新建verilog文件,写入测试代码程序

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

`timescale 1ns/1ns

module fsm_led_tb();

reg clk;

reg rst_n;

wire [3:0] led;

parameter TIME = 4'd10;

always #10 clk = ~clk;

initial begin //初始化

clk = 1'b0;

rst_n = 1'b0;

#10 rst_n = 1'b1;//延迟10s

// #1000 $stop

end

fsm_led #(.TIME(TIME))u_fsm_led(

.clk(clk),

.rst_n(rst_n),

.led(led)

);

endmodule

仿真结果:

 

6. 按键控制led灯

使用开发板上的四个按键控制四个led灯。按下不同的按键时,四个LED灯显示不同的效果

按键状态

LED显示效果

无按键按下

四个LED灯全灭

按下KEY0

自右向左的流水灯

按下KEY1

自左向右的流水灯

按下KEY2

四个LED灯同时闪烁

按下KEY3

四个LED灯全亮

四个按键外加时钟和复位信号作为输入,两个计数器模块分别用于0.2s时间的计数和状态的计数。led模式选择模块根据状态计数器的改变,来改变四个led的状态,形成不同的样式。

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

module key_led(

input clk  ,//时钟50MHz

input rst_n,//复位信号,下降沿有效negtive

input [3:0] key  ,//四个按键

output reg [3:0] led   //四个led灯

)

parameter TIME = 26'd49_999999;//0.2S

reg [25:0] cnt ;//计数器0.2S

reg [1:0] stat;//记录四个按键状态

//0.2s计数器模块

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin//复位

cnt <= 25'd0;//计数器清0

end

else if(cnt == TIME )begin//记满10_000_000,0~9_999_999

cnt <= 25'd0;//计数器清0

end

else begin

cnt <= cnt + 1'd1;//其他情况下计数器加1

end

end

//状态计数模块

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin//复位信号

stat <= 2'd0;//状态清0

end

else if(cnt == TIME )begin//记满10_000_000,0~9_999_999 0.2s

stat <= stat + 2'd1;//状态加1

end

else begin

stat <= stat;//其他情况状态保持不变

end

end

//状态控制led模块

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin//复位信号

led <= 4'b0000;//led全灭

end

else if(key[0] == 0)begin//右边第1个按键按下,按键低电平0有效,从右往左得亮,0.2秒亮一个接着亮

case(stat)//判断状态的值

2'd0: led <= 4'b0001;

2'd1: led <= 4'b0010;

2'd2: led <= 4'b0100;

2'd3: led <= 4'b1000;

default:; 

endcase

end

else if(key[1] == 0)begin//右边第2个按键按下,按键低电平0有效,从左像右亮

case(stat)

2'd0: led <= 4'b1000;

2'd1: led <= 4'b0100;

2'd2: led <= 4'b0010;

2'd3: led <= 4'b0001;

default:;

endcase

end

else if(key[2] == 0)begin//右边第3个按键按下,按键低电平0有效,四个灯同时闪烁

case(stat)

2'd0: led <= 4'b1111;//全亮

2'd1: led <= 4'b0000;//全灭

2'd2: led <= 4'b1111;//全亮

2'd3: led <= 4'b0000;//全灭

default:;

endcase

end

else if(key[3] == 0)begin//右边第4个按键按下,按键低电平0有效

case(stat)

2'd0: led <= 4'b1111;//全亮

2'd1: led <= 4'b1111;//全亮

2'd2: led <= 4'b1111;//全亮

2'd3: led <= 4'b1111;//全亮

default:;

endcase

end

else begin

led <= 4'b0000;//其他情况默认4个led灯全灭

end

end

endmodule

新建文件,写入仿真代码并保存:

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

`timescale 1ns/1ns

module key_led_tb();

reg clk;

reg rst_n;

reg [3:0] key;

wire [3:0] led;

parameter TIME  = 10 ;

parameter CYCLE = 20; //周期20ns

always #(CYCLE/2) clk = ~clk; //10ns转一次

initial begin

clk = 1'b0;

rst_n = 1'b0;

#(CYCLE);//时钟20ns

rst_n = 1'b1;

#(CYCLE * TIME * 4);//4个led灯,4个间隔,所以乘以4

key = 4'b1110;

#(CYCLE * TIME * 4);

key = 4'b1101;

#(CYCLE * TIME * 4);

key = 4'b1011;

#(CYCLE * TIME * 4);

key = 4'b0111;

#(CYCLE * TIME * 4);

$stop;

end

key_led #(.TIME(TIME)) u_key_led//参数传递 u_key_led可以换

(

.clk(clk),

.rst_n(rst_n),

.key(key),

.led(led)

);

endmodule

7. 按键消抖+蜂鸣器

本实验是按键控制蜂鸣器发音。蜂鸣器的初始状态为鸣叫状态。

  1.如果是鸣叫状态,按下按键后就停止鸣叫。

  2.如果是停止鸣叫状态,按下按键后就重新开始鸣叫。

本实验一共三个模块,分别用于按键消抖,控制蜂鸣器发声和顶层文件调用。

按键消抖模块代码:

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

module key_beep(

input clk  ,//

input rst_n,//复位信号,下降沿有效

input key  ,//按键

output reg flag ,//0表示还在抖动,1表示抖动结束

output reg key_value//消抖完的有效电平

);

parameter DELAY = 20'd1000_000;//20ms

reg [19:0]delay_cnt;//延迟计数器

reg key_reg;

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

delay_cnt <= 20'd0;//0

key_reg <= 1'b1;//低电平有效

end

else begin

key_reg <= key;

if(key_reg != key)begin//抖动

delay_cnt <= DELAY;

end

else begin//没有抖动

if(delay_cnt > 20'd0)begin

delay_cnt <= delay_cnt - 20'd1;//开始倒数计时

end

else begin

delay_cnt <= 20'd0;//小于0置0

end

end

end

end

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

flag <= 1'b0;

key_value<=1'b1;

end

else if (delay_cnt == 20'd1)begin//消抖完成

flag <= 1'b1;

key_value <=key;

end

else begin

flag <= 1'b0;

key_value <= key_value;

end

end

endmodule

控制蜂鸣器发声代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

module beep_control(

input clk,

input rst_n,

input flag,

input key_value,

output reg beep

);

always@(posedge clk or negedge rst_n)begin

    if (!rst_n)begin

beep <= 1'b0;

end

else begin

if (flag && ~key_value)begin

beep <= ~beep;

end

else

beep <=beep;

end

end

endmodule

顶层文件调用代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

module top_key_beep(

input clk,

input rst_n,

input key,

output wire beep

);

wire flag;

wire key_value;

key_beep u_key_beep(

.clk(clk),

.rst_n(rst_n),

.key(key),

.flag(flag),

.key_value(key_value)

);

beep_control U_beep_control(

.clk(clk),

.rst_n(rst_n),

.flag(flag),

.key_value(key_value),

.beep(beep)

);

endmodule

 

 

8. 数码管静态显示

六个数码管同时间隔0.5s显示0-f。要求:使用一个顶层模块,调用计时器模块和数码管静态显示模块。

模块原理图(Tools——>Netlist Viewers——>RTL Viewer)

 

计时器模块代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

module time_count(

input clk,

input rst_n,

output reg flag

);

parameter MAX_NUM = 25'd25_000_000;//计时0.5s

reg[24:0] cnt;

always@(posedge clk or negedge rst_n)begin

if (!rst_n)begin

flag <= 1'b0;

cnt <= 25'b0;

end

else if (cnt<MAX_NUM-25'd1)begin

flag <= 1'd0;

cnt <=cnt +25'd1;

end

else begin

flag <= 1'b1;

cnt <= 25'd0;

end

end

endmodule

数码管静态显示模块代码:

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

module seg_led_static(

input clk,

input rst_n,

input flag,

output reg [5:0] sel,//数码管位选信号

output reg [7:0] seg  //数码管段选信号

);

reg [3:0] num;

always@(posedge clk or negedge rst_n)begin

if(!rst_n) begin

sel<=6'b111111;

end

else begin

sel<=6'b000000;

end

end

always@(posedge clk or negedge rst_n)begin

if (!rst_n)begin

num<=4'h0;

end

else if(flag)begin

if(num < 4'hf)

num <= num + 1'h1;

else

num <= 4'h0;

end

else begin

num <= num;

end

end

always @(posedge clk or negedge rst_n)begin

if (!rst_n)

seg<=8'b0;

else begin

case (num)

4'h0:    seg <= 8'b1100_0000;//匹配到后参考共阳极真值表

        4'h1:    seg <= 8'b1111_1001;

        4'h2:    seg <= 8'b1010_0100;

        4'h3:    seg <= 8'b1011_0000;

        4'h4:    seg <= 8'b1001_1001;

        4'h5:    seg <= 8'b1001_0010;

        4'h6:    seg <= 8'b1000_0010;

        4'h7:    seg <= 8'b1111_1000;

        4'h8:    seg <= 8'b1000_0000;

        4'h9:    seg <= 8'b1001_0000;

        4'ha:    seg <= 8'b1000_1000;

        4'hb:    seg <= 8'b1000_0011;

        4'hc:    seg <= 8'b1100_0110;

        4'hd:    seg <= 8'b1010_0001;

        4'he:    seg <= 8'b1000_0110;

        4'hf:     seg <= 8'b1000_1110;

      default : seg <= 8'b1100_0000;

endcase

end

end

endmodule

顶层文件代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

module top_seg_led_static(

 input clk,

 input rst_n,

 output [5:0]sel,

 output [7:0]seg

);

parameter MAX_NUM = 25'd25_000_000;// 数码管变化的时间间隔0.5s

wire add_flag;// 数码管变化的通知信号

time_count #(.MAX_NUM(MAX_NUM)) u_time_count(

.clk(clk),//50MHz时钟信号

.rst_n(rst_n),//复位信号

.flag(add_flag)//一个时钟周期的脉冲信号

);

seg_led_static u_seg_led_static(

.clk(clk),

.rst_n(rst_n),

.sel(sel),

.seg(seg),

.flag(add_flag)

);

endmodule

 

9. led跑马灯

跑马灯又叫走马灯、串马灯。由毛竹编织成马头,马尾,属于灯笼的一种。是传统特色手工艺品,亦是传统节日玩具之一。本实验利用FPGA开发板上的4个led灯实现20ms的跑马灯。

工程代码:

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

module horse_led(

input wire clk,

input wire rst_n,

output wire [3:0] led

);

parameter MAX_NUM = 21'd1_999_999;//200ms

reg [19:0] cnt;

reg [7:0] led_r;

//计数功能

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

cnt <= 21'd0;

end

else if(cnt == MAX_NUM)begin

cnt <= 20'd0;

end

else begin

cnt <= cnt + 21'd1;

end

end

//led_r移位操作

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

led_r <= 8'b11110000;//初始化

end

else if(cnt == MAX_NUM)begin//20ms

led_r <= {led_r[0],led_r[7:1]};//移位

end

else begin

led_r <= led_r;

end

end

//取led_r的前四位给led

assign led = led_r[3:0];

endmodule

测试代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

`timescale 1ns/1ns//单位/精度

module horse_led_tb();

parameter MAX_NUM = 4'd10;//记最大数,10x20=200ns

parameter CYCLE = 5'd20;//时钟周期,20ns

reg clk;

reg rst_n;

wire [3:0] led;

always#(CYCLE/2) clk = ~clk;//模拟时钟

initial begin

clk = 1'b0;

rst_n = 1'b0;

#(CYCLE)    ;

rst_n = 1'b1;

#(MAX_NUM * CYCLE * 4);//观察4个led的状态

$stop;

end

//实例化待测模块

horse_led#(.MAX_NUM (MAX_NUM)) horse_led_inst(

.clk (clk),

.rst_n  (rst_n),

.led (led)

);

endmodule

10. 数码管动态显示

通过使用6位数码管显示,实现一个计时器,每0.5s变化一次,系统框图如下:

计时器模块:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

module time_count(

input clk,//时钟频率,50MHz

input rst_n,//复位信号,下降沿有效

output reg flag//一个时钟周期的脉冲信号

);

parameter MAX_NUM = 25'd25_000_000;//0.5s时钟

reg [24:0] cnt ;//时钟计数器

//计时器对时钟计数,每0.5s输出一个时钟周期脉冲信号

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin//按下复位信号时

cnt <= 25'd0;//计数器设置位0

flag <= 1'b0;//周期脉冲信号设置位0

end

else if(cnt == MAX_NUM - 1'd1)begin//如果时间到

cnt <= 25'd0;//计数器清零

flag <= 1'b1;//周期脉冲信号输出为1,表示记慢一个周期

end

else begin//如果时间没有到

cnt <= cnt + 1'd1;//计数器不停计数

flag <= 1'b0;//周期脉冲信号不变

end

end

endmodule

数码管动态显示模块

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

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

module sel_led_dynamic(

input clk,//时钟,50MHz

input rst_n,//复位信号,下降沿有效

input flag,

output reg [5:0] sel ,

output reg [7:0] seg  );

reg[2:0] cstat;//当前

reg[2:0] nstat;//现在

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

cstat <= 3'd0;

end

else begin

cstat <=nstat;

end

end

always@(*)begin

case(cstat)

3'd0: if(flag)begin

nstat = 3'd1;

end

else begin

nstat = 3'd0;

end

3'd1: if(flag)

nstat = 3'd2;

else

nstat = 3'd1;

3'd2: if(flag)begin

nstat = 3'd3;

end

else begin

nstat = 3'd2;

end

3'd3: if(flag)begin

nstat = 3'd4;

end

else begin

nstat = 3'd3;

end

3'd4: if(flag)begin

nstat = 3'd5;

end

else begin

nstat = 3'd4;

end

3'd5: if(flag)begin

nstat = 3'd0;

end

else begin

nstat = 3'd5;

end

default: nstat= 3'd1;

endcase

end

reg[2:0]value;

always@(*)begin

if(!rst_n)begin

value <= 3'd0;

end

else begin

case (cstat)

3'd0:begin

sel = 6'b111_110;//选择最右边数码管。 sel低有效

value = 3'd1;

end

3'd1: begin

sel = 6'b111_101;

value = 3'd2;

end

3'd2:  begin

 sel = 6'b111_011;

value = 3'd3;

end

3'd3:begin

sel = 6'b110_111;

value = 3'd4;

end

3'd4: begin

sel = 6'b101_111;

value = 3'd5;

end

3'd5: begin

 sel = 6'b011_111;

value = 3'd6;

end

   default: begin//默认就第1种情况

sel = 6'b111_110;

value = 3'd1;

  end

endcase

end

end

always@(*)begin

if(!rst_n)begin

seg = 8'b11111111;

end

else begin

case (value)

    3'd1:   seg = 8'b11111001;//根据数码管真值表查找

 3'd2:   seg = 8'b10100100;

 3'd3:   seg = 8'b10110000;

 3'd4:   seg = 8'b10011001;

 3'd5:   seg = 8'b10010010;

 3'd6:   seg = 8'b10000010;

 default:seg = 8'b00000000;

endcase

end

end

endmodule

11. 蜂鸣器播放两只老虎

两只老虎乐谱一共有34个音符,1对应DO,2对应RE,3对应MI…。一个音符持续的时间很短,需要设置一个持续时间,重复播放该音符,这样我们才能听得出来。本实验中设置音符持续时间(节拍)300毫秒

 

音符选择模块代码

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

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

module freq_select(

input  wire clk ,

input  wire rst_n,

output reg flag

);

parameter   CNT_MAX = 24'd14_999_999;//300ms切换一个音符

parameter   NUM_FRE = 6'd33;

parameter   D1 = 16'd47750;//1

parameter   D2 = 16'd42250;//2

parameter   D3 = 16'd37900;//3

parameter   D4 = 16'd37550;//4

parameter   D5 = 16'd31850;//5

parameter   D6 = 16'd28400;//6

parameter   D7 = 16'd25400;//7

reg  [23:0] cnt_delay ;//计数器

reg  [5:0] lut_data ;//乐谱数据

reg  [15:0] cnt_freq ;//音符音频计数器

reg  [15:0] freq_data;//音符数据寄存器

wire [14:0] duty_data;//占空比

wire end_note ;//结束标记

wire end_spectrum;//音谱结束

//持续时间计时

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

cnt_delay <= 24'd0;

end

else if(cnt_delay == CNT_MAX)begin

cnt_delay <= 24'd0;

end

else begin

cnt_delay <= cnt_delay+1'd1;

end

end

always@(posedge clk or negedge rst_n)begin//每个音符震动次数的模块

if(!rst_n)begin

cnt_freq <= 16'd0;

end

else if(end_note)begin

cnt_freq <= 16'd0;

end

else begin

cnt_freq <= cnt_freq + 1'd1;//音符切换,从上一个音符切换到下一个音符

end

end

//音谱计时

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

lut_data <= 6'd0;

end

else if(end_spectrum)begin

lut_data <= 6'd0; //回到第一个音符

end

else if(cnt_delay == CNT_MAX)begin

lut_data <= lut_data + 1'd1; //计数器达到300ms,计数器切换到下一位

end

else begin

lut_data <= lut_data;

end

end

always@(posedge clk or negedge rst_n)begin//对音符进行排序

if(!rst_n)begin

freq_data <= D1;

end

else begin

case(lut_data)//对音符进行赋值

6'd0: freq_data <= D1;

6'd1: freq_data <= D2;

6'd2: freq_data <= D3;

6'd3: freq_data <= D1;

6'd4: freq_data <= D1;

6'd5: freq_data <= D2;

6'd6: freq_data <= D3;

6'd7: freq_data <= D1;

6'd8: freq_data <= D3;

6'd9: freq_data <= D4;

6'd10: freq_data <= D5;

6'd11: freq_data <= D3;

6'd12: freq_data <= D4;

6'd13: freq_data <= D5;

6'd14: freq_data <= D5;

6'd15: freq_data <=D6;

6'd16: freq_data <= D5;

6'd17: freq_data <= D4;

6'd18: freq_data <= D3;

6'd19: freq_data <= D1;

6'd20: freq_data <= D5;

6'd21: freq_data <=D6;

6'd22: freq_data <= D5;

6'd23: freq_data <= D4;

6'd24: freq_data <= D3;

6'd25: freq_data <= D1;

6'd26: freq_data <= D2;

6'd27: freq_data <= D5;

6'd28: freq_data <= D1;

6'd29: freq_data <= D1;

6'd30: freq_data <= D2;

6'd31: freq_data <= D5;

6'd32: freq_data <= D1;

6'd33: freq_data <= D1;

default:freq_data <= D1;

default:freq_data <= D1;

endcase  

end         

end  

assign duty_data = freq_data >> 1;//右移一位,得到的就是当前值的一半

assign end_note = cnt_freq == freq_data || cnt_delay == CNT_MAX;

//计时器等于寄存器或者延时等于了max代表播放完成

assign end_spectrum = lut_data == NUM_FRE && cnt_delay == CNT_MAX;

//乐谱的结束标志,记号到达了最大音符序号并且到达的最后一个音符并且结束

//pwm信号产生模块

always@(posedge clk or negedge rst_n)begin

if(!rst_n)begin

flag <= 1'b0;

end

else begin

flag <= (cnt_freq >= duty_data) ? 1'b1 : 1'b0;

//当震动次数大于占空比数据就输出1,否则输出0 前半段低电平,后半段高电平

end  

end         

endmodule                

PWM产生模块代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

module gen_pwm

(

input  wire clk ,

input  wire rst_n,

input  wire flag,

output reg beep

);

always@(posedge clk or negedge rst_n)begin//pwm控制蜂鸣器模块

if(!rst_n)begin

beep <= 1'b0;

end

else if(flag)begin

beep <= 1'b1;

end

else begin

beep <= 1'b0;//停止鸣叫

end

end

endmodule

顶层模块代码:

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

module pwm_beep(

input  wire clk  ,

input  wire rst_n,

output wire beep

);

parameter   CNT_MAX = 24'd14_999_999;//300ms

parameter   D1   = 16'd47750 ;//1

parameter   D2   = 16'd42250 ;//2

parameter   D3   = 16'd37900 ;//3

parameter   D4   = 16'd37550 ;//4

parameter   D5   = 16'd31850 ;//5

parameter   D6     = 16'd28400 ;//6

parameter   D7   = 16'd25400 ;//7

wire flag;

//实例化音频选择模块

freq_select#(

.CNT_MAX (CNT_MAX),

.D1      (D1)     ,

.D2      (D2)     ,

.D3      (D3)     ,

.D4      (D4)     ,

.D5      (D5)     ,

.D6      (D6)     ,

.D7      (D7)

) u_freq_select(

.clk (clk)  ,

.rst_n (rst_n),

.flag (flag)

);

//实例化pwm产生模块

gen_pwm u_gen_pwm

(

.clk (clk)  ,

.rst_n (rst_n),

.flag (flag) ,

.beep (beep)

);

endmodule

引脚

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值