发布时间:2022-12-27 文章分类:编程知识 投稿人:李佳 字号: 默认 | | 超大 打印

这次设计一个可以接收多字节(通过修改例化时的位宽实现)的串口接收模块。

当接收到9个字节的数据,但是我们只需要8个字节的数据时候,我们需要的是前八位的数据还是后八位的数据我们无法确定。
串口多字节数据的接收

所以我们需要设定一种传输协议,这种协议我们可以自定义规则。我们就设定前缀为8'h55+8'hA5,后缀为8'hF0的一串数据即为我们需要的数据。
串口多字节数据的接收

1、状态机的设定

串口多字节数据的接收

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)仿真结果

串口多字节数据的接收

5、应用实例

我们把它应用为一个通过接收电脑串口发送的数据从而改变8位LED每位是否闪烁闪烁的周期的程序。

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