FPGA 按键控制串口发送

按键消抖

消抖时间一般为10ms,我使用的板子是ACX720,晶振为50MHZ,20ns为一周期。

状态机

模块设计

设计文件

verilog 复制代码
`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

仿真验证

verilog 复制代码
`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),然后信号线上的信号值是由信号线电平和参考电平线的电压差决定。所以我们一定要养成模块之间共地的好习惯。

串口帧

模块设计

verilog 复制代码
`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

仿真验证

verilog 复制代码
`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视图

verilog 复制代码
`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

板级验证

相关推荐
上理考研周导师5 小时前
第二章 虚拟仪器及其构成原理
fpga开发
FPGA技术实战6 小时前
《探索Zynq MPSoC》学习笔记(二)
fpga开发·mpsoc
bigbig猩猩17 小时前
FPGA(现场可编程门阵列)的时序分析
fpga开发
Terasic友晶科技1 天前
第2篇 使用Intel FPGA Monitor Program创建基于ARM处理器的汇编或C语言工程<二>
fpga开发·汇编语言和c语言
码农阿豪1 天前
基于Zynq FPGA对雷龙SD NAND的测试
fpga开发·sd nand·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
江山如画,佳人北望1 天前
EDA技术简介
fpga开发
淘晶驰AK1 天前
电子设计竞赛准备经历分享
嵌入式硬件·fpga开发
最好有梦想~1 天前
FPGA时序分析和约束学习笔记(4、IO传输模型)
笔记·学习·fpga开发
檀越剑指大厂1 天前
【基于Zynq FPGA对雷龙SD NAND的测试】
fpga开发