首页 > 其他分享 >10 UART串口发送驱动设计

10 UART串口发送驱动设计

时间:2023-12-29 11:22:52浏览次数:38  
标签:10 end tx UART 发送 uart wreq 串口

软件版本:VIVADO2021.1

操作系统:WIN10 64bit

硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA

登录米联客(MiLianKe)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!

1 概述

UART串口通信是应用非常广泛的一种串行异步通信方式,常用的异步串口包括RS232\RS482\RS485。

RS232的逻辑1的电平为-3~-15V,逻辑0的电平为+3~+15V,下图是老式的DB9串口线。

标准的DB9接口定义如下:

序号

DB9公头

DB9母头

1

CD

载波检测

CD

载波检测

2

RXD

接收端接收

TXD

发送端发送数据

3

TXD

发送数据

RXD

接收端接收数据

4

DTR

数据终端就绪

DTR

数据终端就绪

5

GND

系统接地

GND

系统接地

6

DSR

数据准备就绪

DSR

数据准备就绪

7

RTS

发送请求

CTS

清除请求

8

CTS

清除发送

RTS

发送请求

9

RI

振铃指示器

RI

振铃指示器

实际上一般RS232只使用到TXD RXD两个信号。

RS485/422采用差分信号负逻辑,逻辑"1"以两线间的电压差为-(2~6)V表示;逻辑"0"以两线间的电压差为+(2~6)V表示,采用差分方式可以具有更高的速度和更强的抗干扰能力,具有更远的传输距离。由于RS485/RS422更多出现在工业场合,因此一般采用接线端子方式接线。

老式DB9串口基本上已经消失了,但是UART串口更加广泛地使用,比如我们常见的USB串口,就是通过USB接口芯片,实现了以TTL电平方式的UART串口通信。数据通过USB接口进行传输,通过UART串口芯片完成USB协议到UART串口协议的相互转换。

实验目的:

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

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

3:完成仿真验证

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

2 UART发送驱动设计

2.1 系统框图

如下图所示,米联客设计的UART发送控制器包含3个主要模块:波特率发生器,发送使能模块,移位模块,其中移位模块中包含了移位计数器和移位控制器。

2.2 UART发送时序

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

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

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

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

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

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

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

2.3 驱动接口时序图

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

xxx_wreq:数据发送请求

xxx_wdata:需要发送的数据

xxx_wbusy:数据发送忙状态指示

这里xxx代表了uart

2.4 驱动源码

代码如下:

/*******************************UART发送驱动器*********************

--以下是米联客设计的UART发送驱动器

--1.代码简洁,占用极少逻辑资源,代码结构清晰,逻辑设计严谨

--2.I_uart_wreq,用户程序通过设置I_uart_wreq为高,请求UART发送驱动器,发送数据,UART发送驱动器把I_uart_wdata数据通过UART TX总线发送出去

--3.O_uart_wbusy,表示当前UART发送总线整忙,这个时候用户程序需要等待非忙的时候,请求发送数据。*********************************************************************/

 

`timescale 1ns / 1ns//仿真时间刻度/精度

 

module uiuart_tx#

(

 parameter integer BAUD_DIV     = 10416                           //设置采样系数 (时钟/采样率-1)

)

(

    input        I_clk,//系统时钟输入

    input        I_uart_rstn,//系统复位输入

    input        I_uart_wreq, //发送数据请求  

    input [7:0] I_uart_wdata, //发送数据      

    output      O_uart_wbusy,//发送状态忙,代表正在发送数据  

    output      O_uart_tx//uart tx 发送总线

);

 

localparam  UART_LEN = 4'd10; //设置uart 发送的bit数量为10,代表1bit起始位,8bits数据,1bit停止位

wire        bps_en ; //发送使能

reg         uart_wreq_r   = 1'b0;//寄存一次I_uart_wreq

reg         bps_start_en    = 1'b0; //波特率计数器启动使能,也是发送启动使能

reg [13:0]  baud_div        = 14'd0;//波特率计数器

reg [9 :0]  uart_wdata_r  = 10'h3ff;//寄存I_uart_wreq

reg [3 :0]  tx_cnt          = 4'd0;//计数发送了多少bits

 

assign O_uart_tx = uart_wdata_r[0];//总线上的数据,始终是uart_wdata_r[0]

assign O_uart_wbusy = bps_start_en;//总线忙标志,即是bps_start_en为有效,即当总线忙于发送,总线忙

 

// 发送使能

assign bps_en = (baud_div == BAUD_DIV);                 //产生一次发送使能信号,条件是baud_div == BAUD_DIV,波特率计数达成

 

//波特率计数器

always@(posedge I_clk )begin

    if((I_uart_rstn== 1'b0) || (I_uart_wreq==1'b1&uart_wreq_r==1'b0))begin

        baud_div <= 14'd0;

    end

    else begin

        if(bps_start_en && baud_div < BAUD_DIV)        //bps_start_en的信号拉高,表示开始发送

           baud_div <= baud_div + 1'b1;                //且baud_div < BAUD_DIV波特率计算,未达到波特率baud_div+1

        else

            baud_div <= 14'd0;                         //达到清零

    end

end

 

always@(posedge I_clk)begin

    uart_wreq_r <= I_uart_wreq;                           //寄存一次I_uart_wreq信号

end

 

//当I_uart_wreq从低电平变为高电平,启动发送

always@(posedge I_clk)begin

    if(I_uart_rstn == 1'b0)

        bps_start_en    <= 1'b0;                           //复位,计数清零

    else if(I_uart_wreq==1'b1&uart_wreq_r==1'b0)          //I_uart_wreq上升沿激活

        bps_start_en    <= 1'b1;                           //激活后将 bps_start_en拉高,传输开始

    else if(tx_cnt == UART_LEN)                 //tx_cnt用于计数当前发送的bits数量,当达到预定值UART_LEN

        bps_start_en    <= 1'b0;                           //将 bps_start_en拉低,传输结束

    else

        bps_start_en    <= bps_start_en;                    

end

 

 

//发送bits计数器

always@(posedge I_clk)begin

    if(((I_uart_rstn== 1'b0) || (I_uart_wreq==1'b1&uart_wreq_r==1'b0))||(tx_cnt == 10))//当复位、启动发送、发送完成,重置tx_cnt

        tx_cnt <=4'd0;

    else if(bps_en && (tx_cnt < UART_LEN))   //tx_cnt计数器,每发送一个bit加1

        tx_cnt <= tx_cnt + 1'b1;

end

 

//uart发送并串移位控制器

always@(posedge I_clk)begin

    if((I_uart_wreq==1'b1&uart_wreq_r==1'b0)) //当发送请求有效,寄存需要发送的数据到uart_wdata_r

        uart_wdata_r  <= {1'b1,I_uart_wdata[7:0],1'b0};//寄存需要发送的数据,包括1bit 起始位,8bits数据,1bit停止位

    else if(bps_en && (tx_cnt < (UART_LEN - 1'b1)))                               //shift 9 bits

        uart_wdata_r <= {uart_wdata_r[0],uart_wdata_r[9:1]};         //并串转换,将并行数据依次传输

    else

        uart_wdata_r <= uart_wdata_r;

end  

endmodule

3 UART用户数据发送模块

3.1 发送流程图

用户发送模块负责把数据听过UART驱动模块发送出去。

用户发送模块

3.2 用户数据发送源码

`timescale 1ns / 1ns

 

module uart_top

(

input  I_sysclk,//系统时钟输入

output O_uart_tx //UART串口发送总线

);

 

wire     uart_rstn_i; //内部同步复位

wire     uart_wbusy;  //UART发送驱动器正忙

reg      t1s_dly_en;  //1S延迟

reg[1:0] S_UART_TX;   //UART 发送状态机

reg[3:0] tx_index;    //发送index计数器

reg      uart_wreq;   //UART发送请求  

reg[7:0] uart_wdata;  //UART发送数据寄存器

reg[7:0] uart_tx_buf[0:11]; //发送缓存

 

reg [15:0]rst_cnt = 16'd0; //复位计数器

reg [24:0]delay_cnt = 25'd0; //延迟计数器

 

assign uart_rstn_i = rst_cnt[15]; //复位

 

//上电通过计数器计数,实现复位

always @(posedge I_sysclk)begin

    rst_cnt <= (rst_cnt[15] == 1'b0) ? (rst_cnt + 1'b1) : rst_cnt ;

end

 

//帧延迟计数器

always @(posedge I_sysclk)begin

    if(t1s_dly_en == 1'b0)

        delay_cnt <= 25'd0;

    else

        delay_cnt <= delay_cnt + 1'b1;

end

 

//数据发送状态机

always @(posedge I_sysclk)begin

    if(uart_rstn_i==1'b0)begin //初始化uart_tx_buf,为hello fpga等字符共计12 BYTES,以及其他寄存器

        uart_tx_buf[0]  <=8'h48;//h

        uart_tx_buf[1]  <=8'h45;//e

        uart_tx_buf[2]  <=8'h4c;//l

        uart_tx_buf[3]  <=8'h4c;//l

        uart_tx_buf[4]  <=8'h4f;//o

        uart_tx_buf[5]  <=8'h20;//space

        uart_tx_buf[6]  <=8'h46;//f

        uart_tx_buf[7]  <=8'h50;//p

        uart_tx_buf[8]  <=8'h47;//g  

        uart_tx_buf[9]  <=8'h41;//a      

        uart_tx_buf[10] <=8'h0d;//Enter

        uart_tx_buf[11] <=8'h0a;//newline

 

        uart_wdata      <= 8'd0;

        uart_wreq       <= 1'b0;

        S_UART_TX       <= 2'd0;

        t1s_dly_en      <= 1'b0;

        tx_index        <= 4'd0;

    end

    else begin

        case(S_UART_TX)

        0:begin

            if(!uart_wbusy)begin//如果UART发送驱动器不忙

                uart_wdata <= uart_tx_buf[tx_index];//准备发送数据,发送tx_index所指向的数据

                uart_wreq <= 1'b1; //设置uart_wreq为高电平,请求发送数据

            end

            else begin //当总线忙

                uart_wreq <= 1'b0; //重置uart_wreq

                S_UART_TX <= 2'd1; //进入下一状态

            end

        end

        1:begin//该状态等待总线空闲

            S_UART_TX <= (uart_wbusy == 1'b0) ? 2'd2: S_UART_TX;

        end

        2:begin//更新tx_index计数器

            if(tx_index < 11)begin //每一帧发送12个字节

                tx_index <= tx_index + 1'b1; //tx_index 加计数

                S_UART_TX  <= 2'd0; //进入下一状态

            end

            else begin //如果tx_index==11 代表所有数据发送完毕

                tx_index   <= 4'd0; //重置tx_index

                t1s_dly_en <= 1'b1; //1s 延迟计数器开始计数

                S_UART_TX  <= 2'd3; //下一状态

            end

        end

        3:begin//登台延迟计数器计数

            if(delay_cnt[24] == 1'b1)begin //这里的1S不是精确的,使用delay_cnt[24]可以节省逻辑资源

                S_UART_TX <= 2'd0;   //回到状态0

                t1s_dly_en <= 1'b0;  //关闭1S延迟计数器

            end

            else  //否则还是在当前状态等待

               S_UART_TX <= S_UART_TX;  

        end        

        endcase

    end  

     

end

 

//例化UART 发送驱动器模块

uiuart_tx#

(

.BAUD_DIV(50_000_000/115200-1)  //波特率计算    BAUD_DIV = 系统时钟/波特率-1

)

uart_tx_u

(

.I_clk(I_sysclk),//系统时钟输入

.I_uart_rstn(uart_rstn_i), //系统复位输入

.I_uart_wreq(uart_wreq), //UART发送(写)数据请求

.I_uart_wdata(uart_wdata), //UART发送(写)数据

.O_uart_wbusy(uart_wbusy),//UART发送驱动器忙

.O_uart_tx(O_uart_tx) //UART 发送串行总线

);

     

endmodule

4 FPGA工程

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

 

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

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

02_sim:仿真文件或者工程

03_ip:放使用到的ip文件

04_pin:放fpga的pin脚约束文件或者时序约束文件

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

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

5 RTL仿真

5.1 添加仿真测试源码

仿真测试文件存放在工程目录uisrc\02_sim中,源码如下:

`timescale 1ns/1ns  //定义仿真时间刻度/精度

 

module uart_top_tb();

   

localparam      SYS_TIME   =  'd20;//时钟周期,以ns为单位

reg              I_sysclk;          //系统时钟

 

//例化顶层模块

uart_top uart_top_inst

(

.I_sysclk(I_sysclk),

.O_uart_tx(O_uart_rx)

);

 

//仿真初始化

initial begin  

I_sysclk =0;

 

 #2000000 $finish;          

end

   

always #(SYS_TIME/2) I_sysclk = ~I_sysclk;    //产生主时钟

 

endmodule

5.2 开始仿真

选择我们主程序信号

 

添加进观察窗

删除多余的信号

点击run按键,开始仿真

观察发送程序的仿真结果准确无误

6 添加PIN脚XDC文件

配套工程的 FPGA PIN 脚定义路径为 fpga_prj/uisrc/04_pin/ fpga_pin.xdc。

7 下载演示

下载程序前,先确保FPGA工程已经编译。

7.1 硬件连接

(该教程为通用型教程,教程中仅展示一款示例开发板的连接方式,具体连接方式以所购买的开发板型号以及结合配套代码管脚约束为准。)

请确保下载器和开发板已经正确连接,并且开发板已经上电(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)

7.2 运行结果

标签:10,end,tx,UART,发送,uart,wreq,串口
From: https://www.cnblogs.com/milianke/p/17934408.html

相关文章

  • Cisco Secure Firewall 3100 Series, Firepower Threat Defense (FTD) Software 7.4.1
    CiscoSecureFirewall3100Series,FirepowerThreatDefense(FTD)Software7.4.1&ASASoftware9.20.2FirepowerThreatDefense(FTD)Software作者主页:sysin.org为什么选择CiscoSecure防火墙?CiscoSecure防火墙为行业最完善和开放的安全平台提供基础支持。世界一流的......
  • Cisco Firepower 2100 Series FTD Software 7.4.1 & ASA Software 9.20.2
    CiscoFirepower2100SeriesFTDSoftware7.4.1&ASASoftware9.20.2FirepowerThreatDefense(FTD)Software作者主页:sysin.org为什么选择CiscoSecure防火墙?CiscoSecure防火墙为行业最完善和开放的安全平台提供基础支持。世界一流的安全控制保护网络免受日益复杂的威胁......
  • Cisco Firepower 1000 Series FTD Software 7.4.1 & ASA Software 9.20.2
    CiscoFirepower1000SeriesFTDSoftware7.4.1&ASASoftware9.20.2作者主页:sysin.org面向小型办公室的企业级保护在企业发展的过程中为企业保驾护航。Firepower1000系列提供高性能、易用性、深入的可视性与可控性,可快速检测和阻止威胁。该系列在设计上优化了安全服务,而不会......
  • 东北师范大学 计算机2010课程表
    东北师范大学 计算机学院(研究生)课程表2010学年春季学期班次       项目 星   节   期    次   2009年级       计算机软件与理论 专业  课            程学分教  师课程类别教室地点星期一......
  • win10安装linux
    1.下载linux系统:win10,打开应用商店 2.下载linux系统:输入Linux点击搜索 3.下载linux系统:在这里我选择的是ubuntu18.04LTS,如图所示,下载完成后,应用商店已下载页面可以找到已下载的ubuntu,在其后有“启动”按钮,此时不要点击。4.设置开发者选项——进入个性化菜单在桌面空白处右......
  • 初中英语优秀范文100篇-041Computer Improves My English Study-电脑有助于我英语学习
    PDF格式公众号回复关键字:SHCZFW041记忆树1Nowadays,wecannotlivewithoutcomputersforoneday.翻译现在,我们一天都无法离开电脑。简化记忆电脑句子结构1Nowadays是副词,表示“现在”,作状语。2wecannotlivewithoutcomputersforoneday是主句,表示“一天也......
  • 学期2023-2024-1 20231310 《计算机基础与程序设计》第十四周学习总结
    作业信息这个作业属于哪个课程2023-2024-1-计算机基础与程序设计这个作业要求在哪里2023-2024-1计算机基础与程序设计第十四周作业这个作业的目标《C语言程序设计》第13章并完成云班课测试作业正文https://www.cnblogs.com/wang-hoNbang/p/17933629.html教......
  • OS-Ubuntu-Server-Connect to Wi-Fi From Terminal on Debian 11/10 with WPA Supplic
    ConnecttoWi-FiFromTerminalonDebian11/10withWPASupplicantLastUpdated:November8th,2022XiaoGuoan(Admin)31CommentsDebianThistutorialisgoingtoshowyouhowtoconnecttoWi-FinetworkfromthecommandlineonDebian11/10serverandd......
  • 乡村青年小永的AIGC变现之旅:从第002天开始,挑战100天极限
    2023年12月28日:乡村青年小永的AIGC变现之旅:从第002天开始,挑战100天极限!AIGC学习+挑战+变现真实记录第一天,持续更新大家好,我是小永,一个来自贵州毕节小镇的中专生。这个地方可能很多人并不熟悉,但对我来说,这是我成长的地方,是我磨练自我的地方。中专毕业后,我尝试过各种工作,无论是......
  • 代码随想录算法训练营第十六天 |104.二叉树的最大深度,559.n叉树的最大深度,111.二叉树
    一、104.二叉树的最大深度题目链接:LeetCode104.二叉树的最大深度学习:思路:分别求左子树和右子树的高度,返回给根结点,加1之后是根结点的深度,这是后序遍历的思路二、559.n叉树的最大深度题目链接:LeetCode559.N叉树的最大深度学习前:思路:后序遍历。分别所有孩子结点的深......