用Verilog HDL语言实现UART通信

一、UART简介

UART是一种通用串行数据总线,用于异步通信。该总线双向通信,实现全双工传输和接收。

二、UART的通信协议和传输时序

UART 通信 UART 首先将接收到的并行数据转换成串行数据来传输。消息帧从一个低位起始位开始,后面是 7 个或 8 个数据位,一个可用的奇偶位和一个或几个高位停止位。接收器发现开始位时它就知道数据准备发送,并尝试与发送器时钟频率同步。如果选择了奇偶校验,UART 就在数据位后面加上奇偶位。奇偶位可用来帮助错误校验。在接收过程中,UART 从消息帧中去掉起始位和结束位,对进来的字节进行奇偶校验,并将数据字节从串行转换成并行。

UART 传输时序如下图所示:
这里写图片描述

起始位:先发出一个逻辑”0”的信号,表示传输字符的开始。

数据位:从最低位开始传送,靠时钟定位。

奇偶校验位:资料位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送的正确性。

停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。

三、Verilog实现

由于 UART 是异步传输,没有传输同步时钟。为了能保证数据传输的正确性,UART 采用16 倍数据波特率的时钟进行采样。每个数据有 16 个时钟采样,取中间的采样值,以保证采样不会滑码或误码。一般 UART 一帧的数据位数为 8,这样即使每个数据有一个时钟的误差,接收端也能正确地采样到数据。

3.1 接收程序

`timescale 1ns / 1ps
module uartrx(clk, rx, dataout, rdsig, dataerror, frameerror);
input clk;             //采样时钟
input rx;              //UART数据输入
output dataout;        //接收数据输出
output rdsig;          //接收数据有效,高说明接收到一个字节
output dataerror;      //数据出错指示
output frameerror;     //帧出错指示

reg[7:0] dataout;
reg rdsig, dataerror;
reg frameerror;
reg [7:0] cnt;
reg rxbuf, rxfall, receive;
parameter paritymode = 1'b0;
reg presult, idle;

always @(posedge clk)   
begin
  rxbuf <= rx;
  rxfall <= rxbuf & (~rx);
end

always @(posedge clk)
begin
  if (rxfall && (~idle))  //检测到线路的下降沿并且原先线路为空闲,启动接收数据进程
  begin
    receive <= 1'b1;      //开始接收数据
  end
  else if(cnt == 8'd168)  //接收数据完成
  begin
   receive <= 1'b0;
  end
end

/
//使用176个时钟接收一个数据(起始位、8位数据、奇偶校验位、停止位),每位占用16个时钟//

always @(posedge clk)
begin
  if(receive == 1'b1)
  begin
   case (cnt)
   8'd0:                   //0~15个时钟为接收第一个比特,起始位
     begin
      idle <= 1'b1;
      cnt <= cnt + 8'd1;
      rdsig <= 1'b0;
     end
   8'd24:                  //16~31个时钟为第1bit数据,取中间第24个时钟的采样值
    begin
      idle <= 1'b1;
      dataout[0] <= rx;
      presult <= paritymode^rx;
      cnt <= cnt + 8'd1;
      rdsig <= 1'b0;
     end
   8'd40:                 //47~32个时钟为第2bit数据,取中间第40个时钟的采样值 
    begin
      idle <= 1'b1;
      dataout[1] <= rx;
      presult <= presult^rx;
      cnt <= cnt + 8'd1;
      rdsig <= 1'b0;
     end
   8'd56:                 //63~48个时钟为第3bit数据,取中间第56个时钟的采样值   
    begin
      idle <= 1'b1;
      dataout[2] <= rx;
      presult <= presult^rx;
      cnt <= cnt + 8'd1;
      rdsig <= 1'b0;
     end
   8'd72:                //79~64个时钟为第4bit数据,取中间第72个时钟的采样值   
    begin
      idle <= 1'b1;
      dataout[3] <= rx;
      presult <= presult^rx;
      cnt <= cnt + 8'd1;
      rdsig <= 1'b0;
     end
   8'd88:               //95~80个时钟为第5bit数据,取中间第88个时钟的采样值    
    begin
      idle <= 1'b1;
      dataout[4] <= rx;
      presult <= presult^rx;
      cnt <= cnt + 8'd1;
      rdsig <= 1'b0;
     end
   8'd104:             //111~96个时钟为第6bit数据,取中间第104个时钟的采样值    
    begin
      idle <= 1'b1;
      dataout[5] <= rx;
      presult <= presult^rx;
      cnt <= cnt + 8'd1;
      rdsig <= 1'b0;
     end
   8'd120:             //127~112个时钟为第7bit数据,取中间第120个时钟的采样值     
    begin
      idle <= 1'b1;
      dataout[6] <= rx;
      presult <= presult^rx;
      cnt <= cnt + 8'd1;
      rdsig <= 1'b0;
     end
   8'd136:            //143~128个时钟为第8bit数据,取中间第136个时钟的采样值   
    begin
      idle <= 1'b1;
      dataout[7] <= rx;
      presult <= presult^rx;
      cnt <= cnt + 8'd1;
      rdsig <= 1'b1;      //接收数据有效
     end
   8'd152:            //159~144个时钟为接收奇偶校验位,取中间第152个时钟的采样值     
    begin
      idle <= 1'b1;
      if(presult == rx)
        dataerror <= 1'b0;
      else
        dataerror <= 1'b1;       //如果奇偶校验位不对,表示数据出错
      cnt <= cnt + 8'd1;
      rdsig <= 1'b1;             
      end
   8'd168:            //160~175个时钟为接收停止位,取中间第168个时钟的采样值  
     begin
     idle <= 1'b1;
     if(1'b1 == rx)
       frameerror <= 1'b0;
     else
       frameerror <= 1'b1;      //如果没有接收到停止位,表示帧出错
        cnt <= cnt + 8'd1;
        rdsig <= 1'b1;
     end
   default:
     begin
      cnt <= cnt + 8'd1;
     end
   endcase
  end
  else
  begin
    cnt <= 8'd0;
    idle <= 1'b0;
    rdsig <= 1'b0;
  end
 end
endmodule

3.2发送程序

`timescale 1ns / 1ps
module uarttx(clk, datain, wrsig, idle, tx);
input clk;                //UART时钟
input [7:0] datain;       //需要发送的数据
input wrsig;              //发送命令,上升沿有效
output idle;              //线路状态指示,高为线路忙,低为线路空闲
output tx;                //发送数据信号
reg idle, tx;
reg send;
reg wrsigbuf, wrsigrise;
reg presult;
reg[7:0] cnt;             //计数器
parameter paritymode = 1'b0;

//检测发送命令是否有效,判断wrsig的上升沿
always @(posedge clk)
begin
   wrsigbuf <= wrsig;
   wrsigrise <= (~wrsigbuf) & wrsig;
end

always @(posedge clk)
begin
  if (wrsigrise &&  (~idle))  //当发送命令有效且线路为空闲时,启动新的数据发送进程
  begin
     send <= 1'b1;
  end
  else if(cnt == 8'd168)      //一帧资料发送结束
  begin
     send <= 1'b0;
  end
end

/
//使用168个时钟发送一个数据(起始位、8位数据、奇偶校验位、停止位),每位占用16个时钟//

always @(posedge clk)
begin
  if(send == 1'b1)  begin
    case(cnt)                 //tx变低电平产生起始位,0~15个时钟为发送起始位
    8'd0: begin
         tx <= 1'b0;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd16: begin
         tx <= datain[0];    //发送数据位的低位bit0,占用第16~31个时钟
         presult <= datain[0]^paritymode;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd32: begin
         tx <= datain[1];    //发送数据位的第2位bit1,占用第47~32个时钟
         presult <= datain[1]^presult;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd48: begin
         tx <= datain[2];    //发送数据位的第3位bit2,占用第63~48个时钟
         presult <= datain[2]^presult;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd64: begin
         tx <= datain[3];    //发送数据位的第4位bit3,占用第79~64个时钟
         presult <= datain[3]^presult;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd80: begin 
         tx <= datain[4];   //发送数据位的第5位bit4,占用第95~80个时钟
         presult <= datain[4]^presult;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd96: begin
         tx <= datain[5];    //发送数据位的第6位bit5,占用第111~96个时钟
         presult <= datain[5]^presult;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd112: begin
         tx <= datain[6];    //发送数据位的第7位bit6,占用第127~112个时钟
         presult <= datain[6]^presult;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd128: begin 
         tx <= datain[7];    //发送数据位的第8位bit7,占用第143~128个时钟
         presult <= datain[7]^presult;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd144: begin
         tx <= presult;      //发送奇偶校验位,占用第159~144个时钟
         presult <= datain[0]^paritymode;
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd160: begin
         tx <= 1'b1;         //发送停止位,占用第160~167个时钟            
         idle <= 1'b1;
         cnt <= cnt + 8'd1;
    end
    8'd168: begin
         tx <= 1'b1;             
         idle <= 1'b0;       //一帧资料发送结束
         cnt <= cnt + 8'd1;
    end
    default: begin
          cnt <= cnt + 8'd1;
    end
   endcase
  end
  else  begin
    tx <= 1'b1;
    cnt <= 8'd0;
    idle <= 1'b0;
  end
end
endmodule
  • 12
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值