FPGA自定义UART传输(包含:matlab数据拆分)

引言:本程序是在工程实践中写的模块,经验证,可靠。由于大多书本上讲的都是仅仅传输1个数据,然后根据这个数据亮灯。这种在工程中几乎用不上工程实际中,往往我们需要根据地址不同,传给不同的寄存器,从而控制比如脉冲宽度,长度,延时,个数等功能。
由于我们需要在testbench中算出每一帧的检验和,所以我们用了matlab来进行数据拆分,从而计算校验和。如果手工算也可以,但帧数过多后,相当麻烦,而且易错。
此处,先把matlab代码上传

#matlab数据拆分代码(获得检验值)

clc;
clear;
add='18' ;%地址,16进制
data0=16;%输入十进制的16bit数据

add_dec=uint8(hex2dec(add));

data1=uint16(data0);%输入16bit
data1_bit=dec2bin(data1);   %输入数据二进制格式
data2unit8=uint8(zeros([1 2]));%存储2个8位数据的数组
data2unit8(1)=bitand(data1,255);%低8位
data2unit8(2)=bitand(bitshift(data1,-8),255);%高8位
data_low8bit=dec2hex(data2unit8(1));%低8位的数据16进制
data_high8bit=dec2hex(data2unit8(2));%高8位的数据16进制数据
output1=bitxor(add_dec,data2unit8(1));
check_sum=bitxor(output1,data2unit8(2));%得到校验和
check_sum_hex=dec2hex(check_sum);%得到校验和的16进制形式

#UART代码

接收顶层模块

`timescale 1ns / 1ps

module uart_rx_core #(
    parameter BAUD_RATE  = 115_200,     // Baud rate
    parameter CLOCK_RATE = 50_000_000  // Clock rate
    )(
    input         clk,
    input         rx_en,  // 接收使能
    input         rx_pin, // 数据输入管脚
    output [ 7:0] addr,   // 接收的32bit数据
    output [15:0] data,   // 接收的32bit数据
    output        valid   // 一次数据协议解码成功
    );
    
    wire [ 7:0] rx_data;      // 接收的8 bit数据
    wire        rx_done;      // 数据接收完毕,产生一个高脉冲
    uart_rx_module #(
        .BAUD_RATE  (  BAUD_RATE ),     // Baud rate
        .CLOCK_RATE ( CLOCK_RATE )  // Clock rate
        ) Uuart_rx (
        .clk( clk ),
        .rx_en( rx_en ),        // 接收使能
        .rx_pin( rx_pin ),       // 数据输入管脚
        .rx_data( rx_data ),      // 每次接收的8 bit数据
        .rx_done( rx_done )       // 数据接收完毕,产生一个高脉冲
        );
    
    uart_decode_module Uuart_decode (
        .clk( clk ),
        
        .rx_data( rx_data ),   // 接收的8 bit数据
        .rx_done( rx_done ),   // 数据接收完毕,产生一个高脉冲
        
        .addr( addr ),      // 地址码
        .data( data ),      // 数据组
        .valid( valid )      // 一次数据协议解码成功
        );
    
endmodule

`timescale 1ns / 1ps
    module uart_rx_module #(
        parameter BAUD_RATE  = 115_200,     // Baud rate
        parameter CLOCK_RATE = 50_000_000  // Clock rate
        )(
        input         clk,
        input         rx_en,      // 接收使能
        input         rx_pin,     // 数据输入管脚
        output [ 7:0] rx_data,    // 接收的8 bit数据
        output        rx_done     // 数据接收完毕,产生一个高脉冲
        );
        
        /* 产生波特率采样时钟 --------------------------------------------------- */
        wire baud_clk;      // 波特率采样时钟
        wire en_baud_gene;  // 使能波特率采样时钟
        baud_gene_module #(
            .BAUD_RATE  ( BAUD_RATE ),  // Baud rate
            .CLOCK_RATE ( CLOCK_RATE )  // Clock rate
            ) Urx_baud_gene (
            .clk( clk ),
            .en( en_baud_gene ),  // 计数使能,高电平时波特率发生器才工作
            .baud_clk( baud_clk ) // 波特率采样时钟输出
            );
        /* ---------------------------------------------------------------------- */
        
        /* rx_pin下降沿检测 ----------------------------------------------------- */
        reg [1:0] rx_pin_r = 1'b0;
        always @ (posedge clk)
            rx_pin_r <= {rx_pin_r[0:0], rx_pin};
        
        wire rx_pin_fall = (!rx_pin_r[0]) & rx_pin_r[1];// 下降沿检测
        wire rx_start = rx_pin_fall;
        /* ---------------------------------------------------------------------- */
        
        /* 串口接收控制 --------------------------------------------------------- */
        reg        en_baud_r = 1'b0;//Li:接收数据使能区域
        reg [ 7:0] rx_data_r = 8'b0;
        reg        rx_done_r = 1'b0;
        reg [ 3:0] rx_ii     = 4'b0;
        
        /* 接收数据格式:1位起始位,8位数据位,1位停止位,
                         其中起始位为0;无校验位;停止位为1 */
        always @ (posedge clk)
            if (rx_en)          // 当rx_en被拉高时开始工作
                case (rx_ii)   // 每个baud_clk接收一位数据
                4'd0  : // 开始信号,有数据发送过来,准备接收------//Li:启动
                    if (rx_start) begin 
                        en_baud_r <= 1'b1; rx_done_r <= 1'b0; rx_ii <= rx_ii + 1'b1;    //Li:刚开始的下降沿启动
                    end
                4'd1  : // 接收起始位
                    if (baud_clk) begin 
                        en_baud_r <= 1'b1; rx_done_r <= 1'b0; rx_ii <= rx_ii + 1'b1; 
                    end
                4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8, 4'd9 :
                    // 将rx_pin的值保存到rx_data_r中,从最低位到最高位; LSB first default
                    if (baud_clk) begin 
                        en_baud_r <= 1'b1; rx_data_r[rx_ii-2] <= rx_pin; 
                        rx_done_r <= 1'b0; rx_ii <= rx_ii + 1'b1; 
                    end
                4'd10 :     // 接收停止位,无校验位,如果没有什么特别需求,可直接忽略
                    if (baud_clk) begin 
                        en_baud_r <= 1'b0; rx_done_r <= 1'b1; rx_ii <= rx_ii + 1'b1; 
                    end
                4'd11 :     // 产生一个时钟周期的高脉冲rx_done_r,表示数据接收完毕
                    begin 
                        en_baud_r <= 1'b0; rx_done_r <= 1'b0; rx_ii <= 4'd0; 
                    end
                default: ;
                endcase
            else begin
                en_baud_r <= 1'b0; rx_data_r <= 8'd0; rx_done_r <= 1'b0; rx_ii <= 4'd0; 
            end
        
        assign en_baud_gene = en_baud_r;
        /* ---------------------------------------------------------------------- */
        
        assign rx_data      = rx_data_r;
        assign rx_done      = rx_done_r;
        
    endmodule

`timescale 1ns / 1ps
/*-------------------------------------------------------------------------------------
 * 串口波特率产生
 *----------------------------------------------------------------------------------*/
module baud_gene_module #(
    parameter BAUD_RATE  = 115_200,     // Baud rate
    parameter CLOCK_RATE = 50_000_000  // Clock rate
    )(
    input  clk,
    input  en,      // 计数使能,高电平时波特率发生器才工作
    output baud_clk // 波特率采样时钟输出
    );
    
    /* 以下函数供Verilog使用,Verilog_2005和SystemVerilog有"$clog2"函数 */
    function integer clog2(input integer value);
        begin
            value = value-1;
            for (clog2=0; value>0; clog2=clog2+1)
                value = value>>1;
        end
    endfunction
    
    // 分频计数DIVIDER = 时钟CLOCK_RATE / 波特率BAUD_RATE,并取整
    // (所以整除之前先加波特率BAUD_RATE的一半)
    localparam DIVIDER    = (CLOCK_RATE+BAUD_RATE/2) / BAUD_RATE;
    
    // 计数器的值
    localparam BAUD_VALUE = DIVIDER - 1;
    localparam BAUD_HALF  = BAUD_VALUE/2 - 1;
    // 计数器位宽计算
    localparam CNT_WID    = clog2(DIVIDER);
    
    reg [CNT_WID-1:0] baud_cnt = BAUD_VALUE;
    always @ (posedge clk)
        if (baud_cnt==BAUD_VALUE)
            baud_cnt <= {CNT_WID{1'b0}};
        else if (en)
            baud_cnt <= baud_cnt + 1'b1;
        else
            baud_cnt <= {CNT_WID{1'b0}};
    
    assign baud_clk = (baud_cnt==BAUD_HALF) ? 1'b1 : 1'b0;
    
endmodule

`timescale 1ns / 1ps

module uart_decode_module(
    input         clk,
    
    input  [ 7:0] rx_data,    // 接收的8 bit数据
    input         rx_done,    // 数据接收完毕,产生一个高脉冲
    
    output [ 7:0] addr,       // 地址码
    output [15:0] data,       // 数据组
    output        valid       // 一次数据协议解码成功
    );
    
    /* 有限状态机,三过程块建模风格 ********************************************/
    // 状态编码
    localparam FRAME_HEAD = 4'd0,  // 帧头(1byte)
               CMD_ADDR   = 4'd1,  // 地址(1byte)
               CMD_DATA0  = 4'd2,  // 数据(2byte)
               CMD_DATA1  = 4'd3,
               CHECK_SUM  = 4'd4,  // 校验(1byte,异或和)
               FRAME_TAIL = 4'd5,  // 帧尾(1byte)
               DONE       = 4'd6;  // 本帧接收结束
    
    reg [ 3:0] now_state = FRAME_HEAD, next_state = FRAME_HEAD;
    
    reg [ 7:0] rx_data_r = 8'd0;
    reg        rx_done_r = 1'b0;
	 //reg data_number='d0;
    
    // 1.实现状态转换
    always @ (posedge clk) begin//: trans_state
        rx_data_r <= rx_data;
        rx_done_r <= rx_done;
        if (rx_done_r || now_state==DONE)
            now_state <= next_state;
    end//: trans_state
    
    reg [ 7:0] check_sum;
    // 2.产生下一个状态
    always @ (*) begin//: set_next_state
        check_sum  = addr ^ data[ 7: 0]^data[15: 8];
       // next_state = now_state; // 下面分支的缺省状态
        case (now_state)
        FRAME_HEAD  : if(rx_data_r == 8'hF1) next_state = CMD_ADDR;
                      else next_state = FRAME_HEAD;
        CMD_ADDR    : next_state = CMD_DATA0;
		 
        CMD_DATA0   : next_state = CMD_DATA1;
        CMD_DATA1   : next_state = CHECK_SUM;
		  
        CHECK_SUM   : if(rx_data_r == check_sum) next_state = FRAME_TAIL;
                      else next_state = FRAME_HEAD;
        FRAME_TAIL  : if(rx_data_r == 8'hF2) next_state = DONE;
                      else next_state = FRAME_HEAD;
        DONE        : next_state = FRAME_HEAD;
        default     : next_state = FRAME_HEAD;
        endcase
    end//: set_next_state
    
    // 3.产生状态机的输出值
    reg [ 7:0] addr_r  =  8'd0;
    reg [32:0] data_r  = 32'd0;
    reg        valid_r =  1'b0;
	 reg        valid_r_r =  1'b0;
    always @ (posedge clk) begin
        case (now_state)
        FRAME_HEAD  :       valid_r <= 1'b0;
        CMD_ADDR    : begin valid_r <= 1'b0; addr_r        <= rx_data_r; end
		  
        CMD_DATA0   : begin valid_r <= 1'b0; data_r[7:0] <= rx_data_r; end
        CMD_DATA1   : begin valid_r <= 1'b0; data_r[15:8] <= rx_data_r; end

        CHECK_SUM   :       valid_r <= 1'b0;
        FRAME_TAIL  :       valid_r <= 1'b0;
        DONE        :       valid_r <= 1'b1;
								//	 data_number<=data_number+1;	
        default     : ;
        endcase
    end//: set_out_state
    
    assign addr  = addr_r;
    assign data  = data_r;
	 always @ (posedge clk)
	 begin
	 valid_r_r<=valid_r;
	 end
    assign valid = valid_r_r;
    
endmodule

#心得体会:

matlab中16进制是没法直接表示的,我尝试a=0x7 这样赋值是不行的。16进制必须是字符形式a=‘0x7’,如果要参与运算,请转换为10进制来参与运算。

注意即使check_sum没错,也许测试文件里帧头这些错误也可能。

最终效果:
自定义uart,一共24帧。控制24个变量参数。
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值