zoukankan      html  css  js  c++  java
  • 协议——UART(RS232)

    一、UART简介

      UART(universal asynchronous receiver-transmitter)是一种采用异步串行通信方式的通用异步收发传输器。一般来说,UART总是和RS232成对出现,那RS232又是什么呢? RS232也就是我们计算机上的串口,它的全称是EIA-RS-232C (简称232,或者是RS232 )。其中EIA(Electronic Industry Association)代表美国电子工业协会,RS是Recommended Standard的缩写,代表推荐标准,232 是标识符,C表示修改次数,它被广泛用于计算机串行接口外设连接。如果你的计算机上还有串口的话,那么你就可以在主机箱后面看到RS232的接口:

      随着时代的发展,这种借口已经很少用了,取而代之的是“USB转串口”,功能和原先一样,但接口更高效了。

      串口的主要功能为:在发送数据时将并行数据转换成串行数据进行传输,在接收数据时将接收到的串行数据转换成并行数据。这应该是大多数人接触电子后学习到的第一个通信协议吧。

    二、通信格式

      下面来说说串口的具体要点:

    1.传输时序

      UART串口通信需要两个信号线来实现,一根用于串口发送,另外一根负责串口接收。一开始高电平,然后拉低表示开始位,接着8个数据位,然后校验位,最后拉高表示停止位,并且进入空闲状态,等待下一次的数据传输。

      很多时候我们的校验位是允许省略的,所以协议就变成了:开始+数据+停止。

    2.传输速率:波特率

      串口通信的速率用波特率表示,它表示麦苗传输二进制数据的位数,单位是bps(位/秒)。常用的波特率有9600、19200、35400、57600以及115200等。

      FPGA开发串口时,设计波特率的方法:FPGA的时钟频率/波特率。例如我的FPGA开发板时钟频率为50Mhz,即50_000_000hz,我想使用的波特率为9600bps,因此我需要的计数为:50000000/9600≈5208。

    三、串口回环设计

      现在用FPGA开发板做一个串口回环的实验,要求是PC端通过串口助手发送数据给FPGA,FPGA接收到数据后返回给PC端,并在串口助手处显示数值。即串口助手发什么就能收回什么。实验框图如下:

    1.uart_rx

      1 //**************************************************************************
      2 // *** 名称 : uart_rx.v
      3 // *** 作者 : xianyu_FPGA
      4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
      5 // *** 日期 : 2019-01-10
      6 // *** 描述 : 串口接收模块,计数9.5下,其中停止位0.5下
      7 //            因为串口助手发送本次停止位和下次开始位中间没有留空闲位
      8 //            若计满10下,则才结束本次传输下次数据就来了,会来不及接收
      9 //**************************************************************************
     10 
     11 module uart_rx
     12 //========================< 参数 >==========================================
     13 #(
     14 parameter  CLK              = 50_000_000        , //系统时钟,50Mhz
     15 parameter  BPS              = 9600              , //波特率
     16 parameter  BPS_CNT          = CLK/BPS             //波特率计数
     17 )
     18 //========================< 端口 >==========================================
     19 (
     20 input   wire                clk                 , //时钟,50Mhz
     21 input   wire                rst_n               , //复位,低电平有效
     22 input   wire                din                 , //输入数据
     23 output  reg   [7:0]         dout                , //输出数据
     24 output  reg                 dout_vld              //输出数据的有效指示
     25 );
     26 //========================< 信号 >==========================================
     27 reg                         rx0                 ;
     28 reg                         rx1                 ;
     29 reg                         rx2                 ;
     30 wire                        rx_en               ;
     31 reg                         flag                ;
     32 reg   [15:0]                cnt0                ;
     33 wire                        add_cnt0            ;
     34 wire                        end_cnt0            ;
     35 reg   [ 3:0]                cnt1                ;
     36 wire                        add_cnt1            ;
     37 wire                        end_cnt1            ;
     38 reg   [ 7:0]                data                ;
     39 
     40 //==========================================================================
     41 //==    消除亚稳态 + 下降沿检测
     42 //==========================================================================
     43 always @(posedge clk or negedge rst_n) begin
     44     if(!rst_n) begin
     45         rx0 <= 1;
     46         rx1 <= 1;
     47         rx2 <= 1;
     48     end
     49     else begin
     50         rx0 <= din;
     51         rx1 <= rx0;
     52         rx2 <= rx1;
     53     end
     54 end
     55 
     56 assign rx_en = rx2 && ~rx1;
     57 
     58 //==========================================================================
     59 //==    接收状态指示
     60 //==========================================================================
     61 always @(posedge clk or negedge rst_n) begin
     62     if(!rst_n)
     63         flag <= 0;
     64     else if(rx_en)
     65         flag <= 1;
     66     else if(end_cnt1)
     67         flag <= 0;
     68 end
     69 
     70 //==========================================================================
     71 //==    波特率计数
     72 //==========================================================================
     73 always @(posedge clk or negedge rst_n) begin
     74     if(!rst_n)
     75         cnt0 <= 0;
     76     else if(add_cnt0) begin
     77         if(end_cnt0)
     78             cnt0 <= 0;
     79         else
     80             cnt0 <= cnt0 + 1;
     81     end
     82 end
     83 
     84 assign add_cnt0 = flag;
     85 assign end_cnt0 = cnt0== BPS_CNT-1 || end_cnt1;
     86 
     87 //==========================================================================
     88 //==    开始1位(不接收) + 数据8位 + 停止0.5位(不接收),共10位
     89 //==========================================================================
     90 always @(posedge clk or negedge rst_n) begin 
     91     if(!rst_n)
     92         cnt1 <= 0;
     93     else if(add_cnt1) begin
     94         if(end_cnt1)
     95             cnt1 <= 0;
     96         else
     97             cnt1 <= cnt1 + 1;
     98     end
     99 end
    100 
    101 assign add_cnt1 = end_cnt0;
    102 assign end_cnt1 = cnt1==10-1 && cnt0==BPS_CNT/2-1;
    103 
    104 //==========================================================================
    105 //==    缓存数据
    106 //==========================================================================
    107 always @ (posedge clk or negedge rst_n)begin
    108     if(!rst_n)
    109         data <= 8'd0;
    110     else if(cnt1>=1 && cnt1<=8 && cnt0==BPS_CNT/2-1) //中间采样
    111         data[cnt1-1] <= rx2;                         //或 dout <= {rx2,dout[7:1]};
    112 end
    113 
    114 //==========================================================================
    115 //==    输出数据
    116 //==========================================================================
    117 always @ (posedge clk or negedge rst_n)begin
    118     if(!rst_n)
    119         dout <= 0;
    120     else if(end_cnt1)   
    121         dout <= data;
    122 end
    123 
    124 always @ (posedge clk or negedge rst_n)begin
    125     if(!rst_n)
    126         dout_vld <= 0;
    127     else if(end_cnt1)   
    128         dout_vld <= 1;  
    129     else    
    130         dout_vld <= 0;
    131 end
    132 
    133 
    134 
    135 endmodule

    2.uart_tx

      1 //**************************************************************************
      2 // *** 名称 : uart_tx.v
      3 // *** 作者 : xianyu_FPGA
      4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
      5 // *** 日期 : 2019-01-10
      6 // *** 描述 : 串口接收模块,计数9.5下,其中停止位0.5下
      7 //            因为极端情况是本次停止位和下次开始位中间没有留空闲位
      8 //            若计满10下,则才结束本次传输下次数据就来了,会来不及发送
      9 //**************************************************************************
     10 
     11 module uart_tx
     12 //========================< 参数 >==========================================
     13 #(
     14 parameter  CLK              = 50_000_000        , //系统时钟,50Mhz
     15 parameter  BPS              = 9600              , //波特率
     16 parameter  BPS_CNT          = CLK/BPS             //波特率计数
     17 )
     18 //========================< 端口 >==========================================
     19 (
     20 input   wire                clk                 , //时钟,50Mhz
     21 input   wire                rst_n               , //复位,低电平有效
     22 input   wire  [7:0]         din                 , //输入数据
     23 input   wire                din_vld             , //输入数据的有效指示
     24 output  reg                 dout                  //输出数据
     25 );
     26 //========================< 信号 >==========================================
     27 reg                         flag                ;
     28 reg   [ 7:0]                din_tmp             ;
     29 reg   [15:0]                cnt0                ;
     30 wire                        add_cnt0            ;
     31 wire                        end_cnt0            ;
     32 reg   [ 3:0]                cnt1                ;
     33 wire                        add_cnt1            ;
     34 wire                        end_cnt1            ;
     35 wire  [ 9:0]                data                ;
     36 
     37 //==========================================================================
     38 //==    数据暂存(din可能会消失,暂存住)
     39 //==========================================================================
     40 always @ (posedge clk or negedge rst_n) begin
     41     if(!rst_n)
     42         din_tmp <=8'd0;
     43     else if(din_vld)
     44         din_tmp <= din;
     45 end
     46 
     47 //==========================================================================
     48 //==    发送状态指示
     49 //==========================================================================
     50 always  @(posedge clk or negedge rst_n)begin
     51     if(!rst_n)
     52         flag <= 0;
     53     else if(din_vld)
     54         flag <= 1;
     55     else if(end_cnt1)
     56         flag <= 0;
     57 end
     58 
     59 //==========================================================================
     60 //==    波特率计数
     61 //==========================================================================
     62 always @(posedge clk or negedge rst_n) begin
     63     if(!rst_n)
     64         cnt0 <= 0;
     65     else if(add_cnt0) begin
     66         if(end_cnt0)
     67             cnt0 <= 0;
     68         else
     69             cnt0 <= cnt0 + 1;
     70     end
     71 end
     72 
     73 assign add_cnt0 = flag;
     74 assign end_cnt0 = cnt0== BPS_CNT-1 || end_cnt1;
     75 
     76 //==========================================================================
     77 //==    开始1位 + 数据8位 + 停止0.5位,共10位
     78 //==========================================================================
     79 always @(posedge clk or negedge rst_n) begin 
     80     if(!rst_n)
     81         cnt1 <= 0;
     82     else if(add_cnt1) begin
     83         if(end_cnt1)
     84             cnt1 <= 0;
     85         else
     86             cnt1 <= cnt1 + 1;
     87     end
     88 end
     89 
     90 assign add_cnt1 = end_cnt0;
     91 assign end_cnt1 = cnt1==10-1 && cnt0==BPS_CNT/2-1;
     92 
     93 //==========================================================================
     94 //==    数据输出(用case语句也行)
     95 //==========================================================================
     96 assign data = {1'b1,din_tmp,1'b0};  //停止,数据,开始
     97 
     98 always @(posedge clk or negedge rst_n) begin
     99     if(!rst_n)
    100         dout <= 1'b1;
    101     else if(flag)
    102         dout <= data[cnt1];
    103 end
    104 
    105 
    106 
    107 endmodule

    3.top层

     1 //**************************************************************************
     2 // *** 名称 : uart_top.v
     3 // *** 作者 : xianyu_FPGA
     4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
     5 // *** 日期 : 2019-01-10
     6 // *** 描述 : 串口实验顶层文件
     7 //**************************************************************************
     8 
     9 module uart_top
    10 //========================< 端口 >==========================================
    11 (
    12 input  wire                 clk                 , //时钟,50Mhz
    13 input  wire                 rst_n               , //复位,低电平有效
    14 input  wire                 uart_rx             , //FPGA通过串口接收的数据
    15 output wire                 uart_tx               //FPGA通过串口发送的数据
    16 );
    17 
    18 //========================< 连线 >==========================================
    19 wire [7:0]                  data                ;
    20 wire                        data_vld            ;
    21 
    22 //==========================================================================
    23 //==    模块例化
    24 //==========================================================================
    25 uart_rx
    26 #(
    27     .BPS_CNT                (52                 )    //仿真用
    28 )
    29 u_uart_rx
    30 (
    31     .clk                    (clk                ),
    32     .rst_n                  (rst_n              ),
    33     .din                    (uart_rx            ),
    34     .dout                   (data               ),
    35     .dout_vld               (data_vld           )
    36 );
    37 
    38 uart_tx
    39 #(
    40     .BPS_CNT                (52                 )   //仿真用
    41 )
    42 u_uart_tx
    43 (
    44     .clk                    (clk                ),
    45     .rst_n                  (rst_n              ),
    46     .din_vld                (data_vld           ),
    47     .din                    (data               ),
    48     .dout                   (uart_tx            )
    49 );
    50 
    51 
    52 
    53 endmodule

    四、仿真调试

    1、testbench

     1 `timescale 1ns/1ps  //时间精度
     2 `define    Clock 20 //时钟周期
     3 
     4 module uart_top_tb;
     5 
     6 //========================< 端口 >==========================================
     7 reg                         clk                 ; //时钟,50Mhz
     8 reg                         rst_n               ; //复位,低电平有效
     9 reg                         uart_rx             ;
    10 wire                        uart_tx             ;
    11 
    12 //==========================================================================
    13 //==    模块例化
    14 //==========================================================================
    15 uart_top u_uart_top
    16 (
    17     .clk                    (clk                ),
    18     .rst_n                  (rst_n              ),
    19     .uart_rx                (uart_rx            ),
    20     .uart_tx                (uart_tx            )
    21 );
    22 
    23 //==========================================================================
    24 //==    时钟信号和复位信号
    25 //==========================================================================
    26 initial begin
    27     clk = 1;
    28     forever
    29         #(`Clock/2) clk = ~clk;
    30 end
    31 
    32 initial begin
    33     rst_n = 0; #(`Clock*20+1);
    34     rst_n = 1;
    35 end
    36 
    37 //==========================================================================
    38 //==    task任务
    39 //==========================================================================
    40 reg  [7:0]              mem[15:0]           ; //位宽为8,深度为16个数据
    41 integer                 i                   ;
    42 integer                 j                   ;
    43 
    44 //读取外部数据
    45 initial $readmemh("./data.txt",mem);
    46 
    47 //位赋值
    48 task rx_bit
    49 (
    50     input [7:0]         data
    51 );
    52     begin
    53         for(i=0;i<=9;i=i+1) begin   //10个bit为
    54             case(i)
    55                  0: uart_rx = 1'b0;
    56                  1: uart_rx = data[i-1];
    57                  2: uart_rx = data[i-1];
    58                  3: uart_rx = data[i-1];
    59                  4: uart_rx = data[i-1];
    60                  5: uart_rx = data[i-1];
    61                  6: uart_rx = data[i-1];
    62                  7: uart_rx = data[i-1];
    63                  8: uart_rx = data[i-1];
    64                  9: uart_rx = 1'b1;
    65             endcase 
    66             #1040; //一个完整波特延时:52*20=1040
    67         end        //考虑到空闲位,也可以设置得1040稍大一些
    68     end
    69 endtask 
    70 
    71 //字节赋值
    72 task rx_byte;
    73     begin
    74         for(j=0;j<=15;j=j+1) //16个byte数据
    75             rx_bit(mem[j]);
    76     end
    77 endtask
    78 
    79 //==========================================================================
    80 //==    调用task
    81 //==========================================================================
    82 initial begin
    83     #(`Clock*20+1);
    84     rx_byte();
    85 end
    86 
    87 initial begin
    88     #180000;
    89     $stop;
    90 end
    91 
    92 endmodule

    2、data.txt

      testbench中调用了一个 data.txt 文本文档,里面存储了此次仿真的16个数据,将其放置到 Modelsim 软件的工程目录中(非 work)即可。

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    a
    b
    c
    d
    e
    f

    3、仿真波形

      由波形可以看到,本次设计应该是成功的。

    五、上板验证

      本次上位机采用友善串口助手,无校验位,停止位为1。当串口助手发送数据给FPGA后,FPGA很快又将原数据返回给上位机。

      经上板验证,本次设计成功!

    参考资料:

    [1]明德扬FPGA教程

    [2]正点原子FPGA教程

    [2]威三学院FPGA教程

  • 相关阅读:
    为图片指定区域添加链接
    数值取值范围问题
    【leetcode】柱状图中最大的矩形(第二遍)
    【leetcode 33】搜索旋转排序数组(第二遍)
    【Educational Codeforces Round 81 (Rated for Div. 2) C】Obtain The String
    【Educational Codeforces Round 81 (Rated for Div. 2) B】Infinite Prefixes
    【Educational Codeforces Round 81 (Rated for Div. 2) A】Display The Number
    【Codeforces 716B】Complete the Word
    一个简陋的留言板
    HTML,CSS,JavaScript,AJAX,JSP,Servlet,JDBC,Structs,Spring,Hibernate,Xml等概念
  • 原文地址:https://www.cnblogs.com/xianyufpga/p/11086676.html
Copyright © 2011-2022 走看看