FPGA USB serial communication (two) end

table of Contents

Four, design input

Five, simulation test

Six, lower board test

1. PC sends data to FPGA

2. FPGA sends data to PC

to sum up


Four, design input

As shown in the figure, the mind map contains the state of the design, function design, and signal design. Based on this design, write the code.

1. Main module: instantiate button module


/*************串口通信********************/
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. Key debounce module



/**********按键按下,输出一个有用脉冲信号******/
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


Five, simulation test

Write simulation files and test their functions. Do not add the button module to test.

// 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

The simulation graphics are as follows:

 

Six, lower board test

1. PC sends data to FPGA

Press button 2 to enter the state of PC sending data to FPGA.

Send data 8'h00, 8 LEDs should all light up.


Send data 8'hff, 8 LEDs should all be off.


Send data 8'ha0, 8 LEDs should light up partly.


2. FPGA sends data to PC

Press button 3, you can see the serial port receives the cyclic data sent by FPGA.

to sum up

Serial communication mainly understands its protocol and understands its data transmission frame format, which is actually very simple.

Guess you like

Origin blog.csdn.net/qq_33231534/article/details/105372396