zl程序教程

您现在的位置是:首页 >  硬件

当前栏目

【数字IC/FPGA】UART的Verilog实现

IC 实现 数字 FPGA Verilog uart
2023-09-14 09:10:01 时间

UART

通用异步收发器(Universal Asynchronous Receiver/Transmitter,UART)可以和各种标准串行接口,如RS 232和RS 485等进行全双工异步通信,具有传输距离远、成本低、可靠性高等优点。一般UART由专用芯片如8250,16450来实现,但专用芯片引脚都较多,内含许多辅助功能,在实际使用时往往只需要用到UART的基本功能,使用专用芯片会造成资源浪费和成本提高。
UART只需要两条信号线:RXDTXD,其中RXD是UART的接收端,TXD是UART的发送端,接收与发送是全双工形式。下图为UART的工作原理如下
在这里插入图片描述
如上图所示,uart是将传输数据的每个字符一位接一位地传输。
其中每一位(Bit)的意义如下:
空闲位:高电平,表明当前无传输事务。
起始位:一位低电平信号,标志着数据传输的开始。
数据位:紧接着起始位之后。数据位的个数可以是4、5、6、7、8等,构成一个字符。通常采用ASCII码。从最低位开始传送,靠时钟定位。
奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。
停止位:一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
uart数据传输的速率用波特率表示,其含义为从一设备发到另一设备,每秒钟可以通信的数据比特个数。典型的波特率有300, 1200, 2400, 9600, 19200, 115200等。一般通信两端设备都要设为相同的波特率,但有些设备也可设置为自动检测波特率。

UART的Verilog实现

uart的设计分为两个部分,一部分为发送模块,另一部分为接收模块。程序设计主要通过状态机实现,在本设计中,我们略去了奇偶校验位,每次传输8bit数据,且停止位为1位。
我们共设置了如下几个状态:
IDLE:空闲状态,电平恒为1;
START:起始位,电平为0,1位;
DATA:数据位,共8位,先发送低比特;
STOP:停止位,高电平,共1位。
其中,发送模块在上一状态向下一状态切换时,改变电平,而接收模块,则在状态的中间位置进行采样,如下图所示:
在这里插入图片描述

代码实现

发送模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/02/26 15:27:26
// Design Name: 
// Module Name: uart_tx_sim
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_tx_sim
#(parameter BPS=5208)
(
input clk,
input rst_n,
input start,
input [7:0] send_data,
output reg rs232_tx
    );

//
// parameter BPS=5208;          //计算传输一次的计数时间:
//                              //计数时间 = 1000000000ns/9600 = 104166.7ns
//                              //50MHz的时钟周期为20ns,所以计数传输一个比特的次数为104166.7 / 20 = 5208
parameter IDLE = 0;
parameter START = 1;      //起始位
parameter SEND = 2;       //8bit数据发送,最低位开始传送,靠时钟定位。
parameter STOP =3;        //停止位
reg [2:0] cur_state;
reg [2:0] next_state;
reg [31:0] count;         //计数器
reg [31:0] send_cnt;      //发送bit计数器
//三段式状态机
always@(posedge clk,negedge rst_n)
if(~rst_n)
    cur_state<=IDLE;
else
    cur_state<=next_state;
//
always@(*)
case(cur_state)
    IDLE:if(start)
             next_state=START;
         else
             next_state=IDLE;
    START:if(count==BPS-1)
             next_state=SEND;
          else
             next_state=START;
    SEND:if(send_cnt==8-1&&count==BPS-1)
             next_state=STOP;
         else
             next_state=SEND;
    STOP:if(count==BPS-1)
             next_state=IDLE;
         else
             next_state=STOP;
    default:next_state=IDLE;
endcase
//count
always@(posedge clk,negedge rst_n)
if(~rst_n)
    count<=0;
else if(cur_state!=IDLE)
begin
    if(count==BPS-1)
        count<=0;
    else
        count<=count+1;
end
else
    count<=0;
//send_cnt
always@(posedge clk,negedge rst_n)
if(~rst_n)
    send_cnt<=0;
else if(cur_state==SEND&&count==BPS-1)
    send_cnt<=send_cnt+1;
else if(cur_state!=SEND)
    send_cnt<=0;
//rs232_tx
always@(*)
case(cur_state)
    IDLE:rs232_tx=1;
    START:rs232_tx=0;
    SEND:begin
         case(send_cnt)
            0:rs232_tx=send_data[0];
            1:rs232_tx=send_data[1];
            2:rs232_tx=send_data[2];
            3:rs232_tx=send_data[3];
            4:rs232_tx=send_data[4];
            5:rs232_tx=send_data[5];
            6:rs232_tx=send_data[6];
            7:rs232_tx=send_data[7];
            default:rs232_tx=1;
        endcase
    end
    STOP:rs232_tx=1;
    default:rs232_tx=1;
endcase
endmodule

接收模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/02/26 16:25:38
// Design Name: 
// Module Name: uart_rx_sim
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module uart_rx_sim
#(parameter BPS=5208)
(
input clk,
input rst_n,
input rs232_rx,
output reg valid,
output reg [7:0] recv_data
    );
parameter IDLE = 0;
parameter START = 1;
parameter RECV = 2;
parameter STOP =3;
//parameter BPS=5208;
//
reg sample_edge;
reg [2:0] cur_state;
reg [2:0] next_state;
reg [31:0] count;
reg [2:0] recv_cnt;                //0-7
reg rs232_rx_ff1;                  //检测到IDLE状态下tx信号拉低
//rs232_tx_ff1
always@(posedge clk)
   rs232_rx_ff1<=rs232_rx;
//
always@(posedge clk,negedge rst_n)
if(~rst_n)
    cur_state<=IDLE;
else
    cur_state<=next_state;
//
always@(*)
begin
    case(cur_state)
        IDLE:if(~rs232_rx&&rs232_rx_ff1)           //检测到下降沿
                 next_state=START;
             else
                 next_state=IDLE;
        START:if(count==BPS-2)                      //检测到下降沿占去一个周期
                  next_state=RECV;
              else
                  next_state=START;
        RECV:if(recv_cnt==8-1&&count==BPS-1)
                  next_state=STOP;
             else
                  next_state=RECV;
        STOP:if(count==BPS-1)
                 next_state=IDLE;
             else
                 next_state=STOP;
        default:next_state=IDLE;
    endcase
end
//count
always@(posedge clk,negedge rst_n)
if(~rst_n)
    count<=0;
else 
case(cur_state)
    IDLE:count<=0;
    START:if(count==BPS-2)
             count<=0;
          else
             count<=count+1;
    RECV:if(count==BPS-1)
             count<=0;
         else
             count<=count+1;
    STOP:if(count==BPS-1)
             count<=0;
         else
             count<=count+1;
    default:count<=0;
endcase
//recv_cnt
always@(posedge clk,negedge rst_n)
if(~rst_n)
    recv_cnt<=0;
else if(cur_state==RECV&&count==BPS-1)
    recv_cnt<=recv_cnt+1;
else if(cur_state==STOP)
    recv_cnt<=0;
//sample_edge
always@(posedge clk,negedge rst_n)
if(~rst_n)
   sample_edge<=0;
else if(cur_state==RECV&&count==BPS/2-1)                //在数据最中间采用
   sample_edge<=1;
else
   sample_edge<=0;
//recv_data
always@(posedge clk)
if(cur_state==RECV&&sample_edge)
begin
    case(recv_cnt)
        0:recv_data[0]<=rs232_rx;
        1:recv_data[1]<=rs232_rx;
        2:recv_data[2]<=rs232_rx;
        3:recv_data[3]<=rs232_rx;
        4:recv_data[4]<=rs232_rx;
        5:recv_data[5]<=rs232_rx;
        6:recv_data[6]<=rs232_rx;
        7:recv_data[7]<=rs232_rx;
        default:;
    endcase
end
//valid
always@(posedge clk,negedge rst_n)
if(~rst_n)
    valid<=0;
else if(cur_state==STOP)
    valid<=1;
else if(~rs232_rx&&rs232_rx_ff1&&cur_state==IDLE)
    valid<=0;
endmodule

完整工程

见链接:https://pan.baidu.com/s/1TdDGv-eiPZ91tepzJGSvhg
提取码:zdfw