• 串口UART学习笔记(一)


            买了一个开发板学习FPGA,找到的各种东西就记录在这个博客里了,同时也方便把自己不会的问题找到的结果记录一下,都是自己手打,所以可能说的话不那么严谨,不那么精准,看到的人要带着自己的思考去看,记住尽信书不如无书,哈哈哈。。。。。。

             一、UART是什么?

             UART是一种通用串行数据总线,也就是用于数据传输。是用于主机与辅助设备进行通信。这里的主机理解为计算机,计算机内部采用并行数据,辅助设备采用串行数据。中间需要设备进行数据转换,这也决定了UART工作原理是将传输数据的每个字符一位接一位地传输。UART采用异步传输模式,异步传输将比特分成小组进行传送,小组可以是8位的1个字符或更长。发送方可以在任何时刻发送这些比特组,而接收方从不知道它们会在什么时候到达。例如计算机键盘与主机的通信。UART用于远距离传输较为适合。可以数据同时发送和接收。

                                                    1,异步传输是面向字符的传输,而同步传输是面向比特的传输。

                                                    2,异步传输的单位是字符同步传输的单位是帧。
                                                    3,异步传输通过字符起始和停止码抓住再同步的机会,而同步传输则是在数据中抽取同步信息。
                                                    4,异步传输对时序的要求较低,同步传输往往通过特定的时钟线路协调时序。
                                                    5,异步传输相对于同步传输效率较低。
    我的理解是对时序要求是采用UART的原因,数据到达需要给出到达信号也解释了UART采用8位1字符的串行数据输出模式,也解释了协议中起始位与停止位的重要性。
    uart指通用异步收发传输器,本质上是硬件,用来异步传输数据。RS232是一种物理层协议,规定了特定的接口标准。https://www.zhihu.com/question/22632011 
     
    进行数据传输时,需要考虑时钟同步问题,传输速率有一个很重要的概念。波特率是衡量资料传送速率的指标。表示每秒钟传送的符号数(symbol)。一个符号代表的信息量(比特数)与符号的阶数有关。例如资料传送速率为120字符/秒,传输使用256阶符号,每个符号代表8bit,则波特率就是120baud,比特率是120*8=960bit/s。位 bit (比特)(Binary Digits):存放一位二进制数,即 0 或 1,最小的存储单位。字节Byte:8个二进制位为一个字节(B),最常用的单位。
    常用波特率9600,19200,38400,115200等。
    其中各位的意义如下: 
    起始位:先发出一个逻辑”0”信号,表示传输字符的开始。 
    数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。 
    校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验)。 
    停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 
    空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。

         二、UART串口通信一般包括部分
        任何程序都包括一个主控制程序,因为是双向通信,还需要串口发送程序,串口接收程序,时钟控制程序。举例来分别解释四个程序的代码。
          这里采用50MHz的系统时钟,产生UART时钟信号产生和发送的波特率为9600bps。为了保证采样的时候不会发生误码或者滑码,我们对于一位数据不只采用一个时钟周期进行数据采集,而采用16个时钟周期。那么就需要对时钟进行分频处理。50,000,000/(16*9600),分频系数取整为326 。偶数分频比较简单,此处采用了326,至于奇数分频以及如何做出占空比为50%以后整理。
     
     
     1 `timescale 1ns / 1ps
     2 //////////////////////////////////////////////////////////////////////////////////
     3 // Module Name:    clkdiv
     4 // 产生一个波特率9600的16倍频的时钟,9600*16= 153600
     5 // 相当于50MHz的326分频,50000000/153600=326
     6 //////////////////////////////////////////////////////////////////////////////////
     7 module clkdiv(clk50, clkout);
     8 input clk50;              //系统时钟
     9 output clkout;          //采样时钟输出
    10 reg clkout;
    11 reg [15:0] cnt;
    12 
    13 //分频进程,对50Mhz的时钟326分频
    14 always @(posedge clk50)   
    15 begin
    16   if(cnt == 16'd162)
    17   begin
    18     clkout <= 1'b1;
    19     cnt <= cnt + 16'd1;
    20   end
    21   else if(cnt == 16'd325)
    22   begin
    23     clkout <= 1'b0;
    24     cnt <= 16'd0;
    25   end
    26   else
    27   begin
    28     cnt <= cnt + 16'd1;
    29   end
    30 end
    31 endmodule

    分频比较简单,我写的另一个,

     1  `timescale 1ns / 1ps
     2 
     3  module clkdiv(clk50, clkout);
     4  input clk50;              //系统时钟
     5  output clkout;          //采样时钟输出
     6  reg clkout;
     7  reg [15:0] cnt;
     8 
     9 always @(posedge clk50)
    10     begin
    11       if (cnt < 16'd162)
    12       cnt <= cnt + 16'b1;
    13      else  if  (cnt == 16'd162)
    14       begin
    15           clkout <= ~clkout;
    16           cnt <= 16'b0;
    17       end
    18      end

    三、串口发送程序

         UART采用异步传输,就涉及起始位与停止位,下面是代码例子,我的总结用红笔标出。

      1 `timescale 1ns / 1ps
      2 //////////////////////////////////////////////////////////////////////////////////
      3 // Module Name:    uarttx 
      4 // 说明:16个clock发送一个bit,
      5 //////////////////////////////////////////////////////////////////////////////////
      6 module uarttx(clk, datain, wrsig, idle, tx);
      7 input clk;                //UART时钟
      8 input [7:0] datain;       //需要发送的数据
      9 input wrsig;              //发送命令,上升沿有效
     10 output idle;              //线路状态指示,高为线路忙,低为线路空闲
     11 output tx;                //发送数据信号
     12 reg idle, tx;
     13 reg send;
     14 reg wrsigbuf, wrsigrise;
     15 reg presult;
     16 reg[7:0] cnt;             //计数器
     17 parameter paritymode = 1'b0;
     18 
     19 //检测发送命令是否有效,判断wrsig的上升沿            //先检测发送命令是否有效,然后判断线路状态
     20 always @(posedge clk)
     21 begin
     22    wrsigbuf <= wrsig;
     23    wrsigrise <= (~wrsigbuf) & wrsig;         //边沿检测,检测发送命令
     24 end
     25 
     26 always @(posedge clk)
     27 begin
     28   if (wrsigrise &&  (~idle))  //当发送命令有效且线路为空闲时,启动新的数据发送进程
     29   begin
     30      send <= 1'b1;
     31   end
     32   else if(cnt == 8'd168)      //一帧资料发送结束
     33   begin
     34      send <= 1'b0;
     35   end
     36 end
     37 
     38 /////////////////////////////////////////////////////////////////////////
     39 //使用168个时钟发送一个数据(起始位、8位数据、奇偶校验位、停止位),每位占用16个时钟//    //停止位为8个时钟周期
     40 ////////////////////////////////////////////////////////////////////////
     41 always @(posedge clk)
     42 begin
     43   if(send == 1'b1)  begin
     44     case(cnt)                 //tx变低电平产生起始位,0~15个时钟为发送起始位
     45     8'd0: begin
     46          tx <= 1'b0;
     47          idle <= 1'b1;
     48          cnt <= cnt + 8'd1;
     49     end
     50     8'd16: begin
     51          tx <= datain[0];    //发送数据位的低位bit0,占用第16~31个时钟
     52          presult <= datain[0]^paritymode;     //奇偶校验位的获取
    53 idle <= 1'b1; 54 cnt <= cnt + 8'd1; 55 end 56 8'd32: begin 57 tx <= datain[1]; //发送数据位的第2位bit1,占用第47~32个时钟 58 presult <= datain[1]^presult; 59 idle <= 1'b1; 60 cnt <= cnt + 8'd1; 61 end 62 8'd48: begin 63 tx <= datain[2]; //发送数据位的第3位bit2,占用第63~48个时钟 64 presult <= datain[2]^presult; 65 idle <= 1'b1; 66 cnt <= cnt + 8'd1; 67 end 68 8'd64: begin 69 tx <= datain[3]; //发送数据位的第4位bit3,占用第79~64个时钟 70 presult <= datain[3]^presult; 71 idle <= 1'b1; 72 cnt <= cnt + 8'd1; 73 end 74 8'd80: begin 75 tx <= datain[4]; //发送数据位的第5位bit4,占用第95~80个时钟 76 presult <= datain[4]^presult; 77 idle <= 1'b1; 78 cnt <= cnt + 8'd1; 79 end 80 8'd96: begin 81 tx <= datain[5]; //发送数据位的第6位bit5,占用第111~96个时钟 82 presult <= datain[5]^presult; 83 idle <= 1'b1; 84 cnt <= cnt + 8'd1; 85 end 86 8'd112: begin 87 tx <= datain[6]; //发送数据位的第7位bit6,占用第127~112个时钟 88 presult <= datain[6]^presult; 89 idle <= 1'b1; 90 cnt <= cnt + 8'd1; 91 end 92 8'd128: begin 93 tx <= datain[7]; //发送数据位的第8位bit7,占用第143~128个时钟 94 presult <= datain[7]^presult; 95 idle <= 1'b1; 96 cnt <= cnt + 8'd1; 97 end 98 8'd144: begin 99 tx <= presult; //发送奇偶校验位,占用第159~144个时钟 //将计算结果作为奇偶校验码输出 100 presult <= datain[0]^paritymode; //无意思,此行计算结果无用,多余
    101 idle <= 1'b1; 102 cnt <= cnt + 8'd1; 103 end 104 8'd160: begin 105 tx <= 1'b1; //发送停止位,占用第160~167个时钟 106 idle <= 1'b1; 107 cnt <= cnt + 8'd1; 108 end 109 8'd168: begin 110 tx <= 1'b1; 111 idle <= 1'b0; //一帧资料发送结束 112 cnt <= cnt + 8'd1; 113 end 114 default: begin 115 cnt <= cnt + 8'd1; 116 end 117 endcase 118 end 119 else begin 120 tx <= 1'b1; 121 cnt <= 8'd0; 122 idle <= 1'b0; 123 end 124 end 125 endmodule

    四、串口接收程序

          成为UART接收信号,将一位一位的串口数据转化为并行数据。

      1 `timescale 1ns / 1ps
      2 //////////////////////////////////////////////////////////////////////////////////
      3 // Module name    uartrx.v
      4 // 说明:          16个clock接收一个bit,16个时钟采样,取中间的采样值
      5 //////////////////////////////////////////////////////////////////////////////////
      6 module uartrx(clk, rx, dataout, rdsig, dataerror, frameerror);
      7 input clk;             //采样时钟
      8 input rx;              //UART数据输入
      9 output dataout;        //接收数据输出
     10 output rdsig;          //接收数据有效,高说明接收到一个字节 ,来区分数据处于接收状态或者接收控制信号
     11 output dataerror;      //数据出错指示
     12 output frameerror;     //帧出错指示
     13 
     14 reg[7:0] dataout;
     15 reg rdsig, dataerror;
     16 reg frameerror;
     17 reg [7:0] cnt;
     18 reg rxbuf, rxfall, receive;
     19 parameter paritymode = 1'b0;
     20 reg presult, idle;
     21 
     22 always @(posedge clk)   //检测线路rx的下降沿, 线路空闲的时候rx为高电平
     23 begin
     24   rxbuf <= rx;
     25   rxfall <= rxbuf & (~rx);      //下降沿检测,检测是否接收到接收信号
    26 end 27 28 always @(posedge clk) 29 begin 30 if (rxfall && (~idle)) //检测到线路的下降沿并且原先线路为空闲,启动接收数据进程 31 begin 32 receive <= 1'b1; //开始接收数据 33 end 34 else if(cnt == 8'd168) //接收数据完成 35 begin 36 receive <= 1'b0; 37 end 38 end 39 40 ///////////////////////////////////////////////////////////////////////// 41 //使用176个时钟接收一个数据(起始位、8位数据、奇偶校验位、停止位),每位占用16个时钟// 42 //////////////////////////////////////////////////////////////////////// 43 always @(posedge clk) 44 begin 45 if(receive == 1'b1) 46 begin 47 case (cnt) 48 8'd0: //0~15个时钟为接收第一个比特,起始位 49 begin 50 idle <= 1'b1; 51 cnt <= cnt + 8'd1; 52 rdsig <= 1'b0; 53 end 54 8'd24: //16~31个时钟为第1个bit数据,取中间第24个时钟的采样值 //在发送程序中,第0位数据在16个时钟周期后开始传输,接收过程中, 55 begin //从第24个时钟周期开始接收第0位数据,保证信号被采集。 56 idle <= 1'b1; 57 dataout[0] <= rx; 58 presult <= paritymode^rx; 59 cnt <= cnt + 8'd1; 60 rdsig <= 1'b0; 61 end 62 8'd40: //47~32个时钟为第2个bit数据,取中间第40个时钟的采样值 63 begin 64 idle <= 1'b1; 65 dataout[1] <= rx; 66 presult <= presult^rx; 67 cnt <= cnt + 8'd1; 68 rdsig <= 1'b0; 69 end 70 8'd56: //63~48个时钟为第3个bit数据,取中间第56个时钟的采样值 71 begin 72 idle <= 1'b1; 73 dataout[2] <= rx; 74 presult <= presult^rx; 75 cnt <= cnt + 8'd1; 76 rdsig <= 1'b0; 77 end 78 8'd72: //79~64个时钟为第4个bit数据,取中间第72个时钟的采样值 79 begin 80 idle <= 1'b1; 81 dataout[3] <= rx; 82 presult <= presult^rx; 83 cnt <= cnt + 8'd1; 84 rdsig <= 1'b0; 85 end 86 8'd88: //95~80个时钟为第5个bit数据,取中间第88个时钟的采样值 87 begin 88 idle <= 1'b1; 89 dataout[4] <= rx; 90 presult <= presult^rx; 91 cnt <= cnt + 8'd1; 92 rdsig <= 1'b0; 93 end 94 8'd104: //111~96个时钟为第6个bit数据,取中间第104个时钟的采样值 95 begin 96 idle <= 1'b1; 97 dataout[5] <= rx; 98 presult <= presult^rx; 99 cnt <= cnt + 8'd1; 100 rdsig <= 1'b0; 101 end 102 8'd120: //127~112个时钟为第7个bit数据,取中间第120个时钟的采样值 103 begin 104 idle <= 1'b1; 105 dataout[6] <= rx; 106 presult <= presult^rx; 107 cnt <= cnt + 8'd1; 108 rdsig <= 1'b0; 109 end 110 8'd136: //143~128个时钟为第8个bit数据,取中间第136个时钟的采样值 111 begin 112 idle <= 1'b1; 113 dataout[7] <= rx; 114 presult <= presult^rx; 115 cnt <= cnt + 8'd1; 116 rdsig <= 1'b1; //接收数据有效 117 end 118 8'd152: //159~144个时钟为接收奇偶校验位,取中间第152个时钟的采样值 119 begin 120 idle <= 1'b1; 121 if(presult == rx) 122 dataerror <= 1'b0; 123 else 124 dataerror <= 1'b1; //如果奇偶校验位不对,表示数据出错 125 cnt <= cnt + 8'd1; 126 rdsig <= 1'b1; 127 end 128 8'd168: //160~175个时钟为接收停止位,取中间第168个时钟的采样值 129 begin 130 idle <= 1'b1; 131 if(1'b1 == rx) 132 frameerror <= 1'b0; 133 else 134 frameerror <= 1'b1; //如果没有接收到停止位,表示帧出错 135 cnt <= cnt + 8'd1; 136 rdsig <= 1'b1; 137 end 138 default: 139 begin 140 cnt <= cnt + 8'd1; 141 end 142 endcase 143 end 144 else 145 begin 146 cnt <= 8'd0; 147 idle <= 1'b0; 148 rdsig <= 1'b0; 149 end 150 end 151 endmodule

    五、控制程序

         这里主要学习程序的调用,如何用总的控制程序完成UART数据传输。

     1 `timescale 1ns / 1ps
     2 //////////////////////////////////////////////////////////////////////////////////
     3 // Module Name:    uart_test 
     4 // 
     5 //////////////////////////////////////////////////////////////////////////////////
     6 module uart_test(clk50, rx, tx, reset);
     7 input clk50;
     8 input reset;
     9 input rx;
    10 output tx;
    11 
    12 wire clk;       //clock for 9600 uart port
    13 wire [7:0] txdata,rxdata;     //串口发送数据和串口接收数据
    14 
    15 
    16 
    17 //产生时钟的频率为16*9600
    18 clkdiv u0 (
    19         .clk50                   (clk50),               //50Mhz的晶振输入                     
    20         .clkout                  (clk)                  //16倍波特率的时钟                        
    21  );
    22 
    23 //串口接收程序
    24 uartrx u1 (
    25         .clk                     (clk),                 //16倍波特率的时钟 
    26       .rx                       (rx),                     //串口接收
    27         .dataout                 (rxdata),              //uart 接收到的数据,一个字节                     
    28       .rdsig                   (rdsig),               //uart 接收到数据有效 
    29         .dataerror               (),
    30         .frameerror              ()
    31 );
    32 
    33 //串口发送程序
    34 uarttx u2 (
    35         .clk                     (clk),                  //16倍波特率的时钟  
    36        .tx                      (tx),                      //串口发送
    37         .datain                  (txdata),               //uart 发送的数据   
    38       .wrsig                   (wrsig),                //uart 发送的数据有效  
    39       .idle                    ()     
    40     
    41  );

    54 endmodule

    就像c语言里调用子函数一样,每个 模块是并行运行的, 各个模块连接完成整个系统需要一个顶层文件(top-module) 。 顶层文件 通过调用、连接低层模块的实例来实现复杂的功能。

    学习UART通信,最主要还是理解异步和串口这两个东西,方便远距离传输,串口一位一位传输,牺牲了时间降低时序要求。

     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    ORACLE 11.2.0.4 OCR VOTING DISK 模拟恢复场景
    Oracle Enterprise Linux 6.4 下配置vncserver
    Oracle Enterprise Linux 6.4 下挂载ISCSI 设备
    关于Solaris 的磁盘的分区
    【Google Earth】pro之视频录制
    【爱江山越野跑】ITRA积分认证流程
    android发送邮件
    android手机有多个摄像头,打开其中一个
    Android截图
    Android中的ACCESS_MOCK_LOCATION权限使用Demo
  • 原文地址:https://www.cnblogs.com/uiojhi/p/7531478.html
Copyright © 2020-2023  润新知