RS232通信协议简介

1、RS232是UART的一种,没有时钟线,只有两根数据线,分别是rx和tx,这两根线都是1bit位宽的。其中rx是接受数据的线,tx是发送数据的线。

2、rx的位宽为1bit,PC机通过串口调试助手往FPGA发送8bit数据时,FPGA通过串口线rx一位一位的接收数据,从最低位到最高位依次接收,最后在FPGA里面位拼接成8bit数据。

3、tx 位宽为 1bit,FPGA 通过串口往 PC 机发 8bit 数据时,FPGA 把 8bit 数据通过 tx 线一位一位的传给 PC 机,从最低位到最高位依次发送,最后上位机通过串口助手按照 RS232 协议把这一位一位的数据位拼接成 8bit 数据。

4、串口数据的发送与接收是基于帧结构的,即一帧一帧的发送与接收数据。每一帧除了中间包含 8bit 有效数据外,还在每一帧的开头都必须有一个起始位,且固定为 0;在每一帧的结束时也必须有一个停止位,且固定为 1,即最基本的帧结构(不包括校验等)有 10bit。在不发送或者不接收数据的情况下,rx 和 tx 处于空闲状态,此时 rx 和 tx 线都保持 高电平,如果有数据帧传输时,首先会有一个起始位,然后是 8bit 的数据位,接着有 1bit 的停止位,然后 rx 和 tx 继续进入空闲状态,然后等待下一次的数据传输。如下图所示 为一个最基本的 RS232 帧结构。

PS:Rx:一共是发送十位数据,但是第一位和第十位分别是0和1,中间八位任意,只需要对中间八位进行传输即可

5、波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是 1bit 进 行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元 的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)”。串口常见的波特率有 4800、9600、115200 等,我们选用 9600 的波特率进行串口的编写与仿真。

6、比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为 “每秒比特数(bps)”。比特率可由波特率计算得出,公式为:比特率=波特率 * 单个调制状态对应的二进制位数。如果使用的是 9600 的波特率,其串口的比特率为:9600Bps * 1bit= 9600bps。

7、由计算得串口发送或者接收 1bit 数据的时间为一个波特,即 1/9600 秒,如果用 50MHz(周期为 20ns)的系统时钟来计数,需要计数的个数为 cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208 个系统时钟周期,即每个 bit 数据之间的间隔要在 50MHz 的时钟 频率下计数 5208 次

8、上位机通过串口发 8bit 数据时,会自动在发 8 位有效数据前发一个波特时间的起始位,也会自动在发完 8 位有效数据后发一个停止位。同理,串口助手接收上位机发送的数据前,必须检测到一个波特时间的起始位才能开始接收数据,接收完 8bit 的数据后,再接收一个波特时间的停止位。

程序设计

1、顶层模块框图如下

可以看到,顶层的框图一共包含两个模块,分别是接收和发送模块。

2、接收模块

接收模块的作用是将从PC输入的串行数据转换为并行数据 。

一帧数据一共有10bit,其中起始位和停止位分别为0和1,接收模块只接收中间八位数据,将中间8位的串行数据转化为并行,然后把8位的并行数据输入到发送模块。

接收模块的框图如下图所示:

接收模块波形图如下:

对波形图的一些理解: 接收的Rx信号需要同步到系统时钟sys_clk下,即单比特信号从低速时钟域同步到快速时钟域,通常使用的方法都是打两拍。但为何要同步三次呢?因为start_flag需要提取一个下降沿,而第一个同步的时钟是一个不稳定的信号,所以需要用第二个和第三个同步后的来提取下降沿。

根据波形图写出代码如下:

module uart_rx
#(parameter uart_bps    = 'd9600,parameter    clk_freq    = 'd50_000_000
)
(input  wire            sys_clk     ,input  wire            sys_rst_n   ,input  wire            rx          ,output reg [7:0]   po_data     ,output reg         po_flag     );localparam    boud_cnt_MAX    =  clk_freq/uart_bps   ;reg            rx_reg1         ;reg            rx_reg2         ;reg            rx_reg3         ;reg            start_flag      ;reg            work_en         ;reg    [13:0]  boud_cnt        ;reg            bit_flag        ;reg    [3:0]   bit_cnt         ;reg    [7:0]   rx_data         ;reg            rx_flag         ;//regesiter delay, two clock time
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_reg1<=1'b1;elserx_reg1<=rx;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_reg2<=1'b1;elserx_reg2<=rx_reg1;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_reg3<=1'b1;elserx_reg3<=rx_reg2;//start_flag
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)start_flag<=1'b0;else  if((~rx_reg2)&&(rx_reg3))start_flag<=1'b1;elsestart_flag<=1'b0;//work_en
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)work_en<=1'b0;else if(bit_cnt==4'd8&&bit_flag==1'b1)work_en<=1'b0;else  if(start_flag==1'b1)work_en<=1'b1;elsework_en<=work_en;//boud_cnt
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)boud_cnt<=14'd0;else   if((boud_cnt==boud_cnt_MAX-1'b1)||(work_en==1'b0))boud_cnt<=14'd0;else   if(work_en==1'b1)boud_cnt<=boud_cnt+1'b1;//bit_flag
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)bit_flag<=1'b0;else    if(boud_cnt==boud_cnt_MAX/2-1'b1)bit_flag<=1'b1;elsebit_flag<=1'b0;//bit_cnt
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)bit_cnt<=1'b0;else if(bit_cnt==4'd8&&bit_flag==1'b1)bit_cnt<=1'b0;else  if(bit_flag==1'b1)bit_cnt<=bit_cnt+1'b1;elsebit_cnt<=bit_cnt;//rx_data[7:0]
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_data<=8'b0;else if((bit_cnt>=4'd1)&&(bit_cnt<=4'd8)&&(bit_flag==1'b1))rx_data<={rx_reg3,rx_data[7:1]};//rx_flag
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)rx_flag<=1'b0;else if(bit_flag==1'b1&&bit_cnt==4'd8)rx_flag<=1'b1;elserx_flag<=1'b0;//po_data
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)po_data<=8'd0;else if(rx_flag==1'b1)po_data<=rx_data;
//po_flag
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)po_flag<=1'b0;else if(rx_flag==1'b1)po_flag<=1'b1;elsepo_flag<=1'b0;endmodule

接收模块仿真代码如下:

`timescale 1ns / 1ns
module tb_uart_rx();reg         sys_clk          ;
reg         sys_rst_n        ;
reg         rx               ;wire  [7:0]   po_data     ;
wire            po_flag     ;initialbeginsys_clk=1'b1;sys_rst_n<=1'b0;rx<=1;#20sys_rst_n<=1'b1;endalways #10    sys_clk=~sys_clk;initialbegin#200rx_bit(8'd0);rx_bit(8'd1);rx_bit(8'd2);rx_bit(8'd3);rx_bit(8'd4);rx_bit(8'd5);rx_bit(8'd6);rx_bit(8'd7);endtask rx_bit(input  [7:0]   data
);integer i;for(i=0;i<10;i=i+1)begincase(i)0:rx<=1'b0;1:rx<=data[0];2:rx<=data[1];3:rx<=data[2];4:rx<=data[3];5:rx<=data[4];6:rx<=data[5];7:rx<=data[6];8:rx<=data[7];9:rx<=1'b1;endcase#(5208*20);end
endtaskuart_rx  inst
(
.sys_clk        (sys_clk)       ,
.sys_rst_n      (sys_rst_n)     ,
.rx             (rx)            ,.po_data       (po_data)       ,
.po_flag        (po_flag)       );
endmodule

 Vivado仿真波形图

整体波形如下

对仿真波形图的理解: 在空闲状态下,Rx保持高电平,当检测到低电平时,波特计数器开始计数,当波特计数器计数到最大值的时候,则开始传输数据,一个8bit的数据传输完成后,po_data才开始输出。

3、发送模块

发送模块的作用的接收来自接收模块的八位并行数据,然后将这八位并行数据转换位串行数据,一位一位的发送出去 。

一帧共有10bit,第一位和最后一位分别位0和1,来自接收模块的八位数据在中间。

模块设计

我们将串口接收模块取名为 uart_tx,根据功能简介我们对整个设计要求有了大致的了解,其中设计的关键点是如何将串并行数据转化为串行数据并发送出去,也就是按照顺序将并行数据发送至 PC 机上。FPGA 发送的串行数据同样没有时钟,所以要和 PC 机接约定好使用相同的波特率,一个一个地发送比特,为了后面做串口的回环测试我们仍选择使用 9600bps的波特率

模块的Visio框图如下:

发送模块的波形图如下:

ps:注意发送同样是10bit的数据,当第一个bit_flag=1的时候开始发送起始位0,当bit_flag=9的时候发送停止位1,其余时间Tx保持空闲状态。

根据波形图可以写出如下代码:

module uart_tx
#(parameter uart_bps    = 'd9600,parameter    clk_freq    = 'd50_000_000
)
(input  wire            sys_clk         ,input  wire            sys_rst_n       ,input  wire    [7:0]   pi_data         ,input  wire            pi_data_flag    ,output reg             tx              );localparam    boud_cnt_max=clk_freq/uart_bps;reg             work_en     ;reg        [12:0]  boud_cnt    ;reg                bit_flag    ;reg        [3:0]   bit_cnt     ;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)work_en<=1'b0;else    if((bit_cnt==4'd9)&&(bit_flag==1'b1)) work_en<=1'b0;else if(pi_data_flag==1'b1)work_en<=1'b1;elsework_en<=work_en;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)boud_cnt<=13'd0;else  if((boud_cnt==boud_cnt_max-1'b1)||work_en==1'b0)boud_cnt<=13'd0;else if(work_en==1'b1)boud_cnt<=boud_cnt+1'b1;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)bit_flag<=1'b0;else  if(boud_cnt==13'd1)bit_flag<=1'b1;elsebit_flag<=1'b0;always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)bit_cnt<=4'd0;else   if((bit_cnt==4'd9)&&(bit_flag==1'b1))bit_cnt<=4'd0;else  if((bit_flag==1'b1)&&(work_en==1'b1))bit_cnt<=bit_cnt+1'b1;elsebit_cnt<=bit_cnt;
always@(posedge sys_clk or negedge sys_rst_n)if(sys_rst_n==1'b0)tx<=1'b1;else  if(bit_flag==1'b1)   case(bit_cnt)0:tx<=1'b0;1:tx<=pi_data[0];2:tx<=pi_data[1];3:tx<=pi_data[2];4:tx<=pi_data[3];5:tx<=pi_data[4];6:tx<=pi_data[5];7:tx<=pi_data[6];8:tx<=pi_data[7];9:tx<=1'b1;default:tx<=1'b1;endcaseendmodule

发送模块的仿真代码如下:

`timescale 1ns / 1nsmodule tb_uart_tx();reg         sys_clk     ;
reg         sys_rst_n   ;
reg [7:0]   pi_data     ;
reg         pi_data_flag;
wire        tx          ;initialbeginsys_clk=1'b1;sys_rst_n<=1'b0;#20sys_rst_n<=1'b1;end
always #10 sys_clk=~sys_clk;initialbeginpi_data<=8'b0;pi_data_flag<=1'b0;#200
//transport the data 0pi_data<=8'd0;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 1      pi_data<=8'd1;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 2      pi_data<=8'd2;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 3      pi_data<=8'd3;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 4      pi_data<=8'd4;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 5      pi_data<=8'd5;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 6      pi_data<=8'd6;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);
//transport the data 7      pi_data<=8'd7;pi_data_flag<=1'b1;#20pi_data_flag<=1'b0;#(5208*20*10);enduart_tx  inst
(.sys_clk       (sys_clk)   ,.sys_rst_n     (sys_rst_n) ,.pi_data       (pi_data)   ,.pi_data_flag  (pi_data_flag)  ,.tx                (tx)    );
endmodule

Vivado 仿真波形如下:

顶层模块

顶层模块代码如下:

module top_rs232(input   wire        sys_clk     ,input  wire        sys_rst_n   ,input  wire        rx          ,output wire        tx          );parameter uart_bps    = 14'd9600;parameter  clk_freq    = 26'd50_000_000;wire [7:0]   po_data;wire            po_flag;uart_rx
#(.uart_bps(uart_bps),.clk_freq(clk_freq)
)
uart_rx_inst
(
.sys_clk    (sys_clk)   ,
.sys_rst_n  (sys_rst_n) ,
.rx         (rx)    ,.po_data   (po_data)   ,
.po_flag    (po_flag)   );uart_tx
#(.uart_bps(uart_bps),.clk_freq(clk_freq)
)
uart_tx_inst1
(
.sys_clk            (sys_clk),
.sys_rst_n          (sys_rst_n),
.pi_data            (po_data),
.pi_data_flag       (po_flag),.tx                   (tx));  endmodule

仿真测试代码如下:

`timescale 1ns / 1nsmodule tb_top_rs232();reg       sys_clk     ;
reg     sys_rst_n   ;
reg     rx          ;wire   tx          ;initialbeginsys_clk=1'b1;sys_rst_n<=1'b0;rx<=1'b1;#20sys_rst_n<=1'b1;end
always #10 sys_clk=~sys_clk;initialbegin#200rx_byte();endtask  rx_byte();integer   j;for(j=0;j<8;j=j+1)rx_bit(j);
endtasktask rx_bit(input    [7:0]data
);integer i;for(i=0;i<10;i=i+1)begincase(i)0:rx<=1'b0;1:rx<=data[0];2:rx<=data[1];3:rx<=data[2];4:rx<=data[3];5:rx<=data[4];6:rx<=data[5];7:rx<=data[6];8:rx<=data[7];9:rx<=1'b1;endcase#(5208*20);end
endtasktop_rs232 inst(.sys_clk  (sys_clk)   ,
.sys_rst_n  (sys_rst_n) ,
.rx         (rx)    ,.tx            (tx));endmodule

Vivado仿真波形图如下:

串口RS232的学习相关推荐

  1. 串口通信协议简介—学习笔记

    串口通信协议简介-学习笔记 文章目录 串口通信协议简介-学习笔记 一.串口.COM口.UART口, TTL.RS-232.RS-485区别详解 1.物理接口形式 2.电平标准 2.1 **TTL** ...

  2. C#接收串口RS232的CD、CTS、DSR信号

    通过串口RS232和工控机连接,工控机可以接收设备(光电传感器)的IO信号 SerialPortSwitch portSwitch = new SerialPortSwitch("COM3& ...

  3. 通过编写串口助手工具学习MFC过程——(三)Unicode字符集的宽字符和多字节字符转换...

    通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...

  4. 串口RS232/485/UART转CANbus总线转换器网关CSM100模块CANCOM

    串口RS232/485/UART转CANbus总线转换器网关CSM100模块CANCOM CANUART-100T系列智能双向UART转CAN模块具有一路TTL UART串口通道和一路CAN通道,实现 ...

  5. java 串口 dtr rts_串口(RS232 RS485等)通讯中RTS/CTS,DTR/DSR的含义详解

    串口(RS232 RS485等)通讯中RTS/CTS,DTR/DSR的含义详解 先贴出缩写的含义: DTR – Data Terminal Ready DSR – Data Set Ready RTS ...

  6. 串口RS232 RS485最本质的区别!-!I2C通讯协议 最简单的总线通讯!-深入理解SPi通讯协议!

    5分钟看懂!串口RS232 RS485最本质的区别!-4分钟看懂!I2C通讯协议 最简单的总线通讯!-深入理解SPi通讯协议,5分钟看懂! 一.5分钟看懂!串口RS232 RS485最本质的区别! 二 ...

  7. 串口/RS232/RS485 简解

    前言 首先归一下类(借鉴网友的说法,还可深入了解): UART SPI IIC 这些是一类,一般只规定了高低电平的逻辑. RS232 RS485 RS422这些属于一类,规定了电平的电压范围,数据逻辑 ...

  8. 【整理】串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解

    [整理]串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解 RS232 crifan 7年前 (2013-10-17) 14942浏览 0评论 [背景] 之前就折腾过很多关 ...

  9. 串口通讯助手学习交流

    串口通讯助手学习交流 最近花了一些时间用C#写了一个串口通讯助手,因为工作中用到了串口通信,最开始是用C++中的MFC写的,后面觉得C#更方便一些,使用的都是SerialPort类:而且我的C#也是自 ...

  10. 通过JAVA与串口(RS232)通信实例

    通过JAVA与串口(RS232)通信实例 博客分类: J2SE JavaVBthread  最近了解到的需求是需要需激光打刻机进行(RS232)串口通信,  这里使用的是RXTX开源包实现的.  之前 ...

最新文章

  1. C/C++中constkeyword
  2. rs485编程java_串行编程RS485
  3. apache2配置支持php7,Ubuntu14.04服务器环境下配置PHP7.0+Apache2+Mysql5.7的方法
  4. Selenium API-WebDriver 方法
  5. 推荐系统系列教程之十四:经典模型融合方法----线性模型和树模型的组合拳
  6. 真正的程序员(转贴)
  7. Bailian4147 汉诺塔问题(Hanoi)
  8. css3中的文字效果
  9. Session销毁方式
  10. wow模型修改器_wow模型修改器
  11. mysql5.6卸载干净_Mysql完全干净卸载教程
  12. rx590 黑苹果 无货_RX 590显卡在Mojave黑苹果系统中识别为RX 480问题的解决一例。...
  13. 机器人拉格朗日动力学应用公式详解
  14. IDEA的界面布局折腾(Main Menu主菜单栏消失的问题)
  15. 51单片机——人体红外感应报警
  16. dr优先级默认_OSPF路由器不能成为DR/BDR唯一的方法:DR优先级=0
  17. scikit-learn学习系列 - 广义线性模型
  18. Dpark的安装测试
  19. RabbitMQ学习笔记(一)
  20. PTA_2019春_电话聊天狂人

热门文章

  1. 清华同方服务器硬盘更换,清华同方bios设置硬盘教程
  2. 【Linux】Linux系统编程入门
  3. WooyunWifi路由器
  4. 台式计算机怎么查看网卡号,怎么查看台式机电脑有没有无线网卡?
  5. 【STM32Cube笔记】4-STM32Cube配置时钟设置
  6. 毕业实用统计模型(一)——时间序列
  7. 物联网需要php和嵌入式吗,物联网与嵌入式两者是什么关系
  8. 语义分割-FCNs in the wild: Pixel-level adversarial and constraint-based adaptation 对抗方法实现不同数据集语义分割
  9. 我在华为外包一年的经历分享。
  10. potato电脑版连接不上_potato chat正式版PC端安装教程