这次设计一个可以接收多字节(通过修改例化时的位宽实现)的串口接收模块。
 
当接收到9个字节的数据,但是我们只需要8个字节的数据时候,我们需要的是前八位的数据还是后八位的数据我们无法确定。
image
 
所以我们需要设定一种传输协议,这种协议我们可以自定义规则。我们就设定前缀为8'h55+8'hA5,后缀为8'hF0的一串数据即为我们需要的数据。
image

1、状态机的设定

 
image
 

2、需要的模块

(1) 8位串口接收模块

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: Lclone
// 
// Create Date: 2022/12/16 15:37:44
// Design Name: uart_byte_rx
// Module Name: uart_byte_rx
// Project Name: uart_byte_rx
// Target Devices: 
// Tool Versions: 
// Description: 8位串口接收模块
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module uart_byte_rx
  # (
        parameter   RX_BAUD  = 9600,				//波特率
        parameter   CLK_FQC  = 50_000_000,			//模块时钟频率
        parameter   BAUD_CNT = CLK_FQC/RX_BAUD)			//模块每波特需要计数的次数(设置此端口方便仿真用)
    (
        input               Clk,				//时钟频率接口
        input               Rst_n,				//复位接口
        input               Uart_rx,				//串口接收接口
        output  reg  [7:0]  Data,				//接收到的数据接口
        output  reg         Rx_done				//接收完成信号
    );
    
    reg            uart_rx_r;					//延一拍
    reg            uart_rx_rr;					//延两拍
    reg            receiv_begin;				//接收开始信号
    reg            receiv_flag;					//接收状态信号
    reg   [ 3:0]   state;					//状态机寄存器
    reg   [15:0]   baud_cnt;					//波及计数器
    reg   [ 3:0]   sampel_cnt;					//采样计数器
    reg            sampel_en;					//采样使能
    reg            sampel_ref;					//样本寄存器
    reg   [ 3:0]   acc;						//累加寄存器
    reg   [ 3:0]   bit_cnt;					//数据位寄存器
    
    always @(posedge Clk) begin   //延两拍为下降沿捕获
        uart_rx_r <= Uart_rx;
        uart_rx_rr <= uart_rx_r;
    end
    
    always @(posedge Clk or negedge Rst_n) begin	//接收信号发生
        if(Rst_n == 0)
            receiv_begin <= 0;
        else if(state == 0 & uart_rx_rr & ~uart_rx_r)
            receiv_begin <= 1'b1;
        else
            receiv_begin <= 0;            
    end
    
    always @(posedge Clk or negedge Rst_n) begin	//状态机
        if(Rst_n == 0) begin
            state <= 0;
            sampel_ref <= 8'b0;
            acc <= 8'b0;
            Data <= 8'b0;
        end
        else case(state)
            0: 		//空闲状态
                if(receiv_begin == 1)
                    state <= 3'd1;
                else
                    state <= 0;
            
            1: begin	//抽样状态
                    if(sampel_en == 1) begin
                           sampel_ref <= Uart_rx;
                           state <= 3'd2;
                    end

                    else
                        state <= 3'b1;
               end   
                    
            2: begin	//数据判断状态

                    acc <= acc + sampel_ref;
                   
                    if(sampel_cnt == 7) begin
                        if(acc >= 4)
                            begin Data[7] <= 1'b1; state <= 3'd3;acc <= 8'b0; end
                        else
                            begin Data[7] <= 0; state <= 3'd3;acc <= 8'b0; end
                    end
                    
                    else
                        state <= 3'd1;
               end                            

            3: begin	//数据移位状态
                    if(bit_cnt < 8) begin
                        Data <= Data >> 1;
                        state <= 3'd1; 
                    end
                    
                    else 
                        state <= 0;
            end
            
            default:;
       endcase
    end
    
    always @(posedge Clk or negedge Rst_n) begin	//接收进行标志
        if(Rst_n == 0)
            receiv_flag <= 0;
        else if(receiv_begin == 1)
            receiv_flag <= 1'b1;
        else if(bit_cnt == 9 & baud_cnt == BAUD_CNT/9*8) //这里设置为记到BAUD_CNT/9*8是为了让Rx_done信号提前一点产生,避免因为Rx_done出现过晚,导致错过下一个起始位的下降沿。后面和其相同的条件判断,也是因为相同原因设置的。
            receiv_flag <= 1'b0;
    end
    
    always @(posedge Clk or negedge Rst_n) begin	//波特计数
        if(Rst_n == 0)     
            baud_cnt <= 0;
        else if(receiv_flag == 1) begin
            if(baud_cnt == BAUD_CNT - 1)
                baud_cnt <= 0;
            else
                baud_cnt <= baud_cnt + 1'b1;
            end
        else
            baud_cnt <= 0;      
    end
    
    always @(posedge Clk or negedge Rst_n) begin	//采样计数
        if(Rst_n == 0) begin
            sampel_cnt <= 0;
            sampel_en <= 0;
        end
        else if(receiv_flag == 1) begin
            case(baud_cnt)
                BAUD_CNT/9*1-1 : begin sampel_cnt <= 0; sampel_en <=1; end
                BAUD_CNT/9*2-1 : begin sampel_cnt <= 1; sampel_en <=1; end
                BAUD_CNT/9*3-1 : begin sampel_cnt <= 2; sampel_en <=1; end
                BAUD_CNT/9*4-1 : begin sampel_cnt <= 3; sampel_en <=1; end
                BAUD_CNT/9*5-1 : begin sampel_cnt <= 4; sampel_en <=1; end
                BAUD_CNT/9*6-1 : begin sampel_cnt <= 5; sampel_en <=1; end
                BAUD_CNT/9*7-1 : begin sampel_cnt <= 6; sampel_en <=1; end
                BAUD_CNT/9*8-1 : begin sampel_cnt <= 7; sampel_en <=1; end
                BAUD_CNT/9*9-1 : sampel_cnt <= 0;
                default:sampel_en <=0;
            endcase
        end
    end
    
    always @(posedge Clk or negedge Rst_n) begin	//数据位计数
        if(Rst_n == 0)
            bit_cnt <= 0;
        else if(bit_cnt == 9 & baud_cnt == BAUD_CNT/9*8)
            bit_cnt <= 0;
        else if(baud_cnt == BAUD_CNT - 1)
            bit_cnt <= bit_cnt + 1'b1;
    end
	
    always @(posedge Clk or negedge Rst_n) begin	//接收完成信号产生
        if(Rst_n == 0)
            Rx_done <= 0;
        else if(bit_cnt == 9 & baud_cnt == BAUD_CNT/9*8)
            Rx_done <= 1'b1;
        else
            Rx_done <= 0;
    end
endmodule

3、设计的模块代码

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/12/25 00:26:10
// Design Name: 
// Module Name: uart_bytes_rx
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module uart_bytes_rx
    #(  parameter                       DATA_WIDTH = 64,//数据位宽
        parameter                       PREFIX1 = 8'h55,//前缀1
        parameter                       PREFIX2 = 8'hA5,//前缀2
        parameter                       ENDINGS = 8'hF0)//后缀
    (
        input                           Clk,            //时钟信号
        input                           Rst_n,          //复位信号
        input                           Uart_rx,        //串口接收端口
        output  reg[DATA_WIDTH-1-8*3:0] Bytes_data,     //多字节数据端口
        output  reg                     Bytes_Rx_done   //多字节接收完成
    );
    
    reg     [2:0]                       state;          //状态机寄存器
    reg     [DATA_WIDTH-1:0]            bytes_data_reg; //多字节数据接收寄存器
    wire    [7:0]                       rx_data_reg;    //8位数据接收寄存器
    wire                                Rx_done;        //8位数据接收完成信号
    
    uart_byte_rx
      # (
            .RX_BAUD                    (115200),       //波特率
            .CLK_FQC                    (50_000_000))   //时钟频率
    uart_byte_rx_inst
        (
            .Clk                        (Clk),          //时钟
            .Rst_n                      (Rst_n),        //复位
            .Uart_rx                    (Uart_rx),      //串口接收端口
            .Data                       (rx_data_reg),  //8位数据端口
            .Rx_done                    (Rx_done)       //8位数据接收完成
        );
	
    always @(posedge Clk or negedge Rst_n) begin//状态机
        if(Rst_n == 0) begin
            state <= 0;
            bytes_data_reg <= 0;
            Bytes_Rx_done <= 0;
            Bytes_data <= 0;
        end else case(state)

            0:begin
                if(Rx_done) begin
                    bytes_data_reg[DATA_WIDTH-1:DATA_WIDTH-1-7] <= rx_data_reg;//数据装载
                    state <= 3'd1;
                end else begin
                    state <= 0;
                    Bytes_Rx_done <= 0;
                end
            end
            
            1:begin
                if(bytes_data_reg[DATA_WIDTH-1:DATA_WIDTH-1-7] == ENDINGS 
                    && bytes_data_reg[15:8] ==PREFIX2
                     && bytes_data_reg[7:0] ==PREFIX1)//数据协议判断
                      begin
                        Bytes_data <= bytes_data_reg[DATA_WIDTH-1-8:16];
                        state <= 1'b0;
                        Bytes_Rx_done <= 1'b1;
                        bytes_data_reg <= 0;
                      end 
                else
                    state <= 3'd2;
            end
            
            2:begin
                bytes_data_reg <= bytes_data_reg >> 8;//数据移位
                state <= 0;
            end
        endcase
    end
	
endmodule

4、仿真验证

(1)仿真激励文件

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/12/26 16:14:35
// Design Name: 
// Module Name: uart_bytes_rx_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module uart_bytes_rx_tb();
    
    reg         CLK_50M;
    reg         RST_N;
    wire [39:0] Bytes_data;    
    reg         Uart_rx;
    wire        Bytes_Rx_done;
    uart_bytes_rx 
  # (
        .DATA_WIDTH         (64))
    uart_bytes_rx_inst
    (
        .Clk                (CLK_50M),
        .Rst_n              (RST_N),
        .Uart_rx            (Uart_rx),
        .Bytes_data         (Bytes_data),
        .Bytes_Rx_done      (Bytes_Rx_done)
    );
    
    defparam    uart_bytes_rx_inst.uart_byte_rx_inst.BAUD_CNT = 50;
	
    always #10 CLK_50M  <= ~CLK_50M;
    
    initial begin
    CLK_50M <= 1'b0;
    RST_N   <= 1'b0;
    Uart_rx <= 1'b1;
    #100
    RST_N   <= 1'b1;
    #20
    data_deliver(8'h55);
    #100
    data_deliver(8'hA5);
    #100
    data_deliver(8'h01);
    #100
    data_deliver(8'h23);
    #100
    data_deliver(8'h45);
    #100
    data_deliver(8'h67);
    #100
    data_deliver(8'h89);
    #100
    data_deliver(8'hf0);
    #100
    $stop;
    end

    task data_deliver;
        input [7:0]	test_data;
        begin
            Uart_rx <= 1'b0;
            #1000             
            Uart_rx <= test_data[0];
            #1000             
            Uart_rx <= test_data[1];
            #1000             
            Uart_rx <= test_data[2];
            #1000             
            Uart_rx <= test_data[3];
            #1000             
            Uart_rx <= test_data[4];
            #1000             
            Uart_rx <= test_data[5];
            #1000             
            Uart_rx <= test_data[6];
            #1000             
            Uart_rx <= test_data[7];
            #1000             
            Uart_rx <= 1'b1;
            #1000;
        end
    endtask
endmodule

(2)仿真结果

image
 

5、应用实例

 
我们把它应用为一个通过接收电脑串口发送的数据从而改变8位LED每位是否闪烁闪烁的周期的程序。
其中有效的数据段中,第1个字节的数据控制LED灯是否闪烁,第2、3字节的数据控制闪烁的周期。
周期的计算公式为: T = 写入的值 X 10us X 2

top.v

`timescale 1ns / 1ps

module Top(   
    input            Sclk,
    input            Rst_n,
    input            Uart_rx,
    output  [7:0]    LED
);

    wire [23:0]      Bytes_data;
    wire             Bytes_Rx_done;


    uart_bytes_rx
    #  (    .DATA_WIDTH                     (48),
            .PREFIX1                        (8'h55),
            .PREFIX2                        (8'hA5),
            .ENDINGS                        (8'hF0))
    uart_bytes_rx_inst
        (
            .Clk                            (Sclk),
            .Rst_n                          (Rst_n),
            .Uart_rx                        (Uart_rx),
            .Bytes_data                     (Bytes_data),
            .Bytes_Rx_done                  (Bytes_Rx_done)
        );
    
    LED_6    LED_6_inst(
        .SCLK                               (Sclk),
        .RST_N                              (Rst_n),
        .CTRL_IN                            (Bytes_data[7:0]),
        .Time                               (Bytes_data[23:8]),
        .LED                                (LED)
    );
endmodule

 

uart_bytes_rx.v

`timescale 1ns / 1ps

module uart_bytes_rx
    #(  parameter                       DATA_WIDTH = 64,//数据位宽
        parameter                       PREFIX1 = 8'h55,//前缀1
        parameter                       PREFIX2 = 8'hA5,//前缀2
        parameter                       ENDINGS = 8'hF0)//后缀
    (
        input                           Clk,            //时钟信号
        input                           Rst_n,          //复位信号
        input                           Uart_rx,        //串口接收端口
        output  reg[DATA_WIDTH-1-8*3:0] Bytes_data,     //多字节数据端口
        output  reg                     Bytes_Rx_done   //多字节接收完成
    );
    
    reg     [2:0]                       state;          //状态机寄存器
    reg     [DATA_WIDTH-1:0]            bytes_data_reg; //多字节数据接收寄存器
    wire    [7:0]                       rx_data_reg;    //8位数据接收寄存器
    wire                                Rx_done;        //8位数据接收完成信号
    
    uart_byte_rx
      # (
            .RX_BAUD                    (115200),        //波特率
            .CLK_FQC                    (50_000_000))    //时钟频率
    uart_byte_rx_inst
        (
            .Clk                        (Clk),           //时钟
            .Rst_n                      (Rst_n),         //复位
            .Uart_rx                    (Uart_rx),       //串口接收端口
            .Data                       (rx_data_reg),   //8位数据端口
            .Rx_done                    (Rx_done)        //8位数据接收完成
        );
	
    always @(posedge Clk or negedge Rst_n) begin//状态机
        if(Rst_n == 0) begin
            state <= 0;
            bytes_data_reg <= 0;
            Bytes_Rx_done <= 0;
            Bytes_data <= 0;
        end else case(state)

            0:begin
                if(Rx_done) begin
                    bytes_data_reg[DATA_WIDTH-1:DATA_WIDTH-1-7] <= rx_data_reg;//数据装载
                    state <= 3'd1;
                end else begin
                    state <= 0;
                    Bytes_Rx_done <= 0;
                end
            end
            
            1:begin
                if(bytes_data_reg[DATA_WIDTH-1:DATA_WIDTH-1-7] == ENDINGS 
                    && bytes_data_reg[15:8] ==PREFIX2
                     && bytes_data_reg[7:0] ==PREFIX1)//数据协议判断
                      begin
                        Bytes_data <= bytes_data_reg[DATA_WIDTH-1-8:16];
                        state <= 1'b0;
                        Bytes_Rx_done <= 1'b1;
                        bytes_data_reg <= 0;
                      end 
                else
                    state <= 3'd2;
            end
            
            2:begin
                bytes_data_reg <= bytes_data_reg >> 8;//数据移位
                state <= 0;
            end
        endcase
    end
	
endmodule

uart_byte_rx.v
`timescale 1ns / 1ps

module uart_byte_rx
  # (
        parameter   RX_BAUD  = 9600,
        parameter   CLK_FQC  = 50_000_000,
        parameter   BAUD_CNT = CLK_FQC/RX_BAUD)
    (
        input               Clk,
        input               Rst_n,
        input               Uart_rx,
        output  reg  [7:0]  Data,
        output  reg         Rx_done
    );
    
    reg            uart_rx_r;
    reg            uart_rx_rr;
    reg            receiv_begin;
    reg            receiv_flag;    
    reg   [ 3:0]   state;
    reg   [15:0]   baud_cnt;
    reg   [ 3:0]   sampel_cnt;
    reg            sampel_en;
    reg            sampel_ref;
    reg   [ 3:0]   acc;
    reg   [ 3:0]   bit_cnt;
    
    always @(posedge Clk) begin
        uart_rx_r <= Uart_rx;
        uart_rx_rr <= uart_rx_r;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            receiv_begin <= 0;
        else if(state == 0 & uart_rx_rr & ~uart_rx_r)
            receiv_begin <= 1'b1;
        else
            receiv_begin <= 0;            
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0) begin
            state <= 0;
            sampel_ref <= 8'b0;
            acc <= 8'b0;
            Data <= 8'b0;
        end
        else case(state)
            0: 
                if(receiv_begin == 1)
                    state <= 3'd1;
                else
                    state <= 0;
            
            1: begin
                    if(sampel_en == 1) begin
                           sampel_ref <= Uart_rx;
                           state <= 3'd2;
                    end

                    else
                        state <= 3'b1;
               end   
                    
            2: begin

                    acc <= acc + sampel_ref;
                   
                    if(sampel_cnt == 7) begin
                        if(acc >= 4)
                            begin Data[7] <= 1'b1; state <= 3'd3;acc <= 8'b0; end
                        else
                            begin Data[7] <= 0; state <= 3'd3;acc <= 8'b0; end
                    end
                    
                    else
                        state <= 3'd1;
               end                            

            3: begin
                    if(bit_cnt < 8) begin
                        Data <= Data >> 1;
                        state <= 3'd1; 
                    end
                    
                    else 
                        state <= 0;
            end
            
            default:;
       endcase
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            receiv_flag <= 0;
        else if(receiv_begin == 1)
            receiv_flag <= 1'b1;
        else if(bit_cnt == 9 & baud_cnt == BAUD_CNT/9*8)
            receiv_flag <= 1'b0;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)     
            baud_cnt <= 0;
        else if(receiv_flag == 1) begin
            if(baud_cnt == BAUD_CNT - 1)
                baud_cnt <= 0;
            else
                baud_cnt <= baud_cnt + 1'b1;
            end
        else
            baud_cnt <= 0;      
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0) begin
            sampel_cnt <= 0;
            sampel_en <= 0;
        end
        else if(receiv_flag == 1) begin
            case(baud_cnt)
                BAUD_CNT/9*1-1 : begin sampel_cnt <= 0; sampel_en <=1; end
                BAUD_CNT/9*2-1 : begin sampel_cnt <= 1; sampel_en <=1; end
                BAUD_CNT/9*3-1 : begin sampel_cnt <= 2; sampel_en <=1; end
                BAUD_CNT/9*4-1 : begin sampel_cnt <= 3; sampel_en <=1; end
                BAUD_CNT/9*5-1 : begin sampel_cnt <= 4; sampel_en <=1; end
                BAUD_CNT/9*6-1 : begin sampel_cnt <= 5; sampel_en <=1; end
                BAUD_CNT/9*7-1 : begin sampel_cnt <= 6; sampel_en <=1; end
                BAUD_CNT/9*8-1 : begin sampel_cnt <= 7; sampel_en <=1; end
                BAUD_CNT/9*9-1 : sampel_cnt <= 0;
                default:sampel_en <=0;
            endcase
        end
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            bit_cnt <= 0;
        else if(bit_cnt == 9 & baud_cnt == BAUD_CNT/9*8)
            bit_cnt <= 0;
        else if(baud_cnt == BAUD_CNT - 1)
            bit_cnt <= bit_cnt + 1'b1;
    end

    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            Rx_done <= 0;
        else if(bit_cnt == 9 & baud_cnt == BAUD_CNT/9*8)
            Rx_done <= 1'b1;
        else
            Rx_done <= 0;
    end
    
endmodule

LED_6.v

`timescale 1ns / 1ps

module LED_6(
    input                  SCLK,
    input                  RST_N,
    input          [ 7:0]  CTRL_IN,
    input          [15:0]  Time,
    output   reg   [ 7:0]  LED
    );
    
    parameter   DELAY_10US = 500;
    parameter   COUNT_10MS = 1000;
    
    reg [8:0] 	count_10us;
    reg [15:0] 	count_time;
    reg 	led_flag;
    
    always @(posedge SCLK or negedge RST_N) begin
        if(RST_N == 0)
            count_10us <= 0;
        else if(count_10us == DELAY_10US - 1)
            count_10us <= 0;
        else 
            count_10us <= count_10us + 1'b1;
    end
    
    always @(posedge SCLK or negedge RST_N) begin
        if(RST_N == 0)
            count_time <= 0;
        else if(count_time == Time - 1)
            count_time <= 0;
        else if(count_10us == DELAY_10US - 1)
            count_time <= count_time + 1'b1;
    end
    
    always @(posedge SCLK or negedge RST_N) begin
        if(RST_N == 0)
            led_flag <= 0;
        else if(count_time == COUNT_10MS - 1)
            led_flag <= ~led_flag;
    end
    
    always @(posedge SCLK or negedge RST_N) begin
    if(RST_N == 0)
        LED <= 0;
    else if(count_time == COUNT_10MS - 1 & led_flag == 1)
        LED <= CTRL_IN;
    else if(count_time == COUNT_10MS - 1 & led_flag == 0)
        LED <= 0;
    end    
        
endmodule

上板效果

image
这里电脑的串口收到00数据是因为没有设置开发板的串口TX端口,导致其悬空了,受到了干扰所导致的,可以将其设置为高电平输出,以解决该问题。
 
image
 
image
可见的LED灯按照设定方式进行闪烁,可见此设计成功。
(数码管亮是因为控制该数码管的移位寄存器的问题)

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/Lclone/p/17008512.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!