FPGA基于标准计数器电子时钟设计

引言

完全基于标准计数器进行设计,结构简单,采用多个计数器并通过各个计数器之间的“结束条件”与“加一条件”的组合逻辑进行拼接,实现可调时钟与可调闹钟。。

按钮信号处理

在按钮信号处理部分,由于使用的开发板按钮按下表示低电平,所以对输入信号进行取反后再使用,利用输入按钮信号key_1、key_2、key_3的逻辑或结果作为0.5s计数器“加一条件”,输出该计数器的结束信号,并与输入按钮信号key_1、key_2、key_3分别进行逻辑相与并输出信号,此部分做法类似于按键消抖,同时可以实现按钮输入信号的截断,用处理后的按钮信号控制后续计数器时方便调整,例如调整时钟与闹钟大小时不用多次按下按钮,此设计依据个人习惯进行设计。用处理后的按钮信号分别作为计数器“state_sys”和计数器“move”的“加一条件”,并用计数器“state_sys”的状态指示系统的操作状态,用计数器“move”的状态控制“时”、“分”、“秒”的分别调整以及关闭蜂鸣器。在此部分还使用4个led灯对系统状态进行指示便于操作。。

时钟功能计数器设计

在此部分设计中使用一个标准计数器对时钟信号进行计数50_000_000次,得到1s的频率用于时钟的正常计时。在控制“时”、“分”、“秒”的计数器部分,对标准计数器进行调整,增加stop状态,在此状态下进行计数器的手动调整,通过对按钮信号与计数器“state_sys”和计数器“move”的状态进行组合逻辑得到信号来控制该部分计数器。

闹钟功能设计

完全采用标准计数器进行设计,不再过多赘述。最后比较闹钟计数器与时钟计数器得到中间信号,控制蜂鸣器鸣叫。

数码管扫描与显示译码

使用一个标准计数器对时钟信号进行计数50_000次,得到1ms的频率作为数码管扫描频率,再使用一个标准计数器“cnt_sel”用来指示显示译码部分的信号流入并控制数码管扫描。用计数器“state_sys”的状态指示显示译码器中间参量的信号传递,用取余和求模的方法将“时”、“分”、“秒”计数器的高低位进行分离。

说明

使用ALINX公司AX301开发板进行验证,蜂鸣器只需要给低电平就能响,要实现不同效果鸣叫可以自己加PWM模块。仿真部分由于电脑配置不了modelsim,故而使用Robei EDA进行验证,做一点验证了一点就不展示了。

上代码

/
//*********************************************************//
//*  Author  : Petter                                     *//                                      
//*  Time    : 2022.03.28                                 *//
//*  IC      : EP4CE6F17C8                                *//
//*  Project : clock_test                                 *//
//*  Module  : clock_test                                 *//
//*                                                       *//
//*********************************************************//
/


/****************************************************************************************************************/
module clock_test(
  input  clk,
  input  rst_n,
  input  key_1,            //system control
  input  key_2,            //select location to change
  input  key_3,            //add '1'
  output reg  [5:0] sel,   //位选输出
  output reg  [6:0] ment,  //段选输出
  output reg  pot,         //小数点
  output reg  ring,        //蜂鸣器
  output reg  [3:0] led    //led_0:reset instruction led, led_1:hour instruction led,
                           //led_2:minute instruction led, led_3:second instruction led,                         
);

  reg  [23:0] cnt_HALF;   //24_999_999*0.02us=0.5s,用于按键消抖
  reg  [3:0] state_sys;   //'0':复位态;'1':state run;'2':state change;'3':state alarm 
  reg  [3:0] move;        //'0':close buzzer;'1':change second;'2':change minute;'3':change hour
  wire add;               //控制各位数据加'1'
  wire add_cnt_HALF;
  wire end_cnt_HALF;
  wire add_state_sys;
  wire end_state_sys;
  wire add_move;
  wire end_move;
  
  reg  [27:0] cnt_div_s;   //49_999_999 1s
  reg  [19:0] cnt_div_ms;  //49_999 1ms,数码管扫描频率
  reg  [3:0]  cnt_sel;     //数码管扫描计数器
  reg  [7:0]  cnt_s;       //时钟second计数器
  reg  [7:0]  cnt_min;     //时钟minute计数器
  reg  [7:0]  cnt_h;       //时钟hour计数器
  reg  [3:0]  data_sL;     //second低位
  reg  [3:0]  data_sH;     //second高位
  reg  [3:0]  data_minL;   //minute低位
  reg  [3:0]  data_minH;   //minute高位
  reg  [3:0]  data_hL;     //hour低位
  reg  [3:0]  data_hH;     //hour高位
  reg  [3:0]  data_ment;   //数码管数据传递中间参量
  wire add_cnt_div_s;
  wire end_cnt_div_s;
  wire add_cnt_div_ms;
  wire end_cnt_div_ms; 
  wire add_cnt_sel;
  wire end_cnt_sel;
  wire add_cnt_s;
  wire add_cnt_min;
  wire add_cnt_h;
  wire end_cnt_s;
  wire end_cnt_min;
  wire end_cnt_h;
  wire stop_cnt_s;
  wire stop_cnt_min;
  wire stop_cnt_h;
  wire change_cnt_s;
  wire change_cnt_min;
  wire change_cnt_h;
  
  reg  [7:0]  alarm_s;     //闹钟second计数器
  reg  [7:0]  alarm_min;   //闹钟minute计数器
  reg  [7:0]  alarm_h;     //闹钟hour计数器
  wire add_alarm_s;
  wire add_alarm_min;
  wire add_alarm_h;
  wire end_alarm_s;
  wire end_alarm_min;
  wire end_alarm_h;
  wire flag_s;
  wire flag_min;
  wire flag_h;
  wire flag_ring;          //蜂鸣器鸣叫信号
  
  wire clock;              //标志数码管显示数据为时钟
  wire alarm;              //标志数码管显示数据为闹钟
 
 
/****************************************************************************************************************/   
//按钮信号处理部分并用led对系统状态进行指示
   
//cnt_HALF
always@(posedge clk or negedge rst_n)
begin 
 if(!rst_n) begin
   cnt_HALF <= 24'd0; end
 else if(add_cnt_HALF) begin
   if(end_cnt_HALF) begin 
     cnt_HALF <= 24'd0; end
	else begin
	  cnt_HALF <= cnt_HALF + 24'd1; end
	end
end

assign add_cnt_HALF = (key_1 == 1'b0) || (key_2 == 1'b0)  || (key_3 == 1'b0 );  //key低电平有效
assign end_cnt_HALF = add_cnt_HALF && cnt_HALF  == 24'd24_999_999;


//state_sys
always@(posedge clk or negedge rst_n)
begin 
 if(!rst_n) begin
   state_sys <= 4'd0; end
 else if(add_state_sys ) begin
   if(end_state_sys ) begin 
     state_sys <= 4'd0; end
	else begin
	  state_sys <= state_sys + 4'd1; end
	end
end

assign add_state_sys = end_cnt_HALF && ~key_1;
assign end_state_sys = add_state_sys && state_sys  == 4'd3;


//move
always@(posedge clk or negedge rst_n)
begin 
 if(!rst_n) begin
   move <= 4'd0; end
 else if(add_move ) begin
   if(end_move ) begin 
     move <= 4'd0; end
	else begin
	  move <= move + 4'd1; end
	end
end

assign add_move = end_cnt_HALF && ~key_2;
assign end_move = add_move && move  == 4'd3;

//add
assign add = end_cnt_HALF && ~key_3;

//led  
always@(posedge clk )
begin
 if(state_sys == 2 || state_sys == 3) begin
	  case(move)
	  4'd0 : begin
	        led <= 4'b0001; end	  
	  4'd1 : begin
	        led <= 4'b1000; end
	  4'd2 : begin
	        led <= 4'b0100; end	
	  4'd3 : begin
	        led <= 4'b0010; end
  default : begin
	        led <= 4'b0001; end	
		endcase
	end
 else if(state_sys == 1) begin
   led <= 4'b1110; end
end

	
/****************************************************************************************************************/  
//时钟功能计数器

//1s计数器 
//cnt_div_s 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin
  cnt_div_s <= 28'd0; end
 else if(add_cnt_div_s) begin
  if(end_cnt_div_s) begin
   cnt_div_s <= 28'd0; end
  else begin
	cnt_div_s <= cnt_div_s + 28'd1; end
	end
end

assign add_cnt_div_s = state_sys == 1 || state_sys == 3;  //'1'&'3'态驱动时钟,'0'态用来复位后清零
assign end_cnt_div_s = add_cnt_div_s && cnt_div_s == 28'd49_999_999; //28'd1_999_999; 

//second计数器
//cnt_s 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin
  cnt_s <= 8'd0; end
 else if(add_cnt_s) begin
  if(end_cnt_s) begin
   cnt_s <= 8'd0; end
  else begin
	cnt_s <= cnt_s + 8'd1; end 
	end
 else if(stop_cnt_s) begin
   if(change_cnt_s) begin
	 cnt_s <= cnt_s + 8'd1; end
	else if(cnt_s== 8'd60) begin
    cnt_s <= 8'd0; end
	else begin
	 cnt_s <= cnt_s; end
	 end 
end

assign add_cnt_s = end_cnt_div_s;
assign end_cnt_s = add_cnt_s && cnt_s == 8'd59;
assign stop_cnt_s = state_sys == 2;
assign change_cnt_s = move == 1 && add;

//minute计数器
//cnt_min 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin
  cnt_min <= 8'd0; end
 else if(add_cnt_min) begin
   if(end_cnt_min) begin
    cnt_min <= 8'd0; end
   else begin
	 cnt_min <= cnt_min + 8'd1; end 
	end
 else if(stop_cnt_min) begin
   if(change_cnt_min) begin
	 cnt_min <= cnt_min + 8'd1; end
	else if(cnt_min== 8'd60) begin
    cnt_min <= 8'd0; end
	else begin
	 cnt_min <= cnt_min; end
	 end   
end

assign add_cnt_min = end_cnt_s;
assign end_cnt_min = (add_cnt_min && cnt_min== 8'd59)  || (add_cnt_min && cnt_min == 8'd59 && cnt_s == 8'd59);
assign stop_cnt_min = state_sys == 2;
assign change_cnt_min = move == 2 && add;

//hour计数器
//cnt_h 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin
  cnt_h <= 8'd0; end
 else if(add_cnt_h) begin
   if(end_cnt_h) begin
    cnt_h <= 8'd0; end
   else begin
	 cnt_h <= cnt_h + 8'd1; end 
	end
 else if(stop_cnt_h) begin
   if(change_cnt_h) begin
	 cnt_h <= cnt_h + 8'd1; end
	else if( cnt_h== 8'd24) begin
    cnt_h <= 8'd0; end
	else begin
	 cnt_h <= cnt_h; end
	 end
end

assign add_cnt_h = end_cnt_min && end_cnt_s;
assign end_cnt_h = add_cnt_h && cnt_h == 8'd23 && cnt_min == 8'd59 && cnt_s == 8'd59;
assign stop_cnt_h = state_sys == 2;
assign change_cnt_h = move == 3 && add;


/****************************************************************************************************************/  
//闹钟功能计数器
  
//alarm_s 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin
  alarm_s <= 8'd0; end
 else if(add_alarm_s) begin
  if(end_alarm_s) begin
   alarm_s <= 8'd0; end
  else begin
	alarm_s <= alarm_s + 8'd1; end 
	end
end

assign add_alarm_s = move == 1 && state_sys == 3 && add;
assign end_alarm_s = add_alarm_s && alarm_s == 8'd59;

//alarm_min 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin
  alarm_min <= 8'd0; end
 else if(add_alarm_min) begin
  if(end_alarm_min) begin
   alarm_min <= 8'd0; end
  else begin
	alarm_min <= alarm_min + 8'd1; end 
	end
end

assign add_alarm_min = move == 2 && state_sys == 3 && add;
assign end_alarm_min = add_alarm_min && alarm_min == 8'd59;


//alarm_h 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin
  alarm_h <= 8'd6; end
 else if(add_alarm_h) begin
  if(end_alarm_h) begin
   alarm_h <= 8'd0; end
  else begin
	alarm_h <= alarm_h + 8'd1; end 
	end
end

assign add_alarm_h = move == 3 && state_sys == 3 && add;
assign end_alarm_h = add_alarm_h && alarm_h == 8'd23;

	
//ring
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin       //强制复位
   ring <= 1'b1; end
 else if(flag_ring) begin
   ring <= 1'b0; end
 else if(move == 0) begin  //手动关闭buzzer
   ring <= 1'b1; end 
end 

assign flag_s = cnt_s == alarm_s;
assign flag_min = cnt_min == alarm_min;
assign flag_h = cnt_h == alarm_h;
assign flag_ring = flag_s && flag_min && flag_h;

/****************************************************************************************************************/  
//数码管显示控制与译码
  
assign clock = state_sys == 1 || state_sys == 2;
assign alarm = state_sys == 3;

//高低位数据分离
always@(posedge clk)
begin 
 if(clock) begin
   data_sL <= cnt_s % 8'd10;
   data_sH <= cnt_s / 8'd10;
   data_minL <= cnt_min % 8'd10;
   data_minH <= cnt_min / 8'd10;
   data_hL <= cnt_h % 8'd10;
   data_hH <= cnt_h / 8'd10; end
 else if(alarm) begin
   data_sL <= alarm_s % 8'd10;
   data_sH <= alarm_s / 8'd10;
   data_minL <= alarm_min % 8'd10;
   data_minH <= alarm_min / 8'd10;
   data_hL <= alarm_h % 8'd10;
   data_hH <= alarm_h / 8'd10; end
 
end


//1ms计数器
//cnt_div_ms 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin
  cnt_div_ms <= 20'd0; end
 else if(add_cnt_div_ms) begin
  if(end_cnt_div_ms) begin
   cnt_div_ms <= 20'd0; end
  else begin
	cnt_div_ms <= cnt_div_ms + 20'd1; end
	end
end

assign add_cnt_div_ms = rst_n;
assign end_cnt_div_ms = add_cnt_div_ms && cnt_div_ms == 20'd49_999; //1ms

//位选计数器 
//cnt_sel 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n) begin
  cnt_sel <= 4'd0; end
 else if(add_cnt_sel) begin
  if(end_cnt_sel) begin
   cnt_sel <= 4'd0; end
  else begin
	cnt_sel <= cnt_sel + 4'd1; end 
	end
end

assign add_cnt_sel = end_cnt_div_ms;
assign end_cnt_sel = add_cnt_sel && cnt_sel == 4'd5; 
 
//数据传递 
always@(posedge clk)
begin
 case(cnt_sel)
 4'd0 : begin data_ment <= data_sL; 
               pot<= 1;end
 4'd1 : begin data_ment <= data_sH; 
               pot<= 1;end
 4'd2 : begin data_ment <= data_minL; 
               pot<= 0;end
 4'd3 : begin data_ment <= data_minH;
               pot<= 1;end
 4'd4 : begin data_ment <= data_hL;
               pot<= 0; end
 4'd5 : begin data_ment <= data_hH;
               pot<= 1;end 
 default data_ment <= 4'd0;
 endcase
end

//数码管扫描
always@(posedge clk)
begin
  case(cnt_sel)
    4'd0 : sel <= 6'b111_110;
    4'd1 : sel <= 6'b111_101;
    4'd2 : sel <= 6'b111_011;
    4'd3 : sel <= 6'b110_111;
    4'd4 : sel <= 6'b101_111;
    4'd5 : sel <= 6'b011_111;
 default : sel <= 6'b111_110;
 endcase
end

//显示译码
always@(posedge clk)
begin 
  case(data_ment)	
	 4'd0 : ment <= 7'b1_000_000;
	 4'd1 : ment <= 7'b1_111_001;
	 4'd2 : ment <= 7'b0_100_100;
	 4'd3 : ment <= 7'b0_110_000;
	 4'd4 : ment <= 7'b0_011_001;
	 4'd5 : ment <= 7'b0_010_010;
	 4'd6 : ment <= 7'b0_000_010;
	 4'd7 : ment <= 7'b1_111_000;
	 4'd8 : ment <= 7'b0_000_000;
	 4'd9 : ment <= 7'b0_010_000;
 default : ment <= 7'b1_000_000;
 endcase
end

 

/****************************************************************************************************************/ 
endmodule 
 

结语

设计简单,没有分模块,能力有限,恳请大佬批评指正。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值