赞
踩
基于野火的编写的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
串口数据帧包括:帧头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
仿真波形用了三组数据,分别是校验和正确的数据、校验和错误的数据和校验和正确的数据。仿真波形及源码如下:
`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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。