软件版本: TD_ 5.6.4 _Release_ 97693
操作系统: WIN11 64bit
硬件平台:适用安路 (Anlogic)FPGA
1概述 本章将学习 UART 通信的原理及其硬件电路设计,并使用 FPGA 来 实现UART串口接收控制器的设计。
在完成本实验前,请确保已经完成前面的实验,包括已经掌握以下能力:
1:完成了 TD 软件安装
2:完成了 modelsim 安装以及 TD 库的编译
3:掌握了 TD 仿真环境的设置
4:掌握了 mode l sim通过 do 文件启动仿真
实验目的:
1:实现 UART 串口发送控制器的设计
2:实现主程序中调用串口发送控制器发送字符 “HELLO FPGA”
3:实用 modelsim 完成仿真验证
4:编译并且固化程序到 FPGA 验证
1.1 UART 串口简介 UART串口通信是应用非常广泛的一种串行异步通信方式 , 常用的 接口标准规范有RS232、RS42 2、RS485。
RS-232标准的串口最常见的接口类型为DB9(DB9 接口详细定义见上一课 ), 但是笔记本电脑以及较新一点的台式机都没有D B9串口,它们一般通过 USB 转串口线来实现与外部设备的串口通信。比如我们常见的 U SB串口,就是通过 U SB接口芯片,实现了以 T TL电平方式的 U ART串口通信。数据通过 U SB接口进行传输,通过 U ART串口芯片完成 U SB协议到 U ART串口协议的相互转换。
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 驱动源码 代码如下:
module uiuart_rx#
(
parameter integer BAUD_DIV = 10416
)
(
input I_clk,
input I_uart_rx_rstn,
input I_uart_rx,//uart rx 总线信号输入
output [7:0] O_uart_rdata,//uart rx接收到的数据输出
output O_uart_rvalid// uart rx 接收数据有效信号,为1时O_uart_rdata数据有效
);
localparam BAUD_DIV_SAMP = (BAUD_DIV/8)-1;//多次采样,按照波特率系数的八分之一进行采样
wire bps_en ; //波特率使能信号
wire samp_en ;//采样使能信号
wire bit_cap_done ; //uart rx总线信号采样有效数据完成
wire uarx_rx_done ;//uart 1byte 接收完成
wire bit_data ; //接收的1bit数据
wire I_uart_rxnt ;//I_uart_rx的启动信号检测,当变为低电平,代表可能存在起始位(UART起始位为低电平)
reg [13:0] baud_div = 14'd0;//波特率分频计数器
reg [13:0] samp_cnt = 14'd0;//采样计数器
reg [4 :0] I_uart_rx_r = 5'd0;//异步采集多次寄存
reg [3 :0] bit_cnt=4'd0;//bit 计数器
reg [3 :0] cap_cnt=4'd0;//cap 计数器
reg [4 :0] rx_bit_tmp = 5'd0;//rx_bit_tmp用于多次采样,通过计算采样到高电平次数和低电平次数,判断本次采样是高电平还是低电平
reg [7 :0] rx_data = 8'd0;//数据接收寄存器
reg bps_start_en_r = 1'b0;
reg bit_cap_done_r = 1'b0;
reg bps_start_en,start_check_done,start_check_failed;
assign bps_en = (baud_div == (BAUD_DIV - 1'b1));//完成一次波特率传输信号
assign samp_en = (samp_cnt == (BAUD_DIV_SAMP - 1'b1 ));//完成一次波特率采样信号
assign bit_cap_done = (cap_cnt == 3'd7);//采样计数
assign uarx_rx_done = (bit_cnt == 9)&&(baud_div == BAUD_DIV >> 1);
//当停止位开始,提前二分之一位,发送uart_rx_done信号,以便提前准备进入下一个数据的接收
//received bits finished half the baud rate time earlier
assign bit_data = (rx_bit_tmp < 5'd15) ? 0 : 1;//rx_bit_tmp用于多次采样,通过计算采样到高电平次数和低电平次数,判断本次采样是高电平还是低电平,提高抗干扰能力
//连续5次信号拉低,判断开始传输
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];//判断是否有起始位
assign O_uart_rdata = rx_data;
assign O_uart_rvalid = uarx_rx_done;
//波特率计数器
always@(posedge I_clk)begin
if(bps_start_en && baud_div < BAUD_DIV) //baud_div计数,目标值BAUD_DIV
baud_div <= baud_div + 1'b1;
else
baud_div <= 14'd0;
end
//8bit采样使能,8倍波特率采样,也就是这个计数器,用于产生8倍过采样
always@(posedge I_clk)begin
if(bps_start_en && samp_cnt < BAUD_DIV_SAMP) //bps_start_en高电平有效,开始对bit进行采样,samp_cnt以8倍于波特率速度对每个bit采样
samp_cnt <= samp_cnt + 1'b1;//samp_cnt计数+1
else
samp_cnt <= 14'd0; //samp_cnt计数清零
end
//uart rx bus asynchronous to Synchronous
always@(posedge I_clk)begin
I_uart_rx_r <= {I_uart_rx_r[3:0],I_uart_rx};//I_uart_rx的数据存入I_uart_rx_r进行缓存
end
//uart接收启动检查
always@(posedge I_clk)begin
if(I_uart_rx_rstn == 1'b0 || uarx_rx_done || start_check_failed)
//bps_start_en拉低的三种情况,复位、接收完成、校验失败
bps_start_en <= 1'b0;//接收结束
else if((I_uart_rxnt == 1'b0)&(bps_start_en==1'b0))
//当判断到I_uart_rx == 1'b0,并且总线之前空闲(bps_start_en==1'b0,代表总线空闲)
bps_start_en <= 1'b1;//使能波特率计数器使能
end
//uart接收启动使能
always@(posedge I_clk)begin
bps_start_en_r <= bps_start_en;//bps_start_en信号打一拍,方便后续上升沿捕捉
end
always@(posedge I_clk)begin
if(I_uart_rx_rstn == 1'b0 || start_check_failed)begin
//当系统复位,或者start_check_failed,重置start_check_done和start_check_failed
start_check_done <= 1'b0;
start_check_failed <= 1'b0;
end
else if(bps_start_en == 1'b1&&bps_start_en_r == 1'b0) begin
//当检测到start信号,也重置start_check_done和start_check_failed
start_check_done <= 1'b0;
start_check_failed <= 1'b0;
end
else if((bit_cap_done&&bit_cap_done_r==1'b0)&&(start_check_done == 1'b0))begin
//第一个波特率采样,用于判断是否一个有效的起始位,如果不是有效的,start_check_failed设置为1
start_check_failed <= bit_data ? 1'b1 : 1'b0;
start_check_done <= 1'b1;
//不管是否start_check_failed==1,都会设置start_check_done=1,但是start_check_failed==1,会下一个系统时钟重置start_check_done=0
end
end
//bits 计数器
always@(posedge I_clk)begin
if(I_uart_rx_rstn == 1'b0 || uarx_rx_done || bps_start_en == 1'b0)
bit_cnt <= 4'd0;
else if(bps_en)
bit_cnt <= bit_cnt + 1'b1;
end
//8次过采样,提高抗干扰
always@(posedge I_clk)begin
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
cap_cnt <= 4'd0;
rx_bit_tmp <= 5'd15;
end
else if(samp_en)begin//bit采样使能
cap_cnt <= cap_cnt + 1'b1;//cap_cnt用于记录了当前是第几次过采样,1个bit采样8次
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代表是低电平
end
end
//寄存一次bit_cap_done,用于产生高电平触发脉冲
always@(posedge I_clk)
bit_cap_done_r <= bit_cap_done;
//shift rx bit data
always@(posedge I_clk)begin
if(I_uart_rx_rstn == 1'b0 || bps_start_en == 1'b0)
rx_data <= 8'd0; //当复位或者总线空闲,重置rx_data
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上升沿有效,完成一次移位寄存
rx_data <= {bit_data,rx_data[7:1]}; //串并转换,将数据存入rx_data 中,共8位
end
endmodule 复制代码
Top 文件用于例化 uiuart_rx 模块,代码如下:
module uart_top
(
input I_sysclk,
input I_uart_rx,
input I_uart_rx_rstn,
output [7:0]O_uart_rx_data,
output O_uart_rvalid
);
uiuart_rx#
(
.BAUD_DIV(25000000/115200 -1)
)
uiuart_rx_u
(
.I_clk(I_sysclk),
.I_uart_rx_rstn(I_uart_rx_rstn),
.I_uart_rx(I_uart_rx),
.O_uart_rdata(O_uart_rx_data),
.O_uart_rvalid(O_uart_rvalid)
);
endmodule
复制代码
3 FPGA工程 fpga工程的创建过程不再重复,如有不清楚的请看前面实验,具体的 FPGA 型号以对应的开发板上芯片为准
米联客的代码管理规范 ,在对应的 FPGA 工程路径下创建 uisrc 路径,并且创建以下文件夹
01_rtl:放用户编写的 rtl 代码
02_sim:仿真文件或者工程
03_ip:放使用到的 ip 文件
04_pin:放 fpga 的 pin 脚约束文件或者时序约束文件
05_boot:放编译好的 bit 或者 bin 文件 ( 一般为空 )
06_doc:放本一些相关文档 ( 一般为空 )
4 Modelsim仿真 4. 1 准备工作 Modelsim仿真的创建过程不再重复,如有不清楚的请看前面实验
仿真测试文件源码如下:
复制代码
4. 2 启动 modelsim仿真 启动后,右击需要观察的信号,添加到波形窗口
设置 restart
设置运行 100 ms(如果运行时间太长可以修改小一些 )
本实验只完成仿真演示,在下一个实验中完成 uart收发环路实验。