[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-11 UART串口接收驱动设计

软件版本:Anlogic -TD5.9.1-DR1_ES1.1

操作系统:WIN10 64bit

硬件平台:适用安路(Anlogic)FPGA

实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板

板卡获取平台:https://milianke.tmall.com/

登录"米联客"FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!

 

1概述

本章将学习 UART 通信的原理及其硬件电路设计,并使用FPGA来实现UART串口接收控制器的设计。

在完成本实验前,请确保已经完成前面的实验,包括已经掌握以下能力:

1:完成了TD软件安装

2:完成了modelsim安装以及TD库的编译

3:掌握了TD仿真环境的设置

4:掌握了modesim通过do文件启动仿真

 

实验目的:

1:实现UART串口发送控制器的设计

2:实现主程序中调用串口发送控制器发送字符"HELLO FPGA"

3:实用modelsim完成仿真验证

4:编译并且固化程序到FPGA验证

1.1 UART串口简介

UART串口通信是应用非常广泛的一种串行异步通信方式,常用的接口标准规范有RS232RS422RS485

RS-232标准的串口最常见的接口类型为DB9(DB9 接口详细定义见上一课),但是笔记本电脑以及较新一点的台式机都没有DB9串口,它们一般通过USB 转串口线来实现与外部设备的串口通信。比如我们常见的USB串口,就是通过USB接口芯片,实现了以TTL电平方式的UART串口通信。数据通过USB接口进行传输,通过UART串口芯片完成USB协议到UART串口协议的相互转换。

DB9接口USB串口线

由于传统的DB9接口体积较大,会占用开发板过多空间,在开发板上我们采用的是Mini USB 接口,另一端直接和电脑USB 相连。

Mini USB串口线

1.2硬件电路分析

参照 "UART串口发送驱动设计"硬件电路分析部分

 

2 UART接收驱动设计

2.1系统框图

如下图所示,米联客设计的UART发送控制器包含4个主要模块:波特率发生器、抗干扰过采样模块、起始位检测模块,移位模块。

2.2 UART接收时序

下图中,UART串口通信数据格式包括1bit起始位、8bits数据位、1bit停止位,不包含奇偶校验位。

 

上图中,对接收数据进行过采样,对一个波特率位的数据以8倍波特率采样,并且判断其中7次。多次采样可以提高总线的抗干扰能力。

 

在开始编写串口接收驱动前,我们需要了解下以下概念:

波特率:UART采用异步通信方式,数据首发双方只有在同一波特率才才能正常通信。波特率代表了UART完成1个时间单位数据位或者控制位的时间。通常,我们需要对系统时钟进行分频来产生正确的波特率,所以计算分频系统尤为重要,比如系统时钟是25000000HZ,波特率是115200,那么分频系数为=25000000/115200-1

 

起始位:UART数据总线由高电平变低电平并且持续1个波特率时间代表数据的起始

 

数据位:每个数据位占用1个波特率时间,本文实验发送1BYTE字节需要占用8个波特率时间。

 

停止位:如果没有奇偶校验位,数据位结束后,保持1/1.5/2个波特率的高电平代表了停止位。

 

奇偶校验:用于校对数据,对于UART通信,可以根据实际情况选择是否需要支持奇偶校验

2.3驱动接口时序图

米联客设计了一种通用简洁的驱动接口,包含以下信号:

xxx_rdata:接收的数据

xxx_rvalid:接收的有效数据

这里xxx代表了uart

2.4驱动源码

代码如下:

 

  1 `timescale 1ns / 1ns//仿真时间刻度/精度
  2 
  3 module uiuart_rx#
  4 (
  5  parameter integer  BAUD_DIV     = 10416  //波特率分频参数,BAUD_DIV=系统时钟频率/波特率-1 比如100M系统时钟,波特率115200 BAUD_DIV= 100_000_000/115200-1
  6 )
  7 (
  8 input I_clk, //系统时钟输入
  9 input I_uart_rx_rstn,//系统复位输入
 10 input I_uart_rx,//uart rx 总线信号输入
 11 output [7:0] O_uart_rdata,//uart rx接收到的数据输出
 12 output O_uart_rvalid// uart rx 接收数据有效信号,当为1的时候O_uart_rdata数据有效
 13 );
 14 
 15 localparam  BAUD_DIV_SAMP = (BAUD_DIV/8)-1;                            //多次采样,按照波特率系数的八分之一进行采样
 16 
 17 wire bps_en       ; //波特率使能信号
 18 wire samp_en      ; //采样使能信号
 19 wire bit_cap_done ; //uart rx总线信号采样有效数据完成
 20 wire uart_rx_done ; //uart 1byte 接收完成
 21 wire bit_data     ; //接收的1bit数据
 22 wire I_uart_rxnt  ; //I_uart_rxnt的启动信号检测,当变为低电平,代表可能存在起始位(UART 起始位为低电平)
 23 
 24 reg [13:0]  baud_div = 14'd0;//波特率分频计数器
 25 reg [13:0]  samp_cnt = 14'd0;//采样计数器
 26 reg [4 :0]  I_uart_rx_r = 5'd0;//异步采集多次寄存
 27 reg [3 :0]  bit_cnt=4'd0;//bit 计数器
 28 reg [3 :0]  cap_cnt=4'd0;//cap 计数器
 29 reg [4 :0]  rx_bit_tmp = 5'd0;//rx_bit_tmp用于多次采样,通过计算采样到高电平次数和低电平次数,判断本次采样是高电平还是低电平
 30 reg [7 :0]  rx_data = 8'd0;//数据接收寄存器
 31 
 32 reg bps_start_en_r = 1'b0;
 33 reg bit_cap_done_r = 1'b0;
 34 reg bps_start_en,start_check_done,start_check_failed;
 35 
 36 assign bps_en       =   (baud_div == (BAUD_DIV - 1'b1));                     //完成一次波特率传输信号
 37 assign samp_en      =   (samp_cnt == (BAUD_DIV_SAMP - 1'b1 ));               //完成一次波特率采样信号
 38 assign bit_cap_done =   (cap_cnt  == 3'd7);//采样计数
 39 assign uart_rx_done =   (bit_cnt  == 9)&&(baud_div == BAUD_DIV >> 1);//当停止位开始,提前二分之一位,发送uart_rx_done信号,以便提前准备进入下一个数据的接收
 40 
 41 assign bit_data     =   (rx_bit_tmp < 5'd15) ? 0 : 1; //rx_bit_tmp用于多次采样,通过计算采样到高电平次数和低电平次数,判断本次采样是高电平还是低电平,提高抗干扰能力
 42 //连续5次信号拉低,判断开始传输
 43 assign I_uart_rxnt  =   I_uart_rx_r[4] | I_uart_rx_r[3] | I_uart_rx_r[2] | I_uart_rx_r[1] | I_uart_rx_r[0];
 44 assign O_uart_rdata   =   rx_data;
 45 assign O_uart_rvalid  =   uart_rx_done;   
 46 
 47 //波特率计数器
 48 always@(posedge I_clk)begin
 49     if(bps_start_en && baud_div < BAUD_DIV)                 //baud_div计数,目标值BAUD_DIV 
 50         baud_div <= baud_div + 1'b1;
 51     else 
 52         baud_div <= 14'd0;
 53 end
 54 
 55 //8bit采样使能,8倍波特率采样,也就是这个计数器,用于产生8倍过采样
 56 always@(posedge I_clk)begin
 57     if(bps_start_en && samp_cnt < BAUD_DIV_SAMP)             //bps_start_en高电平有效,开始对bit进行采样,samp_cnt以8倍于波特率速度对每个bit采样
 58         samp_cnt <= samp_cnt + 1'b1;                         //samp_cnt计数+1       
 59     else 
 60         samp_cnt <= 14'd0;                                   //samp_cnt计数清零
 61 end
 62 
 63 //uart rx bus asynchronous to Synchronous
 64 always@(posedge I_clk)begin 
 65     I_uart_rx_r <= {I_uart_rx_r[3:0],I_uart_rx};             //I_uart_rx的数据存入I_uart_rx_r进行缓存
 66 end
 67 
 68 //uart接收启动检查
 69 always@(posedge I_clk)begin
 70     if(I_uart_rx_rstn == 1'b0 || uart_rx_done || start_check_failed) //bps_start_en拉低的三种情况,复位、接收完成、校验失败
 71         bps_start_en    <= 1'b0;                                               //接收结束
 72     else if((I_uart_rxnt == 1'b0)&(bps_start_en==1'b0))//当判断到I_uart_rxnt == 1'b0,并且总线之前空闲(bps_start_en==1'b0,代表总线空闲)
 73         bps_start_en    <= 1'b1;//使能波特率计数器使能
 74 end
 75 
 76 //uart接收启动使能
 77 always@(posedge I_clk)begin
 78         bps_start_en_r    <= bps_start_en;                              //bps_start_en信号打一拍,方便后续上升沿捕捉
 79 end
 80 
 81 always@(posedge I_clk)begin
 82     if(I_uart_rx_rstn == 1'b0 || start_check_failed)begin//当系统复位,或者start_check_failed,重置start_check_done和start_check_failed
 83         start_check_done    <= 1'b0;
 84         start_check_failed  <= 1'b0;
 85     end    
 86     else if(bps_start_en == 1'b1&&bps_start_en_r == 1'b0) begin//当检测到start信号,也重置start_check_done和start_check_failed
 87         start_check_done    <= 1'b0;
 88         start_check_failed  <= 1'b0;
 89     end
 90     else if((bit_cap_done&&bit_cap_done_r==1'b0)&&(start_check_done == 1'b0))begin//第一个波特率采样,用于判断是否一个有效的起始位,如果不是有效的,start_check_failed设置为1
 91         start_check_failed <= bit_data ? 1'b1 : 1'b0;
 92         start_check_done   <= 1'b1;//不管是否start_check_failed==1,都会设置start_check_done=1,但是start_check_failed==1,会下一个系统时钟重置start_check_done=0
 93     end     
 94 end
 95 
 96 //bits 计数器
 97 always@(posedge I_clk)begin
 98     if(I_uart_rx_rstn == 1'b0 || uart_rx_done || bps_start_en == 1'b0)//复位、接收完成、或者总线空闲(bps_start_en == 1'b0),重置bit_cnt
 99         bit_cnt   <= 4'd0;                                                    
100     else if(bps_en)//每一个bps_en有效,加1
101         bit_cnt <= bit_cnt + 1'b1;  // bit_cnt计数器用于计算当前采样了第几个bit 
102 end
103 
104 //8次过采样,提高抗干扰
105 always@(posedge I_clk)begin
106     if(I_uart_rx_rstn == 1'b0 || bps_en == 1'b1 || bps_start_en == 1'b0) begin //当I_uart_rx_rstn=0或者bps_en=1或者bps_start_en==0,重置cap_cnt和rx_bit_tmp
107         cap_cnt     <= 4'd0;
108         rx_bit_tmp  <= 5'd15; 
109     end
110     else if(samp_en)begin//bit采样使能
111         cap_cnt     <= cap_cnt + 1'b1;//cap_cnt用于记录了当前是第几次过采样,1个bit采样8次
112         rx_bit_tmp  <= I_uart_rx_r[4] ? rx_bit_tmp + 1'b1 :  rx_bit_tmp - 1'b1;   //多次采样,如果是高电平+1,如果是低电平-1,最终看本次bit采样结束rx_bit_tmp如果小于15代表是低电平
113     end                                                                                   
114 end
115 
116 //寄存一次bit_cap_done,用于产生高电平触发脉冲下面用到
117 always@(posedge I_clk)
118     bit_cap_done_r <= bit_cap_done;
119 
120 always@(posedge I_clk)begin
121     if(I_uart_rx_rstn == 1'b0 || bps_start_en == 1'b0)//当复位或者总线空闲,重置rx_data
122         rx_data  <= 8'd0;  
123     else if(start_check_done&&(bit_cap_done&&bit_cap_done_r==1'b0)&&bit_cnt < 9)//当start_check_done有效,并且bit_cnt<9,每次bit_cap_done有效,完成一次移位寄存
124         rx_data  <= {bit_data,rx_data[7:1]};                                         //串并转换,将数据存入rx_data 中,共8位
125 end
126 
127 endmodule

 

 

 

 

3 FPGA工程

fpga工程的创建过程不再重复,如有不清楚的请看前面实验,具体的FPGA型号以对应的开发板上芯片为准

米联客的代码管理规范,在对应的FPGA工程路径下创建uisrc路径,并且创建以下文件夹

01_rtl:放用户编写的rtl代码

02_sim:仿真文件或者工程

03_ip:放使用到的ip文件

04_pin:fpgapin脚约束文件或者时序约束文件

05_boot:放编译好的bit或者bin文件(一般为空)

06_doc:放本一些相关文档(一般为空)

4 Modelsim仿真

4.1准备工作

Modelsim仿真的创建过程不再重复,如有不清楚的请看前面实验

 

仿真测试文件源码如下:

 

 

 1 module uart_top_tb();
 2  
 3 localparam      BPS          = 'd115200     ;             //波特率
 4 localparam      CLK_FRE    = 'd50_000_000   ;     //系统频率
 5 localparam      CLK_TIME   =  'd500_000_000 /CLK_FRE;//计算系统时钟周期,以ns为单位
 6 localparam      BIT_TIME   = 'd500_000_000  / BPS ; //计算出传输每个bit所需要的时间以ns为单位
 7 localparam      NUM_BYTES  = 3;            //需要发送的BYTES
 8 
 9 reg               I_sysclk;         //系统时钟
10 reg               bsp_clk ;     //波特率时钟
11 reg               O_uart_tx;      //uart 数据发送,该信号接入到,FPGA的uart 接收
12 wire             I_uart_rx;      //uart 数据接收,该信号接入到,FPGA的uart 发送
13 reg [8*NUM_BYTES-1:0] uart_send_data; //需要发送的数据
14 reg [7:0]             uart_send_data_r; //寄存每次需要发送的BYTE
15 
16 integer i,j;
17 
18 //例化顶层模块
19 uart_top uart_top_inst
20 (
21 .I_sysclk(I_sysclk),
22 .I_uart_rx(O_uart_tx),
23 .O_uart_tx(I_uart_rx)
24 );
25 
26 //仿真初始化
27 initial begin   
28 
29 //初始化REG寄存器
30 I_sysclk =0;
31 bsp_clk  = 0;  
32 O_uart_tx  = 1;
33 i=0;
34 j=0;
35 
36 uart_send_data   =0;
37 uart_send_data_r =0;
38 
39 #20000;//延迟20000ns,等待uart测试代码中的复位延迟
40 
41 uart_send_data[(0*8) +: 8] = 8'b1001_0101;//初始化需要发送的第1个BYTE
42 uart_send_data[(1*8) +: 8] = 8'b0000_0101;//初始化需要发送的第2个BYTE
43 uart_send_data[(2*8) +: 8] = 8'b1000_0100;//初始化需要发送的第3个BYTE
44 
45 //uart tx 发送数据
46   for(i=0; i<NUM_BYTES;i=i+1)
47   begin
48 
49       uart_send_data_r = uart_send_data[(i*8) +: 8];//寄存需要发送的数据到寄存器
50       $display("uart_send_data : 0x%h",uart_send_data_r);//打印准备发送的数据
51 
52       @(posedge bsp_clk);  //发送起始位1bit
53       O_uart_tx = 1'b0;
54 
55       for(j=0;j<8;j=j+1)begin//发送数据8bits
56       @(posedge bsp_clk);  //发送
57       O_uart_tx = uart_send_data_r[j];
58       end
59 
60        @(posedge bsp_clk);//发送停止位1bit
61        O_uart_tx = 1'b1;  
62 
63   end
64        @(posedge bsp_clk); 
65        #200 $finish;            
66 end
67  
68 always #(CLK_TIME/2) I_sysclk = ~I_sysclk;    //产生主时钟
69 always #(BIT_TIME/2) bsp_clk  = ~bsp_clk;       //产生波特率时钟
70  
71 
72 endmodule

 

4.2启动modelsim仿真

启动后,右击需要观察的信号,添加到波形窗口

设置restart

设置运行100ms(如果运行时间太长可以修改小一些)

本实验只完成仿真演示,在下一个实验中完成uart收发环路实验。

posted @ 2024-07-29 15:00  米联客(milianke)  阅读(51)  评论(0编辑  收藏  举报