FPGA:三大协议(IIC、UART、SPI)之UART

UART是一种串行通信接口,通过两个引脚实现数据的发送和接收,通信过程中数据由并行转串行再转回并行。通信时序包括起始位、数据位、奇偶校验位和停止位。波特率决定了每秒传输的位数,确保通信双方速率一致。文章还提供了Verilog代码示例,展示了UART接收和发送模块的工作流程。
摘要由CSDN通过智能技术生成

一、UART(串口)结构:

        (1)、所需引脚:两个引脚:一个用来发送输出输出,一个用来接收数据;

        (2)、在发送数据的时候将并行数据转换成串行数据来传输;

        (3)、在接收数据的时候将收到的串行数据转换成并行数据;

 二、UART的时序

1、接收时序(针对的是串口助手(我们操控的)发送的数据给板子,板子来实现接收):

(1)知道了外部信息是通过两个线交流的,那么要清楚怎么通过两个线实现交流的,所以他们就要遵循同一个协议(比如说两个人交流遵循同一个语言:中文,才能实现交流)。

(2)协议内容:

第一:对于RXD线(串口助手发送数据给板子的数据线),在常态(没有任何操作的时候)是高电平的状态(这个状态不是我们设置的,是硬件工程师设计的,不管我们的事);

第二:由于常态是高电平的状态,那么我们就可以认为当数据线是0的时候就表示数据开始传输了(串口助手开始发送东西过来了);

第三:当接收完数据之后,我们再接收一个校验位(就是一个普通的奇偶检验位,根据接收到的数据来判断这一次传输出现差错没有);

第四:接收完之后回到空闲状态的高电平就表示我们接收完成了;

总结:

一帧数据由4部分组成

        1、起始位(1bit)

        2、数据位(6/7/8bit)、LSB、MSB

        3、奇偶校验位(1bit1):奇偶校验位算法:^data_temp:按位异或(相异为1,相同为0

        4、停止位(1bit/1.5bit/2bit)

 注:通信双方要用同一个通信协议,不然不能进行通信。

2、发送时序(针对的是板子发送数据给串口助手,串口助手接收到数据):

第一:对于TXD线(板子发送数据给串口助手的数据线),在常态(没有任何操作的时候)是高电平的状态(这个状态不是我们设置的,是硬件工程师设计的,不管我们的事),首先发送一个高电平;

第二:然后根据数据位的01,分别发送高低电平;

第三:当发送完数据之后,我们再发送一个校验位(就是一个普通的奇偶检验位,根据发送的数据来判断这一次传输出现差错没有);

第四:发送完之后回到空闲状态的高电平就表示我们发送完成了;

3、收发参照速率

(1)什么意思呢:要接收数据的话,双方(串口助手和板子)速率不一样的话,那么可能导致一个发完了,另外一个才接收到第二个数据位,所以双发要遵循一个传播速率:波特率;

(2)波特率:表示传输一bit的数据需要的时间。

(3)一般的波特率:9600;  115200;57600;38400;    115200;

verilog参考代码:

(1)接收模块:(串口助手发送板子接收)

module uart_receiver(
    input		sys_clk,
    input		sys_rst_n,
    input [3:0]	bud_set,
    input       rxd,  //接收数据(11位数据),单bit
    input       key_in,//奇偶校验位控制

    output      rx_done,	    //接收完成标志
    output reg [7:0]     data_rx//接收到的数据整合成8位
);
parameter FREQ = 50_000_000;//系统时钟

//计数模块
reg [15:0]  cnt_bps;//波特率计数
wire        add_bps;
wire        end_bps;
reg [12:0]  max_bps;

//发送位模块,10位
reg [3:0]   cnt_bit;//发送数据位计数
wire        add_bit;
wire        end_bit;

reg         start_keep;//当识别到下降沿的时候进行电平保持,不能收数据的时候为低,为高代表可以开是收数据

//缓存信号
reg [10:0]   data_temp;

//打拍
reg         tx_en_d0;
reg         tx_en_d1;
//打拍识别出下降沿
wire        start_flag;
//识别起始位和终止位,产生停止
/* reg         stop_flag; */
/* reg         start_bit;//开始位接收
reg         stop_bit;//结束位寄存 */

reg         check_bit;//0为偶校验,1为奇校验

//按键控制奇偶校验
    always@(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            check_bit <= 1'd0;
        end
        else if(key_in)begin
            check_bit <= ~check_bit;
        end
        else begin
            check_bit <= check_bit;
        end
    end

//当识别到rdx数据的下降沿,开始接收
    always @(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            tx_en_d0 <= 1'b0;
            tx_en_d1 <= 1'b0;
        end
        else begin
            tx_en_d0 <= rxd;
            tx_en_d1 <= tx_en_d0;
        end
        //rxd_r <= {rxd_r[0],rxd}
    end

assign  start_flag = ~tx_en_d0 && tx_en_d1;//检验下降沿

//检测到发送结束,开始进行接收
    always @(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            start_keep <= 1'b0;
        end
        else if(start_flag)begin
            start_keep <= 1'b1;
        end
        else if(rx_done || data_temp[0] || !data_temp[10])begin
            start_keep <= 1'b0;
        end
        else if((check_bit == 1'b0) && cnt_bit >= 10 && ((^data_temp[8:1]) != data_temp[9]))begin//偶校验
            start_keep <= 1'b0;
        end
        else if((check_bit == 1'b1) && cnt_bit >= 10 && ((~(^data_temp[8:1])) != data_temp[9]))begin//奇校验
            start_keep <= 1'b0;
        end
        else begin
            start_keep <= start_keep;
        end
    end

/* //采集起始位和停止位
    always @(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            start_bit <= 1'b0;//默认是0
            stop_bit <= 1'b1;//默认是一
        end
        else if(cnt_bit == 0 && cnt_bps == (max_bps >> 1))begin
            start_bit <= rxd;
        end
        else if(cnt_bit == 9 && cnt_bps == (max_bps >> 1))begin
            stop_bit<= rxd;
        end
        else begin
            start_bit <= start_bit;
            stop_bit <= stop_bit;
        end
    end */

/* //接收数据第一位接收之后为0的时候开始,不然终止
    always@(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            stop_flag <= 1'd0;
        end
        else if(start_bit != 0)begin
            stop_flag <= 1;
        end
        else if(stop_bit != 1)begin
            stop_flag <= 1;
        end
        else begin
            stop_flag <= 0;//如果以上两个不满足,则保持低
        end
    end */


//计数器计数模块
assign add_bps = start_keep;
assign end_bps = add_bps && cnt_bps >= max_bps - 16'd1;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)begin
            cnt_bps <= 16'd0;
        end
        else if(add_bps)begin
            if(end_bps)begin
                cnt_bps <= 16'd0;
            end
            else begin
                cnt_bps <= cnt_bps + 16'b1;
            end
        end
        else begin
            cnt_bps <= 16'b0;
        end
    end

//计数位变化
assign add_bit = end_bps;
assign end_bit = add_bit && cnt_bit >= 4'd10;//一共输入11位,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)begin
            cnt_bit <= 4'd0;
        end
        else if(add_bit)begin
            if(end_bit)begin
                cnt_bit <= 0;
            end
            else begin
                cnt_bit <= cnt_bit + 4'b1;
            end
        end
        else if(start_keep == 1'b0)begin//当检测到输入第一位不是0时,重新开始
            cnt_bit <= 4'd0;
        end
        else begin
            cnt_bit <= cnt_bit;
        end
    end
//产生波特率计数器的最大值
    always @(*)begin
        case(bud_set)
            3'd0 : max_bps = FREQ   /   9600;   
            3'd1 : max_bps = FREQ   /   115200;
            3'd2 : max_bps = FREQ   /   57600;
            3'd3 : max_bps = FREQ   /   38400;        
            default : max_bps = FREQ   /   115200;
        endcase
    end

//保存数据
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)begin
            data_temp <= 11'b1000_0000_000;
        end
        else if(!start_keep)begin
             data_temp <= 11'b1000_0000_000;
        end
        else if(start_keep && (cnt_bps == (max_bps >> 1)))begin
            case(cnt_bit)
            	4'd0:  data_temp[0] <= rxd; //起始位
                4'd1 : data_temp[1] <= rxd;
                4'd2 : data_temp[2] <= rxd;
                4'd3 : data_temp[3] <= rxd;
                4'd4 : data_temp[4] <= rxd;
                4'd5 : data_temp[5] <= rxd;
                4'd6 : data_temp[6] <= rxd;
                4'd7 : data_temp[7] <= rxd;
                4'd8 : data_temp[8] <= rxd;
                4'd9 : data_temp[9] <= rxd;//偶校验位
                4'd10:  data_temp[10] <= rxd; //停止位	
                default :;
            endcase
        end
        else begin
            data_temp <= data_temp;
        end 
    end

//发送完毕的时候开始进行
    assign rx_done = end_bit;//输出一个结束脉冲
//数据发送
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)begin
            data_rx <= 8'd0;
        end
        else if(cnt_bps == ( max_bps >> 1) && cnt_bit == 4'd10)begin
            data_rx <= data_temp[8:1];
        end
        else begin
            data_rx <= data_rx;
        end
    end
/*     assign data_rx = data_temp; */

endmodule

(2)发送模块:(板子发送串口助手接收):

module uart_send(
    input       sys_clk,
    input       sys_rst_n,
    input [7:0] uart_din,//原始数据,8位,数据要保存下来,以免丢失
    input       tx_en,//发送数据使能,发送一次数据使能一次
    input [3:0] bud_set,
    input       key_in,//按键控制奇偶校验

    output reg  uart_tx, //发送的数据,一位一位的发送,包括起始位,数据位,停止位(有奇偶校验位)
    output      send_stop//数据传输完成标志信号
);

parameter FREQ = 50_000_000;//系统时钟


//计数模块
reg [15:0]  cnt_bps;//波特率计数
wire        add_bps;
wire        end_bps;
reg [12:0]  max_bps;

//发送位模块,10位
reg [3:0]   cnt_bit;//发送数据位计数
wire        add_bit;
wire        end_bit;

/* //打拍
reg         tx_en_d0;
reg         tx_en_d1;
wire        start_flag;//使能信号来临识别脉冲 */
reg         start_keep;//当识别到上升沿的时候进行电平保持,当送完数据的时候为低,为高代表可以开始输出数据

//缓存信号
reg [7:0]   data_temp;
reg			 	send_en_R; 
	
reg         check_bit;//0为偶校验,1为奇校验
//按键控制奇偶校验
    always@(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            check_bit <= 1'd0;
        end
        else if(key_in)begin
            check_bit <= ~check_bit;
        end
        else begin
            check_bit <= check_bit;
        end
    end


//Logic Description	
	always @(posedge sys_clk or negedge sys_rst_n)begin 
		if(!sys_rst_n)begin
			send_en_R <= 1'b0;
		end  
		else begin
			send_en_R <= tx_en;
		end
	end //always end
//根据检测的上升沿生成电平信号,当送完数据的时候为低,为高代表可以开始输出数据
    always @(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            start_keep <= 1'b0;
        end
        else if(tx_en)begin
            start_keep <= 1'b1;
        end
        else if(end_bit)begin
            start_keep <= 1'b0;
        end
        else begin
            start_keep <= start_keep;
        end
    end

//产生波特率计数器的最大值
    always @(*)begin
        case(bud_set)
            3'd0 : max_bps = FREQ   /   9600;   
            3'd1 : max_bps = FREQ   /   115200;
            3'd2 : max_bps = FREQ   /   57600;
            3'd3 : max_bps = FREQ   /   38400;        
            default : max_bps = FREQ   /   115200;
        endcase
    end


/* //使能信号识别,高有效,使能
    always @(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            cnt_bps <= 16'd0;
        end
        else begin
            tx_en_d0 <= tx_en;
            tx_en_d1 <= tx_en_d0;
        end
    end

assign  start_flag = ~tx_en_d0 && tx_en_d1;//检验上升沿 */

//数据缓存
//使能信号来临,开始计时,缓存信号,
    always @(posedge sys_clk or negedge sys_rst_n)begin
        if(!sys_rst_n)begin
            data_temp <= 8'b11111111;
        end
        else if(send_en_R)begin
            data_temp <= uart_din;
        end
        else begin
            data_temp <= data_temp;
        end
    end

//计数器计数模块
assign add_bps = start_keep;
assign end_bps = add_bps && cnt_bps >= max_bps - 16'd1;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)begin
            cnt_bps <= 16'd0;
        end
        else if(add_bps)begin
            if(end_bps)begin
                cnt_bps <= 16'd0;
            end
            else begin
                cnt_bps <= cnt_bps + 16'b1;
            end
        end
        else begin
            cnt_bps <= 16'b0;
        end
    end

//计数位变化
assign add_bit = end_bps;
assign end_bit = add_bit && cnt_bit >= 4'd10;//一共输出11位,
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)begin
            cnt_bit <= 4'd0;
        end
        else if(add_bit)begin
            if(end_bit)begin
                cnt_bit <= 0;
            end
            else begin
                cnt_bit <= cnt_bit + 4'b1;
            end
        end
        else begin
            cnt_bit <= cnt_bit;
        end
    end

//数据输出,根据数据位选,计数BPS_CNT次,换一位
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)begin
            uart_tx <= 1'b1;
        end
        else if(start_keep)begin
            case(cnt_bit)
                4'd0 : uart_tx = 1'b0;//起始位
                4'd1 : uart_tx = data_temp[0];
                4'd2 : uart_tx = data_temp[1];
                4'd3 : uart_tx = data_temp[2];
                4'd4 : uart_tx = data_temp[3];
                4'd5 : uart_tx = data_temp[4];
                4'd6 : uart_tx = data_temp[5];
                4'd7 : uart_tx = data_temp[6];
                4'd8 : uart_tx = data_temp[7];
                4'd9 : uart_tx = (check_bit == 1'b0 ? (^data_temp) : (~(^data_temp)));//发送奇偶校验位
                4'd10 : uart_tx = 1'b1;//停止位
                default :uart_tx = 1'b1;
            endcase
        end
        else begin
            uart_tx = 1'b1;
        end 
    end

    assign send_stop = end_bit;//输出一个结束脉冲

endmodule

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值