当前位置:   article > 正文

FPGA利用UART串口实现一次接收多个数据并进行解析协议帧_fpga串口协议帧解析

fpga串口协议帧解析


基于野火的编写的UART接收模块,在FPGA上实现一次接收多个数据并对数据进行解析。


一、串口接收模块

野火UART串口接收模块源码

`timescale  1ns/1ns

// Author        : EmbedFire
//Create Date    : 2019/06/12
// Module Name   : uart_rx
// Project Name  : rs485
// Target Devices: Altera EP4CE10F17C8N
// Tool Versions : Quartus 13.0
// Description   :
//
// Revision      : V1.0
// Additional Comments:
// 
// 实验平台: 野火_征途Pro_FPGA开发板
// 公司    : http://www.embedfire.com
// 论坛    : http://www.firebbs.cn
// 淘宝    : https://fire-stm32.taobao.com


module  uart_rx
#(
    parameter   UART_BPS    =   'd9600,         //串口波特率
    parameter   CLK_FREQ    =   'd50_000_000    //时钟频率
)
(
    input   wire            sys_clk     ,   //系统时钟50MHz
    input   wire            sys_rst_n   ,   //全局复位
    input   wire            rx          ,   //串口接收数据

    output  reg     [7:0]   po_data     ,   //串转并后的8bit数据
    output  reg             po_flag         //串转并后的数据有效标志信号
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//localparam    define
localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS   ;

//reg   define
reg         rx_reg1     ;
reg         rx_reg2     ;
reg         rx_reg3     ;
reg         start_nedge ;
reg         work_en     ;
reg [12:0]  baud_cnt    ;
reg         bit_flag    ;
reg [3:0]   bit_cnt     ;
reg [7:0]   rx_data     ;
reg         rx_flag     ;

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//插入两级寄存器进行数据同步,用来消除亚稳态
//rx_reg1:第一级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg1 <= 1'b1;
    else
        rx_reg1 <= rx;

//rx_reg2:第二级寄存器,寄存器空闲状态复位为1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg2 <= 1'b1;
    else
        rx_reg2 <= rx_reg1;

//rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_reg3 <= 1'b1;
    else
        rx_reg3 <= rx_reg2;

//start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        start_nedge <= 1'b0;
    else    if((~rx_reg2) && (rx_reg3))
        start_nedge <= 1'b1;
    else
        start_nedge <= 1'b0;

//work_en:接收数据工作使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        work_en <= 1'b0;
    else    if(start_nedge == 1'b1)
        work_en <= 1'b1;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        baud_cnt <= 13'b0;
    else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))
        baud_cnt <= 13'b0;
    else    if(work_en == 1'b1)
        baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,
//此时拉高一个标志信号表示数据可以被取走
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_flag <= 1'b0;
    else    if(baud_cnt == BAUD_CNT_MAX/2 - 1)
        bit_flag <= 1'b1;
    else
        bit_flag <= 1'b0;

//bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
//都接收完成后计数器清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        bit_cnt <= 4'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        bit_cnt <= 4'b0;
     else    if(bit_flag ==1'b1)
         bit_cnt <= bit_cnt + 1'b1;

//rx_data:输入数据进行移位
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_data <= 8'b0;
    else    if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
        rx_data <= {rx_reg3, rx_data[7:1]};

//rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rx_flag <= 1'b0;
    else    if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
        rx_flag <= 1'b1;
    else
        rx_flag <= 1'b0;

//po_data:输出完整的8位有效数据
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_data <= 8'b0;
    else    if(rx_flag == 1'b1)
        po_data <= rx_data;

//po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步)
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <= 1'b0;
    else
        po_flag <= rx_flag;

endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155

二、数据接收解析

串口数据帧包括:帧头aabb(2个字节)+数据1(1个字节)+数据2(4个字节)+数据3(4个字节)+校验和(1个字节)+帧尾ccdd (2个字节)。接收采用状态机实现,虽然有8个状态但是实际只用了7个状态。

DLE (空闲状态)等待接收,接收到aa时,转移到状态HEADER_1。
HEADER_1(等待bb状态)接收到bb时,转移到状态RX_DATA 进行数据接收,接收到的数据错误转移回DLE 。
HEADER_2(暂未用到) RX_DATA (数据接收状态)接收完数据时,转移到状态SUM_CHECK进行校验和。
SUM_CHECK(校验和)校验和正确转移到END_1,校验和不对转移回DLE 。
END_1(等待cc)接收到cc时,转移到状态END_2,接收到的数据错误转移回DLE 。
END_2(等待dd)接收到dd时,转移到状态RX_DONE ,接收到的数据错误转移回DLE 。
RX_DONE (接收完成)接收完成并进行数据拼接

`timescale  1ns/1ns
module data_analysis
(	
    input   wire            sys_clk     ,   //系统时钟,50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            rx          ,   //串口接收数据
	 	 
	output  reg [7:0]       wave        ,  
	output  reg [31:0]      freq        ,  
	output  reg [31:0]      phase       ,  
	output  reg             o_req           //串转并后的数据有效标志信号
);

parameter   pkh1    =   8'haa,
            pkh2    =   8'hbb,
            pkb1    =   8'hcc,
            pkb2    =   8'hdd;
			
localparam  DLE       =  8'b00000001;
localparam  HEADER_1  =  8'b00000010;
localparam  HEADER_2  =  8'b00000100;
localparam  RX_DATA   =  8'b00001000;
localparam  SUM_CHECK =  8'b00010000;
localparam  END_1 	  =  8'b00100000;
localparam  END_2     =  8'b01000000;
localparam  RX_DONE   =  8'b10000000;			
			
wire        po_flag;
reg         data_sum_ok;
reg         data_sum_err;
reg         rx_done;
reg [7:0]   status;
wire [7:0]  r_rx_data;
reg [7:0]   rx_done_cnt;
reg [31:0]  rx_sum;

reg [7:0]   r_data;
reg [7:0]   r_data0;
reg [7:0]   r_data1;
reg [7:0]   r_data2;
reg [7:0]   r_data3;
reg [7:0]   r_data4;
reg [7:0]   r_data5;
reg [7:0]   r_data6;
reg [7:0]   r_data7;
reg [7:0]   r_data8;
 
always @(posedge sys_clk)begin
	if (!sys_rst_n)
		status <= DLE;
	else
	case(status)
		DLE:
		begin
			if(r_rx_data == pkh1)
				status <= HEADER_1;
			else
				status <= DLE;
		end
		HEADER_1:
		begin
			if(rx_done_cnt == 8'd2 && r_rx_data == pkh2)
				status <= RX_DATA;
			else if(rx_done_cnt == 8'd2 && r_rx_data != pkh2)					
				status <= DLE;
			else
				status <= HEADER_1;				
		end
		HEADER_2:		
		begin
			if(rx_done_cnt == 8'd3)
				status <= RX_DATA;
			else
				status <= HEADER_2;				
		end
		RX_DATA:		
		begin
			if(rx_done_cnt == 8'd12)
				status <= SUM_CHECK;
			else
				status <= RX_DATA;	
		end
		SUM_CHECK:
		begin		
			if(data_sum_ok == 1'd1) 
			begin
				if(data_sum_err == 1'd1)								
					status <= DLE;
				else				
					status <= END_1;
			end
			else
				status <= SUM_CHECK;
		end
		END_1:
		begin			
			if(rx_done_cnt == 8'd13 && r_rx_data == pkb1)
				status <= END_2;
			else if(rx_done_cnt == 8'd13&&r_rx_data != pkb1)					
				status <= DLE;
			else
				status <= END_1;		
		end
		END_2:
		begin			
			if(rx_done_cnt == 8'd14 && r_rx_data == pkb2)
				status <= RX_DONE;
			else if(rx_done_cnt == 8'd14&&r_rx_data != pkb2)					
				status <= DLE;
			else
				status <= END_2;		
		end
		RX_DONE:
			status <= DLE;
		default:			
			status <= DLE;
	endcase
end

// 有数据到来,计算接收到的数据个数
always @(posedge sys_clk) begin
    if(!sys_rst_n)
	     rx_done_cnt <= 'd0;
    else if(po_flag==8'd1)
	     rx_done_cnt <= rx_done_cnt + 1'd1;
	else if(status == RX_DONE||status == DLE)		
	     rx_done_cnt <= 'd0;
	else
	     rx_done_cnt <= rx_done_cnt;
end

// 数据接收阶段
always @(posedge sys_clk) begin
    if(!sys_rst_n) 
	begin
		r_data0 <= 8'd0;
		r_data1 <= 8'd0;
		r_data2 <= 8'd0;
		r_data3 <= 8'd0;
		r_data4 <= 8'd0;
		r_data5 <= 8'd0;
		r_data6 <= 8'd0;
		r_data7 <= 8'd0;
		r_data8 <= 8'd0;
	  end
    else if(status == RX_DATA && rx_done==8'd1)
    case(rx_done_cnt)
        8'd3:
            r_data0 <= r_rx_data;
        8'd4:
            r_data1 <= r_rx_data;
        8'd5:
            r_data2 <= r_rx_data;
        8'd6:
            r_data3 <= r_rx_data;
        8'd7:
            r_data4 <= r_rx_data;
        8'd8:
            r_data5 <= r_rx_data;
        8'd9:
            r_data6 <= r_rx_data;
        8'd10:
            r_data7 <= r_rx_data;
        8'd11:
            r_data8 <= r_rx_data;
        default:
		    r_data <= r_rx_data;	
    endcase
end

// 数据和校验
always @(posedge sys_clk) begin
	if(!sys_rst_n)
	begin
		data_sum_ok <= 1'd0;
		rx_sum <= 'd0;
	end
	else if(status == SUM_CHECK) 
	begin			
		rx_sum <= r_data0+r_data1+r_data2+r_data3+r_data4+r_data5+r_data6+r_data7+r_data8;
		data_sum_ok <= 1'd1;
	end
	else
	begin
		data_sum_ok <= 1'd0;
		rx_sum <= 'd0;
	end
end

always @(posedge sys_clk) begin
	if(!sys_rst_n)
		data_sum_err <= 1'd0;
	else if(data_sum_ok == 1'd1) 
	begin			
		if (rx_sum[7:0]==r_rx_data)			
			data_sum_err <= 1'd0;
		else
			data_sum_err <= 1'd1;
	end
	else
		data_sum_err <= 1'd0;
end
// 完成此次接收
always @(posedge sys_clk) begin
	if(!sys_rst_n) o_req <= 'd0;
	else if(status == RX_DONE) 
		o_req <= 1'd1;
	else o_req <= 'd0;
end
// 延迟的接收信号
always @(posedge sys_clk) begin
    if(!sys_rst_n) 
		rx_done <= 1'd0;
    else if(po_flag==8'd1) 
		rx_done <= 1'd1;
	else
		rx_done <= 1'd0;	
end
// 接收完成数据封装
always @(posedge sys_clk) begin
    if(!sys_rst_n) 
	begin
		wave <= 'd0;
		freq <= 'd0;
		phase <= 'd0;
	end
    else if(status == RX_DONE) 
	begin
		wave <= r_data0;
		freq <= {r_data1,r_data2,r_data3,r_data4};
		phase <= {r_data5,r_data6,r_data7,r_data8};
	end
end

uart_rx uart_rx_init
(
	.sys_clk     (sys_clk),   
	.sys_rst_n   (sys_rst_n),   
	.rx          (rx),

	.po_data     (r_rx_data),   //串转并后的8bit数据
	.po_flag	 (po_flag)      //串转并后的数据有效标志信号
);

endmodule


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247

三、仿真文件

仿真波形用了三组数据,分别是校验和正确的数据、校验和错误的数据和校验和正确的数据。仿真波形及源码如下:
在这里插入图片描述

`timescale  1ns/1ns

module  tb_data_analysis();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg         sys_clk;
reg         sys_rst_n;
reg         rx;

wire[7:0]    wave;
wire[31:0]   freq;
wire[31:0]   phase;
wire         o_req;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//初始化系统时钟、全局复位和输入信号
initial begin
        sys_clk    = 1'b1;
        sys_rst_n <= 1'b0;
        rx        <= 1'b1;
        #20;
        sys_rst_n <= 1'b1;
end

//模拟发送8次数据,分别为0~7
initial begin
        #200
        rx_bit(8'haa);  //任务的调用,任务名+括号中要传递进任务的参数
        rx_bit(8'hbb);
        rx_bit(8'd0);
        rx_bit(8'd1);
        rx_bit(8'd1);
        rx_bit(8'd1);
        rx_bit(8'd1);
        rx_bit(8'd2);
        rx_bit(8'd2);
        rx_bit(8'd2);
        rx_bit(8'd2);
        rx_bit(8'h0c);
        rx_bit(8'hcc);
        rx_bit(8'hdd);		
        #200
		rx_bit(8'haa);  //任务的调用,任务名+括号中要传递进任务的参数
        rx_bit(8'hbb);
        rx_bit(8'd0);
        rx_bit(8'd11);
        rx_bit(8'd12);
        rx_bit(8'd13);
        rx_bit(8'd14);
        rx_bit(8'd25);
        rx_bit(8'd28);
        rx_bit(8'd27);
        rx_bit(8'd24);
        rx_bit(8'd35);
        rx_bit(8'hbb);
        rx_bit(8'hdd);
        #200
        rx_bit(8'haa);  //任务的调用,任务名+括号中要传递进任务的参数
        rx_bit(8'hbb);
        rx_bit(8'd2);
        rx_bit(8'd2);
        rx_bit(8'd2);
        rx_bit(8'd2);
        rx_bit(8'd2);
        rx_bit(8'd1);
        rx_bit(8'd1);
        rx_bit(8'd1);
        rx_bit(8'd1);
        rx_bit(8'h0e);
        rx_bit(8'hcc);
        rx_bit(8'hdd);	
end

//sys_clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
always #10 sys_clk = ~sys_clk;

//定义一个名为rx_bit的任务,每次发送的数据有10位
//data的值分别为0~7由j的值传递进来
//任务以task开头,后面紧跟着的是任务名,调用时使用
task rx_bit(
    //传递到任务中的参数,调用任务的时候从外部传进来一个8位的值
        input   [7:0]   data
);
        integer i;      //定义一个常量
//用for循环产生一帧数据,for括号中最后执行的内容只能写i=i+1
//不可以写成C语言i=i++的形式
        for(i=0; i<10; i=i+1) begin
            case(i)
                0: rx <= 1'b0;
                1: rx <= data[0];
                2: rx <= data[1];
                3: rx <= data[2];
                4: rx <= data[3];
                5: rx <= data[4];
                6: rx <= data[5];
                7: rx <= data[6];
                8: rx <= data[7];
                9: rx <= 1'b1;
            endcase
            #(5208*20); //每发送1位数据延时5208个时钟周期
        end
endtask         //任务以endtask结束

data_analysis dataAnalysis
(	
    .sys_clk     	(sys_clk),   //系统时钟,50MHz
    .sys_rst_n   	(sys_rst_n  ),   //复位信号,低电平有效
    .rx          	(rx),   //串口接收数据
	 	 
	.wave        	(wave),   //波形
	.freq        	(freq),   //频率
	.phase       	(phase),  //相位
	.o_req          (o_req)		//串转并后的数据有效标志信号
);

endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121

参考链接

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/905855
推荐阅读
相关标签
  

闽ICP备14008679号