当前位置:   article > 正文

FPGA 课程综合实验——倒计时(简易计时器闹钟)基于STEP MAX10 FPGA_fpga课程设计

fpga课程设计

FPGA 课程综合实验之倒计时

实验要求:

组合使用STEP MAX10 FPGA核心板和STEP BaseBoard扩展底板,编写程序,完成一个倒计时定时
器的设计。
功能要求:

  • 使用扩展底板上相邻的4位数码管显示计时时间,显示数值单位为“秒”。(2分)
  • 定时器最大定时时间为99秒,时间显示分辨力为1/100秒。(2分)
  • 倒计时结束时,扩展底板上的蜂鸣器发出一组“滴答”声(先后发出2种频率的声音,各持续约0.5秒)作为提示。(2分)
  • 时间设置步进间隔1秒,同时支持以下2种操作方式:
    • 完全使用扩展底板上的旋转编码器进行操作:
      • 旋转旋钮设定定时时间。(2分)
    • -短按旋钮启动/暂停计时,长按清零。(2分)
  • 完全使用扩展底板上的矩阵键盘进行操作:
    • 直接按数字键输入设定定时时间。(2分)
    • 分别设置启动/暂停按键和清零按键,实现相应功能。(2分)
      STEP BaseBoard扩展底板
      STEP BaseBoard扩展底板引脚

根据手册与引脚图查阅与需要外设资源,我们可以进行引脚设置于分配

    input           clk,        // 系统时钟 12MHz
    input           rst_n,      // 系统复位 低有效
    input           encoder_a,  // 旋转编码器EC11的A脚
    input           encoder_b,  // 旋转编码器EC11的B脚
    input           encoder_sw, // 旋转编码器EC11的SW脚
    input   [3:0]   col,        // 矩阵按键的列接口
    output  [3:0]   row,        // 矩阵按键的行接口
    output	        seg_rck,	  // 74HC595的RCK管脚
    output	        seg_sck,	  // 74HC595的SCK管脚
    output	        seg_din,	  // 74HC595的SER管脚
    output          beeper      // 蜂鸣器
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

引脚分配

外设module编写

根据使用外设,我们可以编写外设程序

1.数码管显示(来自开源社区)

// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: Segment_scan 
// 
// Author: Step
// 
// Description: Display with Segment tube
// 
// Web: www.stepfpga.com
// 
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2015/11/11   |Initial ver
// --------------------------------------------------------------------
module Segment_scan
(
input				clk,		//系统时钟 12MHz
input				rst_n,		//系统复位 低有效
input		[3:0]	dat_1,		//SEG1 显示的数据输入
input		[3:0]	dat_2,		//SEG2 显示的数据输入
input		[3:0]	dat_3,		//SEG3 显示的数据输入
input		[3:0]	dat_4,		//SEG4 显示的数据输入
input		[3:0]	dat_5,		//SEG5 显示的数据输入
input		[3:0]	dat_6,		//SEG6 显示的数据输入
input		[3:0]	dat_7,		//SEG7 显示的数据输入
input		[3:0]	dat_8,		//SEG8 显示的数据输入
input		[7:0]	dat_en,		//数码管数据位显示使能,[MSB~LSB]=[SEG1~SEG8]
input		[7:0]	dot_en,		//数码管小数点位显示使能,[MSB~LSB]=[SEG1~SEG8]
output	reg			seg_rck,	//74HC595的RCK管脚
output	reg			seg_sck,	//74HC595的SCK管脚
output	reg			seg_din		//74HC595的SER管脚
);

localparam	CNT_40KHz = 300;	//分频系数

localparam	IDLE	=	3'd0;
localparam	MAIN	=	3'd1;
localparam	WRITE	=	3'd2;
localparam	LOW		=	1'b0;
localparam	HIGH	=	1'b1;

//创建数码管的字库,字库数据依段码顺序有关
//这里字库数据[MSB~LSB]={G,F,E,D,C,B,A}
reg[6:0] seg [15:0]; 
always @(negedge rst_n) begin
    seg[0]	=	7'h3f;   // 0
    seg[1]	=	7'h06;   // 1
    seg[2]	=	7'h5b;   // 2
    seg[3]	=	7'h4f;   // 3
    seg[4]	=	7'h66;   // 4
    seg[5]	=	7'h6d;   // 5
    seg[6]	=	7'h7d;   // 6
    seg[7]	=	7'h07;   // 7
    seg[8]	=	7'h7f;   // 8
    seg[9]	=	7'h6f;   // 9
	seg[10]	=	7'h77;   // A
    seg[11]	=	7'h7c;   // b
    seg[12]	=	7'h39;   // C
    seg[13]	=	7'h5e;   // d
    seg[14]	=	7'h79;   // E
    seg[15]	=	7'h71;   // F
end 
	
//计数器对系统时钟信号进行计数
reg [9:0] cnt = 1'b0;
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) cnt <= 1'b0;
	else if(cnt>=(CNT_40KHz-1)) cnt <= 1'b0;
	else cnt <= cnt + 1'b1;
end

//根据计数器计数的周期产生分频的脉冲信号
reg clk_40khz = 1'b0; 
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) clk_40khz <= 1'b0;
	else if(cnt<(CNT_40KHz>>1)) clk_40khz <= 1'b0;
	else clk_40khz <= 1'b1;
end

//使用状态机完成数码管的扫描和74HC595时序的实现
reg		[15:0]		data;
reg		[2:0]		cnt_main;
reg		[5:0]		cnt_write;
reg		[2:0] 		state = IDLE;
always@(posedge clk_40khz or negedge rst_n) begin
	if(!rst_n) begin	//复位状态下,各寄存器置初值
		state <= IDLE;
		cnt_main <= 3'd0; cnt_write <= 6'd0;
		seg_din <= 1'b0; seg_sck <= LOW; seg_rck <= LOW;
	end else begin
		case(state)
			IDLE:begin	//IDLE作为第一个状态,相当于软复位
					state <= MAIN;
					cnt_main <= 3'd0; cnt_write <= 6'd0;
					seg_din <= 1'b0; seg_sck <= LOW; seg_rck <= LOW;
				end
			MAIN:begin
					cnt_main <= cnt_main + 1'b1;
					state <= WRITE;		//在配置完发给74HC595的数据同时跳转至WRITE状态,完成串行时序
					case(cnt_main)
						//对8位数码管逐位扫描
						//data          [15:8]为段选,         [7:0]为位选
						3'd0: data <= {{dot_en[7],seg[dat_1]},dat_en[7]?8'hfe:8'hff};
						3'd1: data <= {{dot_en[6],seg[dat_2]},dat_en[6]?8'hfd:8'hff}; 
						3'd2: data <= {{dot_en[5],seg[dat_3]},dat_en[5]?8'hfb:8'hff}; 
						3'd3: data <= {{dot_en[4],seg[dat_4]},dat_en[4]?8'hf7:8'hff}; 
						3'd4: data <= {{dot_en[3],seg[dat_5]},dat_en[3]?8'hef:8'hff};
						3'd5: data <= {{dot_en[2],seg[dat_6]},dat_en[2]?8'hdf:8'hff}; 
						3'd6: data <= {{dot_en[1],seg[dat_7]},dat_en[1]?8'hbf:8'hff}; 
						3'd7: data <= {{dot_en[0],seg[dat_8]},dat_en[0]?8'h7f:8'hff}; 
						default: data <= {8'h00,8'hff};
					endcase
				end
			WRITE:begin
					if(cnt_write >= 6'd33) cnt_write <= 1'b0;
					else cnt_write <= cnt_write + 1'b1;
					case(cnt_write)
						//74HC595是串行转并行的芯片,3路输入可产生8路输出,而且可以级联使用
						//74HC595的时序实现,参考74HC595的芯片手册
						6'd0:  begin seg_sck <= LOW; seg_din <= data[15]; end		//SCK下降沿时SER更新数据
						6'd1:  begin seg_sck <= HIGH; end							//SCK上升沿时SER数据稳定
						6'd2:  begin seg_sck <= LOW; seg_din <= data[14]; end
						6'd3:  begin seg_sck <= HIGH; end
						6'd4:  begin seg_sck <= LOW; seg_din <= data[13]; end
						6'd5:  begin seg_sck <= HIGH; end
						6'd6:  begin seg_sck <= LOW; seg_din <= data[12]; end
						6'd7:  begin seg_sck <= HIGH; end
						6'd8:  begin seg_sck <= LOW; seg_din <= data[11]; end
						6'd9:  begin seg_sck <= HIGH; end
						6'd10: begin seg_sck <= LOW; seg_din <= data[10]; end
						6'd11: begin seg_sck <= HIGH; end
						6'd12: begin seg_sck <= LOW; seg_din <= data[9]; end
						6'd13: begin seg_sck <= HIGH; end
						6'd14: begin seg_sck <= LOW; seg_din <= data[8]; end
						6'd15: begin seg_sck <= HIGH; end
						6'd16: begin seg_sck <= LOW; seg_din <= data[7]; end
						6'd17: begin seg_sck <= HIGH; end
						6'd18: begin seg_sck <= LOW; seg_din <= data[6]; end
						6'd19: begin seg_sck <= HIGH; end
						6'd20: begin seg_sck <= LOW; seg_din <= data[5]; end
						6'd21: begin seg_sck <= HIGH; end
						6'd22: begin seg_sck <= LOW; seg_din <= data[4]; end
						6'd23: begin seg_sck <= HIGH; end
						6'd24: begin seg_sck <= LOW; seg_din <= data[3]; end
						6'd25: begin seg_sck <= HIGH; end
						6'd26: begin seg_sck <= LOW; seg_din <= data[2]; end
						6'd27: begin seg_sck <= HIGH; end
						6'd28: begin seg_sck <= LOW; seg_din <= data[1]; end
						6'd29: begin seg_sck <= HIGH; end
						6'd30: begin seg_sck <= LOW; seg_din <= data[0]; end
						6'd31: begin seg_sck <= HIGH; end
						6'd32: begin seg_rck <= HIGH; end								//当16位数据传送完成后RCK拉高,输出生效
						6'd33: begin seg_rck <= LOW; state <= MAIN; end
						default: ;
					endcase
				end
			default: state <= IDLE;
		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
  • 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
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166

2.编码器左右脉冲(来自开源社区)

// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: Encoder
// 
// Author: Step
// 
// Description: Driver for rotary encoder
// 
// Web: www.stepfapga.com
// 
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2016/04/20   |Initial ver
// --------------------------------------------------------------------
module Encoder
(
input					clk,		// 系统时钟 12MHz
input					rst_n,		// 系统复位 低有效
input					key_a,		// 旋转编码器EC11的A脚
input					key_b,		// 旋转编码器EC11的B脚
output	reg				L_pulse,	// 左旋脉冲输出
output	reg				R_pulse		// 右旋脉冲输出
);
	
localparam				NUM_250US = 3_000;

reg	[12:0]	cnt;
//count for clk_500us
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) cnt <= 0;
	else if(cnt >= NUM_250US-1) cnt <= 1'b0;
	else cnt <= cnt + 1'b1;
end
	
reg	clk_500us;	
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) clk_500us <= 0;
	else if(cnt == NUM_250US-1) clk_500us <= ~clk_500us;
	else clk_500us <= clk_500us;
end
	
reg	key_a_r,key_a_r1,key_a_r2;
//消除亚稳态
always@(posedge clk_500us) begin
	key_a_r		<=	key_a;
	key_a_r1	<=	key_a_r;
	key_a_r2	<=	key_a_r1;
end 

reg	A_state;
//简单去抖动处理
always@(key_a_r1 or key_a_r2) begin
	case({key_a_r1,key_a_r2})
		2'b11:	A_state <= 1'b1;
		2'b00:	A_state <= 1'b0;
		default: A_state <= A_state;
	endcase
end 

reg	key_b_r,key_b_r1,key_b_r2;
//消除亚稳态
always@(posedge clk_500us) begin
	key_b_r		<=	key_b;
	key_b_r1	<=	key_b_r;
	key_b_r2	<=	key_b_r1;
end 

reg	B_state;
//简单去抖动处理
always@(key_b_r1 or key_b_r2) begin
	case({key_b_r1,key_b_r2})
		2'b11:	B_state <= 1'b1;
		2'b00:	B_state <= 1'b0;
		default: B_state <= B_state;
	endcase
end

reg A_state_r,A_state_r1;
//对A_state信号进行边沿检测
always@(posedge clk) begin
	A_state_r <= A_state; 
	A_state_r1 <= A_state_r;
end

wire	A_pos	= (!A_state_r1) && A_state_r;
wire	A_neg	= A_state_r1 && (!A_state_r);

//当A的上升沿伴随B的高电平或当A的下降沿伴随B的低电平 为向左旋转
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) L_pulse <= 1'b0;
	else if((A_pos&&B_state)||(A_neg&&(!B_state))) L_pulse <= 1'b1;
	else L_pulse <= 1'b0;
end 

//当A的上升沿伴随B的低电平或当A的下降沿伴随B的高电平 为向右旋转
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) R_pulse <= 1'b0;
	else if((A_pos&&(!B_state))||(A_neg&&B_state)) R_pulse <= 1'b1;
	else R_pulse <= 1'b0;
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

3.矩阵键盘获取(来自开源社区)

// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: Encoder
// 
// Author: Step
// 
// Description: Driver for rotary encoder
// 
// Web: www.stepfapga.com
// 
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2016/04/20   |Initial ver
// --------------------------------------------------------------------
module Encoder
(
input					clk,		// 系统时钟 12MHz
input					rst_n,		// 系统复位 低有效
input					key_a,		// 旋转编码器EC11的A脚
input					key_b,		// 旋转编码器EC11的B脚
output	reg				L_pulse,	// 左旋脉冲输出
output	reg				R_pulse		// 右旋脉冲输出
);
	
localparam				NUM_250US = 3_000;

reg	[12:0]	cnt;
//count for clk_500us
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) cnt <= 0;
	else if(cnt >= NUM_250US-1) cnt <= 1'b0;
	else cnt <= cnt + 1'b1;
end
	
reg	clk_500us;	
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) clk_500us <= 0;
	else if(cnt == NUM_250US-1) clk_500us <= ~clk_500us;
	else clk_500us <= clk_500us;
end
	
reg	key_a_r,key_a_r1,key_a_r2;
//消除亚稳态
always@(posedge clk_500us) begin
	key_a_r		<=	key_a;
	key_a_r1	<=	key_a_r;
	key_a_r2	<=	key_a_r1;
end 

reg	A_state;
//简单去抖动处理
always@(key_a_r1 or key_a_r2) begin
	case({key_a_r1,key_a_r2})
		2'b11:	A_state <= 1'b1;
		2'b00:	A_state <= 1'b0;
		default: A_state <= A_state;
	endcase
end 

reg	key_b_r,key_b_r1,key_b_r2;
//消除亚稳态
always@(posedge clk_500us) begin
	key_b_r		<=	key_b;
	key_b_r1	<=	key_b_r;
	key_b_r2	<=	key_b_r1;
end 

reg	B_state;
//简单去抖动处理
always@(key_b_r1 or key_b_r2) begin
	case({key_b_r1,key_b_r2})
		2'b11:	B_state <= 1'b1;
		2'b00:	B_state <= 1'b0;
		default: B_state <= B_state;
	endcase
end

reg A_state_r,A_state_r1;
//对A_state信号进行边沿检测
always@(posedge clk) begin
	A_state_r <= A_state; 
	A_state_r1 <= A_state_r;
end

wire	A_pos	= (!A_state_r1) && A_state_r;
wire	A_neg	= A_state_r1 && (!A_state_r);

//当A的上升沿伴随B的高电平或当A的下降沿伴随B的低电平 为向左旋转
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) L_pulse <= 1'b0;
	else if((A_pos&&B_state)||(A_neg&&(!B_state))) L_pulse <= 1'b1;
	else L_pulse <= 1'b0;
end 

//当A的上升沿伴随B的低电平或当A的下降沿伴随B的高电平 为向右旋转
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) R_pulse <= 1'b0;
	else if((A_pos&&(!B_state))||(A_neg&&B_state)) R_pulse <= 1'b1;
	else R_pulse <= 1'b0;
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

4.PWM生成(来自开源社区)

用于蜂鸣器鸣响

// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: PWM
// 
// Author: Step
// 
// Description: PWM
// 
// Web: www.stepfpga.com
//
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2015/11/11   |Initial ver
// --------------------------------------------------------------------
module PWM #
(
parameter	WIDTH = 32	//ensure that 2**WIDTH > cycle
)
(
input					clk,
input					rst_n,
input		[WIDTH-1:0]	cycle,	//cycle > duty
input		[WIDTH-1:0]	duty,	//duty < cycle
output	reg				pwm_out
);

reg	[WIDTH-1:0]	cnt;
//counter for cycle
always @(posedge clk or negedge rst_n)
	if(!rst_n) cnt <= 1'b1;
	else if(cnt >= cycle) cnt <= 1'b1;
	else cnt <= cnt + 1'b1;

//pulse with duty
always @(posedge clk or negedge rst_n)
	if(!rst_n) pwm_out <= 1'b1;
	else if(cnt < duty) pwm_out <= 1'b1;
	else pwm_out <= 1'b0;

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

5.检测按键长按与短按

module key_detect (
    input clk,
    input rst,
    input key,
    output reg short_press,
    output reg long_press
);

// 状态定义
localparam IDLE = 2'b00;
localparam PRESS = 2'b01;
localparam RELEASE = 2'b10;

// 状态寄存器和下一个状态逻辑
reg [1:0] state, next_state;

always @(posedge clk, negedge rst) begin
    if (!rst) begin
        state <= IDLE;
    end else begin
        state <= next_state;
    end
end

// 下一个状态逻辑
always @(*) begin
    case (state)
        IDLE: begin
            if (key == 1'b0) begin
                next_state = PRESS;
            end else begin
                next_state = IDLE;
            end
        end
        PRESS: begin
            if (key == 1'b0) begin
                next_state = PRESS;
            end else begin
                next_state = RELEASE;
            end
        end
        RELEASE: begin
            if (key == 1'b0) begin
                next_state = IDLE;
            end else begin
                next_state = RELEASE;
            end
        end
        default: next_state = IDLE;
    endcase
end

// 长短按检测逻辑
reg [31:0] press_cnt;

always @(posedge clk or negedge rst) begin
    if (!rst) begin
        short_press <= 1'b0;
        long_press <= 1'b0;
        press_cnt <= 0;
    end else begin
        case (state)
            IDLE: begin
                short_press <= 1'b0;
                long_press <= 1'b0;
                press_cnt <= 0;
            end
            PRESS: begin
                short_press <= 1'b0;
                long_press <= 1'b0;
                press_cnt <= press_cnt + 1;
            end
            RELEASE: begin
                if (press_cnt >= 12_000_000) begin
                    short_press <= 1'b0;
                    long_press <= 1'b1;
                end else if (press_cnt >= 12_000) begin
                    short_press <= 1'b1;
                    long_press <= 1'b0;
                end else begin
                    short_press <= 1'b0;
                    long_press <= 1'b0;
                end
                press_cnt <= 0;
            end
            default: begin
                short_press <= 1'b0;
                long_press <= 1'b0;
                press_cnt <= 0;
            end
        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
  • 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

以脉冲形式输出长短按信号,短按是指时间大于12_000个时钟周期(为了消抖),小于12_000_000个时钟周期的按键行为;长按是指时间大于12_000_000个时钟周期的按键行为。

主程序

现在我们的外设部分已经写完了,开始主体程序书写,首先考虑状态机

状态基

	parameter Set_status    = 5'b00000;
	parameter Press_once    = 5'b00001;
	parameter Timing_status = 5'b00010;
	parameter Ring_status_1 = 5'b00011;
	parameter Ring_status_2 = 5'b00100;
	parameter Clear_status  = 5'b00101;
	parameter Paused_status = 5'b00110;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

状态转移条件

状态基转移
通过代码实现

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        state <= Set_status;
        
    end else begin
        state <= state_next;
    end
end

always @(*) begin
    case(state)
        Set_status: begin
            if(key_pulse[9:0]) state_next <= Press_once;
            else if(key_pulse[12]) state_next <= Timing_status;
            else if(key_pulse[13]) state_next <= Clear_status;
            else if(encoder_sw_pulse) state_next <= Timing_status;
            else if(encoder_sw_long_press) state_next <= Clear_status;
            else state_next <= Set_status;
        end
        Press_once: begin
            if(key_pulse[12]) state_next <= Timing_status;
            else if(key_pulse[13]) state_next <= Clear_status;
            else if(encoder_sw_pulse) state_next <= Timing_status;
            else if(encoder_sw_long_press) state_next <= Clear_status;
            else if(key_pulse[9:0]) state_next <= Set_status;
            else state_next <= Press_once;
        end
        Timing_status: begin
            if(key_pulse[12]) state_next <= Paused_status;
            else if(key_pulse[13]) state_next <= Clear_status;
            else if(encoder_sw_pulse) state_next <= Paused_status;
            else if(encoder_sw_long_press) state_next <= Clear_status;
            else if(seg_data_8==0&&seg_data_7==0&&seg_data_6==0&&seg_data_5==0) state_next <= Ring_status_1;
            else state_next <= Timing_status;
        end
        Ring_status_1: begin
            if(beep_cnt==6_000_000) state_next <= Ring_status_2;
            else state_next <= Ring_status_1;
        end
        Ring_status_2: begin
            if(beep_cnt==12_000_000) state_next <= Set_status;
            else state_next <= Ring_status_2;
        end
        Clear_status: begin
            state_next <= Set_status;
        end
        Paused_status: begin
            if(key_pulse[12]) state_next <= Timing_status;
            else if(key_pulse[13]) state_next <= Clear_status;
            else if(encoder_sw_pulse) state_next <= Timing_status;
            else if(encoder_sw_long_press) state_next <= Clear_status;
            else state_next <= Paused_status;
        end
        default: state_next <= Set_status;
    endcase
end
  • 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

我们注意到在转移条件中有beep_cnt,此变量要实现的功能是蜂鸣器响0.5s计时,在功能实现中累加。

功能实现

Set_status

我们要求实现按键设定时间与编码器实现时间增减,我们所有的输出都是脉冲的,所以我们只需要直接判断条件即可

//编码器增减
if(L_pulse) 
                begin
			        if(seg_data_8==4'd0) 
                    begin
                        if(seg_data_7==4'd0) 
                        begin
                            seg_data_7 <= 4'd0;
			        	    seg_data_8 <= 4'd0;
                        end
				        else 
                        begin
                            seg_data_8 <= 4'd9;
                            seg_data_7 <= seg_data_7 - 1'b1;
                        end
			        end 
                    else seg_data_8 <= seg_data_8 - 1'b1;
                end 
                else if(R_pulse) 
                begin
                    if(seg_data_8==4'd9) 
                    begin
                        if(seg_data_7==4'd9) 
                        begin
                            seg_data_7 <= 4'd9;
                            seg_data_8 <= 4'd9;
                        end
                        else
                        begin
                            seg_data_8 <= 4'd0;
                            seg_data_7 <= seg_data_7 + 1'b1;
                        end
                    end
                    else seg_data_8 <= seg_data_8 + 1'b1;
		        end 
		        //矩阵键盘时间设置
		        else if(key_pulse[9]) seg_data_7 <= 4'd0;
                else if(key_pulse[0]) seg_data_7 <= 4'd1;
                else if(key_pulse[1]) seg_data_7 <= 4'd2;
                else if(key_pulse[2]) seg_data_7 <= 4'd3;
                else if(key_pulse[3]) seg_data_7 <= 4'd4;
                else if(key_pulse[4]) seg_data_7 <= 4'd5;
                else if(key_pulse[5]) seg_data_7 <= 4'd6;
                else if(key_pulse[6]) seg_data_7 <= 4'd7;
                else if(key_pulse[7]) seg_data_7 <= 4'd8;
                else if(key_pulse[8]) seg_data_7 <= 4'd9;
  • 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

并且我们需要考虑数码管点亮使能问题

if(seg_data_7 == 4'd0)
                begin
                    seg_en <= 8'b0000_0001;
                    seg_dot_en <= 8'h00;
                end
                else
                begin
                    seg_en <= 8'b0000_0011;
                    seg_dot_en <= 8'h00;
		        end       
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Press_once

几乎与Set_status一致,不再进行赘述

Timing_status

在此状态中需要一个10ms的时钟信号,所以额外生成一份

//时钟
	reg [7:0] s_counter = 0;
	reg [7:0] counter_10ms = 0;
    reg [21:0] counter_10ms_cnt = 0;

always @(posedge clk or negedge rst_n) 
begin
    if(!rst_n) begin
        counter_10ms <= 0;
    end 
    else if(counter_10ms_cnt == 12_000_0) 
    begin
        counter_10ms <= 1;
    end
    else 
    begin
        counter_10ms <= 0;
    end
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在功能实现中

counter_10ms_cnt<=counter_10ms_cnt+1;
                beep_cnt <= 0;
                if(counter_10ms)
                begin
                    counter_10ms_cnt<=0;
                    if(seg_data_8==4'd0) 
                    begin
                        if(seg_data_7==4'd0)
                        begin
                            if(seg_data_6==4'd0)
                            begin
                                if(seg_data_5==4'd0)
                                begin
                                end
                                else
                                begin
                                    seg_data_8 <= 4'd9;
                                    seg_data_7 <= 4'd9;
                                    seg_data_6 <= 4'd9;
                                    seg_data_5 <= seg_data_5 - 1'b1;
                                end
                            end
                            else
                            begin
                                seg_data_8 <= 4'd9;
                                seg_data_7 <= 4'd9;
                                seg_data_6 <= seg_data_6 - 1'b1;
                            end
                        end
                        else
                        begin
                            seg_data_8 <= 4'd9;
                            seg_data_7 <= seg_data_7 - 1'b1;
                        end
                    end
                    else
                    begin
                        seg_data_8 <= seg_data_8 - 1'b1;
                    end
                end
                seg_en <= 8'b0000_1111;
                seg_dot_en <= 8'b100;
  • 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

Ring_status_1与Ring_status_2

此处可和二唯一,由于初始思路问题,此处不再修改,仅此说明,此处需要用计数器记录响铃时间,所以产生一计数器,并且设置PWM频率

			Ring_status_1: begin
				beeper_cycle <= 16'd36408;
				beeper_state <= 1'b1;
				beep_cnt <= beep_cnt +1;
			end
			Ring_status_2: begin
				beeper_cycle <= 16'd45872;
				beeper_state <= 1'b1;
				beep_cnt <= beep_cnt +1;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Clear_status

此状态只持续一个周期,清楚所有数据

            Clear_status: begin
                beeper_state <= 1'b0;
                seg_data_1 <= 4'h0;
                seg_data_2 <= 4'h0;
                seg_data_3 <= 4'h0;
                seg_data_4 <= 4'h0;
                seg_data_5 <= 4'h0;
                seg_data_6 <= 4'h0;
                seg_data_7 <= 4'h0;
                seg_data_8 <= 4'h0;
                seg_en <= 8'b0000_0000;
                seg_dot_en <= 8'h00;
                beep_cnt <= 32'd0;
                counter_10ms_cnt<=0;
			end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Paused_status

此状态无需进行任何操作,等待即可

至此,所有需要功能皆已实现,整体工程在附件中。
我用夸克网盘分享了「countdown.zip」,点击链接即可保存。打开「夸克APP」,无需下载在线播放视频,畅享原画5倍速,支持电视投屏。
链接:https://pan.quark.cn/s/2dc5273a9b01

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

闽ICP备14008679号