• 协议——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教程

  • 相关阅读:
    【转】ThinkPHP 页面跳转
    thinkphp中select()和find()的区别
    (Python)异常处理try...except、raise
    python中try except处理程序异常的方法
    SNMP消息传输机制
    公钥私钥+数字证书原理
    转:使用python的Flask实现一个RESTful API服务器端
    转:xxe attack学习
    转:php防止sql注入的一点心得
    转:在 Ubuntu 上使用 Nginx 部署 Flask 应用
  • 原文地址:https://www.cnblogs.com/xianyufpga/p/11086676.html
Copyright © 2020-2023  润新知