首页IT科技fpga数字钟设计原理图(数字逻辑实验 9 FPGA数字钟(Verilog))

fpga数字钟设计原理图(数字逻辑实验 9 FPGA数字钟(Verilog))

时间2025-05-20 23:16:27分类IT科技浏览4236
导读:目录 实验 9 FPGA数字钟...

目录

实验 9 FPGA数字钟

实验分析:

实现思路:

硬件支持:

硬件描述语言代码编写:

1 顶层模块

2 时钟分频           ,(正/倒)计时器模块

3 输入处理模块in_out.v

5 24小时时钟                 ,计时      ,秒表模块

6 闹钟

7 时间设置

实验 9 FPGA数字钟

?请使用SystemVerilog/Verilog实现一个数字钟           。

要求:

(1)能够显示时分秒;

(2)能够设置开始时间;

(3)使用你自己的7段数码管显示译码电路实现;

(4)可以使用动态显示方法实现;

(5)依据实现的其他附加功能     ,酌情加分:秒表           、倒计时                 、闹钟      、…

(6)需要在Basys3 FPGA开发板上实现                 ,并通过验收 实验分析:

该实验主要考察对使用硬件设计语言(HDL)进行编程设计            ,锻炼硬件设计能力                 。

实现思路:

采用模块化设计的思想     ,将整个数字钟分解为若干功能模块:

端口映射:约束文件(代码端口映射到硬件端口)

处理按键输入的io模块:消抖                ,判断按下

特定功能模块:时钟分频            ,24小时时钟,正/倒计时                ,秒表                 ,闹钟,秒表           ,时间设置

显示模块:译码                 ,选择输出

硬件支持:

FPGA开发板:Artix-7 Basys3 from Xilinx

学习步骤:

阅读手册      ,了解结构与功能:

Basys 3 Reference Manual - Digilent Reference

若需要更详细的信息           ,则阅读所用模块的原理图

Basys 3 Schematic - Digilent

硬件描述语言代码编写:

1 顶层模块

输入:板载时钟CLK                 ,各种按键

输出:按键LED灯信号      ,数码管12位信号(4位用于位置选择     ,7位用于7段数码管显示                 ,1位用于小数点显示)

内部:

1 分线接线            ,设置缓冲区等

2 例化各种子模块

3 选择数码管输出

digital_clk.v

module digital_clk( input CLK, input BTNU, BTND, BTNL, BTNR, BTNC, input SW0, SW1, SW2, SW3, SW4, SW5, SW6, input SW13, SW14, SW15, output reg[11:0] display_out, output LD0, LD1, LD2, LD3, LD4, LD5, LD6, output LD13, LD14, LD15 ); //时钟分频 wire ms_clk; wire clock_clk; //1Hz wire scan_clk; //1kHZ wire Reset, reset, set; //按键接线 wire btnu_down, btnd_down, btnl_down, btnr_down, btnc_down; wire sw0, sw1, sw2, sw3, sw4, sw5, sw6; wire sw13, sw14, sw15; //各模块数码管输出区 wire [11:0]clock_seg ; wire [11:0]countup_seg; wire [11:0]countdown_seg; wire [11:0]alarm_seg; wire [11:0]reset_seg; wire [11:0]sw_seg; //时间设置接线 wire [3:0] sethour1; wire [3:0] sethour2; wire [3:0] setminute1; wire [3:0] setminute2; wire [3:0] setsecond1; wire [3:0] setsecond2; wire [3:0] hour1; wire [3:0] hour2; wire [3:0] minute1; wire [3:0] minute2; wire [3:0] second1; wire [3:0] second2; //wire [5:0] setbit; wire beep; assign Reset = sw0; assign reset = sw13; assign set = sw1; //LD assign LD0 = sw0; assign LD1 = sw1; assign LD2 = sw2; assign LD3 = sw3; assign LD4 = sw4; assign LD5 = sw5; assign LD6 = sw6; assign LD13 =sw13; assign LD14 = sw14; assign LD15 = sw15; div_n32p #(42950) myscan_clk( //输出1000Hz扫描时钟 .CLK(CLK), .reset(Reset), .clk(scan_clk) ); div_n32p #(43) myclock_clk( //输出1Hz计时时钟 .CLK(CLK), .reset(Reset), .clk(clock_clk) ); div_n32p #(2577) myms_clk( .CLK(CLK), .reset(Reset), .clk(ms_clk) ); // io模块 in_out myio( .CLK(CLK), .BTNU(BTNU), .BTND(BTND), .BTNL(BTNL), .BTNR(BTNR), .BTNC(BTNC), .SW0(SW0), .SW1(SW1), .SW2(SW2), .SW3(SW3), .SW4(SW4), .SW5(SW5), .SW6(SW6), .SW13(SW13), .SW14(SW14), .SW15(SW15), .btnu_down(btnu_down), .btnd_down(btnd_down), .btnl_down(btnl_down), .btnr_down(btnr_down), .btnc_down(btnc_down), .sw0(sw0), .sw1(sw1), .sw2(sw2), .sw3(sw3), .sw4(sw4), .sw5(sw5), .sw6(sw6), .sw13(sw13), .sw14(sw14), .sw15(sw15) ); // 时钟模块 clock myclock( .CLK(CLK), .clock_clk(clock_clk), //1Hz .scan_clk(scan_clk), //1kHZ .Reset(Reset), .reset(reset && sw2), .set(set && sw2), .start_pause(btnc_down && sw2), .display_h(sw15), .sethour1(sethour1), .sethour2(sethour2), .setminute1(setminute1), .setminute2(setminute2), .setsecond1(setsecond1), .setsecond2(setsecond2), .hour1 (hour1), .hour2 (hour2), .minute1(minute1), .minute2(minute2), .second1(second1), .second2(second2), .clock_seg(clock_seg[7:0]),//8 .seln(clock_seg[11:8])//4 ); //正计时模块 countup mycountup( .CLK(CLK), .clock_clk(clock_clk), //1Hz .scan_clk(scan_clk), //1kHZ .Reset(Reset), .reset(reset && sw3), .set(set && sw3), .display_h(sw15), .start_pause(btnc_down && sw3), .sethour1(sethour1), .sethour2(sethour2), .setminute1(setminute1), .setminute2(setminute2), .setsecond1(setsecond1), .setsecond2(setsecond2), .clock_seg(countup_seg[7:0]),//8 .seln(countup_seg[11:8])//4 ); //倒计时模块 countdown mycountdown( .CLK(CLK), .clock_clk(clock_clk), //1Hz .scan_clk(scan_clk), //1kHZ .Reset(Reset), .reset(reset && sw4), .set(set && sw4), .display_h(sw15), .start_pause(btnc_down && sw4), .sethour1(sethour1), .sethour2(sethour2), .setminute1(setminute1), .setminute2(setminute2), .setsecond1(setsecond1), .setsecond2(setsecond2), .clock_seg(countdown_seg[7:0]),//8 .seln(countdown_seg[11:8])//4 ); //闹钟模块 alarm myalarm( .CLK(CLK), .clock_clk(clock_clk), //1Hz .scan_clk(scan_clk), //1kHZ .Reset(Reset), .reset(reset && sw5), .set(set && sw5), .start(sw14), .display_h(sw15), .hour1(hour1), .hour2(hour2), .minute1(minute1), .minute2(minute2), .second1(second1), .second2(second2), .sethour1(sethour1), .sethour2(sethour2), .setminute1(setminute1), .setminute2(setminute2), .setsecond1(setsecond1), .setsecond2(setsecond2), .clock_seg(alarm_seg[7:0]),//8 .seln(alarm_seg[11:8]),//4 .beep(beep) ); //秒表 stopwatch mystopwatch( .CLK(CLK), .ms_clk(ms_clk), .scan_clk(scan_clk), .Reset(Reset),//全局复 .reset(reset && sw6),//功能复 .start_pause(btnc_down && sw6), .display_h(display_h), .clock_seg(sw_seg[7:0]),//8 .seln(sw_seg[11:8])//4 ); button_set_time myset( .CLK(CLK), .Reset(Reset), // .reset(1b0), .reset(reset && set), .set(set), .btnu_down(btnu_down), .btnd_down(btnd_down), .btnl_down(btnl_down), .btnr_down(btnr_down), .btnc_down(btnc_down), //out .sethour1(sethour1), .sethour2(sethour2), .setminute1(setminute1), .setminute2(setminute2), .setsecond1(setsecond1), .setsecond2(setsecond2)); reset_module myreset( .CLK(CLK), .Reset(Reset), .scan_clk(scan_clk), .clock_clk(clock_clk), .clock_seg(reset_seg[7:0]), .seln(reset_seg[11:8])); //选择输出 always@(posedge CLK, negedge Reset)begin if(~Reset) display_out <= reset_seg; else if(sw2) begin//时钟 if(beep && sw14)display_out <=alarm_seg;//闹钟响了 else display_out <= clock_seg; end else if(sw3)display_out <=countup_seg; //正计时 else if(sw4)display_out <=countdown_seg;//倒计时 else if(sw5)display_out <=alarm_seg; //闹钟 else if(sw6)display_out <=sw_seg; //秒表 else display_out <= reset_seg; end endmodule 2 时钟分频     ,(正/倒)计时器模块

时钟分频

将板载输入100MHz分频为所需频率:

1000Hz用于扫描

1Hz用于时钟计时

60Hz用于秒表计时

由分频公式\(f=\frac{p}{2^{N}} f_{0}\)                ,得\(p=\frac{2^{N} f}{f_{0}}\)      。采用32位计数器即可求得对应分频器的加数\(p\)

div_n32p.v

module div_n32p #(parameter p = 43)(//1Hz input CLK, reset, output clk); reg [31:0] counter; always@(posedge CLK, negedge reset)begin if(!reset) counter<=32b0; else begin counter = counter + p; end end assign clk = counter[31]; endmodule

1位计时器

通过输入的进位计时增加            ,满量程后进位     。带有设置时间和开始/暂停功能                 。

counter.v

module counter #(parameter base = 10)( input CLK, input cin, input reset, input set, input start_pause, input [3:0] scount, output reg[3:0] count, output reg cout ); reg [2:0] cin_reg; wire cin_p; reg mode;//1开始 0暂停 always@(posedge CLK, negedge reset)begin if(!reset) mode<=1b0; else if(start_pause) mode<=~mode; else begin mode<=mode; end end always@(posedge CLK, negedge reset)begin if(!reset) begin cin_reg <=3b0; end else begin cin_reg <={cin_reg[1:0], cin}; end end assign cin_p = ~cin_reg[2] && cin_reg[1]; always@(posedge CLK, negedge reset)begin if(!reset) begin //复位 count <= 4b0; cout <= 0; end else if(set==1b1)begin//设置时间 count <= scount; cout <=0; end else begin//自增 if(count >= base)begin cout <= 1; count <=4b0; end else if(cin_p && mode==1b1) begin count <=count+1; cout<=0; end else begin count<=count; cout<=0; end end end endmodule

1位倒计时器

与计时器相仿,减一个数等价于加上其补码                ,-1→+15

decounter.v

`timescale 1ns / 1ps module decounter #(parameter base = 10)( input CLK, input cin, input reset, input set, input start_pause, input [3:0] scount, output reg[3:0] count, output reg cout); reg [2:0] cin_reg; wire cin_p; reg mode;//1开始 0暂停 always@(posedge CLK, negedge reset)begin if(!reset) mode<=1b0; else if(start_pause) mode<=~mode; else begin mode<=mode; end end always@(posedge CLK, negedge reset)begin if(!reset) begin cin_reg <=3b0; end else begin cin_reg <={cin_reg[1:0], cin}; end end assign cin_p = ~cin_reg[2] && cin_reg[1]; always@(posedge CLK, negedge reset)begin if(!reset) begin //复位 count <= 4b0; cout <= 0; end else if(set==1b1)begin//设置时间 count <= scount; end else begin//自增 if(cin_p && mode==1b1) begin if(count !=0 ) count <=count + 4d15; else begin count <=base-1; cout <= 1; end end else begin count<=count; cout<=0

; end end end endmodule 3 输入处理模块in_out.v

输入:未处理的按键接线

输出:处理后按键接线

按键消抖

判断Button按下

按键消抖

由于按键输入可能存在的抖动与对板载时钟的异步输入                 ,所以要进行消抖与同步处理            。

这里采用了简单的串联触发器构成的同步器,通过三级或更多级触发器           ,使得最终采样到的信号为亚稳态的概率尽可能小                 ,即输出趋于稳定     。

module debounce

module debounce( input CLK, input key_in, output key_out ); reg delay1; reg delay2; reg delay3; always@(posedge CLK)begin delay1 <= key_in; delay2 <= delay1; delay3 <= delay2; end assign key_out = delay3; endmodule

判断Button按下

按下      ,电位由低变高(或相反)           ,存在一个上升沿                 ,能不能用posedge检测上升沿来检测按键按下?

本着老师所说的控制信号与其他信号分开处理的要求      ,这里采取了连续采样寄存多次Button信号     ,通过判断临近两位是否出现相反的电位                 ,来确定是否出现了上升沿                。(相当于又做了一次同步??,感觉这里处理的不简练)

btn_down

wire btnu; reg [2:0] btnu_reg; assign btnu_down = ~btnu_reg[2]&(btnu_reg[1]); always@(posedge CLK)begin btnu_reg<= {btnu_reg[1:0], btnu}; end 5 24小时时钟            ,计时     ,秒表模块

?输入:

各时钟与复位信号:CLK                ,clock_clk            ,scan_clk,Reset

设置时间                ,开始/暂停                 ,高位显示信号

?输出:

设置时间值,输出时间值           ,数码管信号

例化counter                 ,采用逐级进位

counter #(6) s1_counter( .CLK(CLK), .reset(Reset&&~reset), .set(set), .start_pause(start_pause), .cin(cout[0]), .scount(setsecond1), .count(second1), .cout(cout[1]) );

扫描

reg [2:0] sel; always@(posedge scan_clk, negedge Reset)begin if(!Reset) sel<=0; else if(sel==3) sel<=0; else sel <= sel+1; end always@(posedge scan_clk, negedge Reset)begin if(~Reset || full)begin {seln, clock_seg} <= 12b1101_0000001_1; end else if(~display_h)begin case(sel) 3d0:{seln, clock_seg} <= {4b0111, encoder(minute1), 1b1}; 3d1:{seln, clock_seg} <= {4b1011, encoder(minute2), dot }; 3d2:{seln, clock_seg} <= {4b1101, encoder(second1), 1b1}; 3d3:{seln, clock_seg} <= {4b1110, encoder(second2), dot }; default:{seln, clock_seg} <= 12b1011_0000000_0; endcase end else begin case(sel) 3d0:{seln, clock_seg} <= {4b0111, encoder(hour1), 1b1}; 3d1:{seln, clock_seg} <= {4b1011, encoder(hour2), dot }; 3d2:{seln, clock_seg} <= {4b1101, encoder(minute1), 1b1}; 3d3:{seln, clock_seg} <= {4b1110, encoder(minute2), dot }; default:{seln, clock_seg} <= 12b0111_0000000_0; endcase end end

译码器(函数)

function [6:0]encoder; input [3:0] dec; begin case(dec) 4d0: encoder = 7b0000_001; 4d1: encoder = 7b1001_111; 4d2: encoder = 7b0010_010; 4d3: encoder = 7b0000_110; 4d4: encoder = 7b1001_100; 4d5: encoder = 7b0100_100; 4d6: encoder = 7b0100_000; 4d7: encoder = 7b0001_111; 4d8: encoder = 7b0000_000; 4d9: encoder = 7b0000_100; default: encoder = 7b1000_000; endcase end endfunction

正倒计时模块:正计时与24小时时钟模块相同      ,倒计时采用decounter即可

秒表:接入ms_clk即可           ,其他与时钟相同

6 闹钟

输入同上                 ,另增一闹钟开关输入

输出另增一beep输出      ,用于响铃

思路:记录所设置的闹钟时间     ,与时钟时间比较                 ,判断是否触发响铃            。

响铃逻辑beep

output reg beep; always@(posedge clock_clk, negedge Reset)begin if(~Reset)begin count <= 4b0; pre <= 1b0; count <=4b0; end else if(onclk && start)begin //到时间 pre <=1; count<=0; beep <=1; end else if(pre==1 && count <4d5)begin count<= count+1; end else if(pre==1 && count>=4d5)begin pre<=0; count<=0; beep<=0; end else begin pre<=pre; count<=count; beep<=beep; end end 7 时间设置

输入:各控制信号            ,按键信号

输出:所设置的时间

维护reg [5:0] setbit;通过左右按键调整设置哪一位     ,上下按键增减数值。

按键功能:

SW15显示高位 SW14闹钟开关 SW13功能归零reset高有效 SW6秒表 SW5闹钟 SW4倒计时 SW3正计时 SW2时钟 SW1设置时间 SW0全局复位

按键去抖动:

(154条消息) FPGA中的按键消抖_Super-fei的博客-CSDN博客_fpga按键消抖

Verilog——FPGA按键去抖操作_Footprints明轩的博客-CSDN博客

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
大公司的管理制度(robbin谈管理:大公司体制内创新的困境) 网站关键词排名优化公司(如何选择优秀的SEO网站关键词优化公司?)