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


扫描二维码关注公众号,回复: 11799361 查看本文章

五、仿真测试

编写仿真文件,测试其功能。不加按键模块,进行测试。

// 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发送的循环数据。

总结

串口通信主要对其协议进行理解,了解其数据发送帧格式,其实是很简单的。

猜你喜欢

转载自blog.csdn.net/qq_33231534/article/details/105372396