基于fpga的电子时钟

news2024/10/5 20:07:15

文章目录

  • 前言
  • 实验手册
  • 一、实验目的
  • 二、实验原理
    • 1.理论原理
    • 2.硬件原理
  • 三、系统架构设计
  • 四、模块说明
    • 1.模块端口信号列表
      • 按键消抖模块(key)
      • 计数器模块(counter)
      • 蜂鸣器乐谱模块(music)
      • 蜂鸣器发声模块(beep)
      • 数码管驱动模块(seg_driver)
      • 顶层模块(top)
    • 2.状态转移图
    • 3.时序图
  • 五、仿真波形图
  • 六、引脚分配
        • tcl引脚分配文件
  • 七、代码编写
      • 按键消抖模块(key)
      • 计数器模块(counter)
      • 蜂鸣器乐谱模块(music)
      • 蜂鸣器发声模块(beep)
      • 数码管驱动模块(seg_driver)
      • 顶层模块(top)
  • 八、总结

前言

本次实验是本人全部用状态机实现的,所以导致我的状态机空间有很多状态,有一部分状态可以进行修改,不过我认为全部写成状态机更有利于本人的理解,可能有人会觉得很绕,不过根据本人自身所画的状态图,就能较为清晰明了,本文结尾会附上所有代码,如有需要自取即可,但我希望不要一味的复制粘贴,理解之后手敲一遍更能提升自己,如需要工程文件私信我或者评论区找我,现写下此博客以便日后复习。

该开发板为EP4CE6F17C8

实验手册

一、实验目的

1、完成基本24时计数显示2、显示时间可调整
3、设置可调闹钟-闹钟触发时蜂鸣器响起

二、实验原理

1.理论原理

蜂鸣器原理输入一定频率的方波或者PWM信号,蜂鸣器就可以发出声音。输入不同频率的信号,蜂鸣器可以发出不同音色的声音
电子时钟原理是在数码管中由于有位选信号和段选信号的存在,我们无法做到将数码管同时进行变换,所以我们通过快速的切换和刷新,使得我们看到的是连续的多个数码管显示结果。通过控制位选信号和段选信号,可以实现不同的数字、字母或符号的动态显示。
将按键进行按键消抖获得稳定信号。
使用按键通过状态机切换状态控制位选信号,在相应的位选信号下,控制时位,分位,秒位的数值加一或减一。
利用相同原理使用状态机切换状态——闹钟设置状态和常规电子时钟状态,闹钟设置状态下对时分秒位的操作与电子时钟状态一致
闹钟状态中将设定的值与电子时钟的值比对,如果相等则传给蜂鸣器一个信号,并将该信号持续5s,是蜂鸣器发声五秒

2.硬件原理

我所用的板子自带的蜂鸣器是无源蜂鸣器——内部无振荡源,需要通以方波、PWM信号才能发出声音,以及是低电平有效
开发板上的数码管数码管位选和段选信号都是低电平有效。
位选信号根据需要的显示位数进行生成(该实验所用的开发板可用6个位选信号),它们用于选择要显示的数码管。
段选信号根据需要的显示内容进行生成,它们用于控制数码管的七段LED灯以及小数点LED灯的亮灭状态(该实验所用的开发板可用8个段选信号)。

三、系统架构设计

本次实验有按键消抖模块、计数器模块、蜂鸣器模块、音符模块、数码管驱动模块
按键消抖模块将按键信号转化为稳定的有效信号
计数器模块通过输入的有效按键信号,key3切换闹钟和电子时钟状态,以及key1加,key2减,时位、分位、秒位的加减,比对闹钟空间和电子时钟的时间数值,若相等则传出一个蜂鸣器信号给音符模块,再将hour、min、sec数值输入进数码管驱动模块进行显示。
数码管驱动模块,将计数器模块产生的时分秒显示出来,以及key0切换时位、分位、秒位、空闲状态的切换。
音符模块,接收到闹钟信号时将pwm波发给蜂鸣器发声模块,持续5s
蜂鸣器模块,接收到音符模块的pwm波,发声
顶层模块,统合所有模块。

四、模块说明

1.模块端口信号列表

按键消抖模块(key)

端口信号信号类型信号名称信号作用
inputwireclk时钟信号
inputwirerst_n复位信号
inputwirekey_in按键信号
outputregkey_flag稳定按键信号

计数器模块(counter)

端口信号信号类型信号名称信号作用
inputwireclk时钟信号
inputwirerst_n复位信号
inputwirekey_flag稳定按键信号
outputwirehour小时数
outputwiremin分钟数
outputwiresec秒钟数
outputwiremusic_flag闹钟信号

蜂鸣器乐谱模块(music)

端口信号信号类型信号名称信号作用
inputwireclk时钟信号
inputwirerst_n复位信号
inputwiremusic_flag闹钟到时信号
outputregflag蜂鸣器发声信号

蜂鸣器发声模块(beep)

端口信号信号类型信号名称信号作用
inputwireclk时钟信号
inputwirerst_n复位信号
inputwireflag控制蜂鸣器发声信号
outputregbeep蜂鸣器发声信号

数码管驱动模块(seg_driver)

端口信号信号类型信号名称信号作用
inputwireclk时钟信号
inputwirerst_n复位信号
inputwirehour小时数
inputwiremin分钟数
inputwiresec秒钟数
inputwirekey_flag稳定按键信号
outputregseg数码管段选信号
outputregsel数码管位选信号

顶层模块(top)

端口信号信号类型信号名称信号作用
inputwireclk时钟信号
inputwirerst_n复位信号
inputwirekey_in按键信号
outputwiresel数码管位选信号
outputwirebeep蜂鸣器发声信号
outputwireseg数码管段选信号

2.状态转移图

请添加图片描述

闹钟空间时分秒位也有相同的加一减一,这里本人偷懒了没有画全。

3.时序图

请添加图片描述

五、仿真波形图

无仿真波形

六、引脚分配

请添加图片描述

tcl引脚分配文件

# Copyright (C) 2018  Intel Corporation. All rights reserved.
# Your use of Intel Corporation's design tools, logic functions 
# and other software and tools, and its AMPP partner logic 
# functions, and any output files from any of the foregoing 
# (including device programming or simulation files), and any 
# associated documentation or information are expressly subject 
# to the terms and conditions of the Intel Program License 
# Subscription Agreement, the Intel Quartus Prime License Agreement,
# the Intel FPGA IP License Agreement, or other applicable license
# agreement, including, without limitation, that your use is for
# the sole purpose of programming logic devices manufactured by
# Intel and sold by Intel or its authorized distributors.  Please
# refer to the applicable agreement for further details.

# Quartus Prime Version 18.1.0 Build 625 09/12/2018 SJ Standard Edition
# File: D:\intelFPGA\code\dig_clock\tcl\dig_clock.tcl
# Generated on: Mon Aug 07 16:06:21 2023

package require ::quartus::project

set_location_assignment PIN_A5 -to seg[7]
set_location_assignment PIN_B8 -to seg[6]
set_location_assignment PIN_A7 -to seg[5]
set_location_assignment PIN_B6 -to seg[4]
set_location_assignment PIN_B5 -to seg[3]
set_location_assignment PIN_A6 -to seg[2]
set_location_assignment PIN_A8 -to seg[1]
set_location_assignment PIN_B7 -to seg[0]
set_location_assignment PIN_E1 -to clk
set_location_assignment PIN_A4 -to sel[0]
set_location_assignment PIN_B4 -to sel[1]
set_location_assignment PIN_A3 -to sel[2]
set_location_assignment PIN_B3 -to sel[3]
set_location_assignment PIN_A2 -to sel[4]
set_location_assignment PIN_B1 -to sel[5]
set_location_assignment PIN_E15 -to key_in[0]
set_location_assignment PIN_E16 -to key_in[1]
set_location_assignment PIN_M16 -to key_in[2]
set_location_assignment PIN_M15 -to key_in[3]
set_location_assignment PIN_J1 -to beep

七、代码编写

按键消抖模块(key)

module key
(
    input wire clk,
    input wire rst_n,
    input wire key_in,
    output reg key_out,
    output reg key_flag
);

parameter  CNT_MAX =20'd999_999; //20ms计数

reg [19:0] cnt_20ms;
//reg key_flag;
//20ms消抖
always@(posedge clk or negedge rst_n)
    if(!rst_n)
        cnt_20ms<=20'b0;
    else if(key_in==1'b1)
        cnt_20ms<=20'd0;
    else if(cnt_20ms==CNT_MAX)
        cnt_20ms<=CNT_MAX;
    else
        cnt_20ms<=cnt_20ms+20'd1;
//取单个脉冲信号
always@(posedge clk or negedge rst_n)
    if(!rst_n)
        key_flag<=1'b0;
    else if(cnt_20ms==(CNT_MAX-20'd1))
        key_flag<=1'b1;
    else
        key_flag<=1'b0;
//有效长信号
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_out <= 1'b0;
    end
    else if(key_flag == 1'b1)
        key_out <= ~key_out;
    else
        key_out <= key_out;
end
    
endmodule

计数器模块(counter)

module counter (
    input    wire         clk    ,
    input    wire         rst_n  ,
    input    wire [3:0]   key_flag,
    output   wire [4:0]   hour   ,
    output   wire [5:0]   min    ,
    output   wire [5:0]   sec    ,
    output   wire         music_flag    
);

reg        [8:0]       cnt_min    ;//存放分钟加减
reg        [8:0]       cnt_sec    ;//存放秒钟加减
parameter MAXDAY = 17'd86400;//24小时 24*60*60
parameter SEC = 26'd50_000_000;//1s
reg        [5:0]       c_state;//现态,数码管选位加减
reg        [5:0]       n_state;//次态,数码管选位加减
parameter   IDLE = 6'd0,        //空闲状态
            S1   = 6'd1,        //数码管时位
            S2   = 6'd2,        //数码管分位
            S3   = 6'd3,        //数码管秒位
            A1   = 6'd4,        //时位加
            M1   = 6'd5,        //时位减
            A2   = 6'd6,        //分位加
            M2   = 6'd7,        //分位减
            A3   = 6'd8,        //秒位减
            M3   = 6'd9,        //秒位减

            R1   = 6'd10,       //闹钟功能空间      
            P1   = 6'd11,        //数码管时位
            P2   = 6'd12,        //数码管分位
            P3   = 6'd13,        //数码管秒位
            A4   = 6'd14,        //时位加
            M4   = 6'd15,        //时位减
            A5   = 6'd16,        //分位加
            M5   = 6'd17,        //分位减
            A6   = 6'd18,        //秒位减
            M6   = 6'd19;        //秒位减
reg        [25:0]      cnt_1s     ;
reg        [25:0]      cnt_1s_r   ;
reg        [16:0]      cnt_day    ;
reg        [16:0]      cnt_day_r  ;
reg        [31:0]      cnt_10ms   ;
wire [4:0]   hour_r  ;//存放时钟加减
wire [5:0]   min_r   ;
wire [5:0]   sec_r   ;
parameter MAX_10ms = 32'd499_999;
//10ms计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_10ms <= 26'd0;
    end
    else if (cnt_10ms == MAX_10ms) begin
        cnt_10ms <= 26'd0;
    end
    else begin
        cnt_10ms <= cnt_10ms + 1'd1;
    end
end
//1秒计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_1s <= 26'd0;
    end
    else if (cnt_1s == SEC - 1'd1) begin
        cnt_1s <= 26'd0;
    end
    else begin
        cnt_1s <= cnt_1s + 1'd1;
    end
end
//1天计时器_电子时钟
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		cnt_day <= 17'd0;
	end 
	else if(cnt_day >= MAXDAY - 1'd1)begin
		cnt_day <= 17'd0;
	end 
	else if( cnt_1s == SEC -1'd1)begin
		cnt_day <= cnt_day + 1'd1;
	end
    else if(n_state == A1) begin
        cnt_day <= cnt_day + 17'd3600;
    end
    else if(n_state == M1) begin
        cnt_day <= cnt_day - 17'd3600;
    end
    else if (c_state== S1 &&key_flag[2] && (cnt_day / 3600)== 0) begin
        cnt_day <= cnt_day + 17'd82800;
    end
    else if(n_state == A2) begin
        cnt_day <= cnt_day + 17'd60;
    end
    else if(n_state == M2) begin
        cnt_day <= cnt_day - 17'd60;
    end
    else if(n_state == A3) begin
        cnt_day <= cnt_day + 17'd1;
    end
    else if(n_state == M3) begin
        cnt_day <= cnt_day - 17'd1;
    end
	else begin
		cnt_day <= cnt_day;
	end 
end 
//1天计时器_r_闹钟空间
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		cnt_day_r <= 17'd0;
	end 
	else if(cnt_day_r >= MAXDAY - 1'd1)begin
		cnt_day_r <= 17'd0;
	end 
    else if(n_state == A4) begin
        cnt_day_r <= cnt_day_r + 17'd3600;
    end
    else if(n_state == M4) begin
        cnt_day_r <= cnt_day_r - 17'd3600;
    end
    else if (key_flag[2] && (cnt_day_r / 3600)== 0) begin
        cnt_day_r <= cnt_day_r + 17'd82800;
    end
    else if(n_state == A5) begin
        cnt_day_r <= cnt_day_r + 17'd60;
    end
    else if(n_state == M5) begin
        cnt_day_r <= cnt_day_r - 17'd60;
    end
    else if(n_state == A6) begin
        cnt_day_r <= cnt_day_r + 17'd10;
    end
    else if(n_state == M6) begin
        cnt_day_r <= cnt_day_r - 17'd10;
    end
	else begin
		cnt_day_r <= cnt_day_r;
	end 
end 
//如果处于闹钟空间则输出闹钟空间的值,否则输出电子时钟的值
assign hour = (c_state == R1||c_state == P1||c_state == P2||c_state == P3)?hour_r:cnt_day / 3600;
assign min = (c_state == R1||c_state == P1||c_state == P2||c_state == P3)?min_r:cnt_day % 3600 / 60;
assign sec = (c_state == R1||c_state == P1||c_state == P2||c_state == P3)?sec_r:cnt_day % 3600 % 60;

assign hour_r = cnt_day_r /3600;
assign min_r = cnt_day_r % 3600 / 60;
assign sec_r = cnt_day_r % 3600 % 60;
//如果闹钟空间的值除了零以外的时候和电子时钟的值相等,则输出音乐信号给乐谱模块
assign music_flag = (cnt_day == cnt_day_r&&cnt_day_r != 0)?1:0;
//数码管选位状态机,第一段
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        c_state <= IDLE;
    end
    else begin
        c_state <= n_state;
    end
end
//数码管选位状态机,第二段
always @(*) begin
    case (c_state)
    //空闲状态
        IDLE    :begin
            if (key_flag[0]) begin
                n_state = S1;
            end
            else if (key_flag[3]) begin
                n_state = R1;
            end
            else begin
                n_state = IDLE;
            end
        end
    //闹钟状态
        R1      :begin
            if (key_flag[0]) begin
                n_state = P1;
            end
            else if (key_flag[3]) begin
                n_state = IDLE;
            end
            else begin
                n_state = R1;
            end
        end
    //换位状态:时位
        S1      :begin
            if (key_flag[0]) begin
                n_state = S2;
            end
            else if (key_flag[1]) begin
                n_state = A1;
            end
            else if (key_flag[2]) begin
                n_state = M1;
            end
            else begin
                n_state = S1;
            end
        end  
    //换位状态:分位
        S2      :begin
            if (key_flag[0]) begin
                n_state = S3;
            end
            else if (key_flag[1]) begin
                n_state = A2;
            end
            else if (key_flag[2]) begin
                n_state = M2;
            end
            else begin
                n_state = S2;
            end
        end
    //换位状态:秒位  
        S3      :begin
            if (key_flag[0]) begin
                n_state = IDLE;
            end
            else if (key_flag[1]) begin
                n_state = A3;
            end
            else if (key_flag[2]) begin
                n_state = M3;
            end
            else begin
                n_state = S3;
            end
        end
    //时位加一
        A1      :begin
            if (c_state == A1) begin
                n_state = S1;
            end
            else begin
                n_state = A1;
            end
        end
    //时位减一  
        M1      :begin
            if (c_state == M1) begin
                n_state = S1;
            end
            else begin
                n_state = M1;
            end
        end
    //分位加一
        A2      :begin
            if (c_state == A2) begin
                n_state = S2;
            end
            else begin
                n_state = A2;
            end
        end
    //分位减一  
        M2      :begin
            if (c_state == M2) begin
                n_state = S2;
            end
            else begin
                n_state = M2;
            end
        end
    //秒位加一
        A3      :begin
            if (c_state == A3) begin
                n_state = S3;
            end
            else begin
                n_state = A3;
            end
        end
    //秒位减一  
        M3      :begin
            if (c_state == M3) begin
                n_state = S3;
            end
            else begin
                n_state = M3;
            end
        end
    //闹钟空间:时位
        P1      :begin
            if (key_flag[0]) begin
                n_state = P2;
            end
            else if (key_flag[1]) begin
                n_state = A4;
            end
            else if (key_flag[2]) begin
                n_state = M4;
            end
            else begin
                n_state = P1;
            end
        end
    //闹钟空间:分位  
        P2      :begin
            if (key_flag[0]) begin
                n_state = P3;
            end
            else if (key_flag[1]) begin
                n_state = A5;
            end
            else if (key_flag[2]) begin
                n_state = M5;
            end
            else begin
                n_state = P2;
            end
        end
    //闹钟空间:秒位  
        P3      :begin
            if (key_flag[0]) begin
                n_state = R1;
            end
            else if (key_flag[1]) begin
                n_state = A6;
            end
            else if (key_flag[2]) begin
                n_state = M6;
            end
            else begin
                n_state = P3;
            end
        end
    //闹钟空间:时位加一  
        A4      :begin
            if (c_state == A4) begin
                n_state = P1;
            end
            else begin
                n_state = A4;
            end
        end
    //闹钟空间:时位减一    
        M4      :begin
            if (c_state == M4) begin
                n_state = P1;
            end
            else begin
                n_state = M4;
            end
        end
    //闹钟空间:分位加一  
        A5      :begin
            if (c_state == A5) begin
                n_state = P2;
            end
            else begin
                n_state = A5;
            end
        end
    //闹钟空间:分位减一    
        M5      :begin
            if (c_state == M5) begin
                n_state = P2;
            end
            else begin
                n_state = M5;
            end
        end
    //闹钟空间:秒位加一  
        A6      :begin
            if (c_state == A6) begin
                n_state = P3;
            end
            else begin
                n_state = A6;
            end
        end
    //闹钟空间:秒位减一    
        M6      :begin
            if (c_state == M6) begin
                n_state = P3;
            end
            else begin
                n_state = M6;
            end
        end
        default:n_state = IDLE;
    endcase
end    
endmodule

蜂鸣器乐谱模块(music)

module music (
    input clk,
    input rst_n,
    input [3:0] key_out,
    input music_flag,
    output reg flag
);



parameter MAX_0_3s = 25'd14_999_999;
reg [31:0] cnt_0_3s;
parameter MAX_1s = 32'd49_999_999;
reg        [31:0]      cnt_1s     ;//1秒计数器
parameter MAX_5s = 32'd5;
reg        [31:0]      cnt_5s     ;//1秒计数器

parameter MAX_7    = 6'd33;//乐谱
reg [15:0]  cnt_7; //乐谱1寄存器
//parameter MAX_two_tiger = 6'd33;
//reg [15:0]  two_tiger;//乐谱2寄存器
//parameter MAX_300ms = 25'd14_999_999;
//reg [31:0] cnt_300ms;
reg [15:0] music_cnt;//音符音频计数器         
wire[15:0] duty;//占空比
reg [15:0] music;//音符数据寄存器
reg music_delay;

parameter   DO  	= 16'd47750		;//1
parameter   RE  	= 16'd42250		;//2
parameter   MI  	= 16'd37900		;//3
parameter   FA  	= 16'd37550		;//4
parameter   SO  	= 16'd31850		;//5
parameter   LA      = 16'd28400		;//6
parameter   XI      = 16'd25400		;//7
//1s计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_1s <= 1'd0;
    end
    else if (cnt_1s == MAX_1s) begin
        cnt_1s <= 1'd0;
    end
    else begin
        cnt_1s <= cnt_1s + 1;
    end
end
//5s计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_5s <= 1'd0;
    end
    else if (cnt_5s == MAX_5s) begin
        cnt_5s <= 1'd0;
    end
    else if (cnt_1s == MAX_1s)begin
        cnt_5s <= cnt_5s + 1;
    end
end
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        music_delay <= 1'd0;
    end
    else if (cnt_5s == MAX_5s) begin
        music_delay <= 1'd0;
    end
    else if (music_flag)begin
        music_delay <= 1'd1;
    end
end
//0.3s计数器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_0_3s <= 1'd0;
    end
    else if (cnt_0_3s == MAX_0_3s) begin
        cnt_0_3s <= 1'd0;
    end
    else begin
        cnt_0_3s <= cnt_0_3s + 1;
    end
end
//300ms计数器
//always @(posedge clk or negedge rst_n) begin
//    if (!rst_n) begin
//        cnt_300ms <= 1'd0;
//    end
//    else if (cnt_300ms == MAX_300ms) begin
//        cnt_300ms <= 1'd0;
//    end
//    else begin
//        cnt_300ms <= cnt_300ms + 1;
//    end
//end
//音符频率计数器
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		music_cnt <= 16'd0;
	end 
	else if(music_cnt == music)begin
		music_cnt <= 16'd0;
	end

	else begin
		music_cnt <= music_cnt + 1'd1;
	end 
end


//乐谱1每隔0.5s音符切换
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_7 <= 1'd0;
    end
    else if (cnt_7 == MAX_7 && cnt_0_3s == MAX_0_3s) begin
        cnt_7 <= 1'd0;
    end
    else if (cnt_0_3s == MAX_0_3s) begin
        cnt_7 <= cnt_7 + 1'd1;
    end
    else begin
        cnt_7 <= cnt_7;
    end
end

//乐谱2的切换间隔为300ms
//always @(posedge clk or negedge rst_n) begin
//    if (!rst_n) begin
//        two_tiger <= 1'd0;
//    end
//    else if (two_tiger == MAX_two_tiger && cnt_300ms == MAX_300ms) begin
//        two_tiger <= 1'd0;
//    end
//    else if (cnt_300ms == MAX_0_5s) begin
//        two_tiger <= two_tiger + 1'd1;
//    end
//    else begin
//        two_tiger <= two_tiger;
//    end
//end
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        music <= 1'd0;
    end
    //else if(key_out[1])begin
        case (cnt_7)
            6'd0:	music <= DO;
			6'd1:	music <= RE;
			6'd2:	music <= MI;						
			6'd3:	music <= DO;					
			6'd4:	music <= DO;					
			6'd5:	music <= RE;					
			6'd6:	music <= MI;					
			6'd7:	music <= DO;					
			6'd8:	music <= MI;					
			6'd9:	music <= FA;					
			6'd10:	music <= SO;
			6'd11:	music <= MI;
			6'd12:	music <= FA;
			6'd13:	music <= SO;
			6'd14:	music <= SO;
			6'd15:	music <= LA;
			6'd16:	music <= SO;
			6'd17:	music <= FA;
			6'd18:	music <= MI;
			6'd19:	music <= DO;
			6'd20:	music <= SO;
			6'd21:	music <= LA;
			6'd22:	music <= SO;
			6'd23:	music <= FA;
			6'd24:	music <= MI;
			6'd25:	music <= DO;
			6'd26:	music <= RE;
			6'd27:	music <= SO;
			6'd28:	music <= DO;
			6'd29:	music <= DO;
			6'd30:	music <= RE;
			6'd31:	music <= SO;
			6'd32:	music <= DO;
			6'd33:	music <= DO;
			default:music <= DO;
        endcase
    //end
    //else if(!key_out[1])begin
    //   case(cnt_7)
	//		10'd0: music <= DO  ;
    //        10'd1: music <= RE  ;
    //        10'd2: music <= MI  ;
    //        10'd3: music <= FA  ;
    //        10'd4: music <= SO  ;
    //        10'd5: music <= LA  ;
    //        10'd6: music <= XI  ;
    //        10'd7: music <= DO  ;
    //        10'd8: music <= RE  ;
    //        10'd9: music <= MI  ;
    //        10'd10: music <= FA  ;
    //        10'd11: music <= SO  ;
    //        10'd12: music <= LA  ;
    //        10'd13: music <= XI  ;
    //        10'd14: music <= DO  ;
    //        10'd15: music <= RE  ;
    //        10'd16: music <= MI  ;
    //        10'd17: music <= FA  ;
    //        10'd18: music <= SO  ;
    //        10'd19: music <= LA  ;
    //        10'd20: music <= XI  ;
    //        default: music <= DO;
    //    endcase
    //end
end
assign duty = music >> 1;//占空比50%
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		flag <= 1'b0;
	end 
	else if (music_delay)begin
		flag <= (music_cnt >= duty) ? 1'b1 : 1'b0; 
	end
	else begin
        flag <= 1'b0;
    end
end
    
endmodule

蜂鸣器发声模块(beep)

module beep (
    input clk,
    input rst_n,
    input flag,
    output reg beep
);

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
       beep <= 1'b1; 
    end
    else if (flag) begin
        beep <= 1'b0;
    end
    else begin
        beep <= 1'b1;
    end
end
    
endmodule

数码管驱动模块(seg_driver)

module seg_driver (
    input   wire         clk     ,
    input   wire         rst_n   ,
    input   wire [4:0]   hour   ,
    input   wire [5:0]   min    ,
    input   wire [5:0]   sec    ,    
    input   wire [3:0]   key_flag,
    output   reg  [7:0]  seg    , //段选信号
    output   reg  [5:0]  sel      //位选信号

);


reg        [8:0]       cnt_num    ;
reg        [19:0]      cnt_delay  ;
reg        [7:0]       point      ;//小数点
reg        [5:0]       c_state;//现态,数码管选位加减
reg        [5:0]       n_state;//次态,数码管选位加减
parameter   IDLE = 6'd0,        //空闲状态
            S1   = 6'd1,        //数码管时位
            S2   = 6'd2,        //数码管分位
            S3   = 6'd3;        //数码管秒位
            


parameter MAX_1s = 32'd24_999_999;
reg        [31:0]      cnt_1s     ;//1秒计数器

parameter CNT_DELAY_MAX= 10'd1000   ;
parameter ZERO         = 8'b1100_0000    ,
          ONE          = 8'b1111_1001    ,
          TWO          = 8'b1010_0100    ,
          THREE        = 8'b1011_0000    ,
          FOUR         = 8'b1001_1001    ,
          FIVE         = 8'b1001_0010    ,
          SIX          = 8'b1000_0010    ,
          SEVEN        = 8'b1111_1000    ,
          EIGHT        = 8'b1000_0000    ,
          NINE         = 8'b1001_0000    ;
			 
//1s计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_1s <= 10'd0;
    end
    else if(cnt_1s == MAX_1s)begin
        cnt_1s <= 10'd0;
    end
    else begin
        cnt_1s <= cnt_1s + 1'd1;
    end
end
//20us计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_delay <= 10'd0;
    end
    else if(cnt_delay == CNT_DELAY_MAX - 1'd1)begin
        cnt_delay <= 10'd0;
    end
    else begin
        cnt_delay <= cnt_delay + 1'd1;
    end
end

//该板块无法同时变化,只能依次闪烁,该方法为依次闪烁的间隔,人肉眼无法判断 20us
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        sel <= 6'b111_110;
    end
    else if (cnt_delay == CNT_DELAY_MAX - 1'd1) begin
        sel <= {sel[4:0],sel[5]};
    end
    else begin
        sel <= sel ;
    end
end


always @(*) begin
    //若处于时位,则相应的位选信号闪烁
    if (c_state == S1) begin
    case (sel)
        6'b111110:begin
            if (cnt_1s <= MAX_1s/2) begin
                cnt_num = 9'd20;
            end
            else cnt_num = hour / 10;
        end 
        6'b111101: begin
            if (cnt_1s <= MAX_1s/2) begin
                cnt_num = 9'd20;
            end
            else cnt_num = hour % 10 + 10;
        end 
        6'b111011: cnt_num = min / 10;
        6'b110111: cnt_num = min % 10 + 10;
        6'b101111: cnt_num = sec / 10;
        6'b011111: cnt_num = sec % 10;
        default: cnt_num = 4'd0;
    endcase
    end
    //若处于分位,则相应的位选信号闪烁
    else if (c_state == S2) begin
    case (sel)
        6'b111110: cnt_num = hour / 10;
        6'b111101: cnt_num = hour % 10 + 10;
        6'b111011: begin
            if (cnt_1s <= MAX_1s/2) begin
                cnt_num = 9'd20;
            end
            else cnt_num = min / 10;
        end 
        6'b110111: begin
            if (cnt_1s <= MAX_1s/2) begin
                cnt_num = 9'd20;
            end
            else cnt_num = min % 10 + 10;
        end
        6'b101111: cnt_num = sec / 10;
        6'b011111: cnt_num = sec % 10;
        default: cnt_num = 4'd0;
    endcase
    end
    //若处于秒位,则相应的位选信号闪烁
    else if (c_state == S3) begin
    case (sel)
        6'b111110: cnt_num = hour / 10;
        6'b111101: cnt_num = hour % 10 + 10;
        6'b111011: cnt_num = min / 10;
        6'b110111: cnt_num = min % 10 + 10;
        6'b101111: begin
            if (cnt_1s <= MAX_1s/2) begin
                cnt_num = 9'd20;
            end
            else cnt_num = sec / 10;
        end
        6'b011111: begin
            if (cnt_1s <= MAX_1s/2) begin
                cnt_num = 9'd20;
            end
            else cnt_num = sec % 10;
        end
        default: cnt_num = 4'd0;
    endcase
    end
    else begin 
    //其他情况数码管正常显示   
    case (sel)
        6'b111110: cnt_num = hour / 10;
        6'b111101: cnt_num = hour % 10 + 10;
        6'b111011: cnt_num = min / 10;
        6'b110111: cnt_num = min % 10 + 10;
        6'b101111: cnt_num = sec / 10;
        6'b011111: cnt_num = sec % 10;
        default: cnt_num = 4'd0;
    endcase
    end
end

//通过数字num解析出seg值
always @(*) begin
    case (cnt_num)
        9'd0:begin    seg <=   ZERO  ;    end
        9'd1:begin    seg <=   ONE   ;    end
        9'd2:begin    seg <=   TWO   ;    end
        9'd3:begin    seg <=   THREE ;    end
        9'd4:begin    seg <=   FOUR  ;    end
        9'd5:begin    seg <=   FIVE  ;    end
        9'd6:begin    seg <=   SIX   ;    end
        9'd7:begin    seg <=   SEVEN ;    end
        9'd8:begin    seg <=   EIGHT ;    end
        9'd9:begin    seg <=   NINE  ;    end
        9'd10:begin    seg <=   {point[7],ZERO[6:0]} ;    end
        9'd11:begin    seg <=   {point[7],ONE[6:0]}   ;    end
        9'd12:begin    seg <=   {point[7],TWO[6:0]}   ;    end
        9'd13:begin    seg <=   {point[7],THREE[6:0]} ;    end
        9'd14:begin    seg <=   {point[7],FOUR[6:0]}  ;    end
        9'd15:begin    seg <=   {point[7],FIVE[6:0]}  ;    end
        9'd16:begin    seg <=   {point[7],SIX[6:0]}   ;    end
        9'd17:begin    seg <=   {point[7],SEVEN[6:0]} ;    end
        9'd18:begin    seg <=   {point[7],EIGHT[6:0]} ;    end
        9'd19:begin    seg <=   {point[7],NINE[6:0]}  ;    end
        9'd20:begin    seg <=   8'b11111111  ;    end
        default: seg <= ZERO;
    endcase
end

//数码管选位状态机,第一段
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        c_state <= IDLE;
    end
    else begin
        c_state <= n_state;
    end
end
//数码管选位状态机,第二段
always @(*) begin
    case (c_state)
    //空闲状态
        IDLE    :begin
            if (key_flag[0]) begin
                n_state = S1;
            end
            else begin
                n_state = IDLE;
            end
        end
    //数码管时位
        S1      :begin
            if (key_flag[0]) begin
                n_state = S2;
            end
            else begin
                n_state = S1;
            end
        end 
    //数码管分位 
        S2      :begin
            if (key_flag[0]) begin
                n_state = S3;
            end
            else begin
                n_state = S2;
            end
        end
    //数码管秒位   
        S3      :begin
            if (key_flag[0]) begin
                n_state = IDLE;
            end
            else begin
                n_state = S3;
            end
        end

        default:n_state = IDLE;
    endcase
end


endmodule

顶层模块(top)

module top(
    input wire clk,
    input wire rst_n,
    input wire [3:0] key_in,
    output wire [7:0] seg,
    output wire [5:0] sel,
    output wire beep    
);
wire [3:0] key_flag;
wire [4:0] hour;
wire [5:0] min;
wire [5:0] sec;
wire music_flag;
wire flag;
    key  u_key//key_fsm
(
    /* input wire */ .clk           (clk    )        ,              
    /* input wire */ .rst_n         (rst_n  )        ,            
    /* input wire */ .key_in        (key_in[0])        ,
    /* output reg */ .key_flag       (key_flag[0])              
   
);
    key  u_key1
(
    /* input wire */ .clk           (clk    )        ,              
    /* input wire */ .rst_n         (rst_n  )        ,            
    /* input wire */ .key_in        (key_in[1])       ,     
    /* output reg */ .key_flag       (key_flag[1])                
     
);
    key     u_key2
(
    /* input wire */ .clk           (clk    )        ,              
    /* input wire */ .rst_n         (rst_n  )        ,            
    /* input wire */ .key_in        (key_in[2])        , 
    /* output reg */ .key_flag       (key_flag[2])                  

);
    key     u_key3
(
    /* input wire */ .clk           (clk    )        ,              
    /* input wire */ .rst_n         (rst_n  )        ,            
    /* input wire */ .key_in        (key_in[3])       , 
    /* output reg */ .key_flag      (key_flag[3])                        
);  
beep u_beep (
    /* input  */.clk  (clk  ),
    /* input  */.rst_n(rst_n),
    /* input  */.flag (flag ),
    /* output */.beep (beep )
);
music u_music(
    /* input  */.clk       (clk       )     ,
    /* input  */.rst_n     (rst_n     )     ,
    /* input  */.key_out   (key_out   )     ,
    /* input  */.music_flag(music_flag)     ,
    /* output */.flag(flag)
);
counter u_counter(
    .clk        (clk)   ,
    .rst_n      (rst_n) ,
    .hour       (hour)  ,
    .key_flag   (key_flag),
    .music_flag (music_flag),
    .min        (min)   ,
    .sec        (sec)   
);
seg_driver u_seg_driver(
    .clk        (clk)   ,
    .rst_n      (rst_n) ,
    .hour       (hour)   ,
    .key_flag   (key_flag),
    .min        (min)   ,
    .sec        (sec)   ,
    .seg        (seg)   ,
    .sel        (sel)   
);
endmodule

八、总结

虽然该代码仍有许多不足,但如果对各位有所帮助,还请各位不要吝啬手中的点赞和关注。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/841945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Shadow插件化框架使用

作者&#xff1a;CCtomorrow 说明 最近项目想要做模块动态升级&#xff0c;所以了解了最近还在维护的插件化框架 Shadow . shadow框架的官网的顶置 issue &#xff0c;里面有非常多的关于框架的解析的文章。想要了解此框架&#xff0c;这个必看。 这里还是截取一张项目代码图…

Drools用户手册翻译——第四章 Drools规则引擎(十二)复杂事件处理(CEP)的时间操作

甩锅声明&#xff1a;本人英语一般&#xff0c;翻译只是为了做个笔记&#xff0c;所以有翻译错误的地方&#xff0c;错就错了&#xff0c;如果你想给我纠正&#xff0c;就给我留言&#xff0c;我会改过来&#xff0c;如果懒得理我&#xff0c;就直接划过即可。 对于事件的时间…

Python数据分析实战-dataframe指定多列去重(附源码和实现效果)

实现功能 Python数据分析实战-利用df.drop_duplicates(subset[,])对dataframe指定多列去重 实现代码 import pandas as pddata{state:[1,1,2,2,1,2,2],pop:[a,b,c,d,b,c,d]} framepd.DataFrame(data)frameframe.drop_duplicates(subset[pop,state]) print(frame) 实现效果 本…

【SpringCloud】RabbitMQ基础

1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;…

Unity Git项目添加子模块

在 当前仓库根目录下执行命令 git submodule add https://github.com/xxx/child.git 检查仓库状态 git status 更新子库 git submodule update --remote 下拉父仓库Git并保住子库也更新 git pull --recurse-submodules 推荐使用 Githubdesktop工具 这样你可以更清楚的看到自己…

前端如何设置表单元素的默认值?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 文本框、文本域⭐ 单选按钮、复选框⭐ 对于下拉列表⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是…

全网最强,Jmeter性能测试-web接口性能压测总结(超细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 压测的目的 对于…

b站视频标题的获取(xpath、jsonpath的一个简单应用)

目录 1.目的2.代码的演示 注&#xff1a;该篇文章为本人原创&#xff0c;由于本人学习有限&#xff0c;若有错误或者笔误或者有问题&#xff0c;欢迎大家进行批评指正&#xff0c;谢谢。 1.目的 在b站大学上&#xff0c;为了更好的写笔记&#xff0c;本人根据学到的Python(即Py…

2. 内存分区模型

一、内存分区模型 C程序在执行时&#xff0c;将内存大方向划分为4个区域 代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理的全局区&#xff1a;存放全局变量和静态变量以及常量栈区&#xff1a;由编译器自动分配释放&#xff0c;存放函数的参数值&a…

【JVM】垃圾回收 ——自问自答2

Q: System.gc() 的理解 System.gc()底层调用的是 Runtime.getRuntime.gc(),会现实出发FullGC。 但是&#xff0c;它的调用附带一个免责声明&#xff0c;无法保证对垃圾收集器的调用。 Q&#xff1a; 内存溢出和内存泄漏&#xff1f; 内存溢出&#xff1a; 简而言之&#xf…

泊松损坏图像的快速尺度间小波去噪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

最新ChatGPT网站源码运营版+支持ai绘画(Midjourney)+GPT4.0+GPT官方3.5key绘画+实时语音识别输入+后台一键版本更新!

最新ChatGPT网站源码运营版支持ai绘画GPT4.0GPT官方3.5key绘画实时语音识别输入后台一键版本更新&#xff01; 1.网站系统源码介绍&#xff1a; 程序已支持ChatGPT4.0、Midjourney绘画、GPT3.5 API绘画、语音识别输入、用户会员套餐用户每日签到功能后台管理一键更新版本。支…

docker中的jenkins去配置sonarQube

docker中的jenkins去配置sonarQube 1、拉取sonarQube macdeMacBook-Pro:~ mac$ docker pull sonarqube:8.9.6-community 8.9.6-community: Pulling from library/sonarqube 8572bc8fb8a3: Pull complete 702f1610d53e: Pull complete 8c951e69c28d: Pull complete f95e4f8…

【代码随想录-LeetCode第一题】二分查找及实现

LeetCode刷题第一题&#xff1a;704二分查找法 什么是二分查找&#xff1f;题目思路和边界问题 参考 代码随想录 什么是二分查找&#xff1f; 二分查找&#xff08;Binary Search&#xff09;是一种在有序数组中查找特定元素的查找算法。它通过将目标值与数组的中间元素进行比…

配置虚拟机中常见问题

1.Centos8的问题 用root运行宝塔官方一键安装脚本&#xff0c;结果报错了&#xff0c;提示&#xff1a;为仓库 appstream 下载元数据失败 : Cannot prepare internal mirrorlist&#xff1b; 出现原因&#xff1a; CentOS 8在2022年12月31日将迎来到生命周期终点&#xff0c;…

Spring Boot集成EasyPoi实现导入导出操作

文章目录 Spring Boot集成EasyPoi实现导入导出操作0 简要说明1 环境搭建1.1 项目目录1.2 依赖管理2.3 关于swagger处理2.4 关于切面处理耗时1 自定义注解2 定义切面类3 如何使用 2.5 核心导入操作2.6 核心导出操作 2 最佳实线2.1 导入操作1 实体类说明2 业务层3 效果3 控制层 2…

前端工程师的摸鱼日常(19)

【图为恐子真身】 史记记载恐子九尺六寸高&#xff0c;根据春秋的尺度换算&#xff0c;有一米九至两米多高&#xff0c;人皆畏之。 一米九的山东大汉&#xff0c;手下七十二堂口&#xff08;帮派&#xff09;、三千门生&#xff08;小弟&#xff09;。 由他弟子所编写的《抡…

【数学建模学习(10):遗传算法】

遗传算法简介 • 遗传算法&#xff08;Genetic Algorithms&#xff09;是基于生物进化理论的原理发展起来的一种广为 应用的、高效的随机搜索与优化的方法。其主要特点是群体搜索策略和群体中个体之 间的信息交换&#xff0c;搜索不依赖于梯度信息。它是20世纪70年代初期由美国…

MFC第二十八天 WM_SIZE应用,CFrameWnd类LoadFrame的方法,PreCreateWindow窗口预处理,Frame-view窗口视图

文章目录 WM_SIZE应用通过WM_SIZE消息实现窗口布局管理通过控件属性实现窗口布局管理 CFrameWnd类CFrameWnd类简介OnCreate和OnCreateClient的方法注册时的要素与窗口设置PreCreateWindow创建窗口的预处理函数 附录预处理函数的结构体CFrameWnd::LoadFrame与CreateFrame WM_SIZ…

K8S系列文章之 Traefik快速入门

traefik 与 nginx 一样&#xff0c;是一款优秀的反向代理工具&#xff0c;或者叫 Edge Router。至于使用它的原因则基于以下几点 无须重启即可更新配置自动的服务发现与负载均衡与 docker 的完美集成&#xff0c;基于 container label 的配置漂亮的 dashboard 界面metrics 的支…