问答 店铺
热搜: ZYNQ FPGA discuz

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

微信扫一扫 分享朋友圈

已有 103 人浏览分享

开启左侧

[MILIANPAI-F01-EG4D]FPGA程序设计基础实验连载-11 UART串口接收驱动设计

[复制链接]
103 0
安路-FPGA课程
安路课程: 基础入门 » 新手入门实验
安路系列: EG4
软件版本:TD_5.6.4_Release_97693
操作系统:WIN11 64bit
硬件平台:适用安路(Anlogic)FPGA
登录米联客”FPGA社区-www.uisrc.com视频课程、答疑解惑!
1概述
本章将学习 UART 通信的原理及其硬件电路设计,并使用FPGA实现UART串口接收控制器的设计。
在完成本实验前,请确保已经完成前面的实验,包括已经掌握以下能力:
1:完成了TD软件安装
2:完成了modelsim安装以及TD库的编译
3:掌握了TD仿真环境的设置
4:掌握了modelsim通过do文件启动仿真
实验目的:
1:实现UART串口发送控制器的设计
2:实现主程序中调用串口发送控制器发送字符“HELLO FPGA”
3:实用modelsim完成仿真验证
4:编译并且固化程序到FPGA验证
1.1 UART串口简介
UART串口通信是应用非常广泛的一种串行异步通信方式,常用的接口标准规范有RS232、RS422、RS485。
RS-232标准的串口最常见的接口类型为DB9(DB9 接口详细定义见上一课),但是笔记本电脑以及较新一点的台式机都没有DB9串口,它们一般通过 USB 转串口线来实现与外部设备的串口通信。比如我们常见的USB串口,就是通过USB接口芯片,实现了以TTL电平方式的UART串口通信。数据通过USB接口进行传输,通过UART串口芯片完成USB协议到UART串口协议的相互转换。
image.jpg image.jpg
                         DB9接口                           USB串口线
由于传统的DB9接口体积较大,会占用开发板过多空间,在开发板上我们采用的是Mini USB 接口,另一端直接和电脑 USB 相连。
image.jpg
                 Mini USB串口线
1.2 硬件电路分析
参照 “UART串口发送驱动设计”硬件电路分析部分
2 UART接收驱动设计2.1 系统框图
如下图所示,米联客设计的UART发送控制器包含4个主要模块:波特率发生器、抗干扰过采样模块、起始位检测模块,移位模块。

image.jpg

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

image.jpg

上图中,对接收数据进行过采样,对一个波特率位的数据以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

image.jpg

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

image.jpg

米联客的代码管理规范,在对应的FPGA工程路径下创建uisrc路径,并且创建以下文件夹
01_rtl:放用户编写的rtl代码
02_sim:仿真文件或者工程
03_ip:放使用到的ip文件
04_pin:fpgapin脚约束文件或者时序约束文件
05_boot:放编译好的bit或者bin文件(一般为空)
06_doc:放本一些相关文档(一般为空)

image.jpg

4 Modelsim仿真4.1 准备工作
Modelsim仿真的创建过程不再重复,如有不清楚的请看前面实验
仿真测试文件源码如下:
  1. `timescale 1ns / 1ns
  2. module uart_rx_tb( );
  3. reg   I_sysclk;
  4. reg   I_uart_rx;
  5. wire  [7:0]O_uart_rx_data;
  6. wire  O_uart_rvalid;
  7. reg   I_uart_rx_rstn;
  8. uart_top u_uart_top
  9. (
  10. .I_sysclk       (I_sysclk),
  11. .I_uart_rx_rstn (I_uart_rx_rstn),
  12. .I_uart_rx      (I_uart_rx),
  13. .O_uart_rx_data (O_uart_rx_data),
  14. .O_uart_rvalid  (O_uart_rvalid)
  15. );
  16. parameter FREQ      = 25000000;
  17. parameter BAUD      = 115200;
  18. parameter TBAUD     = FREQ/BAUD*40;//传输1bit所需时间
  19. initial
  20. begin
  21.       I_sysclk  = 0;
  22.                   I_uart_rx = 1'b1;
  23.       I_uart_rx_rstn = 1'b0;
  24.             #200 // Wait 200 ns for global reset to finish
  25.       I_uart_rx_rstn = 1'b1;
  26.                   #200 // Wait 200 ns for global reset to finish
  27.                     #TBAUD
  28.                     I_uart_rx = 1'b1;
  29.                     #TBAUD
  30.         I_uart_rx = 1'b0;//start
  31.         //1001_0101
  32.         //由低位至高位传输
  33.         #TBAUD
  34.         I_uart_rx = 1'b1;
  35.          #TBAUD
  36.         I_uart_rx = 1'b0;
  37.          #TBAUD
  38.         I_uart_rx = 1'b1;
  39.          #TBAUD
  40.         I_uart_rx = 1'b0;
  41.          #TBAUD
  42.         I_uart_rx = 1'b1;
  43.          #TBAUD
  44.         I_uart_rx = 1'b0;
  45.          #TBAUD
  46.         I_uart_rx = 1'b0;
  47.          #TBAUD
  48.         I_uart_rx = 1'b1;        
  49.          #TBAUD
  50.         I_uart_rx = 1'b1;//stop            
  51.          #808320
  52.         //00000101
  53.         I_uart_rx = 1'b0;//start
  54.         #TBAUD
  55.         I_uart_rx = 1'b1;
  56.          #TBAUD
  57.         I_uart_rx = 1'b0;
  58.          #TBAUD
  59.         I_uart_rx = 1'b1;
  60.          #TBAUD
  61.         I_uart_rx = 1'b0;
  62.          #TBAUD
  63.         I_uart_rx = 1'b0;
  64.           #TBAUD
  65.         I_uart_rx = 1'b0;
  66.           #TBAUD
  67.         I_uart_rx = 1'b0;
  68.           #TBAUD
  69.         I_uart_rx = 1'b0;        
  70.            #TBAUD
  71.         I_uart_rx = 1'b1;//stop   
  72.         
  73.            #808320
  74.       //10000100
  75.         I_uart_rx = 1'b0;//start
  76.         #TBAUD
  77.         I_uart_rx = 1'b0;
  78.         #TBAUD
  79.         I_uart_rx = 1'b0;
  80.         #TBAUD
  81.         I_uart_rx = 1'b1;
  82.         #TBAUD
  83.         I_uart_rx = 1'b0;
  84.         #TBAUD
  85.         I_uart_rx = 1'b0;
  86.         #TBAUD
  87.         I_uart_rx = 1'b0;
  88.         #TBAUD
  89.         I_uart_rx = 1'b0;
  90.         #TBAUD
  91.         I_uart_rx = 1'b1;           
  92.         #TBAUD
  93.         I_uart_rx = 1'b1;//stop                     
  94. end
  95. always  #20 I_sysclk = ~I_sysclk;
  96. endmodule
复制代码
4.2 启动modelsim仿真
启动后,右击需要观察的信号,添加到波形窗口

image.jpg

设置restart

image.jpg

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

image.jpg

image.jpg

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














您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

0

粉丝

293

主题
精彩推荐
热门资讯
网友晒图
图文推荐

  • 微信公众平台

  • 扫描访问手机版