其他分享
首页 > 其他分享> > FPGA串口接收与发送详解( part 3 )

FPGA串口接收与发送详解( part 3 )

作者:互联网

之前的part1~2已经详解完了单个数据的串口接收与发送,链接如下:

FPGA串口接收与发送 详解 (part 1 )_居安士的博客-CSDN博客

FPGA串口接收与发送详解( part 2 )_居安士的博客-CSDN博客

我们在实际使用串口进行数据传输的时候,不会只能传输一个字节的单个数据,而是以数据帧的形式进行发送,下面就介绍一下,如何打包数据帧,以及数据帧如何发送与接收

目录

一、打包数据帧

二、一帧数据的接收

三、一帧数据的发送


一、打包数据帧

数据帧,就是数据链路层的协议数据单元,它包括三部分:帧头,数据部分,帧尾。

其中,帧头和帧尾包含一些必要的控制信息,比如同步信息、地址信息、差错控制信息等;

数据部分则包含网络层传下来的数据

 其中,AA 55就是帧头,00FF是帧尾,ADDR是地址,DATA是数据

一帧数据的接收,一共4个地址2个数据,帧头AA 55,帧尾00 FF,通过接收PC端的数据,将数据部分包含的地址和数据解析出来

一帧数据的发送,发送AA  55   4个地址   2个数据  00  FF

(上面的帧格式都是我自己定义的,而非固定格式,大家可以根据自己需要进行修改) 

二、一帧数据的接收

一帧数据的接收是通过接收PC端的数据,将数据部分包含的地址和数据解析出来

输入的是单个的8位并行数据[7:0]dout,以及单个数据接收完毕使能信号dout_en

输出的是写数据[15:0]wr_data,写地址[31:0]wr_addr,写使能wr_en

接收一帧数据步骤:

(1)定义写数据[15:0]wr_data,写地址[31:0]wr_addr

(2)定义一帧数据的缓存寄存器(10个),让帧头,数据,帧尾缓存进去:通过把[8:0]dout移位10次得到dout9~dout0

(3)定义状态机,分为初始,写判断,写3个状态

(4) 初始状态:把读写地址和数据清零

          写判断:根据协议判断是不是写

          写:把dout4—dout7存入写地址,dout2—dout3存入写数据

(5)设置清零clean,在dout存入地址/数据完成之后,清掉地址/数据

module uart_RX(
input clk_25m,
input reset,
input [7:0] dout,//单个的8位并行数据
input dout_en,//单个数据接收完毕使能信号
output reg [15:0] wr_data, //写数据
output reg [31:0] wr_addr,//写地址
output reg wr_en//写使能
    );

/*定义一帧数据的缓存寄存器(10个)*/    
reg [7:0] dout0;
reg [7:0] dout1;
reg [7:0] dout2;
reg [7:0] dout3;
reg [7:0] dout4;
reg [7:0] dout5;
reg [7:0] dout6;
reg [7:0] dout7;
reg [7:0] dout8;
reg [7:0] dout9;

reg clean;//定义清除信号

  always@(posedge clk_25m)begin
  	if(reset||clean)begin
  		dout0<=8'd0;
      dout1<=8'd0;
      dout2<=8'd0;
      dout3<=8'd0;
      dout4<=8'd0;
      dout5<=8'd0;
      dout6<=8'd0;
      dout7<=8'd0;
      dout8<=8'd0;
      dout9<=8'd0;
  	end
  	else if(dout_en)begin//来一个dout_en移位一次
  		dout0<=dout;
      dout1<=dout0;
      dout2<=dout1;
      dout3<=dout2;
      dout4<=dout3;
      dout5<=dout4;
      dout6<=dout5;
      dout7<=dout6;
      dout8<=dout7;
      dout9<=dout8;
  	end
  end


/*接收一帧数据*/
parameter S0=4'd0;//初始状态
parameter S1=4'd1;//写判断
parameter S2=4'd2;//读

reg [2:0] state;

    always@(posedge clk_25m)begin
    	if(reset)begin
    		clean<=1'd0;
    		wr_data<=16'd0;
        wr_addr<=32'd0;
        wr_en  <=1'd0;
        state  <=S0;
    	end
    	else begin
    		case(state)
    			S0:begin
    				clean  <=1'd0;   
            wr_data<=16'd0;
            wr_addr<=32'd0;
            wr_en  <=1'd0; 
            state  <=S1;
    			end
          S1:begin
          	if(dout9==8'hAA&&dout8==8'h55&&dout1==8'h00&&dout0==8'hFF)begin
          		wr_en  <=1'd1;//判断帧头帧尾
          		state  <=S2;
          	end
          	else begin
          		wr_en  <=1'd0;
          		state  <=S1;
          	end
          end
          S2:begin
          	clean  <=1'd1;
          	wr_data<={dout2,dout3};//从低位到高位
            wr_addr<={dout4,dout5,dout6,dout7};//从低位到高位排列
          	state  <=S0;
          end
    		endcase
    	end
    end
endmodule

三、一帧数据的发送

一帧数据的发送需要发送AA  55   4个地址   2个数据  00  FF

输入4个地址[31:0]send_addr,2个数据[15:0]send_data

输出单个数据[7:0]din,单个数据发送使能din_en

目的是把串行send_addr和串行send_data解析出并行数据[7:0]din

此外,定义帧发送使能send_en,发送帧状态send_state

一帧数据发送步骤:

(1)定义10个寄存器data0~data9,在开始发送后,把协议与10个寄存器对应

(2)定义发送数据个数send_num,把波特率结束的下降沿作为一个数据发送完的标志baud_finish_falg

(3)状态机:

初始状态:din_en=0,din清零,send_state=0

启动状态:在帧发送使能send_en=1时,产生单个数据发送使能信号din_en=1,发送帧状态send_state=1

发送状态:每baud_finish_falg一下,计数send_num一次

停止状态:send_num=10停止,din_en=0,send_en=0,send_state=0

(4)对应din

写case(send_num)

send_num=0时,发送第0位;send_num=1时,发送第1位…send_num=9时,发送第9位

module uart_TX(
input clk_25m,
input reset,
input send_en,//发送帧使能
input TX_start,
input [15:0] send_data,
input [31:0] send_addr,
output reg din_en        ,//发送使能信号
output reg [7:0]din  ,//发送的8位数据
output reg send_state    //发送帧状态(是否发送完成)
    );
    
    /*定义10个寄存器*/
reg [7:0] data9;//定义10个寄存器
reg [7:0] data8;
reg [7:0] data7;
reg [7:0] data6;
reg [7:0] data5;
reg [7:0] data4;
reg [7:0] data3;
reg [7:0] data2;
reg [7:0] data1;
reg [7:0] data0;

  always@(posedge clk_25m)begin//缓存数据
    if(reset)begin
      data9<=8'd0;
      data8<=8'd0;
      data7<=8'd0;
      data6<=8'd0;
      data5<=8'd0;
      data4<=8'd0;
      data3<=8'd0;
      data2<=8'd0;
      data1<=8'd0;
      data0<=8'd0; 
   end     
   else if(send_en&&!send_state) begin//发送帧使能=1&&不在发送状态
        data9<=8'hAA;
        data8<=8'h55;
        data7<=send_addr[31:24];
        data6<=send_addr[23:16];
        data5<=send_addr[15:8];
        data4<=send_addr[7:0];
        data3<=send_data[15:8];
        data2<=send_data[7:0];
        data1<=8'h00;
        data0<=8'hFF;
   end     
   else begin
     data9<=data9;
     data8<=data8;
     data7<=data7;
     data6<=data6;
     data5<=data5;
     data4<=data4;
     data3<=data3;
     data2<=data2;
     data1<=data1;
     data0<=data0;
   end     
 end       




/*把波特率结束的下降沿作为一个数据发送完的标志*/
reg TX_start_dly;
wire baud_finish_flag;//定义一个数据发送完成的标志

	always@(posedge clk_25m)begin//buad_start延时一拍
 		if(reset)begin
  		TX_start_dly<=1'd0;
 		end
 		else begin
  		TX_start_dly<=TX_start;
 		end
	end

assign baud_finish_flag=!TX_start&&TX_start_dly;//下降沿检测




 /*发送数据状态机*/   
    
parameter S0=4'd0;//初始状态
parameter S1=4'd1;//启动状态
parameter S2=4'd2;//发送状态
parameter S3=4'd3;//完成状态

reg [2:0]state;

reg [3:0] send_num;//定义发送数据个数
reg clean;//定义清除信号

	always@(posedge clk_25m)begin
		if(reset)begin
			send_state<=1'd0;
			send_num    <=4'd0;
			din_en      <=1'd0 ;
			state<=S0;
		end
		else begin
			clean<=1'd0;
			case(state)
				S0:begin
					if(send_en)begin//产生单个数据发送使能信号din_en
						state<=S1;
						din_en      <=1'd1 ;
 	  	      send_state  <=1'd1 ;//帧状态=1
					end
					else begin
						din_en      <=1'd0 ;
						send_state<=1'd0;
						send_num    <=4'd0;
						state<=S0;
					end
				end
        S1:begin
        	din_en    <=1'b0;
        	send_state<=send_state;
        	if(baud_finish_flag)begin//发完一个数据计数一次send_num
        		send_state  <=1'd2 ;
        		send_num<=send_num+4'd1;
        	end
        	else begin
        		send_state  <=1'd1 ;
        		send_num<=send_num;
        	end        	
				end
        S2:begin
    			if(send_num==4'd10)begin
	        	next_state=S0;
            clean<=1'd1;
            send_num<=4'd0;
            send_state<=1'd0 ;
	       	end
	       	else begin
	          next_state=S2;     
            clean<=1'd0;      
            send_num<=send_num;   
            send_state<=1'd1 ;
				  end
        end
			endcase
		end
	end
	
	
/*发送din*/
always @(posedge clk_25m)begin
 if(reset)begin
  din<=8'hFF;
 end
 else begin
  case(send_num)
   4'd0:din<=data9;
   4'd1:din<=data8;
   4'd2:din<=data7;
   4'd3:din<=data6;
   4'd4:din<=data5;
   4'd5:din<=data4;
   4'd6:din<=data3;
   4'd7:din<=data2;
   4'd8:din<=data1;
   4'd9:din<=data0;
   default:din<=8'hFF;
  endcase
 end
end

    
endmodule

标签:en,一帧,FPGA,send,发送,part,串口,数据,reg
来源: https://blog.csdn.net/weixin_46188211/article/details/123488569