UART串口通信——FPGA学习笔记9

news2024/11/16 5:26:34

一、数据通信基本概念

按数据通信方式分类:

串行通信、并行通信

按数据传输方向分类:

单工通信、半双工通信、全双工通信

按数据同步方式分类:

同步通信、异步通信

常见的串行通信接口:

二、串口通信:

        UART 是一种采用异步串行通信方式的通用异步收发传输器(universal asynchronous receiver-transmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

        UART 串口通信需要两根信号线来实现, 一根用于串口发送,另外一根负责串口接收。 UART 在发送或接收过程中的一帧数据由 4 部分组成,起始位、数据位、奇偶校验位和停止位,如图 19.1.1 所示。其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束, 数据位是一帧数据中的有效数据。 校验位分为奇校验和偶校验, 用于检验数据在传输过程中是否出错。 奇校验时, 发送方应使数据位中 1 的个数与校验位中 1 的个数之和为奇数;接收方在接收数据时, 对 1 的个数进行检查,若不为奇数,则说明数据在传输过程中出了差错。 同样,偶校验则检查 1 的个数是否为偶数。

        直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据。例如规定由起始位、数据位、奇偶校验位、停止位等。某些通讯中还需要双方约定数据的传输速率,以便更好地同步、

物理层:       

 UART 通信过程中的数据格式及传输速率是可设置的,为了正确的通信,收发双方应约定并遵循同样的设置。数据位可选择为 5、 6、 7、 8 位,其中 8 位数据位是最常用的, 在实际应用中一般都选择 8 位数据位;校验位可选择奇校验、偶校验或者无校验位;停止位可选择 1 位(默认) , 1.5 或 2 位。 串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是 bps(位/秒) ,常用的波特率有 9600、 19200、38400、 57600 以及 115200 等。

        在设置好数据格式及传输速率之后, UART 负责完成数据的串并转换, 而信号的传输则由外部驱动电路实现。 电信号的传输过程有着不同的电平标准和接口规范, 针对异步串行通信的接口标准有 RS232、 RS422、RS485 等, 它们定义了接口不同的电气特性,如 RS-232 是单端输入输出,而 RS-422/485 为差分输入输出等。

        RS232 接口标准出现较早, 可实现全双工工作方式,即数据发送和接收可以同时进行。在传输距离较短时(不超过 15m) , RS232 是串行通信最常用的接口标准, RS-232 标准的串口最常见的接口类型为 DB9,样式如图 19.1.2 所示,工业控制领域中用到的工控机一般都配备多个串口, 很多老式台式机也都配有串口。

协议层:

UART通信协议

起始位:一帧的开始,必须保持一个比特位的低电平0

数据位:传输的有效数据,数据位可选5~8位;LSB(低位)在前,MSB(高位)在后

校验位:可选位,占用1个比特位,也可以没有校验

停止位:一帧的结束,必须有,可选占用0.5/1/1.5/2个比特位,保持逻辑高电平1

UART的传输速率:波特率

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

计算:

例如通信速率115200         那么一个比特位所占用的时间为\frac{1x10^{9}ns}{115200}=8680ns

如果系统时钟为50MHz(周期20ns),那么每个周期占用时间为8680/20=434个时钟周期。

三、程序设计

1、实验任务

        本节的实验任务是上位机通过串口调试助手发送数据给开发板,开发板通过USB UART串口接收数据并将接收到的数据发送给上位机,完成串口数据环回。UART通信波特率:115200,停止位:1,数据位:8位,无校验位。

2、框图分析

 

接收模块要有接收完成标志

 发送模块要求有忙信号标志

串口通信实现思维导图:

针对异步信号的同步处理——打三拍

什么是亚稳态?亚稳态是由于违背了触发器的建立和保持时间而产生的。寄存器采样需要满足一定的建立时间(setup)和保持时间(holdup),而异步电路没有办法保证建立时间(setup)和保持时间(holdup),所以会出现亚稳态。

消除亚稳态

3、波形分析

接收模块:

发送模块:

四、代码编写

1、 串口接收部分

`timescale 1ns / 1ps

module uart_rx(
input               sys_clk,
input               sys_rst_n,
input               uart_rxd,                   //串口输入信号
output  reg         uart_rx_done,               //串口接收完成信号
output  reg  [7:0]  uart_rx_data                //串口接收数据
);


parameter  CLK_FREQ      = 50_000_000;        //时钟
parameter  UART_BPS      = 115200;            //波特率
localparam BAUD_CNT_MAX  = CLK_FREQ / UART_BPS;   //50Mhz 115200

reg             uart_rx_d0  ;       
reg             uart_rx_d1  ;
reg             uart_rx_d2  ;
reg             rx_flag     ;       //高 在进行读取
reg   [3:0]     rx_cnt      ;       //位数计数器
reg   [15:0]    baud_cnt    ;       //波特率计数器
reg   [7:0]     rx_data_t   ;       //临时寄存器

wire        start_en    ;  //开始信号  

assign start_en = uart_rx_d2 &(!uart_rx_d1)&(!rx_flag);

//数据打拍
always @(posedge sys_clk or negedge sys_rst_n ) begin
    if(!sys_rst_n)begin
        uart_rx_d0  <= 1'b0     ;
        uart_rx_d1  <= 1'b0     ;
        uart_rx_d2  <= 1'b0     ;
    end
    else begin
        uart_rx_d0  <= uart_rxd    ;
        uart_rx_d1  <= uart_rx_d0  ;
        uart_rx_d2  <= uart_rx_d1  ;
    end
end

//给接收标志rx_flag赋值
always @(posedge sys_clk or negedge sys_rst_n ) begin
    if (!sys_rst_n) begin
        rx_flag <= 1'b0;
    end
    else if (start_en == 1'b1) begin
        rx_flag <= 1'b1;
    end
    //计数到9,在停止位一半的时候拉低
    else if ((rx_cnt == 4'd9)&&(baud_cnt == BAUD_CNT_MAX/2 - 1'b1)) begin
        rx_flag <= 1'b0;
    end
    else begin
        rx_flag <= rx_flag;
    end
end

//对波特率计数器baud_cnt 赋值
always @(posedge sys_clk or negedge sys_rst_n ) begin
    if (!sys_rst_n) begin
        baud_cnt <= 16'd0;
    end
    else if (rx_flag == 1'b1) begin
        if(baud_cnt < BAUD_CNT_MAX - 1'b1)begin
            baud_cnt <= baud_cnt + 16'b1;
        end
        else begin
            baud_cnt <= 16'd0;
        end
    end
    else begin
        baud_cnt <= 16'd0;    
    end
end

//对数据计数器rx_cnt 赋值
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        rx_cnt <= 4'd0;
    end
    else if(rx_flag == 1'b1)begin
        if (baud_cnt == BAUD_CNT_MAX - 1'b1) begin
            rx_cnt <= rx_cnt + 4'b1;
        end
        else begin
            rx_cnt <= rx_cnt;
        end
    end
    else begin
        rx_cnt <= 4'd0;       
    end
end

//根据rx_cnt寄存输入数据
always @(posedge sys_clk or negedge sys_rst_n  ) begin
    if (!sys_rst_n) begin
        rx_data_t <= 8'd0;
    end
    else if (rx_flag == 1'b1) begin
        if(baud_cnt == BAUD_CNT_MAX/2 - 1'b1)begin
            case (rx_cnt)
                4'd1: rx_data_t[0] <= uart_rx_d2;
                4'd2: rx_data_t[1] <= uart_rx_d2;
                4'd3: rx_data_t[2] <= uart_rx_d2;
                4'd4: rx_data_t[3] <= uart_rx_d2;
                4'd5: rx_data_t[4] <= uart_rx_d2;
                4'd6: rx_data_t[5] <= uart_rx_d2;
                4'd7: rx_data_t[6] <= uart_rx_d2;
                4'd8: rx_data_t[7] <= uart_rx_d2;
                default: ;
            endcase
        end
        else begin
            rx_data_t <= rx_data_t;
        end
    end
    else begin
        rx_data_t <= 8'd0;   
    end
end

//给接收完成信号uart_rx_done 和接收uart_rx_data 赋值
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 ((rx_cnt == 4'd9)&&(baud_cnt == BAUD_CNT_MAX/2 - 1'b1)) begin
        uart_rx_done <= 1'b1;
        uart_rx_data <= rx_data_t;
    end
    else begin
        uart_rx_done <= 1'b0;
        uart_rx_data <= uart_rx_data;        
    end
end

endmodule

2、串口发送部分

`timescale 1ns / 1ps
module uart_tx(
input           sys_clk,
input           sys_rst_n,
input           uart_tx_en,             //串口发送数据使能
input   [7:0]   uart_tx_data,           //串口准备发送的数据
output  reg     uart_txd,               //串口输出
output  reg     uart_tx_busy            //串口忙的标志
);

parameter  CLK_FREQ      = 50_000_000;              //时钟
parameter  UART_BPS      = 115200;                  //波特率
localparam BAUD_CNT_MAX  = CLK_FREQ / UART_BPS;     //50Mhz 115200

reg     [7:0]   tx_data_t;
reg     [3:0]   tx_cnt;
reg     [15:0]  baud_cnt    ;       //波特率计数器

//当uart_tx_en为高时    寄存输入的并行数据   且拉高busy信号
always @(posedge sys_clk or negedge sys_rst_n ) begin
    if (!sys_rst_n) begin
        tx_data_t       <= 8'd0;
        uart_tx_busy    <= 1'b0;
    end
    else if (uart_tx_en == 1'b1) begin
        tx_data_t       <= uart_tx_data;
        uart_tx_busy    <= 1'b1;
    end
    else if ((tx_cnt == 4'd9)&&(baud_cnt == BAUD_CNT_MAX - BAUD_CNT_MAX/16)) begin
        tx_data_t       <= 8'd0;
        uart_tx_busy    <= 1'b0;        
    end
    else begin
        tx_data_t       <=  tx_data_t     ;
        uart_tx_busy    <=  uart_tx_busy  ;      
    end
end

//对波特率计数器baud_cnt 赋值
always @(posedge sys_clk or negedge sys_rst_n ) begin
    if (!sys_rst_n) begin
        baud_cnt <= 16'd0;
    end
    else if (uart_tx_busy == 1'b1) begin
        if(baud_cnt < BAUD_CNT_MAX - 1'b1)begin
            baud_cnt <= baud_cnt + 16'b1;
        end
        else begin
            baud_cnt <= 16'd0;
        end
    end
    else begin
        baud_cnt <= 16'd0;    
    end
end

//对数据计数器tx_cnt 赋值
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        tx_cnt <= 4'd0;
    end
    else if(uart_tx_busy == 1'b1)begin
        if (baud_cnt == BAUD_CNT_MAX - 1'b1) begin
            tx_cnt <= tx_cnt + 4'b1;
        end
        else begin
            tx_cnt <= tx_cnt;
        end
    end
    else begin
        tx_cnt <= 4'd0;       
    end
end

//根据tx_cnt控制TXD信号
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n) begin
        uart_txd <= 1'd1;
    end
    else if (uart_tx_busy == 1'b1) begin
        case (tx_cnt)
            4'd0: uart_txd <= 1'b0;             //起始位
            4'd1: uart_txd <= tx_data_t[0];     //d0
            4'd2: uart_txd <= tx_data_t[1];     //d1
            4'd3: uart_txd <= tx_data_t[2];     //d2
            4'd4: uart_txd <= tx_data_t[3];     //d3
            4'd5: uart_txd <= tx_data_t[4];     //d4
            4'd6: uart_txd <= tx_data_t[5];     //d5
            4'd7: uart_txd <= tx_data_t[6];     //d6
            4'd8: uart_txd <= tx_data_t[7];     //d7
            4'd9: uart_txd <= 1'b1;             //停止位
            default: uart_txd <= 1'b1;
        endcase
    end
    else begin
        uart_txd <= 1'b1;
    end
end
    
endmodule

3、顶层代码

`timescale 1ns / 1ps

module uart_loopback(
input           sys_clk,
input           sys_rst_n,

//UART端口
input           uart_rxd,
output          uart_txd

);

parameter  CLK_FREQ      = 50_000_000   ;              //时钟
parameter  UART_BPS      = 115200       ;                  //波特率

wire           uart_rx_done ;
wire    [7:0]  uart_rx_data ;

uart_rx #(
    .CLK_FREQ   (CLK_FREQ),
    .UART_BPS   (UART_BPS)  
    )
    u_uart_rx(
        .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)
);

uart_tx #(
    .CLK_FREQ   (CLK_FREQ),
    .UART_BPS   (UART_BPS) 
    )
    u_uart_tx(
        .sys_clk        (sys_clk     )  ,
        .sys_rst_n      (sys_rst_n   )  ,
        .uart_tx_en     (uart_rx_done)  ,  
        .uart_tx_data   (uart_rx_data)  ,
        .uart_txd       (uart_txd    )  ,    
        .uart_tx_busy   ()  
);


endmodule

 4、tb代码

`timescale 1ns / 1ps

module tb_uart_loopbak( );

parameter       CLK_PERIOD = 20;

reg     sys_clk;
reg     sys_rst_n;
reg     uart_rxd;
wire    uart_txd;

initial begin
    sys_clk <= 1'b0;
    sys_rst_n <= 1'b0;
    uart_rxd <= 1'b1;   //空闲状态
    #200
    sys_rst_n <= 1'b1;
    #1000
    //发送0x55   8'b0101_0101
    uart_rxd  <= 1'b0;  //起始位
    #8680
    uart_rxd  <= 1'b1;  //d0
    #8680
    uart_rxd  <= 1'b0;  //d1
    #8680
    uart_rxd  <= 1'b1;  //d2
    #8680
    uart_rxd  <= 1'b0;  //d3
    #8680
    uart_rxd  <= 1'b1;  //d4
    #8680
    uart_rxd  <= 1'b0;  //d5
    #8680
    uart_rxd  <= 1'b1;  //d6
    #8680
    uart_rxd  <= 1'b0;  //d7
    #8680
    uart_rxd  <= 1'b1;  //停止位
    #8680
    uart_rxd <= 1'b1;   //空闲状态
end

always #(CLK_PERIOD/2) sys_clk = !sys_clk;
    
uart_loopback u_uart_loopback(
.sys_clk        (sys_clk  ) ,
.sys_rst_n      (sys_rst_n) , 
.uart_rxd       (uart_rxd ) ,
.uart_txd       (uart_txd ) 
);

endmodule

5、管脚定义 

五、仿真与程序验证

1、Modelsim仿真

仿真结果符合预期结果。

2、下载验证

最终结果符合预期。

六、源码获取

私聊笔者获取源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2104678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cobalt Strike 4.8 用户指南-第六节-Payload Artifacts和反病毒规避

Fortra 经常回答有关规避的问题。Cobalt Strike 会绕过反病毒产品吗&#xff1f;它绕过了哪些反病毒产品&#xff1f;多久检查一次&#xff1f; Cobalt Strike 默认Artifact可能会被大多数端点安全解决方案拦截。尽管规避不是Cobalt Strike产品默认的目标&#xff0c;但Cobalt…

记一次cms的web渗透测试练习

点击文章发现 尝试进行sql注入 得到漏洞为数字型 通过order by语句得到字段总数为15 通过union联合查询得到显示的字段编号 将database()带入得到数据库名为cms 接下来就是查表名、字段名、具体数据 发现密码经过加密&#xff0c;尝试解密 通过解密得知admin账户的密码为12…

潮玩宇宙斗兽场游戏源码多个猿猴boss模式玩法开发成品案例代码示例

潮玩宇宙中的斗猿场是很多潮玩人都喜欢玩的一种大逃杀类游戏&#xff0c;玩法大概是这样的&#xff0c;玩家选择一个格子躲避恶猿&#xff0c;满足人数后恶猿会按随机数顺时针前行&#xff0c;在某个格子停留后杀死里面所有猴子&#xff0c;被踩中的格子内的玩家则会被恶猿杀掉…

glsl着色器学习(十)缩放

对二维图形进行缩放&#xff0c;需要用到顶点着色器&#xff0c;顶点着色器经过矩阵变换&#xff0c;会将模型空间最终转换成裁剪空间。下面就来操作矩阵 这里需要用到一个库glMatrix。 首先修改顶点着色器 <script id"vertex-shader-2d" type"x-shader/x-…

【AI大模型】2024 大模型爆发年,这些教程足够你入局AI大模型,搭上行业风口!

如果说 2023 年被称为 AI 元年&#xff0c;那么 2024 年就正式步入了爆发年&#xff01;几乎身边的每个程序员/每个产品经理都在思考&#xff1a;如何入局AI大模型&#xff1f;怎样才能成为这只风口上的猪&#xff1f; 作为一个普通人&#xff0c;应该如何入局&#xff1f;怎样…

多面体随旋转发出不同色彩效果

多面体随旋转发出不同色彩效果 下载地址&#xff1a;https://download.csdn.net/download/Allen7474/89713552 效果图&#xff1a; 实现细节&#xff1a; >>阶段1&#xff1a; >>阶段2&#xff1a; >>阶段3&#xff1a;

微软Azure OpenAI 集成矢量化

Azure AI Search中已全面支持集成矢量化与 Azure OpenAI 嵌入。 这标志着我们持续致力于简化和加快检索增强生成 (RAG)和传统应用程序的数据准备和索引创建的重要里程碑。 集成矢量化简化了 RAG 流程 参考链接&#xff1a;微软Azure OpenAI 免费试用申请 为什么矢量化很重要…

VBA中类的解读及应用第十五讲:让文本框在激活时改变颜色(上)

《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。 类&#xff0c;是非常抽象的&#xff0c;更具研究的价值。随着我们学习、应用VBA的深入&#xff0…

css设置让整个盒子的内容渐变透明(非颜色渐变透明)

css设置让整个盒子的内容渐变透明&#xff08;非颜色渐变透明&#xff09; 效果核心css代码 效果 核心css代码 /* 设置蒙版上下左右渐变显示 */ mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 10%, rgba(0, 0, 0, 1) 90%, rgba(0, 0, 0, 0) 1…

使用命令行窗口新建一个Java文件,输出HelloWorld

进入桌面 cd desktop在桌面新建文件夹 mkdir zwt进入文件夹&#xff0c;新建.Java文件 cd zwt echo. > helloWorld.java使用记事本打开文件编写代码 notepad HelloWorld.java此时电脑就会自动打开文件&#xff0c;直接编写代码 public class HelloWorld(){public stati…

北京君正低功耗MCU芯片应用在柯氏音法电子血压计

汉王科技&#xff0c;作为一家在中关村有着30年品牌历史的人工智能企业&#xff0c;始终致力于利用先进技术提升人们的生活品质。近日&#xff0c;继血压计KSY-FF660上市掀起血压测量新标准后&#xff0c;汉王科技再次突破行业创新&#xff0c;推出一体式柯氏音法电子血压计FY7…

高速信号真的可以参考电源层吗?

信号电流必须具有完整的环路&#xff0c;才可传递信息。完整的环路即信号由驱动器发出&#xff0c;经导线传输至接收器&#xff0c;然后再通过某种途径由接收器返回驱动器&#xff0c;形成闭环回路。   高速信号的参考平面一般为信号GND平面&#xff0c;是否可以参考电源平面…

使用iframe在Vue中实现ChatGPT嵌入及微前端解决方案

更多内容个人网站&#xff1a;孔乙己大叔 在现代Web开发中&#xff0c;iframe&#xff08;Inline Frame&#xff09;是一个强大的工具&#xff0c;它允许开发者在一个HTML文档中嵌入另一个HTML文档。这种技术不仅可用于简单的网页内容嵌入&#xff0c;还广泛应用于复杂的应用集…

C++基础之杂项

目录 思维导图&#xff1a; 学习内容&#xff1a; 1. Lambda表达式 1.1 基本概念 1.2 定义格式 1.3 常用情况 二、异常处理 2.1 什么是异常处理 2.2 何时使用异常处理 2.3 异常处理的格式 2.4 异常实例 2.5 构造和析构中的异常 2.6 系统提供异常类 三、C中文件…

Arduino基础入门学习——使用BH1750(GY-302)光照强度传感器获取光照强度

使用BH1750&#xff08;GY-302&#xff09;光照强度传感器获取光照强度 一、前言二、BH1750&#xff08;GY-302&#xff09;介绍三、准备工作四、程序代码五、运行结果六、结束语 一、前言 话不多说&#xff0c;先给大家来一句鸡汤&#xff08;鸡汤来咯&#xff01;&#xff09…

人工智能中常用的python模块

一&#xff1a;pypinyin 作用&#xff1a;汉字转拼音 pip3 install pypinyin使用 from pypinyin import lazy\_pinyin,TONE2,TONEres \ lazy\_pinyin(先帝创业未半) print(res) #\[xian, di, chuang, ye, wei, ban\] res \ lazy\_pinyin(先帝创业未半,styleTONE2) print(re…

什么是网络威胁情报?

目录 网络威胁情报定义 网络威胁情报的优势 常见攻击指标(IOC) 数据与情报 谁从威胁情报中受益&#xff1f; 我的组织是否具备威胁情报能力&#xff1f; 全面网络威胁情报的价值 提供威胁情报的三种方式 战略威胁情报 战术威胁情报 运营威胁情报 威胁情报解决方案中…

【主机入侵检测】开源安全平台Wazuh之Wazuh Server

引言 Wazuh是一个开源的、免费的企业级安全监控解决方案&#xff0c;专注于威胁检测、完整性监控、事件响应和合规性。它由部署在受监控系统的端点安全代理和管理服务器组成&#xff0c;服务器收集并分析代理收集的数据。Wazuh支持多平台&#xff0c;包括Windows、Linux、macOS…

Qt (14)【Qt窗口 —— 文件对话框 QFileDialog】

阅读导航 引言一、文件对话框 QFileDialog简介二、基本用法1. 打开文件&#xff08;一次只能打开一个文件&#xff09;2. 打开多个文件&#xff08;一次可以打开多个文件&#xff09;3. 保存文件⭕参数说明 三、使用示例四、注意事项 引言 在之前的文章中&#xff0c;我们学习…

【Java】封装

文章目录 前言一、封装是什么&#xff1f;总结 前言 了解封装&#xff0c;运用起来。 一、封装是什么&#xff1f; 封装&#xff1a;就是隐藏对象的属性和实现细节&#xff0c;仅对外提供公共访问方式。 专业术语有点难以理解&#xff0c;但是可以用通俗易懂的例子来理解&am…