当前位置:   article > 正文

基于FPGA的电子时钟设计与实现 (在EDA开发板上实现电子时钟功能)

基于fpga的电子时钟设计

开发板:此款开发板使用的是 ALTERA 公司的 Cyclone IV 系列 FPGA,型号为 EP4CE6F17C8, 256 个引脚的 FBGA 封装。

 题目:在EDA开发板上实现电子时钟功能

要求:实现电子时钟程序编写,实现在7段数码管显示时、分、秒,使用4x4矩阵按键模拟调节时钟指令输入按键,并实现整点报时功能。按键功能包括但不限以下功能:选择(时分秒选择按键、可以一一对应,也可以只用1个按键)、复位、+(时分秒加)、-(时分秒减)。

程序设计步骤:

1、七段数码管显示

1

2.

4

6.

5

7

图1  开机显示画面,其中.为时分秒的间隔

2、 数据输入:在图1所示的状态下,用4x4矩阵按键来进行时分秒的调节。

3、整点报时:到整点时,自动播放一段音乐。

 4、一键复原:完成时间调节后,按“复原”按键,七段数码管显示最初的状态,回到图1所示状态。


以上既是该电子时钟的设计要求。

功能设计部分,分为以下7个模块。

各功能模块简介
EDA_clock顶层模块文件将各个模块进行整合
clk1hz时钟频率将50MHz晶振频率转化为1秒钟的频率,1Hz=1s
cnt2424计数器时钟中 “时”的计数,并判断
cnt6060计数器时钟中“分”、“秒”的计数,并判断个位十位实时状态
key4x4_clock4x4矩阵键盘矩阵键盘功能设置
seg_dynamic七段数码管接收当前计数器值
buzzer蜂鸣器控制当整点时播放音乐3秒钟

对于以下各个代码块有较为详细的标注,请自行对照理解,有疑惑的可以评论区,看到回复

1. EDA_clock.v

创建工程后,该模块为顶层文件

代码如下:

  1. /**
  2. * @copyright: 2782978674@qq.com
  3. * @author: xietiancheng
  4. * @date: 2023.6.4
  5. *
  6. **/
  7. ​module EDA_clock(
  8. input wire sys_clk,
  9. input wire rst_n,
  10. input wire [3:0] key_in_y, // 输入矩阵键盘的列信号(KEY0~KEY3)
  11. output wire [3:0] key_out_x, // 输出矩阵键盘的行信号(KEY4~KEY7)
  12. output wire [5:0] sel , //数码管位选信号
  13. output wire [7:0] seg , //数码管段选信号
  14. output wire buzzer
  15. );
  16. wire clk_1hz;
  17. wire count_s;
  18. wire count_m;
  19. wire [6:0] Sec; //秒
  20. wire [6:0] Sec_out; //秒输出
  21. wire [6:0] Min; //分
  22. wire [6:0] Min_out; //分输出
  23. wire [6:0] Hour; //时
  24. wire [6:0] Hour_out; //时输出
  25. wire [4:0] Sec1; //秒的个位
  26. wire [4:0] Sec2; //秒的十位
  27. wire [4:0] Min1; //分的个位
  28. wire [4:0] Min2; //分的十位
  29. wire [4:0] Hour1; //时的个位
  30. wire [4:0] Hour2; //时的十位
  31. wire en_s; //秒的使能信号
  32. wire en_m; //分的使能信号
  33. wire en_h; //时的使能信号
  34. wire time_en;//当设置时间时,设置使能信号,当有效时时间停止,进行时钟各位赋值。
  35. // assign en_s=1'b1;
  36. assign en_m=count_s && en_s;
  37. assign en_h= (Min_out == 7'd59 && Sec_out == 7'd58)?1:0;
  38. //1hz代表一秒钟,时钟频率设置
  39. clk1hz
  40. #(
  41. .N (26'd25_000_000 ) //代表
  42. )
  43. clk1hz_inst
  44. (
  45. .clk(sys_clk),
  46. .rst_n(rst_n),
  47. .clk_out(clk_1hz)
  48. );
  49. /*********秒和分使用使能信号en_s和en_m来控制*********/
  50. //秒的代码
  51. cnt60 cnt60_inst1(
  52. .clk(clk_1hz),
  53. .rst_n(rst_n),
  54. .en(en_s),
  55. .time_en(time_en),
  56. .cnt0(Sec),
  57. .cnt_out(Sec_out),
  58. .cnt1(Sec1), //秒的个位
  59. .cnt2(Sec2), //秒的十位
  60. .flag(count_s) //秒的使能信号
  61. );
  62. //分的代码
  63. cnt60 cnt60_inst2(
  64. .clk(clk_1hz),
  65. .rst_n(rst_n),
  66. .en(en_m),
  67. .time_en(time_en),
  68. .cnt0(Min),
  69. .cnt_out(Min_out),
  70. .cnt1(Min1), //分的个位
  71. .cnt2(Min2), //分的十位
  72. .flag(count_m)
  73. );
  74. //时间的设置
  75. cnt24 cnt24_inst(
  76. .clk(clk_1hz),
  77. .rst_n(rst_n),
  78. .en(en_h),
  79. .time_en(time_en),
  80. .cnt0(Hour),
  81. .cnt_out(Hour_out),
  82. .cnt1(Hour1),
  83. .cnt2(Hour2)
  84. );
  85. //数码管控制显示模块
  86. seg_dynamic seg_dynamic_inst
  87. (
  88. .clk(sys_clk),
  89. .rst_n(rst_n),
  90. .Sec1(Sec1), //秒的个位
  91. .Sec2(Sec2), //秒的十位
  92. .Min1(Min1), //分的个位
  93. .Min2(Min2), //分的十位
  94. .Hour1(Hour1), //时的个位
  95. .Hour2(Hour2), //时的十位
  96. .sel (sel), //数码管位选信号
  97. .seg(seg) //数码管段选信号
  98. );
  99. //矩阵键盘设置调节时间
  100. key4x4_clock key4x4_clock_inst(
  101. .clk(sys_clk) ,
  102. .rst_n(rst_n) ,
  103. .Sec_out (Sec_out) ,
  104. .Min_out (Min_out) ,
  105. .Hour_out (Hour_out) ,
  106. .key_in_y(key_in_y) , // 输入矩阵键盘的列信号(KEY0~KEY3)
  107. .key_out_x(key_out_x) , // 输出矩阵键盘的行信号(KEY4~KEY7)
  108. .Sec(Sec) , //秒修改
  109. .Min(Min) , //分修改
  110. .Hour(Hour) , //时修改
  111. .en(en_s) , //时钟是否运行的使能信号
  112. .time_en(time_en)
  113. );
  114. buzzer buzzer_inst(
  115. .clk (sys_clk),
  116. .rst_n (rst_n ),
  117. .Sec_out(Sec_out),
  118. .Min_out(Min_out),
  119. .buzzer (buzzer)
  120. );
  121. endmodule
  122. /**********************************************************
  123. quartusII综合报错(Error (10028): Can't resolve multiple constant drivers for net "txd_cnt[3]")
  124. 出现这个错误的原因在于,在不同的always逻辑块中,对同一个reg变量进行了赋值。
  125. 在多个alwasy逻辑块同时并行工作的时候,会出现冲突。解决的办法就是,对于一个变量,只在一个always块中,进行赋值。
  126. ***********************************************************/

2. clk1hz.v

将50MHz转化为1Hz的频率,可通过parameter参数N进行更改转化为其他频率

代码如下:

  1. module clk1hz
  2. #(parameter N = 26'd25_000_000)
  3. (
  4. input clk,
  5. input rst_n,
  6. output reg clk_out
  7. );
  8. reg [32:0] tmp;
  9. always @(posedge clk or negedge rst_n)
  10. begin
  11. if(!rst_n) begin
  12. tmp <= 0;
  13. clk_out<=0;
  14. end
  15. else if (tmp == N-1)
  16. begin
  17. clk_out<= ~clk_out;
  18. tmp <= 0;
  19. end
  20. else
  21. begin
  22. tmp <= tmp+1;
  23. clk_out <=clk_out;
  24. end
  25. end
  26. endmodule

3. cnt24.v

代码如下:

  1. module cnt24
  2. // #(parameter CNT = 5'd0)
  3. (
  4. input wire clk ,
  5. input wire rst_n ,
  6. input wire en ,
  7. input wire time_en ,
  8. input wire [6:0] cnt0,
  9. output reg [6:0] cnt_out, //控制cnt的传入传出
  10. output reg [4:0] cnt1,
  11. output reg [4:0] cnt2
  12. );
  13. reg [6:0] cnt;
  14. always @(posedge clk or negedge rst_n)
  15. begin
  16. if(!rst_n) begin
  17. cnt<=cnt0;
  18. cnt1<=cnt%10;
  19. cnt2<=cnt/10;
  20. cnt_out<=cnt;
  21. end
  22. else
  23. begin
  24. if(en==1)
  25. begin
  26. if(cnt<23)
  27. begin
  28. cnt<=cnt+1;
  29. cnt1<=cnt%10;
  30. cnt2<=cnt/10;
  31. cnt_out<=cnt;
  32. end
  33. else begin
  34. cnt<=0;
  35. cnt1<=cnt%10;
  36. cnt2<=cnt/10;
  37. cnt_out<=cnt;
  38. end
  39. end
  40. else if(time_en==1'b1)begin
  41. cnt <=cnt0;
  42. cnt1<=cnt%10;
  43. cnt2<=cnt/10;
  44. cnt_out<=cnt;
  45. end
  46. else begin //if(en_m==0 &&en == 0)begin
  47. cnt<=cnt;
  48. cnt1<=cnt%10;
  49. cnt2<=cnt/10;
  50. cnt_out<=cnt;
  51. end
  52. end
  53. end
  54. endmodule
  55. /*
  56. if(cnt1<10)
  57. begin
  58. cnt1 <= cnt1+1;
  59. end
  60. else
  61. begin
  62. cnt1<= 0;
  63. cnt2<=cnt2+1;
  64. end
  65. */

4. cnt60.v

代码如下:

  1. module cnt60
  2. (
  3. input wire clk,
  4. input wire rst_n,
  5. input wire en,
  6. input wire time_en, //修改时钟值的使能信号
  7. input wire [6:0] cnt0, //控制cnt的传入传出
  8. output reg [6:0] cnt_out, //控制cnt的传入传出
  9. output reg [4:0] cnt1,
  10. output reg [4:0] cnt2,
  11. output reg flag
  12. );
  13. reg [6:0] cnt; //时钟始终变化着运行着的值
  14. always @(posedge clk or negedge rst_n)
  15. begin
  16. if(!rst_n)
  17. begin
  18. cnt<=cnt0;
  19. //控制个位和十位的变化
  20. cnt1<=cnt%10;
  21. cnt2<=cnt/10;
  22. cnt_out<=cnt;
  23. end
  24. else
  25. begin
  26. if(en==1)
  27. begin
  28. if(cnt<59)
  29. begin
  30. cnt <=cnt +1;
  31. //控制个位和十位的变化
  32. cnt1<=cnt%10;
  33. cnt2<=cnt/10;
  34. cnt_out<=cnt;
  35. end
  36. else begin
  37. cnt <= 0;
  38. cnt1<=cnt%10;
  39. cnt2<=cnt/10;
  40. cnt_out<=cnt;
  41. end
  42. end
  43. else if(time_en==1'b1)begin
  44. cnt <=cnt0 ;
  45. cnt1<=cnt%10;
  46. cnt2<=cnt/10;
  47. cnt_out<=cnt;
  48. end
  49. else begin
  50. cnt <=cnt;
  51. cnt1<=cnt%10;
  52. cnt2<=cnt/10;
  53. cnt_out<=cnt;
  54. end
  55. end
  56. end
  57. //当cnt到达58时flag的变化
  58. always @(posedge clk or negedge rst_n)
  59. begin
  60. if(!rst_n)
  61. flag<=0;
  62. else if(cnt <58)
  63. flag <=0;
  64. else if(cnt == 58)
  65. flag <= 1;
  66. else flag <= 0;
  67. end
  68. endmodule

5. key4x4_clock.v

代码如下:

  1. `timescale 1ns / 1ps //精度 可不用
  2. module key4x4_clock(
  3. input wire clk,
  4. input wire rst_n,
  5. input wire [6:0] Sec_out,
  6. input wire [6:0] Min_out,
  7. input wire [6:0] Hour_out,
  8. input wire [3:0] key_in_y, // 输入矩阵键盘的列信号(KEY0~KEY3)
  9. output reg [3:0] key_out_x, // 输出矩阵键盘的行信号(KEY4~KEY7)
  10. output reg [6:0] Sec, //秒修改
  11. output reg [6:0] Min, //分修改
  12. output reg [6:0] Hour, //时修改
  13. output reg en , //时钟是否运行的使能信号
  14. output reg time_en //时钟是否运行的使能信号
  15. );
  16. //寄存器定义
  17. reg [19:0] count;
  18. reg [1:0] count1; //时钟修改的时候,在修改时它的值等于当前已经变化后的值,count1作为该修改值的使能信号
  19. //==============================================
  20. // 输出矩阵键盘的行信号,20ms 扫描矩阵键盘一次,采样频率小于按键毛刺频率,相当于滤除掉了高频毛刺信号。
  21. //==============================================
  22. always @(posedge clk or negedge rst_n) //检测时钟的上升沿和复位的下降沿
  23. begin
  24. if(!rst_n) begin //复位信号低有效
  25. count <= 20'd0; //计数器清 0
  26. key_out_x <= 4'b1111;
  27. end
  28. else begin
  29. if(count == 20'd0) //0ms 扫描第一行矩阵键盘
  30. begin
  31. key_out_x <= 4'b1110; //开始扫描第一行矩阵键盘,第一行输出0
  32. count <= count + 20'b1; //计数器加1
  33. end
  34. else if(count == 20'd249_999) //5ms 扫描第二行矩阵键盘,5ms 计数(50M/200-1=249_999)
  35. begin
  36. key_out_x <= 4'b1101; //开始扫描第二行矩阵键盘,第二行输出 0
  37. count <= count + 20'b1; //计数器加 1
  38. end
  39. else if(count ==20'd499_999) //10ms扫描第三行矩阵键盘 ,10ms计数(50M/100-1=499_999)
  40. begin
  41. key_out_x <= 4'b1011; //扫描第三行矩阵键盘,第三行输出0
  42. count <= count + 20'b1; //计数器加1
  43. end
  44. else if(count ==20'd749_999) //15ms扫描第四行矩阵键盘 ,15ms 计 数(50M/67.7-1=749_999)
  45. begin
  46. key_out_x <= 4'b0111; //扫描第四行矩阵键盘,第四行输出0
  47. count <= count + 20'b1; //计数器加 1
  48. end
  49. else if(count ==20'd999_999) //20ms 计数(50M/50-1=999_999)
  50. begin
  51. count <= 0; //计数器为 0
  52. end
  53. else
  54. count <= count + 20'b1; //计数器加 1
  55. end
  56. end
  57. //====================================================
  58. // 采样列的按键信号
  59. //====================================================
  60. reg [3:0] key_h1_scan; //第一行按键扫描值 KEY
  61. reg [3:0] key_h1_scan_r; //第一行按键扫描值寄存器 KEY
  62. reg [3:0] key_h2_scan; //第二行按键扫描值 KEY
  63. reg [3:0] key_h2_scan_r; //第二行按键扫描值寄存器 KEY
  64. reg [3:0] key_h3_scan; //第三行按键扫描值 KEY
  65. reg [3:0] key_h3_scan_r; //第三行按键扫描值寄存器 KEY
  66. reg [3:0] key_h4_scan; //第四行按键扫描值 KEY
  67. reg [3:0] key_h4_scan_r; //第四行按键扫描值寄存器 KEY
  68. always @(posedge clk)
  69. begin
  70. if(!rst_n) begin //复位信号低有效
  71. key_h1_scan <= 4'b1111;
  72. key_h2_scan <= 4'b1111;
  73. key_h3_scan <= 4'b1111;
  74. key_h4_scan <= 4'b1111;
  75. end
  76. else begin
  77. if(count == 20'd124_999) //2.5ms 扫描第一行矩阵键盘值
  78. key_h1_scan<=key_in_y; //扫描第一行的矩阵键盘值
  79. else if(count == 20'd374_999) //7.5ms 扫描第二行矩阵键盘值
  80. key_h2_scan<=key_in_y; //扫描第二行的矩阵键盘值
  81. else if(count == 20'd624_999) //12.5ms 扫描第三行矩阵键盘值
  82. key_h3_scan<=key_in_y; //扫描第三行的矩阵键盘值
  83. else if(count == 20'd874_999) //17.5ms 扫描第四行矩阵键盘值
  84. key_h4_scan<=key_in_y; //扫描第四行的矩阵键盘值
  85. end
  86. end
  87. //====================================================
  88. // 按键信号锁存一个时钟节拍
  89. //====================================================
  90. always @(posedge clk)
  91. begin
  92. key_h1_scan_r <= key_h1_scan;
  93. key_h2_scan_r <= key_h2_scan;
  94. key_h3_scan_r <= key_h3_scan;
  95. key_h4_scan_r <= key_h4_scan;
  96. end
  97. wire [3:0] flag_h1_key = key_h1_scan_r[3:0] & (~key_h1_scan[3:0]); //当检测到按键有下降沿变化时,代表该按键被按下,按键有效
  98. wire [3:0] flag_h2_key = key_h2_scan_r[3:0] & (~key_h2_scan[3:0]); //当检测到按键有下降沿变化时,代表该按键被按下,按键有效
  99. wire [3:0] flag_h3_key = key_h3_scan_r[3:0] & (~key_h3_scan[3:0]); //当检测到按键有下降沿变化时,代表该按键被按下,按键有效
  100. wire [3:0] flag_h4_key = key_h4_scan_r[3:0] & (~key_h4_scan[3:0]); //当检测到按键有下降沿变化时,代表该按键被按下,按键有效
  101. /* 状态参数的定义 --- 可以使用独热码 */
  102. parameter IDLE = 5'b00001, //初始状态(也是终态) 使能信号正常,可以初次进行时钟正常运行
  103. ONE = 5'b00010, //设置秒的个十位
  104. TWO = 5'b00100, //设置分
  105. THREE = 5'b01000, //设置时
  106. SETTING = 5'b10000; //时间设置键 使能信号失效
  107. /**************按键排列******************
  108. K1设置键 K2设置秒 K3设置分 K4设置时
  109. K5确认键
  110. K9时间加 K10时间减
  111. *****************************************/
  112. reg [4:0] state ; //状态跳变
  113. //状态跳转
  114. always@ (posedge clk or negedge rst_n)
  115. begin
  116. if(rst_n == 1'b0)
  117. state <=IDLE;
  118. else
  119. case(state)
  120. IDLE: if(flag_h1_key[0]==1'b1)
  121. state <= SETTING;
  122. else
  123. state <= IDLE;
  124. SETTING:if(flag_h1_key[1]==1'b1)
  125. state <= ONE;
  126. else if(flag_h1_key[2]==1'b1)
  127. state <= TWO;
  128. else if(flag_h1_key[3]==1'b1)
  129. state <= THREE;
  130. else if(flag_h2_key[0]==1'b1)
  131. state <= IDLE;
  132. else
  133. state <= SETTING;
  134. ONE: if(flag_h1_key[2])
  135. state <= TWO;
  136. else if(flag_h1_key[3]==1'b1)
  137. state <= THREE;
  138. else if(flag_h2_key[0]==1'b1)
  139. state <= IDLE;
  140. else
  141. state <= ONE;
  142. TWO: if(flag_h1_key[1])
  143. state <= ONE;
  144. else if(flag_h1_key[3]==1'b1)
  145. state <= THREE;
  146. else if(flag_h2_key[0]==1'b1)
  147. state <= IDLE;
  148. else
  149. state <= TWO;
  150. THREE: if(flag_h1_key[1]==1'b1)
  151. state <= ONE;
  152. else if(flag_h1_key[2]==1'b1)
  153. state <= TWO;
  154. else if(flag_h2_key[0]==1'b1)
  155. state <= IDLE;
  156. else
  157. state <= THREE;
  158. default:state <=IDLE;
  159. endcase
  160. end
  161. /* 描述状态IDLE和SETTING的输出 */
  162. always@(posedge clk or negedge rst_n)
  163. begin
  164. if(rst_n==1'b0)begin
  165. en <= 1'b0;
  166. time_en <= 1'b0;
  167. count1 <=1'b0;
  168. end
  169. else if(state <= IDLE) begin //相当于确认按键,按下以后使能信号有效,时钟继续运行
  170. en <= 1'b1;
  171. time_en <= 1'b0;
  172. end
  173. else if(state <= SETTING)begin
  174. en <= 1'b0;
  175. time_en <= 1'b1;
  176. count1 <= 1'b1;
  177. end
  178. else if(flag_h2_key[0]==1'b1)
  179. count1 <= 1'b0;
  180. else begin
  181. en <= en;
  182. end
  183. end
  184. /* 描述ONE状态的输出 */
  185. always@(posedge clk or negedge rst_n)
  186. begin
  187. if(rst_n==1'b0)begin
  188. Sec <= 7'd57;
  189. end
  190. else if(state==SETTING && count1 ==1'b0)begin
  191. Sec <= Sec_out;
  192. end
  193. else if (state == ONE && flag_h3_key[0] == 1'b1 && Sec < 59)
  194. Sec <=Sec + 1'b1;
  195. else if (state == ONE && flag_h3_key[1] == 1'b1 && Sec >0)
  196. Sec <=Sec - 1'b1;
  197. else if(state == ONE && flag_h3_key[0] == 1'b1 && Sec == 7'd59)
  198. Sec<= 7'd0;
  199. else if(state == ONE && flag_h3_key[1] == 1'b1 && Sec == 7'd0)
  200. Sec<=7'd59;
  201. else
  202. Sec <=Sec;
  203. end
  204. /* 描述TWO状态的输出 */
  205. always@(posedge clk or negedge rst_n)
  206. begin
  207. if(rst_n==1'b0)begin
  208. Min <= 7'd46;
  209. end
  210. else if(state==SETTING && count1 ==1'b0)begin
  211. Min <= Min_out;
  212. end
  213. else if (state == TWO && flag_h3_key[0] == 1'b1 && Min < 59)
  214. Min <= Min + 1'b1;
  215. else if (state == TWO && flag_h3_key[1] == 1'b1 && Min >0)
  216. Min <= Min - 1'b1;
  217. else if(state == TWO && flag_h3_key[0] == 1'b1 && Min == 7'd59)
  218. Min<= 7'd0;
  219. else if(state == TWO && flag_h3_key[1] == 1'b1 && Min == 7'd0)
  220. Min<=7'd59;
  221. else
  222. Min <= Min;
  223. end
  224. /* 描述THREE状态的输出 */
  225. always@(posedge clk or negedge rst_n)
  226. begin
  227. if(rst_n==1'b0)begin
  228. Hour<= 7'd12;
  229. end
  230. else if(state==SETTING && count1 ==1'b0)begin
  231. Hour <= Hour_out;
  232. end
  233. else if (state == THREE && flag_h3_key[0] == 1'b1 && Hour < 7'd23)
  234. Hour <=Hour + 1'b1;
  235. else if (state == THREE && flag_h3_key[1] == 1'b1 && Hour > 7'd0)
  236. Hour <=Hour - 1'b1;
  237. else if(state == THREE && flag_h3_key[0] == 1'b1 && Hour == 7'd23)
  238. Hour<= 7'd0;
  239. else if(state == THREE && flag_h3_key[1] == 1'b1 && Hour == 7'd0)
  240. Hour<=7'd23;
  241. else
  242. Hour <=Hour;
  243. end
  244. endmodule

6. seg_dynamic.v

代码如下:

  1. module seg_dynamic
  2. (
  3. input wire clk,
  4. input wire rst_n,
  5. input wire [4:0] Sec1, //秒的个位
  6. input wire [4:0] Sec2, //秒的十位
  7. input wire [4:0] Min1, //分的个位
  8. input wire [4:0] Min2, //分的十位
  9. input wire [4:0] Hour1, //时的个位
  10. input wire [4:0] Hour2, //时的十位
  11. output reg [5:0] sel , //数码管位选信号
  12. output reg [7:0] seg //数码管段选信号
  13. );
  14. //十六进制数显示编码
  15. parameter
  16. SEG_0 = 8'b1100_0000,
  17. SEG_1 = 8'b1111_1001,
  18. SEG_2 = 8'b1010_0100,
  19. SEG_3 = 8'b1011_0000,
  20. SEG_4 = 8'b1001_1001,
  21. SEG_5 = 8'b1001_0010,
  22. SEG_6 = 8'b1000_0010,
  23. SEG_7 = 8'b1111_1000,
  24. SEG_8 = 8'b1000_0000,
  25. SEG_9 = 8'b1001_0000,
  26. SEG_A = 8'b1000_1000, SEG_B = 8'b1000_0011,
  27. SEG_C = 8'b1100_0110, SEG_D = 8'b1010_0001,
  28. SEG_E = 8'b1000_0110, SEG_F = 8'b1000_1110;
  29. parameter IDLE = 8'b1111_1111; //不显示状态
  30. //reg define
  31. reg [23:0] data_reg ; //待显示数据寄存器
  32. reg [15:0] cnt_1ms ; //1ms计数器
  33. reg flag_1ms ; //1ms标志信号
  34. reg [2:0] cnt_sel ; //数码管位选计数器
  35. reg [5:0] sel_reg ; //位选信号
  36. reg [3:0] data_disp ; //当前数码管显示的数据
  37. parameter CNT_MAX = 16'd49_999; //数码管刷新时间计数最大值
  38. reg [7:0] led_SG ; //秒的个位
  39. reg [7:0] led_SS ; //秒的十位
  40. reg [7:0] led_MG ; //分的个位
  41. reg [7:0] led_MS ; //分的十位
  42. reg [7:0] led_HG ; //时的个位
  43. reg [7:0] led_HS ; //时的十位
  44. /****************************************************
  45. *****************数码管段选信号**********************
  46. ******************************************************/
  47. //七段数码管显示秒的个位
  48. always @(posedge clk or negedge rst_n)begin
  49. if(rst_n==1'b0)
  50. led_SG<=IDLE;
  51. else begin
  52. case(Sec1)
  53. 0: led_SG<=SEG_0;
  54. 1: led_SG<=SEG_1;
  55. 2: led_SG<=SEG_2;
  56. 3: led_SG<=SEG_3;
  57. 4: led_SG<=SEG_4;
  58. 5: led_SG<=SEG_5;
  59. 6: led_SG<=SEG_6;
  60. 7: led_SG<=SEG_7;
  61. 8: led_SG<=SEG_8;
  62. 9: led_SG<=SEG_9;
  63. default led_SG<=IDLE;
  64. endcase
  65. end
  66. end
  67. //七段数码管显示秒的十位
  68. always @(posedge clk or negedge rst_n)begin
  69. if(rst_n==1'b0)
  70. led_SS<=IDLE;
  71. else begin
  72. case(Sec2)
  73. 0: led_SS<=SEG_0;
  74. 1: led_SS<=SEG_1;
  75. 2: led_SS<=SEG_2;
  76. 3: led_SS<=SEG_3;
  77. 4: led_SS<=SEG_4;
  78. 5: led_SS<=SEG_5;
  79. 6: led_SS<=SEG_6;
  80. 7: led_SS<=SEG_7;
  81. 8: led_SS<=SEG_8;
  82. 9: led_SS<=SEG_9;
  83. default led_SS<=IDLE;
  84. endcase
  85. end
  86. end
  87. //七段数码管显示分的个位
  88. always @(posedge clk or negedge rst_n)begin
  89. if(rst_n==1'b0)
  90. led_MG<=IDLE;
  91. else begin
  92. case(Min1)
  93. 0: led_MG<=8'b0100_0000;
  94. 1: led_MG<=8'b0111_1001;
  95. 2: led_MG<=8'b0010_0100;
  96. 3: led_MG<=8'b0011_0000;
  97. 4: led_MG<=8'b0001_1001;
  98. 5: led_MG<=8'b0001_0010;
  99. 6: led_MG<=8'b0000_0010;
  100. 7: led_MG<=8'b0111_1000;
  101. 8: led_MG<=8'b0000_0000;
  102. 9: led_MG<=8'b0001_0000;
  103. default led_MG<=8'b0111_1111;
  104. endcase
  105. end
  106. end
  107. //七段数码管显示分的十位
  108. always @(posedge clk or negedge rst_n)begin
  109. if(rst_n==1'b0)
  110. led_MS<=IDLE;
  111. else begin
  112. case(Min2)
  113. 0: led_MS<=SEG_0;
  114. 1: led_MS<=SEG_1;
  115. 2: led_MS<=SEG_2;
  116. 3: led_MS<=SEG_3;
  117. 4: led_MS<=SEG_4;
  118. 5: led_MS<=SEG_5;
  119. 6: led_MS<=SEG_6;
  120. 7: led_MS<=SEG_7;
  121. 8: led_MS<=SEG_8;
  122. 9: led_MS<=SEG_9;
  123. default led_MS<=IDLE;
  124. endcase
  125. end
  126. end
  127. //七段数码管显示时的个位
  128. always @(posedge clk or negedge rst_n)begin
  129. if(rst_n==1'b0)
  130. led_HG <= IDLE;
  131. else begin
  132. case(Hour1)
  133. 0: led_HG <= 8'b0100_0000;
  134. 1: led_HG <= 8'b0111_1001;
  135. 2: led_HG <= 8'b0010_0100;
  136. 3: led_HG <= 8'b0011_0000;
  137. 4: led_HG <= 8'b0001_1001;
  138. 5: led_HG <= 8'b0001_0010;
  139. 6: led_HG <= 8'b0000_0010;
  140. 7: led_HG <= 8'b0111_1000;
  141. 8: led_HG <= 8'b0000_0000;
  142. 9: led_HG <= 8'b0001_0000;
  143. default led_HG <= 8'b0111_1111;
  144. endcase
  145. end
  146. end
  147. //七段数码管显示时的十位
  148. always @(posedge clk or negedge rst_n)begin
  149. if(rst_n==1'b0)
  150. led_HS<=IDLE;
  151. else begin
  152. case(Hour2)
  153. 0: led_HS<=SEG_0;
  154. 1: led_HS<=SEG_1;
  155. 2: led_HS<=SEG_2;
  156. 3: led_HS<=SEG_3;
  157. // 4: led_HS<=SEG_4;
  158. // 5: led_HS<=SEG_5;
  159. // 6: led_HS<=SEG_6;
  160. // 7: led_HS<=SEG_7;
  161. // 8: led_HS<=SEG_8;
  162. // 9: led_HS<=SEG_9;
  163. default led_HS<=IDLE;
  164. endcase
  165. end
  166. end
  167. /****************************************************
  168. *****************数码管位选信号**********************
  169. ******************************************************/
  170. //cnt_1ms:1ms循环计数
  171. always@(posedge clk or negedge rst_n)
  172. if(rst_n == 1'b0)
  173. cnt_1ms <= 16'd0;
  174. else if(cnt_1ms == CNT_MAX)
  175. cnt_1ms <= 16'd0;
  176. else
  177. cnt_1ms <= cnt_1ms + 1'b1;
  178. //flag_1ms:1ms标志信号
  179. always@(posedge clk or negedge rst_n)
  180. if(rst_n == 1'b0)
  181. flag_1ms <= 1'b0;
  182. else if(cnt_1ms == CNT_MAX - 1'b1)
  183. flag_1ms <= 1'b1;
  184. else
  185. flag_1ms <= 1'b0;
  186. //cnt_sel:从0到5循环数,用于选择当前显示的数码管
  187. always@(posedge clk or negedge rst_n)
  188. if(rst_n == 1'b0)
  189. cnt_sel <= 3'd0;
  190. else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))
  191. cnt_sel <= 3'd0;
  192. else if(flag_1ms == 1'b1)
  193. cnt_sel <= cnt_sel + 1'b1;
  194. else
  195. cnt_sel <= cnt_sel;
  196. //数码管位选信号寄存器
  197. always@(posedge clk or negedge rst_n)
  198. if(rst_n == 1'b0)
  199. sel_reg <= 6'b000_000;
  200. else if((cnt_sel == 3'd0) && (flag_1ms == 1'b1))
  201. sel_reg <= 6'b000_001;
  202. else if(flag_1ms == 1'b1)
  203. sel_reg <= sel_reg << 1;
  204. else
  205. sel_reg <= sel_reg;
  206. //控制数码管的位选信号,使六个数码管轮流显示
  207. always@(posedge clk or negedge rst_n)
  208. if(rst_n == 1'b0)
  209. seg <= 8'hff;
  210. else if( flag_1ms == 1'b1)
  211. case(cnt_sel)
  212. 3'd0: seg <= led_HS; //给第6个数码管赋十万位值
  213. 3'd1: seg <= led_HG; //给第5个数码管赋万位值
  214. 3'd2: seg <= led_MS; //给第4个数码管赋千位值
  215. 3'd3: seg <= led_MG ; //给第3个数码管赋百位值
  216. 3'd4: seg <= led_SS ; //给第2个数码管赋十位值
  217. 3'd5: seg <= led_SG ; //给第1个数码管赋个位值
  218. default:seg <= 8'hff;
  219. endcase
  220. else
  221. seg <= seg;
  222. //sel:数码管位选信号赋值
  223. always@(posedge clk or negedge rst_n)
  224. if(rst_n == 1'b0)
  225. sel <= 6'b111_111;
  226. else
  227. sel <= ~sel_reg;
  228. endmodule

7. buzzer.v

buzzer==0响否则=1不响,歌曲放了两首,都能使用,自行选择,但意义不大。

代码如下:

  1. module buzzer(
  2. input wire clk , //系统时钟50MHz
  3. input wire rst_n ,
  4. input wire [6:0] Sec_out ,
  5. input wire [6:0] Min_out ,
  6. // input wire Min1 ,
  7. // input wire Min2 ,
  8. output reg buzzer //蜂鸣器输出端
  9. );
  10. // assign buzzer =( Sec_out==1'b0 && Min_out==1'b0 )?0:1; //只响一秒钟
  11. /**********--------------森林狂想曲-----------------************************/
  12. //模块名称song
  13. reg beep_r; //寄存器
  14. reg[7:0] state; //乐谱状态机
  15. reg[16:0]count,count_end;
  16. reg[23:0]count1;
  17. //乐谱参数:D=F/2K (D:参数,F:时钟频率,K:音高频率)
  18. parameter L_3 = 17'd75850, //低音3
  19. L_5 = 17'd63776, //低音5
  20. L_6 = 17'd56818, //低音6
  21. L_7 = 17'd50618, //低音7
  22. M_1 = 17'd47774, //中音1
  23. M_2 = 17'd42568, //中音2
  24. M_3 = 17'd37919, //中音3
  25. M_5 = 17'd31888, //中音5
  26. M_6 = 17'd28409, //中音6
  27. H_1 = 17'd23889; //高音1
  28. parameter TIME = 12000000; //控制每一个音的长短(250ms)
  29. // assign buzzer = beep_r; //输出音乐
  30. //★★★以下两个时序逻辑模块较为重点关注
  31. always @(posedge clk or negedge rst_n)
  32. begin
  33. if(!rst_n)begin
  34. buzzer <= 1'b1;
  35. end
  36. else if(Min_out==1'b0 && Sec_out<=7'd3) //整点报时并3s后停止
  37. buzzer <= beep_r;
  38. else
  39. buzzer <=1'b1;
  40. end
  41. always@(posedge clk) begin
  42. count <= count + 1'b1; //计数器加1
  43. if(count == count_end) begin
  44. count <= 17'h0; //计数器清零
  45. beep_r <= !beep_r; //输出取反
  46. end
  47. end
  48. //曲谱 产生分频的系数并描述出曲谱
  49. always @(posedge clk) begin
  50. if(count1 < TIME) //一个节拍250mS
  51. count1 = count1 + 1'b1;
  52. else begin
  53. count1 = 24'd0;
  54. if(state == 8'd63)
  55. state = 8'd0;
  56. else
  57. state = state + 1'b1;
  58. case(state)
  59. 8'd0:count_end = L_6;
  60. 8'd1:count_end=M_1;
  61. 8'd2:count_end=M_3;
  62. 8'D3:count_end=M_5;
  63. 8'D4,8'D5:count_end=M_3;
  64. 8'D6:count_end=M_3;
  65. 8'D7:count_end=M_2;
  66. 8'D8,8'D9:count_end=M_3;
  67. 8'D10:count_end=M_3;
  68. 8'D11:count_end=M_2;
  69. 8'D12,8'D13:count_end=M_3;
  70. 8'D14:count_end=L_6;
  71. 8'D15:count_end=L_7;
  72. 8'D16:count_end=M_1;
  73. 8'D17:count_end=M_3;
  74. 8'D18:count_end=M_2;
  75. 8'D19:count_end=M_1;
  76. 8'D20,8'D21:count_end=L_6;
  77. 8'D22,8'D23:count_end=L_5;
  78. 8'D24,8'D25,8'D26,8'D27,8'D28,8'D29,8'D30,8'D31:count_end=L_3;
  79. 8'd32:count_end = L_6;
  80. 8'd33:count_end=M_1;
  81. 8'd34:count_end=M_3;
  82. 8'D35:count_end=M_5;
  83. 8'D36,8'D37:count_end=M_3;
  84. 8'D38:count_end=M_3;
  85. 8'D39:count_end=M_2;
  86. 8'D40,8'D41:count_end=M_3;
  87. 8'D42:count_end=M_3;
  88. 8'D43:count_end=M_2;
  89. 8'D44,8'D45:count_end=M_3;
  90. 8'D46:count_end=L_6;
  91. 8'D47:count_end=L_7;
  92. 8'D48:count_end=M_1;
  93. 8'D49:count_end=M_3;
  94. 8'D50:count_end=M_2;
  95. 8'D51:count_end=M_1;
  96. 8'D52,8'D53:count_end=L_6;
  97. 8'D54,8'D55:count_end=L_5;
  98. 8'D56,8'D57,8'D58,8'D59,8'D60,8'D61:count_end=L_6;
  99. 8'D62:count_end=L_6;
  100. 8'D63:count_end=L_7;
  101. default: count_end = 16'h0;
  102. endcase
  103. end
  104. end
  105. /************************************************************************
  106. -------------------------两只老虎----------------------------------------
  107. //中间信号定义
  108. reg [16:0] cnt0 ; //产生PWM的计数器
  109. wire add_cnt0;
  110. wire end_cnt0;
  111. reg [7:0] cnt1 ; //每个音符持续时间的计数器
  112. wire add_cnt1;
  113. wire end_cnt1;
  114. reg [5:0] cnt2 ; //《两只老虎》共32个音节
  115. wire add_cnt2;
  116. wire end_cnt2;
  117. reg [16:0] pre_set ; //存放每个音节的频率在系统中的时钟个数
  118. //每个音符对应的系统周期计数,中音
  119. localparam M1=95602, //音符1do
  120. M2=85178, //音符rui
  121. M3=75872, //音符mi
  122. M4=71633, //音符fa
  123. M5=63775, //音符so
  124. M6=56818, //音符la
  125. M7=50607; //音符xi
  126. //每个音符对应的系统周期计数,低音音符so,频率392
  127. //周期是1/392s,换算成ns是2551020ns,每个
  128. //系统时钟周期是20ns,所以上述是2551020/20个系统周期个数127,551
  129. localparam D5=127551; //音符so,低音
  130. //每个音节的频率在系统时钟周期下对应的系统周期个数
  131. //--------------------------------------------
  132. //比如:音符1的频率是523HZ,它的周期是1/523s,换算成ns是1912045ns,每个
  133. //系统时钟周期是20ns,所以上述是1912045/20个系统周期个数,即cnt0的计数
  134. always @(posedge clk or negedge rst_n)begin
  135. if(!rst_n)begin
  136. cnt0<=0;
  137. end
  138. else if(add_cnt0)begin
  139. if(end_cnt0)
  140. cnt0<=0;
  141. else
  142. cnt0<=cnt0+1;
  143. end
  144. end
  145. assign add_cnt0=1'b1;
  146. assign end_cnt0=add_cnt0 && cnt0==pre_set-1;
  147. // assign buzzer=(cnt0>=(pre_set/2) && Min_out==1'b0)?1:0; //每个音符的占空比为50%
  148. //每个音符持续一段时间
  149. always @(posedge clk or negedge rst_n)
  150. begin
  151. if(!rst_n)begin
  152. buzzer <= 1'b1;
  153. end
  154. else if(cnt0>=(pre_set/2) && Min_out==1'b0 && Sec_out<=7'd3)
  155. buzzer <= 1'b0;
  156. else
  157. buzzer <=1'b1;
  158. end
  159. //每个音符持续一段时间
  160. always @(posedge clk or negedge rst_n)begin
  161. if(!rst_n)begin
  162. cnt1<=0;
  163. end
  164. else if(add_cnt1)begin
  165. if(end_cnt1)
  166. cnt1<=0;
  167. else
  168. cnt1<=cnt1+1;
  169. end
  170. end
  171. assign add_cnt1=end_cnt0;
  172. assign end_cnt1=add_cnt1 && cnt1==150-1;
  173. //计32个音符(两只老虎共32音节)
  174. always @(posedge clk or negedge rst_n)begin
  175. if(!rst_n)begin
  176. cnt2<=0;
  177. end
  178. else if(add_cnt2)begin
  179. if(end_cnt2)
  180. cnt2<=0;
  181. else
  182. cnt2<=cnt2+1;
  183. end
  184. end
  185. assign add_cnt2=end_cnt1;
  186. assign end_cnt2=add_cnt2 && cnt2==32-1;
  187. //存放歌曲的简谱
  188. always @(posedge clk or negedge rst_n)
  189. begin
  190. if(!rst_n)begin
  191. pre_set<=0;
  192. end
  193. else begin
  194. case(cnt2)
  195. 0:pre_set<=M1;
  196. 1:pre_set<=M2;
  197. 2:pre_set<=M3;
  198. 3:pre_set<=M1;
  199. 4:pre_set<=M1;
  200. 5:pre_set<=M2;
  201. 6:pre_set<=M3;
  202. 7:pre_set<=M1;
  203. 8:pre_set<=M3;
  204. 9:pre_set<=M4;
  205. 10:pre_set<=M5;
  206. 11:pre_set<=M3;
  207. 12:pre_set<=M4;
  208. 13:pre_set<=M5;
  209. 14:pre_set<=M5;
  210. 15:pre_set<=M6;
  211. 16:pre_set<=M5;
  212. 17:pre_set<=M4;
  213. 18:pre_set<=M3;
  214. 19:pre_set<=M1;
  215. 20:pre_set<=M5;
  216. 21:pre_set<=M6;
  217. 22:pre_set<=M5;
  218. 23:pre_set<=M4;
  219. 24:pre_set<=M3;
  220. 25:pre_set<=M1;
  221. 26:pre_set<=M2;
  222. 27:pre_set<=D5;
  223. 28:pre_set<=M1;
  224. 29:pre_set<=M2;
  225. 30:pre_set<=D5;
  226. 31:pre_set<=M1;
  227. default:pre_set<=0;
  228. endcase
  229. end
  230. end
  231. */
  232. endmodule

        对于以上各个模块内,进行了非常清晰的层次化设计,大部分都有了较为详细的标注,并通过了基础测试验证。从顶层文件开始,对照各个参数寄存器等依次往下进行理解比较,最后得出自己的设计结果。


对于各个引脚配置如下:

       对于引脚的配置,可直接将如下配置直接复制到EDA_clock.qsf文件中,然后进行综合即可直接配置完成。

  1. set_location_assignment PIN_E1 -to sys_clk
  2. set_location_assignment PIN_N13 -to rst_n
  3. set_location_assignment PIN_R14 -to seg[0]
  4. set_location_assignment PIN_N9 -to sel[0]
  5. set_location_assignment PIN_P9 -to sel[1]
  6. set_location_assignment PIN_M10 -to sel[2]
  7. set_location_assignment PIN_N11 -to sel[3]
  8. set_location_assignment PIN_P11 -to sel[4]
  9. set_location_assignment PIN_M11 -to sel[5]
  10. set_location_assignment PIN_N16 -to seg[1]
  11. set_location_assignment PIN_P16 -to seg[2]
  12. set_location_assignment PIN_T15 -to seg[3]
  13. set_location_assignment PIN_P15 -to seg[4]
  14. set_location_assignment PIN_N12 -to seg[5]
  15. set_location_assignment PIN_N15 -to seg[6]
  16. set_location_assignment PIN_R16 -to seg[7]
  17. set_location_assignment PIN_B5 -to key_in_y[3]
  18. set_location_assignment PIN_A4 -to key_in_y[2]
  19. set_location_assignment PIN_B4 -to key_in_y[1]
  20. set_location_assignment PIN_A3 -to key_in_y[0]
  21. set_location_assignment PIN_B3 -to key_out_x[3]
  22. set_location_assignment PIN_A2 -to key_out_x[2]
  23. set_location_assignment PIN_B1 -to key_out_x[1]
  24. set_location_assignment PIN_C2 -to key_out_x[0]
  25. set_location_assignment PIN_C11 -to buzzer

运行成功:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/847809
推荐阅读
相关标签
  

闽ICP备14008679号