协议篇-串口通信

协议篇-串口通信串口通信有两根通讯线 一根接收数据 uart rxd 一根输出数据 uart txd 串口传输速度单位为波特率 bps 代表每秒钟传输的二进制数的位数

大家好,欢迎来到IT知识分享网。

        串口简介

        串口通信作为最常用通讯协议是每一个工程师需要掌握的基本知识,本章将详细介绍串口基本理论知识。

        RS232

        RS232串口通信有两根通讯线,一根接收数据(uart_rxd),一根输出数据(uart_txd),串口传输速度单位为波特率,bps代表每秒钟传输的二进制数的位数。常用的波特率有9600bps,38400bps,bps。

        RS485

        相比较RS232,RS485特性如下:

        1、电气特性:RS232是单点通信协议,使用一对传输线(TX、RX)进行双向通信。信号电平通常为负电平表示逻辑1,正电平表示逻辑0。RS485是差分通信协议,可以支持多个节点的多点通信,使用两对传输线(A、B)进行双向通信。通过差分信号传输,能更好地抵抗线路干扰和噪声。

        2、数据传输距离和速率:RS232通常适用于较短距离的通信,通信距离最多几十米。最高数据传输速率通常为115.2 kbps。RS485适用于长距离通信,通信距离可以达到几千米。传输速率可以达到10 Mbps,取决于具体的标准和设备。

        3、网络拓扑结构:RS232通常用于点对点连接,即一对一的通信连接。RS485支持多点通信,可以使用总线拓扑结构,连接多个节点。

        4、传输方式:RS232使用全双工通信方式,可以同时发送和接收数据。RS485可以使用半双工或全双工通信方式,可以同时发送和接收数据,也可以分时发送和接收数据。

        如下图所示为串口通信时序图:

协议篇-串口通信

                                                              图1 串口时序图

        总线接收数据时序解析:

        1、空闲状态总线处于高电平,起始位为1个bit低电平;

        2、紧接着传输8bit数据位,小端传输(从最低位开始传输);

        3、奇偶校验位主要验证链路传输是否有误,如果奇偶校验位为 0(偶数奇偶校验),则数据帧中的 1 或逻辑高位总计应为偶数。如果奇偶校验位为 1(奇数奇偶校验),则数据帧中的 1 或逻辑高位总计应为奇数。通常设计中选择无奇偶校验位,传输数据会另外加帧做数据接收判断。

        4、停止位,通常选用一1个bit高电平作为结束位。

        综上所述通常情况下传输一个Byte(8个bit数据)需要10位,起始位(1bit)+数据位(8bit)+结束位(1bit)。而如果添加奇偶校验则需要传输11位。

        FPGA代码

        串口代码如下:

串口顶层

`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2024/01/02 15:05:43 // Design Name: // Module Name: UART_CMD_TOP // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module UART_TOP( input sys_clk, input sys_rst_n, output uart_tx, input uart_rx, output uart_en_port, output[7:0] uart_rec_data, output uart_rec_data_en, input[7:0] uart_tx_data, input uart_tx_data_en, output uart_tx_busy ); localparam CLK_FREQ = ; //定义系统时钟频率 localparam UART_BPS = ; //定义串口波特玿 reg uart_rec_data_done_r1; reg uart_rec_data_done_r2; wire uart_rec_data_done_rise; assign uart_rec_data_done_rise = uart_rec_data_done_r1 && (~uart_rec_data_done_r2); assign uart_rec_data_en = uart_rec_data_done_rise; always@(posedge sys_clk)begin uart_rec_data_done_r1 <= uart_rec_data_done; uart_rec_data_done_r2 <= uart_rec_data_done_r1; end //串口接收模块 uart_recv #( .CLK_FREQ (CLK_FREQ), //设置系统时钟频率 .UART_BPS (UART_BPS)) //设置串口接收波特玿 u_uart_recv( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .uart_rxd (uart_rx), .uart_done (uart_rec_data_done), .uart_data (uart_rec_data) ); //串口发鿁模坿 uart_send #( .CLK_FREQ (CLK_FREQ), //设置系统时钟频率 .UART_BPS (UART_BPS)) //设置串口发鿁波特玿 u_uart_send( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .uart_en (uart_tx_data_en), .uart_din (uart_tx_data), .uart_tx_busy (uart_tx_busy), .uart_txd (uart_tx) ); endmodule 

        串口发送端

module uart_send( input sys_clk, //系统时钟 input sys_rst_n, //系统复位,低电平有效 input uart_en, //发送使能信号 input [7:0] uart_din, //待发送数据 output uart_tx_busy, //发送忙状态标志 output reg uart_txd //UART发送端口 ); //parameter define parameter CLK_FREQ = ; //系统时钟频率 parameter UART_BPS = 9600; //串口波特率 localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次 //reg define reg uart_en_d0; reg uart_en_d1; reg [15:0] clk_cnt; //系统时钟计数器 reg [ 3:0] tx_cnt; //发送数据计数器 reg tx_flag; //发送过程标志信号 reg [ 7:0] tx_data; //寄存发送数据 //wire define wire en_flag; //* // main code //* //在串口发送过程中给出忙状态标志 assign uart_tx_busy = tx_flag; //捕获uart_en上升沿,得到一个时钟周期的脉冲信号 assign en_flag = (~uart_en_d1) & uart_en_d0; //对发送使能信号uart_en延迟两个时钟周期 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin uart_en_d0 <= 1'b0; uart_en_d1 <= 1'b0; end else begin uart_en_d0 <= uart_en; uart_en_d1 <= uart_en_d0; end end //当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin tx_flag <= 1'b0; tx_data <= 8'd0; end else if (en_flag) begin //检测到发送使能上升沿 tx_flag <= 1'b1; //进入发送过程,标志位tx_flag拉高 tx_data <= uart_din; //寄存待发送的数据 end //计数到停止位结束时,停止发送过程 else if ((tx_cnt == 4'd9) && (clk_cnt == BPS_CNT -(BPS_CNT/16))) begin tx_flag <= 1'b0; //发送过程结束,标志位tx_flag拉低 tx_data <= 8'd0; end else begin tx_flag <= tx_flag; tx_data <= tx_data; end end //进入发送过程后,启动系统时钟计数器 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) clk_cnt <= 16'd0; else if (tx_flag) begin //处于发送过程 if (clk_cnt < BPS_CNT - 1) clk_cnt <= clk_cnt + 1'b1; else clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零 end else clk_cnt <= 16'd0; //发送过程结束 end //进入发送过程后,启动发送数据计数器 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) tx_cnt <= 4'd0; else if (tx_flag) begin //处于发送过程 if (clk_cnt == BPS_CNT - 1) //对系统时钟计数达一个波特率周期 tx_cnt <= tx_cnt + 1'b1; //此时发送数据计数器加1 else tx_cnt <= tx_cnt; end else tx_cnt <= 4'd0; //发送过程结束 end //根据发送数据计数器来给uart发送端口赋值 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) uart_txd <= 1'b1; else if (tx_flag) case(tx_cnt) 4'd0: uart_txd <= 1'b0; //起始位 4'd1: uart_txd <= tx_data[0]; //数据位最低位 4'd2: uart_txd <= tx_data[1]; 4'd3: uart_txd <= tx_data[2]; 4'd4: uart_txd <= tx_data[3]; 4'd5: uart_txd <= tx_data[4]; 4'd6: uart_txd <= tx_data[5]; 4'd7: uart_txd <= tx_data[6]; 4'd8: uart_txd <= tx_data[7]; //数据位最高位 4'd9: uart_txd <= 1'b1; //停止位 default: ; endcase else uart_txd <= 1'b1; //空闲时发送端口为高电平 end endmodule 

串口接收端

module uart_recv( input sys_clk, //系统时钟 input sys_rst_n, //系统复位,低电平有效 input uart_rxd, //UART接收端口 output reg uart_done, //接收一帧数据完成标志 output reg [7:0] uart_data //接收的数据 ); //parameter define parameter CLK_FREQ = ; //系统时钟频率 parameter UART_BPS = 9600; //串口波特率 localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率, //需要对系统时钟计数BPS_CNT次 //reg define reg uart_rxd_d0; reg uart_rxd_d1; reg [15:0] clk_cnt; //系统时钟计数器 reg [ 3:0] rx_cnt; //接收数据计数器 reg rx_flag; //接收过程标志信号 reg [ 7:0] rxdata; //接收数据寄存器 //wire define wire start_flag; //* // main code //* //捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号 assign start_flag = uart_rxd_d1 & (~uart_rxd_d0); //对UART接收端口的数据延迟两个时钟周期 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin uart_rxd_d0 <= 1'b0; uart_rxd_d1 <= 1'b0; end else begin uart_rxd_d0 <= uart_rxd; uart_rxd_d1 <= uart_rxd_d0; end end //当脉冲信号start_flag到达时,进入接收过程 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) rx_flag <= 1'b0; else begin if(start_flag) //检测到起始位 rx_flag <= 1'b1; //进入接收过程,标志位rx_flag拉高 //计数到停止位中间时,停止接收过程 else if((rx_cnt == 4'd9) && (clk_cnt == BPS_CNT/2)) rx_flag <= 1'b0; //接收过程结束,标志位rx_flag拉低 else rx_flag <= rx_flag; end end //进入接收过程后,启动系统时钟计数器 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) clk_cnt <= 16'd0; else if ( rx_flag ) begin //处于接收过程 if (clk_cnt < BPS_CNT - 1) clk_cnt <= clk_cnt + 1'b1; else clk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零 end else clk_cnt <= 16'd0; //接收过程结束,计数器清零 end //进入接收过程后,启动接收数据计数器 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) rx_cnt <= 4'd0; else if ( rx_flag ) begin //处于接收过程 if (clk_cnt == BPS_CNT - 1) //对系统时钟计数达一个波特率周期 rx_cnt <= rx_cnt + 1'b1; //此时接收数据计数器加1 else rx_cnt <= rx_cnt; end else rx_cnt <= 4'd0; //接收过程结束,计数器清零 end //根据接收数据计数器来寄存uart接收端口数据 always @(posedge sys_clk or negedge sys_rst_n) begin if ( !sys_rst_n) rxdata <= 8'd0; else if(rx_flag) //系统处于接收过程 if (clk_cnt == BPS_CNT/2) begin //判断系统时钟计数器计数到数据位中间 case ( rx_cnt ) 4'd1 : rxdata[0] <= uart_rxd_d1; //寄存数据位最低位 4'd2 : rxdata[1] <= uart_rxd_d1; 4'd3 : rxdata[2] <= uart_rxd_d1; 4'd4 : rxdata[3] <= uart_rxd_d1; 4'd5 : rxdata[4] <= uart_rxd_d1; 4'd6 : rxdata[5] <= uart_rxd_d1; 4'd7 : rxdata[6] <= uart_rxd_d1; 4'd8 : rxdata[7] <= uart_rxd_d1; //寄存数据位最高位 default:; endcase end else rxdata <= rxdata; else rxdata <= 8'd0; end //数据接收完毕后给出标志信号并寄存输出接收到的数据 always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin uart_data <= 8'd0; uart_done <= 1'b0; end else if(rx_cnt == 4'd9) begin //接收数据计数器计数到停止位时 uart_data <= rxdata; //寄存输出接收到的数据 uart_done <= 1'b1; //并将接收完成标志位拉高 end else begin uart_data <= 8'd0; uart_done <= 1'b0; end end endmodule 

     FPGA仿真

        如下图所示为FPGA实现串口仿真图:

协议篇-串口通信

                                                                图2 FPGA 串口仿真

        信号定义如下:

        en_flag:输出数据有效信号;

        uart_din:输出数据;

        tx_cnt:输出bit标志位,0~9代表10个bit位;

        uart_txd:串口输出信号。

        en_flag输出一个工作时钟周期(100MHz)有效位代表uart_din数据有效,输出数据为0x12,串口开始输出1bit起始位(0),紧接着输出0100_1000数据,再输出1bit结束位(1);串口数据传输结束。

        

        以上为串口通信理论知识,如有疑问可以留言。

        

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/122321.html

(0)
上一篇 2025-10-17 15:45
下一篇 2025-10-17 16:10

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信