按键消抖
消抖时间一般为10ms,我使用的板子是ACX720,晶振为50MHZ,20ns为一周期。
状态机
模块设计
设计文件
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/01/11 12:18:36
// Design Name:
// Module Name: key_filter
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module key_filter(
Clk,
Rst_n,
Key_in,
Key_flag, //按键按下标志位
Key_State //高电平,按键按下
);
input Clk;
input Rst_n;
input Key_in;
output reg Key_flag;
output reg Key_State;
parameter Filter_Time=500_000; //10ms
localparam
S1=4'b0001,//按键松开
S2=4'b0010,//消抖计数
S3=4'b0100,//按键松开
S4=4'b1000;//消抖计数
//捕捉按键上升沿和下降沿
reg [2:0] Pos_Neg_r;
wire pos_edge;
wire neg_edge;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Pos_Neg_r<=0;
else begin
Pos_Neg_r={Pos_Neg_r[1:0],Key_in};
end
end
assign pos_edge=Pos_Neg_r[2:1]==2'b01;//上升沿 //按键松开
assign neg_edge=Pos_Neg_r[2:1]==2'b10;//下降沿 //按键按下
//消抖延迟计数器
reg [18:0] counter_cnt;
reg En_counter_cnt;//按键消抖计数的条件
wire end_counter_cnt;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
counter_cnt<=19'd0;
else if(En_counter_cnt)begin
if(end_counter_cnt)
counter_cnt<=19'd0;
else
counter_cnt<=counter_cnt+1'd1;
end
else
counter_cnt<=19'd0;
end
assign end_counter_cnt=counter_cnt>=(Filter_Time-1);
reg [3:0] cur_state; //定义现态寄存器
reg [3:0] next_state; //定义次态寄存器
/*
-----------------------------------------------------------------------
状态机第一段:同步时序描述状态转移
-----------------------------------------------------------------------
*/
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cur_state <= S1; //复位初始状态
else
cur_state <= next_state; //次态转移到现态
end
/*
-----------------------------------------------------------------------
状态机第二段:组合逻辑判断状态转移条件,描述状态转移规律以及输出
-----------------------------------------------------------------------
*/
always@(*)begin
case(cur_state)
S1:begin //按键松开状态
if(neg_edge) //按键按下--检测到下降沿
next_state=S2;
else
next_state=cur_state;
end
S2:begin
if(pos_edge)
next_state=S1;
else if(end_counter_cnt)
next_state=S3;
else
next_state=cur_state;
end
S3:begin //按键按下状态
if(pos_edge) //按键松开--检测到上升沿
next_state=S4;
else
next_state=cur_state;
end
S4:begin
if(neg_edge)
next_state=S3;
else if(end_counter_cnt)
next_state=S1;
else
next_state=cur_state;
end
default:next_state=cur_state;
endcase
end
/*
-----------------------------------------------------------------------
状态机第三段:时序逻辑描述输出
-----------------------------------------------------------------------
*/
//消抖计数使能
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
En_counter_cnt <= 1'b0; //复位、初始状态
else
case(cur_state) //根据当前状态进行输出
S1: En_counter_cnt <= 1'b0; //不计数
S2: En_counter_cnt <= 1'b1; //计数
S3: En_counter_cnt <= 1'b0; //不计数
S4: En_counter_cnt <= 1'b1; //计数
default:En_counter_cnt <= 1'b0; //默认不计数
endcase
end
//按键按下标志位
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Key_flag <= 1'b0; //复位、初始状态
//Key_State存在一拍
else if(cur_state==S2 && end_counter_cnt)
Key_flag<=1'd1;
else
Key_flag<=1'd0;
end
//输出按键状态
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Key_State <= 1'b0; //复位、初始状态
else if(cur_state==S3)
Key_State<=1'd1;
else if(cur_state==S4 && end_counter_cnt)
Key_State<=1'd0;
else
Key_State<=Key_State;
end
endmodule
仿真验证
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/01/06 16:24:27
// Design Name:
// Module Name: key_filter_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module key_filter_tb();
reg Clk;
reg Rst_n;
reg Key_in;
wire Key_flag;
wire Key_State;
key_filter
#(
.Filter_Time(5000)//100us
)
key_filter(
Clk,
Rst_n,
Key_in,
Key_flag, //按键按下标志位
Key_State //高电平,按键按下
);
initial Clk=1;
always #10 Clk=~Clk;
initial begin
Rst_n=0;
Key_in=1;
#201;
Rst_n=1;
Key_in=1;#20000;
Key_in=0;#20000;
Key_in=1;#10000;
Key_in=0;#20000;
Key_in=1;#20000;
Key_in=0;#600000;
Key_in=1;#20000;
Key_in=0;#20000;
Key_in=1;#10000;
Key_in=0;#20000;
Key_in=1;#20000;
Key_in=0;#20000;
Key_in=1;#10000;
Key_in=0;#20000;
Key_in=1;#20000;
Key_in=0;#20000;
Key_in=1;#10000;
Key_in=1;#600000;
$stop;
end
endmodule
串口发送
**注意:**电平信号的传输线中有一个参考电平线(一般是GND),然后信号线上的信号值是由信号线电平和参考电平线的电压差决定。所以我们一定要养成模块之间共地的好习惯。
串口帧
模块设计
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/01/06 11:30:58
// Design Name:
// Module Name: UART_Byte_Tx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module UART_Byte_Tx
#(
parameter BaudRate = 115200,//波特率
parameter ClockRate = 50_000_000//系统时钟
)
(
Clk,
Rst_n,
Send_En,
data_byte,
Tx_Data,
Tx_Done,
uart_state
);
input Clk;
input Rst_n;
input Send_En;
input [7:0] data_byte;
output reg Tx_Data;
output reg Tx_Done;
output reg uart_state;
//设置使能
reg tx_en;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
tx_en<=0;
else if(Send_En)
tx_en<=1'd1;
else if(Tx_Done)
tx_en<=1'd0;
else
tx_en<=tx_en;
end
//设置波特率
localparam Buad_Num = ClockRate/BaudRate;
//设置计数器
reg [12:0] buad_cnt;
wire add_buad_cnt;
wire end_buad_cnt;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
buad_cnt<=0;
else if(add_buad_cnt)begin
if(end_buad_cnt)
buad_cnt<=0;
else
buad_cnt<=buad_cnt+1'b1;
end
else
buad_cnt<=0;
end
assign add_buad_cnt=tx_en;
assign end_buad_cnt=buad_cnt>=(Buad_Num-1'd1);
//设置发送bit计数
reg [3:0] bit_cnt;
wire add_bit_cnt;
wire end_bit_cnt;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bit_cnt<=0;
else if(add_bit_cnt)
bit_cnt<=bit_cnt+1'd1;
else if(end_bit_cnt)
bit_cnt<=0;
else
bit_cnt<=bit_cnt;
end
assign add_bit_cnt=buad_cnt==1;
assign end_bit_cnt=(bit_cnt==4'd10 && add_bit_cnt) || !tx_en;
//发送数据
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Tx_Data<=1;
else begin
case(bit_cnt)
4'd1:Tx_Data<=0;
4'd2:Tx_Data<=data_byte[0];
4'd3:Tx_Data<=data_byte[1];
4'd4:Tx_Data<=data_byte[2];
4'd5:Tx_Data<=data_byte[3];
4'd6:Tx_Data<=data_byte[4];
4'd7:Tx_Data<=data_byte[5];
4'd8:Tx_Data<=data_byte[6];
4'd9:Tx_Data<=data_byte[7];
4'd10:Tx_Data<=1;
default:Tx_Data<=1;
endcase
end
end
//发送结束
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Tx_Done<=0;
else if(bit_cnt==4'd10 && add_bit_cnt)
Tx_Done<=1;
else
Tx_Done<=0;
end
//发送状态(有效数据)
wire En_uart_state;
wire Nen_uart_state;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_state<=0;
else if(En_uart_state)
uart_state<=1;
else if(Nen_uart_state)
uart_state<=0;
end
assign En_uart_state=bit_cnt==4'd1 && add_bit_cnt;
assign Nen_uart_state=bit_cnt==4'd9 && add_bit_cnt;
endmodule
仿真验证
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/01/06 11:31:09
// Design Name:
// Module Name: UART_Byte_Tx_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module UART_Byte_Tx_tb();
reg Clk;
reg Rst_n;
reg Send_En;
reg [7:0]data_byte;
wire Tx_Data;
wire Tx_Done;
wire uart_state;
initial Clk=1;
always #10 Clk=~Clk;
initial begin
Rst_n=0;
Send_En=0;
data_byte=0;
#201;
Rst_n=1;
data_byte=8'b1001_0110;
Send_En=1;
#20;
Send_En=0;
#100000;
data_byte=8'b0111_0110;
Send_En=1;
#20;
Send_En=0;
#100000;
$stop;
end
UART_Byte_Tx UART_Byte_Tx(
.Clk(Clk),
.Rst_n(Rst_n),
.Send_En(Send_En),
.data_byte(data_byte),
.Tx_Data(Tx_Data),
.Tx_Done(Tx_Done),
.uart_state(uart_state)
);
endmodule
按键控制串口发送
RTL视图
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/01/07 13:43:42
// Design Name:
// Module Name: Uart_Key_Send_cmd
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module Uart_Key_Send_cmd(
Clk,
Rst_n,
Key_in,
uart_tx
);
input Clk;
input Rst_n;
input Key_in;
output uart_tx;
//按键模块
wire Key_flag;
wire Key_State;
key_filter key_filter(
Clk,
Rst_n,
Key_in,
Key_flag, //按键按下标志位
Key_State //高电平,按键按下
);
//串口发送
reg [7:0] data_byte;
wire Tx_Done;
wire uart_state;
//assign data_byte=8'b0100_0001; //发送A
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
data_byte<=8'b0100_0001; //发送A
else if(Tx_Done)
data_byte<=data_byte+1'b1;//数据加一
end
UART_Byte_Tx UART_Byte_Tx(
.Clk(Clk),
.Rst_n(Rst_n),
.Send_En(Key_flag),
.data_byte(data_byte),
.Tx_Data(uart_tx),
.Tx_Done(Tx_Done),
.uart_state(uart_state)
);
endmodule