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

板级验证

相关推荐
I'm a winner13 小时前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper202016 小时前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy1 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
Rambo.xia1 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20201 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子2 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
9527华安2 天前
FPGA纯verilog代码ISP图像处理培训教程,基于IMX214,提供工程源码+视频教程+FPGA开发板
图像处理·fpga开发·isp·imx214·mipi
千寻xun3 天前
二、实战篇-NVME SSD控制之ZYNQ实现(六)-读写NVME SSD硬盘数据
fpga开发·nvme·nvme ssd
千寻xun3 天前
二、实战篇-NVME SSD控制之ZYNQ实现(三)
fpga开发·nvme ssd·nvme协议
忆锦紫3 天前
vivado报错及解决【十】
fpga开发·fpga·vivado