
//////////////////////////////////////////////////////////////////////////////////
//  module button_in_out()
//  功能：模块用于按键输入，带有去抖电路
//  作者：杜伟韬 
////////////////////////////////////////////////////////////////////////////////
// 按键去抖原理：使用计数器，计数器每隔20毫秒会达到最大值然后溢出，在计数器达到
// 溢出值的 8/8， 7/8, 6/8, 0/8 时刻对按键的值进行采样，如果全部采样值相同则说明
// 没有抖动发生输出采样值，否则认为发生了抖动，输出值保持不变
// 时间轴方向 ---------------------------------------------->
// 按键采样值  :v0,            v1,      v2       v3
// 计数器值    :0, 1, 2,  .... 6/8 max, 7/8 max, max
//              |<------计数器溢出时间 10ms------->|
//
// 模块的重用，根据时钟速率，合理配置计数器的溢出值，使得溢出时间为10毫秒
////////////////////////////////////////////////////////////////////////////////
module button_in_out(
  CLK       ,   // clock
  IN     ,   // input button signal
  OUT    );  // output button signal

// 50MHz clock, T = 20ns = 20*1E-6 ms
// button check time = 20*1E-6 * 2^20, about 20ms 
// set the CNT_WL with your system clock freq
parameter CNT_WL          = 20  ;               // counter word length


// 合理配置计数器溢出值，10到20毫秒溢出一次
// 当前时钟周期，20ns，溢出值为 500,000 = 0x7A120，每10毫秒溢出一次
parameter CNT_MAX         = 20'h7A120           ;  // counter max value


parameter CNT_MAX68       = (CNT_MAX >> 3) * 6  ; // 6/8 counter max value
parameter CNT_MAX78       = (CNT_MAX >> 3) * 7  ; // 7/8 counter max value

input   CLK, IN;
output  OUT;

reg     button_in_d1R, button_in_d2R;
reg     [CNT_WL-1:0] counterR;    
reg     buttonIn0R, buttonIn1R, buttonIn2R, buttonIn3R, buttonOutR;
assign  OUT = buttonOutR;
// 输入信号跨时钟域，使用2个级联的D触发器把按键信号引导进入
// FPGA的时钟域，避免采样到按键信号的跳变边沿
always @ (posedge CLK) begin
  button_in_d1R <= IN;
  button_in_d2R <= button_in_d1R;
end
// 由于本模块可以用于复位按键，故本模块的触发器均不使用复位信号
always @ (posedge CLK) begin
  if(counterR == 0)
    buttonIn0R <= button_in_d2R;
  else
    buttonIn0R <= buttonIn0R;

  if(counterR == CNT_MAX68)
    buttonIn1R <= button_in_d2R;
  else
    buttonIn1R <= buttonIn1R;

  if(counterR == CNT_MAX78)
    buttonIn2R <= button_in_d2R;
  else
    buttonIn2R <= buttonIn2R;

  if(counterR == CNT_MAX)
    buttonIn3R <= button_in_d2R;
  else
    buttonIn3R <= buttonIn3R;

end
// different values of button input buffers will start the counter 
always @(posedge CLK) begin
  if(counterR < CNT_MAX)
    counterR <= counterR + 1;
  else
    counterR <= 0;
end
always @(posedge CLK) begin
  if(buttonIn0R == buttonIn1R == buttonIn2R == buttonIn3R )
    buttonOutR <= buttonIn3R;
  else
    buttonOutR <= buttonOutR;
end
endmodule   // module button_in_out() 
