FPGA USB串口通信(二)結束

目錄

四、設計輸入

五、仿真測試

六、下板測試

1.PC向FPGA發送數據

2.FPGA向PC發送數據

總結


四、設計輸入

如圖所示思維導圖,其中包含設計的狀態、功能設計、以及信號設計,根據此設計,寫出代碼。

1.主模塊:例化按鍵模塊


/*************串口通信********************/
module serial_port(
    input                   clk        ,//50M時鐘
    input                   rst_n      ,//復位
    input       [ 2: 0]     key_in     ,//按鍵輸入
    input                   rx_data    ,//計算機端串口數據接收
    output  reg             tx_data    ,//FPGA發送數據
    output  reg [ 7: 0]     led         //[3:0]是板上LED0-LED3,[5:4]是RS232的Tx和Rx端的led指示燈,[7:6]是can的Rxd和Txd端的led指示燈   
);
    
parameter time_1bit = 13'd5208  ;//1bit數據計數次數
parameter data      = 8 'hff    ;//FPGA發送數據時循環發送數據

reg   [  1: 0]         state        ;//狀態,包含3個狀態,空閒、PC向FPGA發送數據、FPGA向PC發送數據
reg   [ 12: 0]         cnt0         ;//發送1bit數據時鐘數的計數,9600bps爲1/9600s=104166.66ns=5208時鐘週期
reg   [  3: 0]         cnt1         ;//發送數據計數
reg                    flag         ;//可以計數指示信號
reg                    data_in_vld  ;//數據輸入使能

wire                   add_cnt0     ;//計數器cnt0加一條件
wire                   end_cnt0     ;//計數器cnt0結束條件
wire                   add_cnt1     ;//計數器cnt1加一條件
wire                   end_cnt1     ;//計數器cnt1結束條件
wire                   neg_flag     ;//始接收數據指示信號
reg                    rx_data_ff1  ;//rx_data打一拍,防止亞穩態
reg                    rx_data_ff2  ;//rx_data打兩拍,防止亞穩態
reg                    rx_data_ff3  ;//再打一拍,獲取rx_data_ff2的下降沿neg_flag
wire	[ 9: 0]        tx_data_f	   ;//帶有起始位和停止位的數據
wire	[ 3: 0]        key_vld		;//按鍵輸出

key_move(
	.clk		(clk)		,//50M時鐘
	.rst_n		(rst_n)	    ,//復位
	.key_in		(key_in)	,//按鍵輸入
	.key_vld	(key_vld)    //按鍵輸出
	);

	//狀態輸出
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)
        state <= 2'b00;
    else if(key_vld==3'b110)
        state <= 2'b00;
    else if(key_vld==3'b101)
        state <= 2'b01;
    else if(key_vld==3'b011)
        state <= 2'b10;
    else
        state <= state;
end

//cnt0計數
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        cnt0 <= 0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)
            cnt0 <= 0;
        else
            cnt0 <= cnt0 + 1'b1;
    end
end
assign add_cnt0 = (state==2'b01 || state==2'b10) && flag==1;
assign end_cnt0 = add_cnt0 && cnt0==time_1bit-1;

////cnt1計數
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        cnt1 <= 0;
    end
    else if(add_cnt1) begin
        if(end_cnt1)
            cnt1 <= 0;
        else
            cnt1 <= cnt1 + 1'b1;
    end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1==10-1;

////flag輸出,flag==1時計數
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)
        flag <= 0;
    else if(state==2'b01&&neg_flag || state==2'b10&&data_in_vld==1) 
        flag <= 1;
    else if(end_cnt1)
        flag <= 0;
    else
        flag <= flag;
end

//數據輸入使能
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)
        data_in_vld <= 1;
    else if(state==2'b10 && flag==1)
        data_in_vld <= 0;
    else if(state==2'b10 && flag==0)
        data_in_vld <= 1;
end

//發送一個數據幀
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        tx_data <= 1;
    end
    else if(state==2'b10 && flag==1 && cnt0==0) begin
        tx_data <= tx_data_f[cnt1];
    end
end
assign tx_data_f = {1'b1,data,1'b0};

//打兩拍,防止亞穩態,同時捕獲下降沿
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rx_data_ff1 <= 1;
        rx_data_ff2 <= 1;
        rx_data_ff3 <= 1;
    end
    else begin
        rx_data_ff1 <= rx_data    ;
        rx_data_ff2 <= rx_data_ff1;
        rx_data_ff3 <= rx_data_ff2;
    end
end
assign neg_flag = rx_data_ff2==0 && rx_data_ff3==1;

//輸出led信號
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        led <= 8'hff;
    end
    else if(state==2'b01 && flag==1 && cnt0==time_1bit/2 && cnt1>0)begin
        led[cnt1-1] <= rx_data_ff2;
    end
end    
endmodule

2.按鍵消抖模塊



/**********按鍵按下,輸出一個有用脈衝信號******/
module key_move(
	input			                clk	,//50M時鐘
	input					rst_n	,//復位
	input	 	[ KEY_W-1: 0]		key_in	,//按鍵輸入
	output reg	[ KEY_W-1: 0]		key_vld	 //按鍵輸出
	);
	
	
	
parameter                       	DATA_W 	  = 20         ;
parameter                       	KEY_W 	  = 3          ;
parameter                       	TIME_20MS = 1_000_000  ;

reg             [DATA_W-1:0]  	    cnt                     ;//計數器計數20ms,防止抖動
wire                                add_cnt                 ;//計數器加一條件
wire                                end_cnt                 ;//計數器結束條件
reg									flag                    ;//計數條件
reg             [KEY_W-1 :0]        key_in_ff1              ;//按鍵輸入打兩拍,防止亞穩態
reg             [KEY_W-1 :0]        key_in_ff0              ;//按鍵輸入打一拍

//計數器,計數20ms防止抖動
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        cnt <= 20'b0;
    end
    else if(add_cnt)begin
        if(end_cnt)
            cnt <= 20'b0;
        else
            cnt <= cnt + 1'b1;
    end
    else begin
        cnt <= 0;
    end
end

assign add_cnt = flag==1'b0 && (&key_in_ff1==0);
assign end_cnt = add_cnt && cnt == TIME_20MS - 1;

//計數條件
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        flag <= 1'b0;
    end
    else if(end_cnt)begin
        flag <= 1'b1;
    end
    else if(&key_in_ff1==1)begin
        flag <= 1'b0;
    end
end

//按鍵輸入打兩拍,防止亞穩態
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_in_ff0 <= {{KEY_W}{1'b1}};
        key_in_ff1 <= {{KEY_W}{1'b1}};
    end
    else begin
        key_in_ff0 <= key_in    ;
        key_in_ff1 <= key_in_ff0;
    end
end

//按鍵按下,輸出一個有用脈衝信號
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        key_vld <= {{KEY_W}{1'b1}};
    end
    else if(end_cnt)begin
        key_vld <= key_in_ff1;
    end
    else begin
        key_vld <= {{KEY_W}{1'b1}};
    end
end
endmodule


五、仿真測試

編寫仿真文件,測試其功能。不加按鍵模塊,進行測試。

// Generated on "04/07/2020 11:04:56"
                                                                                
// Verilog Test Bench template for design : serial_port
// 
// Simulation tool : ModelSim (Verilog)
// 

`timescale 1 ns/ 1 ns
module serial_port_tb();
// constants                                           
// test vector input registers
reg clk;
reg [2:0] key_in;
reg rst_n;
reg rx_data;
// wires                                               
wire [7:0]  led;
wire tx_data;


parameter clk_period = 20;
// assign statements (if any)                          
serial_port i1 (
// port map - connection between master ports and signals/registers   
	.clk(clk),
	.key_in(key_in),
	.led(led),
	.rst_n(rst_n),
	.rx_data(rx_data),
	.tx_data(tx_data)
);

initial	clk=0;
always #(clk_period/2) clk=~clk;

initial begin
	//復位
	#2;
	rst_n = 0;
	key_in = 3'b111;
	rx_data = 1;
	#(clk_period*5);
	rst_n = 1;
	#(clk_period*3);
	
	//空閒狀態
	key_in = 3'b110;
	#(clk_period);
	key_in = 3'b111;
	#(clk_period*50);
	
	//狀態1,接收PC端數據
	key_in = 3'b101;
	#(clk_period);
	key_in = 3'b111;
	#(clk_period*20);
			//發送一個數據幀10'b0111111111
	rx_data = 0;
	#(clk_period*5208);
	rx_data = 1;
	#(clk_period*5208*9);
			//發0;
	#(clk_period*5208);
	rx_data = 0送第二個數據幀
	rx_data = ;
	#(clk_period*5208*4);
	rx_data = 1;
	#(clk_period*5208);
	rx_data = 0;
	#(clk_period*5208);
	rx_data = 1;
	#(clk_period*5208);
	rx_data = 0;
	#(clk_period*5208);
	rx_data = 1;
	#(clk_period*5208);
	
	//狀態2,FPGA發送數據
	key_in = 3'b011;
	#(clk_period*5208*40);
	
	$stop;
end                                              
endmodule

仿真圖形如下:

 

六、下板測試

1.PC向FPGA發送數據

按下按鍵2,進入PC向FPGA發送數據狀態。

發送數據8'h00,8個LED應該都亮起。


發送數據8'hff,8個LED應全部滅掉。


發送數據8'ha0,8個LED應亮起部分。


2.FPGA向PC發送數據

按下按鍵3,可以看到串口收到FPGA發送的循環數據。

總結

串口通信主要對其協議進行理解,瞭解其數據發送幀格式,其實是很簡單的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章