0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

什么是串口(UART)?串口的组成和FPGA实现

硬件攻城狮 来源:CSDN博主孤独的单刀 2024-01-03 11:43 次阅读

1、什么是串口(UART)?

串口作为常用的三大低速总线(UART、SPI、IIC)之一,在设计众多通信接口和调试时占有重要地位。

串口(UART)全称通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),主要用于数据间的串行传递,是一种全双工传输模式。

它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

“异步”两个字即意味着在数据传递的两个模块之间使用的不是同步时钟

实际上在异步串口的传输中是不需要时钟的,而是通过特定的时序来标志传输的开始(起始位--由高到低)和结束(结束位,拉高)。

2、串口的组成

2.1、串口的物理层

UART 通信只有两根信号线,一根是发送数据端口线叫 tx(Transmitter),一根是接收数据端口线叫 rx(Receiver)

如图所示,对于 PC 来说它的 tx 要和对于 FPGA来说的 rx 连接,同样 PC 的 rx 要和 FPGA 的 tx 连接,如果是两个 tx 或者两个 rx 连接那数据就不能正常被发送出去和接收到。

eca06fb6-a9e9-11ee-8b88-92fbcf53809c.png

信号的传输由外部驱动电路实现。电信号的传输过程有着不同的电平标准和接口规范,针对异步串行通信的接口标准有RS232RS422RS485等.

它们定义了接口不同的电气特性,如RS-232是单端输入输 出,而RS-422/485为差分输入输出等。

传输距离较短时(不超过15m),RS232是串行通信最常用的接口标准。RS-232标准的串口最常见的接口类型为DB9,样式如图所示,工业控制领域中用到的工控机一般都配备多个串口,很多老式台式机也都配有串口。

但是笔记本电脑以及较新一点 的台式机都没有串口,它们一般通过USB转串口线来实现与外部设备的串口通信。

DB9接口定义以及各引脚功能说明如图所示,我们一般只用到其中的2(RXD)、3 (TXD)、5(GND)引脚,其他引脚在普通串口模式下一般不使用:

ecbfde46-a9e9-11ee-8b88-92fbcf53809c.png

2.2、UART协议

UART 在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位和停止位,如图所示。

其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。

校验位分为奇校验和偶校验,用于检验数据在传输过程中是否出错。

奇校验时,发送方应使数据位中1的个数与校验位中1的个数之和为奇数;接收方在接收数据时, 对1的个数进行检查.

若不为奇数,则说明数据在传输过程中出了差错。同样,偶校验则检查1的个数是否为偶数。关于奇偶校验可参考:Verilgo实现的FPGA奇偶校验

ecc3d9c4-a9e9-11ee-8b88-92fbcf53809c.png

UART通信过程中的数据格式及传输速率是可设置的,为了正确的通信,收发双方应约定并遵循同样的设置。

数据位可选择为5、6、7、8位,其中8位数据位是最常用的,在实际应用中一般都选择8位数据位;校验位可选择奇校验、偶校验或者无校验位;停止位可选择1位(默认), 1.5或2位。

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

如波特率9600则代表每秒传输9600bit数据,以串口发送1个字节10bit算(起始位1bit+数据8bit+停止位1bit+NO校验位),则传输1个字节需要的时间是1*10/9600秒。

3、串口发送模块

3.1、接口定义与整体设计

发送模块整体框图、输入输出信号如下所示:

ecc76ef4-a9e9-11ee-8b88-92fbcf53809c.png

其中信号端口如下:

ecd9e46c-a9e9-11ee-8b88-92fbcf53809c.png

需要说明的是,uart_tx_data为需要发送的一个字节的数据,uart_tx_en为发送使能位,当其拉高,则代表此时通过串口发送数据线发送数据uart_tx_data。

3.2、设计思路

该模块支持任意波特率(理论上)的发送,但需要在使用该模块时使用参数将其例化,数据位8位,起始位和停止位各1位,无奇偶校验

当使能信号有效后拉高发送标志信号,标志模块进入发送过程;当发送完10个bit后,拉低发送标志信号,标志发送过程结束。使能信号有效时将要发送的数据寄存。

假设波特率为9600,则发送一个bit的时间为1s/9600,一个数据的传输共10bit(数据位8位,起始位和停止位各1位),则共需要1s/9600;

假设系统时钟为50MHz(参数化以便适应不同的系统频率),则其周期为20ns,那么发送一个bit所需要的系统周期数为(1s/9600)/ 20ns ≈ 5208(个)。

在发送过程中使用一个计数器计数,计数区间为(0~5208-1),这样的区间一共10个(一个字节需要发送10个bit);

此外还需一个计数器对发送的bit数计数(每当上一个计数器计数到5207则表示发送完了一个bit),计数区间(0~9)

在发送过程,根据计数器的值(发送bit计数器),对发送数据线进行操作。

若发送bit计数器 = 0,则代表此时需要发送起始位;

若发送bit计数器 = 1,则代表此时需要发送发送数据的最低位LSB(数据的发送总是低位在前,高位在后);

······

若发送bit计数器 = 8,则代表此时需要发送发送数据的最高位MSB;

若发送bit计数器 = 9,则代表此时需要发送停止位;

发送数据线在不处于发送状态时需拉高,以满足UART时序的空闲状态

3.3、Verilg代码

根据上述设计思路,部分发送模块代码如下:

 
// *******************************************************************************************************
// ** 作者 : 孤独的单刀                             
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/31 
// ** 功能 : 1、基于FPGA的串口发送驱动模块;
//    2、可设置波特率BPS、主时钟CLK_FRE;
//    3、起始位1bit,数据位8bit,停止位1bit,无奇偶校验;                                                                       
//    4、每发送1个字节后拉高uart_tx_done一个周期,可用于后续发送多字节模块。                                                                       
// ******************************************************************************************************* 
 
module uart_tx
#(
 parameter integer BPS  = 9_600  , //发送波特率
 parameter  integer CLK_FRE = 50_000_000 //主时钟频率
)
(
//系统接口
 input    sys_clk   ,   //系统时钟
 input    sys_rst_n  ,   //系统复位,低电平有效
//用户接口 
 input [7:0]  uart_tx_data ,   //需要通过UART发送的数据,在uart_tx_en为高电平时有效
 input   uart_tx_en  ,   //发送有效,当其为高电平时,代表此时需要发送的数据有效
//UART发送 
 output reg  uart_tx_done ,   //成功发送1BYTE数据后拉高一个周期
 output  reg  uart_txd     //UART发送数据线tx
);
 
 
//当发送使能信号到达时,寄存待发送的数据以免后续变化、丢失
always @(posedge sys_clk or negedge sys_rst_n)begin
 if(!sys_rst_n)
  uart_tx_data_reg <=8'd0;
  else if(uart_tx_en)              //要发送有效的数据
    uart_tx_data_reg <= uart_tx_data;    //寄存需要发送的数据      
  else 
    uart_tx_data_reg <= uart_tx_data_reg;
end    
//当发送使能信号到达时,进入发送过程
always @(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)
    tx_state <=1'b0;  
  else if(uart_tx_en)                        
    tx_state <= 1'b1;            //发送信号有效则进入发送过程
  //发送完了最后一个数据则退出发送过程    
  else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))    
    tx_state <= 1'b0;                                              
  else 
    tx_state <= tx_state;  
end
 
//发送数据完毕后拉高发送完毕信号一个周期,指示一个字节发送完毕
always @(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)
    uart_tx_done <=1'b0;
  //发送数据完毕后拉高发送完毕信号一个周期     
  else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))                                             
    uart_tx_done <=1'b1;                    
  else 
    uart_tx_done <=1'b0;
end
//进入发送过程后,启动时钟计数器与发送个数bit计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)begin
    clk_cnt <= 32'd0;
    bit_cnt <= 4'd0;
  end
  else if(tx_state) begin                    //在发送状态
    if(clk_cnt < BPS_CNT - 1'd1)begin            //一个bit数据没有发送完
      clk_cnt <= clk_cnt + 1'b1;              //时钟计数器+1
      bit_cnt <= bit_cnt;                  //bit计数器不变
    end          
    else begin                        //一个bit数据发送完了  
      clk_cnt <= 32'd0;                  //清空时钟计数器,重新开始计时
      bit_cnt <= bit_cnt+1'b1;              //bit计数器+1,表示发送完了一个bit的数据
    end          
  end          
  else begin                          //不在发送状态
    clk_cnt <= 32'd0;                             //清零
    bit_cnt <= 4'd0;                              //清零
  end
end
endmodule

3.4、Testbench

Testbench的设计如下:

设定波特率230400(这样的目的是为了更方便的观察发送使能信号uart_tx_en,节约时间)

3000ns后,拉高发送使能信号uart_tx_en一个周期,同时生成1个8bit的随机数据给uart_tx_data作为要发送的数据

观察UART上TX线的时序是否满足要求


// *******************************************************************************************************
// ** 作者 : 孤独的单刀                             
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/29 
// ** 功能 : 1、对基于FPGA的串口发送驱动模块的测试testbench
//    2、发送一个8bit的随机数据,观测其波形是否符合UART时序                                                                     
// *******************************************************************************************************  
 
`timescale 1ns/1ns //定义时间刻度
 
module tb_uart_tx();
 
reg    sys_clk   ;   
reg    sys_rst_n  ;   
reg [7:0]  uart_tx_data ;
reg    uart_tx_en  ;
   
wire    uart_txd  ;
 
parameter integer BPS  = 'd230400  ;   //波特率
parameter integer CLK_FRE = 'd50_000_000 ;   //系统频率50M
 
 
localparam integer BIT_TIME = 'd1000_000_000 / BPS ; //计算出传输每个bit所需要的时间
 
initial begin 
 sys_clk <=1'b0;  
  sys_rst_n <=1'b0;    
  uart_tx_en <=1'b0;
  uart_tx_data <=8'd0;        
  #80                     //系统开始工作
    sys_rst_n <=1'b1;
    
  #200
    @(posedge sys_clk);
    uart_tx_en <=1'b1;  
    uart_tx_data <= ({$random} % 256);    //发送8位随机数据
  #20  
    uart_tx_en <=1'b0;
  
  #(BIT_TIME * 10)              //发送1个BYTE需要10个bit
  #200 $finish;                //结束仿真
end
 
always #10 sys_clk=~sys_clk;          //定义主时钟,周期20ns,频率50M
 
//例化发送驱动模块
uart_tx #(
  .BPS      (BPS      ),    
  .CLK_FRE    (CLK_FRE    )    
)  
uart_tx_inst(  
  .sys_clk    (sys_clk    ),      
  .sys_rst_n    (sys_rst_n    ),
  
  .uart_tx_data  (uart_tx_data  ),      
  .uart_tx_en    (uart_tx_en    ),    
  .uart_tx_done  (uart_tx_done  ),    
  .uart_txd    (uart_txd    )  
);
 
endmodule

3.5、仿真结果分析

仿真结果如下图(注释很详细):

下图中可以看到发送模块发送了1个数据8'h24,一段时间后发送结束,并无法直接观察发送线TX上的时序。

eceeeac4-a9e9-11ee-8b88-92fbcf53809c.png

整体仿真时序

下图中可以看到发送模块发送了1个数据8'h24,一段时间后发送结束,且可以看到发送线TX在以符合UART时序的方式发送数据00100100(低位在前、高位在后),即8'h24。

ecfa7664-a9e9-11ee-8b88-92fbcf53809c.png

单次发送时序

可以看到仿真结果是符合预期设计要求的。

3.6、上板实测

至此已经顺利完成了发送模块的仿真验证,接下来使用一块Altera Cyclone IV E的开发板上板实测。

编写一个发送模块测试模块,该模块调用串口发送模块,并按一定间隔(默认1s)拉高发送使能信号和生成发送数据,发送数据从0x01开始累加1,直到0xFF(溢出到0x00)。

同时在电脑上使用串口调试软件接收发送过来的数据。根据串口调试软件接收到的数据判断串口发送模块是否能成功工作。

发送模块验证模块代码如下:

 
// *******************************************************************************************************
// ** 作者 : 孤独的单刀                             
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/29 
// ** 功能 : 1、基于FPGA的串口发送驱动模块的测试模块;
//    2、每个1s发送1个递增1的数据到上位机。
// ******************************************************************************************************* 
 
module uart_tx_test
(
//系统接口
 input    sys_clk   ,
 input    sys_rst_n  , 
//UART发送线 
 output   uart_txd      //UART发送线
); 
 
parameter integer BPS  = 'd230400  ;  //波特率
parameter integer CLK_FRE = 'd50_000_000 ;  //系统频率50M 
 
reg  [31:0] cnt_time; 
reg    uart_tx_en;       //发送使能,当其为高电平时,代表此时需要发送数据  
reg  [7:0]  uart_tx_data;      //需要通过UART发送的数据,在uart_tx_en为高电平时有效
 
//1s计数模块,每隔1s发送一个数据和拉高发送使能信号一次;数据从0开始递增1
always @(posedge sys_clk or negedge sys_rst_n)begin
 if(!sys_rst_n)begin
  cnt_time <= 'd0;
    uart_tx_en <= 1'd0;
    uart_tx_data <= 8'd0;
  end
  else if(cnt_time == (50_000_000 - 1'b1))begin
    cnt_time <= 'd0;
    uart_tx_en <= 1'd1;              //拉高发送使能
    uart_tx_data <= uart_tx_data + 1'd1;    //发送数据累加1
  end
  else begin
    cnt_time <= cnt_time + 1'd1;
    uart_tx_en <= 1'd0;
    uart_tx_data <= uart_tx_data; 
  end
end 
 
//例化发送模块
uart_tx
#(
  .BPS      (BPS      ),
  .CLK_FRE    (CLK_FRE    )
)  
uart_tx_inst
(  
  .sys_clk    (sys_clk    ),
  .sys_rst_n    (sys_rst_n    ),
  .uart_tx_en    (uart_tx_en    ),
  .uart_tx_data  (uart_tx_data  ),
  .uart_tx_done  (        ),  
  .uart_txd    (uart_txd    )
);  
 
endmodule

串口调试软件结果如下:

依次接收到了数据01、02、03······。说明我们的发送模块工作正常。

ed22b674-a9e9-11ee-8b88-92fbcf53809c.png

4、串口接收模块

4.1、接口定义与整体设计

接收模块整体框图、输入输出信号如下所示:

ed3446c8-a9e9-11ee-8b88-92fbcf53809c.png

其中信号描述如下:

ed406066-a9e9-11ee-8b88-92fbcf53809c.png

需要说明的是,uart_rx_data为接收的一个字节的数据,uart_rx_done为接收完成标志位,当其拉高,则代表此时接收到的串口数据uart_rx_data有效。

4.2、设计思路

该模块支持任意波特率(理论上)的接收,但需要在使用该模块时使用参数将其例化,数据位8位,起始位和停止位各1位,无奇偶校验

串口的传输是以起始位开始的,而起始位是将数据线拉低 ,所以我们需要捕捉数据线的下降沿,将接收数据线打拍3次,捕捉其下降沿。

当捕捉到接收数据线的下降沿,拉高接收标志信号,标志模块进入接收过程;当接收完10个bit后,拉低接收标志信号,标志接收过程结束

假设波特率为9600,则传输一个bit的时间为1s/9600,一个数据的传输共10bit(数据位8位,起始位和停止位各1位),则共需要1s/960;

假设系统时钟为50MHz(参数化以便适应不同的系统频率),则其周期为20ns,那么传输一个bit所需要的系统周期数为(1s/960)/ 20ns ≈ 5208(个)。

在接收过程中使用一个计数器计数,计数区间为(0~5208-1),这样的区间一共10个(一个字节需要传输10个bit);

此外还需一个计数器对接收的bit数计数(每当上一个计数器计数到5207则表示接收完了一个bit),计数区间(0~9)。

在接收过程,根据计数器的值(接收bit计数器),在每个bit计数器的中间接收数据,将其移位寄存(在电平中间数据最稳定)

若接收bit计数器 = 0,则代表是起始位,不需要接收

若接收bit计数器 = 1,则代表此时接收到数据的最低位LSB(数据的传输总是低位在前,高位在后),将其赋值给寄存数据的最低位;

······

若接收bit计数器 = 8,则代表此时接收到数据的最高位MSB,将其赋值给寄存数据的最高位;

若接收bit计数器 = 9,则代表是停止位,不需要接收

4.3、Verilg代码

根据上述设计思路,部分代码如下:

 
// *******************************************************************************************************
// ** 作者 : 孤独的单刀                             
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/08/05 
// ** 功能 : 1、基于FPGA的串口接收驱动模块;
//    2、可重新设置波特率BPS、主时钟CLK_FRE;
//    3、起始位1bit,数据位8bit,停止位1bit,无奇偶校验。                                                                       
// *******************************************************************************************************   
 
module uart_rx
#(
 parameter integer BPS  = 9_600  ,  //发送波特率
 parameter  integer CLK_FRE = 50_000_000  //输入时钟频率
) 
( 
//系统接口
 input     sys_clk   ,   //50M系统时钟
 input     sys_rst_n  ,   //系统复位
//UART接收线 
 input     uart_rxd  ,   //接收数据线
//用户接口 
 output reg    uart_rx_done ,   //数据接收完成标志,当其为高电平时,代表接收数据有效
 output reg [7:0] uart_rx_data    //接收到的数据,在uart_rx_done为高电平时有效
);
 
assign neg_uart_rxd = uart_rx_d3 & (~uart_rx_d2); //捕获数据线的下降沿,用来标志数据传输开始
 
//将数据线打3拍,作用1:同步不同时钟域信号,防止亚稳态;作用2:捕获下降沿
always@(posedge sys_clk or negedge sys_rst_n)begin
 if(!sys_rst_n)begin
  uart_rx_d1 <= 1'b0;
    uart_rx_d2 <= 1'b0;
    uart_rx_d3 <= 1'b0;
  end
  else begin
    uart_rx_d1 <= uart_rxd;
    uart_rx_d2 <= uart_rx_d1;
    uart_rx_d3 <= uart_rx_d2;
  end    
end
//捕获到数据下降沿(起始位0)后,拉高传输开始标志位,并在第9个数据(终止位)的传输过程正中(数据比较稳定)再将传输开始标志位拉低,标志传输结束
always@(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)
    rx_en <= 1'b0;
  else begin 
    if(neg_uart_rxd )                
      rx_en <= 1'b1;
    //接收完第9个数据(终止位)将传输开始标志位拉低,标志传输结束,判断高电平
    else if((bit_cnt == 4'd9) && (clk_cnt == BPS_CNT >> 1'b1) && (uart_rx_d3 == 1'b1) )
   rx_en <= 1'b0;
    else 
      rx_en <= rx_en;      
  end
end
//当数据传输到终止位时,拉高传输完成标志位,并将数据输出
always@(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)begin
    uart_rx_done <= 1'b0;
    uart_rx_data <= 8'd0;
  end  
  //结束接收后,将接收到的数据输出
  else if((bit_cnt == 4'd9) && (clk_cnt == BPS_CNT >> 1'd1) && (uart_rx_d3 == 1'b1))begin  
  uart_rx_done <= 1'b1;                  //仅仅拉高一个时钟周期
    uart_rx_data <= uart_rx_data_reg;  
  end              
  else begin          
    uart_rx_done <= 1'b0;                  //仅仅拉高一个时钟周期
    uart_rx_data <= uart_rx_data;
  end
end
 
//时钟每计数一个BPS_CNT(传输一位数据所需要的时钟个数),即将数据计数器加1,并清零时钟计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
  if(!sys_rst_n)begin
    bit_cnt <= 4'd0;
    clk_cnt <= 32'd0;
  end
  else if(rx_en)begin                            //在接收状态
    if(clk_cnt < BPS_CNT - 1'b1)begin                 //一个bit数据没有接收完
      clk_cnt <= clk_cnt + 1'b1;                    //时钟计数器+1
      bit_cnt <= bit_cnt;                           //bit计数器不变
    end                                               
    else begin                                        //一个bit数据接收完了  
      clk_cnt <= 32'd0;                             //清空时钟计数器,重新开始计时
      bit_cnt <= bit_cnt + 1'b1;                    //bit计数器+1,表示接收完了一个bit的数据
    end                                               
  end                                                   
    else begin                                        //不在接收状态
      bit_cnt <= 4'd0;                              //清零
      clk_cnt <= 32'd0;                             //清零
    end    
end
 
endmodule

4.4、Testbench

仿真模块的Testbench设计如下:

设定波特率230400(这样的目的是为了更方便的观察发送使能信号uart_tx_en)

定义一个任务task,该任务将输入使用波特率230400一个bit一个bit的输出,模拟上位机发送数据给FPGA

3000ns后,发送第1个随机数据

发送完了第1个随机数据后发送第2个随机数据,一共发送4个随机数据

 
// *******************************************************************************************************
// ** 作者 : 孤独的单刀                             
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/29 
// ** 功能 : 1、对基于FPGA的串口接收驱动模块的测试testbench
//    2、通过构建一个task来模拟上位机时序发送数据给串口接收驱动,观察该模块能否成功接收数据。
//    3、依次发送4个随机的8bit数据                                                                       
// *******************************************************************************************************   
 
`timescale 1ns/1ns //定义时间刻度
 
//模块、接口定义
module tb_uart_rx();
 
reg    sys_clk   ;   
reg    sys_rst_n  ;   
reg    uart_rxd  ;
 
wire    uart_rx_done ;  
wire [7:0] uart_rx_data ;
 
localparam integer BPS  = 'd230400    ; //波特率
localparam integer CLK_FRE = 'd50_000_000   ; //系统频率50M
localparam integer CNT   = 1000_000_000 / BPS ; //计算出传输每个bit所需要的时间,单位:ns
 
 
//初始时刻定义
initial begin 
 $timeformat(-9, 0, " ns", 10); //定义时间显示格式 
 sys_clk =1'b0; 
 sys_rst_n <=1'b0;    
  uart_rxd <=1'b1;
  
  #20 //系统开始工作
  sys_rst_n <=1'b1;
  
  #3000
  rx_byte({$random} % 256);    //生成8位随机数1
  rx_byte({$random} % 256);    //生成8位随机数2
  rx_byte({$random} % 256);       //生成8位随机数3
  rx_byte({$random} % 256);       //生成8位随机数4  
  #60  $finish();
end
 
//每当成功接收一个BYTE的数据,就在测试端窗口打印出来
always @(posedge sys_clk)begin
  if(uart_rx_done)begin
    $display("@time%t", $time);  
    $display("rx : 0x%h",uart_rx_data);
  end
end
 
//定义任务,每次发送的数据10 位(起始位1+数据位8+停止位1)
task rx_byte(
  input [7:0] data
);
  integer i; //定义一个常量
  //用 for 循环产生一帧数据,for 括号中最后执行的内容只能写 i=i+1
  for(i=0; i<10; i=i+1) begin
    case(i)
    0: uart_rxd <= 1'b0;    //起始位
    1: uart_rxd <= data[0];    //LSB
    2: uart_rxd <= data[1];
    3: uart_rxd <= data[2];
    4: uart_rxd <= data[3];
    5: uart_rxd <= data[4];
    6: uart_rxd <= data[5];
    7: uart_rxd <= data[6];
    8: uart_rxd <= data[7];    //MSB
    9: uart_rxd <= 1'b1;    //停止位
    endcase
    #CNT;             //每发送 1 位数据延时
  end    
endtask               //任务结束
 
//设置主时钟
always #10 sys_clk <= ~sys_clk;    //时钟20ns,50M
 
//例化被测试的串口接收驱动
uart_rx
#(
  .BPS      (BPS      ),    
  .CLK_FRE    (CLK_FRE    )      
)
uart_rx_inst(
  .sys_clk    (sys_clk    ),      
  .sys_rst_n    (sys_rst_n    ),      
  .uart_rxd    (uart_rxd    ),      
  .uart_rx_done  (uart_rx_done  ),    
  .uart_rx_data  (uart_rx_data  )  
);
 
endmodule

4.5、仿真结果分析

仿真结果如下图(注释很详细):

下图中分别发送了4个数据8'h24--8'h81--8'h09--8'h63;接收模块分别接收到了4个数据8'h24--8'h81--8'h09--8'h63。发送、接收数据一致。

ed5d6238-a9e9-11ee-8b88-92fbcf53809c.png

接收总体时序

下图是第1次接收数据(8'h24,即00100100)是的时序图。

ed79b01e-a9e9-11ee-8b88-92fbcf53809c.png

单个字节接收时序

4.6、上板测试

至此已经顺利完成了接收模块的仿真验证,接下来使用一块Altera Cyclone IV E的开发板上板测试。

首先生成一个IP核--ISSP(In-System Sources and Probes),这个IP核可以提供一个输出用来在线输出,相当于一个简单的信号发生器--Source,此外还可以提供探针Probes来在线监控信号的输出。

在本次设计中,我们使用Probes来观察串口接收数据。ISSP调用如下:

ed895686-a9e9-11ee-8b88-92fbcf53809c.png

编写一个接收模块验证模块,该模块调用接收模块,ISSP IP核。同时在电脑上使用串口调试软件发送数据,根据接收到的数据判断串口接收模块是否能成功工作。

接收模块验证模块uart_rx_test代码如下:

 
// *******************************************************************************************************
// ** 作者 : 孤独的单刀                             
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb 
// ** 日期 : 2022/07/29 
// ** 功能 : 1、基于FPGA的串口接收驱动模块的测试模块;
//    2、例化串口接收驱动与ISSP IP核;
//    3、使用上位机发送随机数据到FPGA,通过观察ISSP监测到的接收驱动接收的数据来进行测试。                                                                       
// *******************************************************************************************************   
 
module uart_rx_test
(
//系统接口
 input     sys_clk   ,
 input     sys_rst_n  , 
//UART接收线  
 input    uart_rxd     //接收数据线
); 
 
parameter integer BPS  = 'd230400  ;  //波特率230400
parameter integer CLK_FRE = 'd50_000_000 ;  //系统频率50MHZ 
 
wire [7:0] uart_rx_data;
 
//例化接收模块
uart_rx
#(
 .BPS   (BPS   ),
 .CLK_FRE  (CLK_FRE  )
) 
uart_rx_isnt
( 
 .sys_clk  (sys_clk  ),   
 .sys_rst_n  (sys_rst_n  ),   
 .uart_rxd  (uart_rxd  ),   
 .uart_rx_done (    ),   
 .uart_rx_data (uart_rx_data )   
);
 
//例化ISSP作为观测手段
issp_uart_rx issp_uart_rx_inst
(
 .probe   (uart_rx_data ),   //观测接收数据
 .source    (    )   
);
 
endmodule

下载程序后,在Quartus II中打开In-System Sources and Probes Editor,然后使用串口调试软件发送数据0x55--0xaa--0x88(随机选的3个),观察 In-System Sources and Probes Editor中寄存器的值,分别如下:

ed97117c-a9e9-11ee-8b88-92fbcf53809c.png

eda8d4a2-a9e9-11ee-8b88-92fbcf53809c.png

edbec4a6-a9e9-11ee-8b88-92fbcf53809c.png

5、总结

串口作为一种常用的通信协议与调试手段,请一定要熟练掌握!

基于FPGA的串口实现不难,只要注意根据波特率合理设计计数器即可,在此计数器的调动下可以实现数据的发送与接收。

来源:CSDN博主「孤独的单刀」的原创文章

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1602

    文章

    21320

    浏览量

    593199
  • 接口
    +关注

    关注

    33

    文章

    7639

    浏览量

    148494
  • 计数器
    +关注

    关注

    32

    文章

    2121

    浏览量

    92977
  • 串口
    +关注

    关注

    14

    文章

    1483

    浏览量

    74511
  • uart
    +关注

    关注

    22

    文章

    1159

    浏览量

    99961

原文标题:串口(UART)的FPGA实现(含源码工程)

文章出处:【微信号:mcu168,微信公众号:硬件攻城狮】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    IO模拟串口UART

    IO模拟串口UART 本文介绍GPIO模拟UART的算法和实现
    发表于 04-03 14:11 86次下载

    niosii的UART串口通信

    niosii的UART串口通信niosii的UART串口通信。
    发表于 04-06 17:03 0次下载

    UART串口在SIM卡读写中的应用

    UART串口在SIM卡读写中的应用,下来看看
    发表于 08-19 16:51 0次下载

    实验五 UART串口通讯实验

    UART串口通讯
    发表于 01-22 20:51 8次下载

    Uart串口与RS232串口的区别

    Uart这里指的是TTL电平的串口;RS232指的是RS232电平的串口。TTL电平串口是一般芯片的串口的输入和输出端,可以接不通的芯片完成
    发表于 11-20 17:48 5.9w次阅读

    uart是什么意思?认识uart串口

    设备没有显示屏,无法获得嵌入式设备实时数据信息,通过UART串口和超级终端相连,打印嵌入式设备输出信息。并且在对嵌入式系统进行跟踪和调试时,UART串口了是必要的通信手段。比如:网络路
    发表于 12-06 14:51 10.7w次阅读
    <b class='flag-5'>uart</b>是什么意思?认识<b class='flag-5'>uart</b><b class='flag-5'>串口</b>

    串口通信的原理,IO口模拟UART串口通信

    UART串口波特率,常用的值是300、600、1200、2400、4800、9600、14400、19200、28800、38400、57600、115200等速率。IO口模拟UART串行通信程序是一个简单的演示程序,我们使用
    的头像 发表于 05-04 15:26 2.1w次阅读
    <b class='flag-5'>串口</b>通信的原理,IO口模拟<b class='flag-5'>UART</b><b class='flag-5'>串口</b>通信

    如何使用Jlink实现虚拟串口功能

    串口调试是单片机开发过程必不可少的一个功能,一般是使用一个UART-TTL的串口模块来实现串口的功能,其实下载调试使用的Jlink仿真器也可
    发表于 06-04 17:52 11次下载
    如何使用Jlink<b class='flag-5'>实现</b>虚拟<b class='flag-5'>串口</b>功能

    C51的UART 串口通信

    C51的UART 串口通信
    发表于 11-29 12:21 9次下载
    C51的<b class='flag-5'>UART</b> <b class='flag-5'>串口</b>通信

    UART串口通讯

    UART串口通讯总结前言串口的基本概念串口配置的基本属性串口(Serial port)和RS-232串口
    发表于 12-20 19:31 23次下载
    <b class='flag-5'>UART</b><b class='flag-5'>串口</b>通讯

    正点原子Mini Linux—UART串口简单介绍

    UART串口一、I.MUX6ULL串口UART1、串口原理2、I.MUX6U的UART时钟源设置
    发表于 01-12 20:14 0次下载
    正点原子Mini Linux—<b class='flag-5'>UART</b><b class='flag-5'>串口</b>简单介绍

    【STM32笔记4】UART的时序解读与硬件运用(即硬件UART实现,另有硬件UART运用和软件UART运用/硬件串口和软件串口/USART的差异说明)

    一、UART简介1、UART(Universal Asynchronous Receiver Transmitter)总线是异步串口,故一般比I2C、SPI两种同步串口的结构要复杂很多
    发表于 01-17 13:00 3次下载
    【STM32笔记4】<b class='flag-5'>UART</b>的时序解读与硬件运用(即硬件<b class='flag-5'>UART</b>的<b class='flag-5'>实现</b>,另有硬件<b class='flag-5'>UART</b>运用和软件<b class='flag-5'>UART</b>运用/硬件<b class='flag-5'>串口</b>和软件<b class='flag-5'>串口</b>/USART的差异说明)

    单片机IO口模拟UART串口通信

    为了让大家充分理解 UART 串口通信的原理,我们先把 P3.0 和 P3.1 当做 IO 口来进行模拟实际串口通信的过程,原理搞懂后,我们再使用寄存器配置实现
    发表于 02-09 10:25 23次下载
    单片机IO口模拟<b class='flag-5'>UART</b><b class='flag-5'>串口</b>通信

    USB转单串口、多串口芯片选型UART.TTL.RS-232等

    沁恒微USB转单串口及多串口芯片选型表,和PIN TO PIN 型号表, 和串口、COM口、
    的头像 发表于 09-07 10:09 4937次阅读
    USB转单<b class='flag-5'>串口</b>、多<b class='flag-5'>串口</b>芯片选型<b class='flag-5'>UART</b>.TTL.RS-232等

    以8寸串口组成为例谈串口屏定制和模块取舍

    以8寸串口组成为例谈串口屏定制和模块取舍
    的头像 发表于 04-03 08:41 534次阅读
    以8寸<b class='flag-5'>串口</b>屏<b class='flag-5'>组成</b>为例谈<b class='flag-5'>串口</b>屏定制和模块取舍