ZYNQ初识10(zynq_7010)UART通信实验

基于bi站正点原子讲解视频:

系统框图(基于串口的数据回环)如下:

        以下,是串口接收端的波形图,系统时钟和波特率时钟不同,为异步时钟,,需要先延时两拍,将时钟同步过来,取到start_flag信号,由start_flag信号结合clk_cnt、bps_cnt两个计数器取到rx_flag信号,随后在rx_flag高电平时计算clk_cnt以及bps_cnt两个信号。最后两个信号uart_done、uart_data则在串口发送模块有所体现。

        实际上,uart_done在串口发送模块中也就是uart_en信号,而uart_data也就是发送模块中的uart_din信号。

`timescale 1ns / 1ps
// Create Date: 2025/01/06 09:38:08
// Design Name: 
// Module Name: uart_recv
module uart_recv(input              sys_clk   ,    //50Mhz系统时钟input              sys_rst_n ,input              uart_rxd  ,   //接收到的数据output  reg [7:0]  uart_data  ,   //输出的并行数据output  reg        uart_done     //一帧信号接收完成);parameter  sys_freq = 50_000_000;parameter  uart_bps = 115_200;parameter  bps_cnt  = sys_freq/uart_bps - 1;//从0开始计算reg         uart_rxd_d0;reg         uart_rxd_d1;wire        start_flag;reg         rx_flag;reg  [15:0] clk_cnt;reg  [3:0]  rx_cnt;reg  [7:0]  rx_data;//中间变量存储提取到的每个位的数据来实现串口端的串并转换//由高电平向低电平的跳变(下降沿),相当于d1延时2个时钟周期;d0延时1个时钟周期//因此判断d1是否为高且d0是否为低即可。assign  start_flag = uart_rxd_d1 & (~uart_rxd_d0);//异步时钟同步化处理always@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginuart_rxd_d0 <= 1'b1;uart_rxd_d1 <= 1'b1;endelse beginuart_rxd_d0 <= uart_rxd;uart_rxd_d1 <= uart_rxd_d0;endend//rx_flagalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)rx_flag <= 1'b0;else if(start_flag == 1'b1)rx_flag <= 1'b1;else if((rx_cnt == 4'd9) && (clk_cnt == bps_cnt/2 - 1'b1)) //为监测到下一帧数据的起始位留半个周期的时间rx_flag <= 1'b0;else rx_flag <= 1'b1;end//clk_cntalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)clk_cnt <= 16'd0;else if(rx_flag == 1'b1)
//            begin   //效果相同否?
//                if(clk_cnt == uart_bps)
//                    clk_cnt <= 16'd0; 
//                else 
//                    clk_cnt <= clk_cnt + 16'd1;
//            endbegin                               if(clk_cnt < uart_bps - 16'd1)         clk_cnt <= clk_cnt + 16'd1;           else                            clk_cnt <= 16'd0; end                                                                     else clk_cnt <= 16'd0;  end//rx_cntalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)rx_cnt <= 4'd0;else if(rx_flag == 1'b1)beginif(clk_cnt == uart_bps - 16'd1)rx_cnt <= rx_cnt + 4'd1;else rx_cnt <= rx_cnt;endelserx_cnt <= 4'd0;end//rx_dataalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)rx_data <= 8'd0;else if((rx_flag == 1'b1)&&(clk_cnt == uart_bps/2)) begincase(rx_cnt)//数据的串转并_uart_rxd是异步信号//所以要需要两拍之后的信号uart_rxd_d1。4'd1: rx_data[0] <= uart_rxd_d1;4'd2: rx_data[1] <= uart_rxd_d1;4'd3: rx_data[2] <= uart_rxd_d1;4'd4: rx_data[3] <= uart_rxd_d1;4'd5: rx_data[4] <= uart_rxd_d1;4'd6: rx_data[5] <= uart_rxd_d1;4'd7: rx_data[6] <= uart_rxd_d1;4'd8: rx_data[7] <= uart_rxd_d1;endcaseendendalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginuart_data <= 8'd0;uart_done <= 1'd0;endelse if(rx_cnt == 4'd9)begin                       uart_data <= rx_data;    uart_done <= 1'd1;    end   elsebegin                 uart_data <= 8'd0;  uart_done <= 1'd0;  end                               end
endmodule

 以下:如何在程序中捕获信号的高低电平:(可实现异步时钟的同步处理以及信号的边沿检测)

d0 <= 1'b1;
d1 <= 1'b1;

d0 <= uart_rxd;
d1 <= d0; 

        也就是说,在初始状态时将d0、d1拉高,通过两次打拍将d0延时1个时钟周期,d1延时2个时钟周期,从而根据d0和d1的状态,设置wire型的标志位flag来捕获需要的高低电平。

          以下,是是串口发送端的波形图,整体过程和接收端基本类似,只需要进行某些变量名称的修改即可,但需要注意,接收到的数据实际上是串口接收端的发送的数据(uart_data);uart_done信号也在发送端检测边沿电平过程中以uart_en的形式完成了两次打拍延迟。

`timescale 1ns / 1ps
// Create Date: 2025/01/06 14:50:27
// Design Name: 
// Module Name: uart_sendmodule uart_send(input         sys_clk   ,    //50Mhz系统时钟  input         sys_rst_n ,                 output  reg   uart_txd  ,   //准备发送的数据      input         uart_en  ,       input   [7:0] uart_din     //从发送模块接收到的数据    );parameter  sys_freq = 50_000_000;parameter  uart_bps = 115_200;parameter  bps_cnt  = sys_freq/uart_bps - 1;//从0开始计算reg          uart_en_d0;reg          uart_en_d1; reg          tx_flag;reg  [15:0]  clk_cnt;reg  [3:0]   tx_cnt; reg  [7:0]   tx_data;//中间变量存储提取到的每个位的数据来实现串口端的串并转换wire         en_flag;//由高电平向低电平的跳变(下降沿),相当于d1延时2个时钟周期;d0延时1个时钟周期//因此判断d1是否为高且d0是否为低即可。assign  en_flag = (~uart_en_d1) & uart_en_d0;//边沿信号检测,检测上升沿always@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginuart_en_d0 <= 1'b1;uart_en_d1 <= 1'b1;endelse beginuart_en_d0 <= uart_en;uart_en_d1 <= uart_en_d0;endend//tx_dataalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)tx_data <= 8'd0;          else if(en_flag == 1'b1) tx_data <= uart_din;else tx_data <= tx_data;end//tx_flagalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)tx_flag <= 1'b0;else if(en_flag == 1'b1)tx_flag <= 1'b1;else if((tx_cnt == 4'd9) && (clk_cnt == bps_cnt/2 - 1))tx_flag <= 1'b0;else //tx_flag <= tx_flag; //这两句话在这里作用相同否?tx_flag <= 1'b1;end//clk_cntalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)clk_cnt <= 16'd0;else if(tx_flag == 1'b1)
//            begin 
//                if(clk_cnt == uart_bps)
//                    clk_cnt <= 16'd0; 
//                else 
//                    clk_cnt <= clk_cnt + 16'd1;
//            endbegin                               if(clk_cnt < uart_bps - 16'd1)         clk_cnt <= clk_cnt + 16'd1;           else                            clk_cnt <= 16'd0; end                                                                     else clk_cnt <= 16'd0;  end//tx_cntalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)tx_cnt <= 4'd0;else if(tx_flag == 1'b1)beginif(clk_cnt == uart_bps - 16'd1)tx_cnt <= tx_cnt + 4'd1;else tx_cnt <= tx_cnt;endelserx_cnt <= 4'd0;end//uart_txdalways@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)uart_txd <= 1'b1;else if((tx_flag == 1'b1)&&(clk_cnt == 16'd0)) begincase(tx_cnt)//数据并转串,首先判断起始位,赋值低电平;//随后将tx_data一次赋值给输出端口uart_txd;//最后注意一帧数据的停止位,赋值高电平。4'd0: uart_txd <= 1'b0;4'd1: uart_txd <= tx_data[0];4'd2: uart_txd <= tx_data[1];4'd3: uart_txd <= tx_data[2];4'd4: uart_txd <= tx_data[3];4'd5: uart_txd <= tx_data[4];4'd6: uart_txd <= tx_data[5];4'd7: uart_txd <= tx_data[6];4'd8: uart_txd <= tx_data[7];4'd9: uart_txd <= 1'b1;endcaseendendendmodule

以下是顶层文件,分别将串口发送端和接收端两部分程序例化。

`timescale 1ns / 1ps
// Create Date: 2025/01/06 15:49:15
// Design Name: 
// Module Name: top_uartmodule top_uart(input    sys_clk   ,    //50Mhz系统input    sys_rst_n ,             input    uart_rxd  ,   //接收到的数据output   uart_txd      //准备发送的数据 );wire  [7:0]  uart_data;wire         uart_done;//串口接收模块的例化:uart_recv  uart_recv_u(.sys_clk    (sys_clk   )  ,    //50Mhz系统时钟.sys_rst_n  (sys_rst_n )  ,.uart_rxd   (uart_rxd  )  ,   //接收到的数据.uart_data  (uart_data )  ,   //输出的并行数据.uart_done  (uart_done )     //一帧信号接收完成);//     wire         uart_en ;
//     wire  [7:0]  uart_din;//串口发送模块的例化;uart_send  uart_send_u(.sys_clk   (sys_clk  )   ,    //50Mhz系统时钟  .sys_rst_n (sys_rst_n)   ,                 .uart_txd  (uart_txd )   ,   //准备发送的数据      .uart_en   (uart_done )  ,       .uart_din  (uart_data )      //从发送模块接收到的数据    );endmodule

以下是对应程序的RTL视图:

另注意1:

 打两拍的异步时钟的同步处理,打一拍是为了将异步时钟转为同步时钟,如上本例子是将波特率时钟(115200bps)转换为系统时钟50Mhz,打两拍则是为了消除亚稳态(0 1之间的状态)。

参考连接:【Chips】跨时钟域的亚稳态处理、为什么要打两拍不是打一拍、为什么打两拍能有效?_跨时钟域为什么打两拍-CSDN博客

另注意2:

为什么在程序中最后1位的停止位需要延时半个周期而不是一个完整的周期?也就是发送模块的如下程序:

always@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)tx_flag <= 1'b0;else if(en_flag == 1'b1)tx_flag <= 1'b1;else if((tx_cnt == 4'd9) && (clk_cnt == bps_cnt/2 - 1))//停止位只有半个波特率周期tx_flag <= 1'b0;else //tx_flag <= tx_flag; //tx_flag <= 1'b1;end

         首先延时半个周期是为了给下一帧数据接收时检测起始位留够时间,在数据回环过程中不会出现太大问题,但是如果单独调用串口的发送模块(或者接收模块)时就会出现问题,在单独调用发送模块时,因为停止位只有半个周期,所以发送1帧数据过程总会先于上位机正常接收而提前结束,造成错误。

        所以选择将停止位周期设置为1个时钟周期,但是实际上位机的波特率和实际计算的波特率仍有微小误差,在将停止位设置为1个时钟周期时,上位机(串口助手)的波特率实际会和自己设置的波特率(如9600,115200等)存在或多或少的误差,因此需要将停止位周期设置为1/2--1之间,和上位机什么时候检测到1帧数据的停止位有关。

另附加上视频中给出得数据环回模块的程序:

`timescale 1ns / 1ps
// Create Date: 2025/01/07 10:50:37
// Intrduction:  将串口接收到的数据进行缓存,消除上位机和串口模块之间的时间误差;
// Module Name: uart_loopmodule uart_loop(input              sys_clk    ,    //50Mhz系统时input              sys_rst_n  , input              recv_done  ,   //接收完成信号  input        [7:0] recv_data  ,   //接收到的数据                                             input              tx_busy    ,output  reg        send_en    ,   output  reg  [7:0] send_data     //待发送数据);reg    recv_done_d0  ;reg    recv_done_d1  ;reg    tx_ready      ;wire   recv_done_flag;  //捕获上升沿assign  recv_done_flag =  (~recv_done_d0) & recv_done_d1;//打两拍实现边沿检测always@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginrecv_done_d0 <= 1'b0;recv_done_d1 <= 1'b0;endelse begin                    recv_done_d0 <= recv_done;recv_done_d1 <= recv_done_d0;end                                                    end//判断接收完成信号,并在串口发送模块空闲时发送是使能信号always@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)begintx_ready  <= 1'b0;send_en   <= 1'b0;  send_data <= 8'd0;endelsebeginif(recv_done_flag)   begintx_ready  <= 1'b1;     send_en <= 1'b0;       send_data <= recv_data;                      end     else if(tx_ready&&(~tx_busy))begintx_ready <= 1'b0;send_en  <= 1'b1;endendend   endmodule

再者,vivado中串口默认波特率115200bps,在调试过程需要注意,如哦需要修改可参考下链接:

 https://zhuanlan.zhihu.com/p/633150036

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java小知识点总结

一、比特流的本质就是数组 二、位运算 位运算就是CPU的底层原理&#xff0c;半导体电路进行位运算 位运算涉及一些算法&#xff0c;&和^ ^ 异或 两变量交换值&#xff0c;不依赖第三个变量 x^s k 异或知道两者就能推另一个 a a<<2就是乘以2的多少次方 相反 a…

vue3后台系统动态路由实现

动态路由的流程&#xff1a;用户登录之后拿到用户信息和token&#xff0c;再去请求后端给的动态路由表&#xff0c;前端处理路由格式为vue路由格式。 1&#xff09;拿到用户信息里面的角色之后再去请求路由表&#xff0c;返回的路由为tree格式 后端返回路由如下&#xff1a; …

贪心算法笔记

贪心算法笔记 大概内容 贪心就是对于一个问题有很多个步骤,我们在每一个步骤中都选取最优的那一个,最后得出答案。就是在一些函数中可行,但是有些比如二次函数,因为它的转折点不一定最优,就是不可行的。那么如何判断贪心呢?有这么几种 看时间复杂度,一般的就是 O ( n…

CVE-2025-22777 (CVSS 9.8):WordPress | GiveWP 插件的严重漏洞

漏洞描述 GiveWP 插件中发现了一个严重漏洞&#xff0c;该插件是 WordPress 最广泛使用的在线捐赠和筹款工具之一。该漏洞的编号为 CVE-2025-22777&#xff0c;CVSS 评分为 9.8&#xff0c;表明其严重性。 GiveWP 插件拥有超过 100,000 个活跃安装&#xff0c;为全球无数捐赠平…

ubuntu官方软件包网站 字体设置

在https://ubuntu.pkgs.org/22.04/ubuntu-universe-amd64/xl2tpd_1.3.16-1_amd64.deb.html搜索找到需要的软件后&#xff0c;点击&#xff0c;下滑&#xff0c; 即可在Links和Download找到相关链接&#xff0c;下载即可&#xff0c; 但是找不到ros的安装包&#xff0c; 字体设…

细说STM32F407单片机以DMA方式读写外部SRAM的方法

目录 一、工程配置 1、时钟、DEBUG、GPIO、CodeGenerator 2、USART3 3、NVIC 4、 FSMC 5、DMA 2 &#xff08;1&#xff09;创建MemToMem类型DMA流 &#xff08;2&#xff09;开启DMA流的中断 二、软件设计 1、KEYLED 2、fsmc.h、fsmc.c、dma.h、dma.c 3、main.h…

二分查找算法——山脉数组的峰顶索引

一.题目描述 852. 山脉数组的峰顶索引 - 力扣&#xff08;LeetCode&#xff09; 二.题目解析 题目给了我们一个山脉数组&#xff0c;山脉数组的值分布就如下面的样子&#xff1a; 然后我们只需要返回数组的峰值元素的下标即可。 三.算法原理 1.暴力解法 因为题目明确说明…

重塑视频创作的格局!ComfyUI-Mochi本地部署教程

一、介绍 mochi是近期Genmo公司开源的先进视频生成模型&#xff0c;具有高保真运动和强大的提示遵循性。此模型的发布极大的缩小了闭源和开源视频生成系统之间的差距。 目前&#xff0c;视频生成模型与现实之间存在巨大差距。其中最影响视频生成的两个关键功能也就是运动质量和…

Docker 安装开源的IT资产管理系统Snipe-IT

一、安装 1、创建docker-compose.yaml version: 3services:snipeit:container_name: snipeitimage: snipe/snipe-it:v6.1.2restart: alwaysports:- "8000:80"volumes:- ./logs:/var/www/html/storage/logsdepends_on:- mysqlenv_file:- .env.dockernetworks:- snip…

Oracle重启后业务连接大量library cache lock

一、现象 数据库和前段应用重启后&#xff0c;出现大量library cache lock等待事件。 二、分析解决 本次异常原因是&#xff1a;原因定位3&#xff1a; 库缓存对象无效 Library cache object Invalidations 三、各类情况具体分析如下 原因定位1&#xff1a;由于文字导致的非…

硬件设计-七位半电压表硬件方案(下)

目录 摘要 简介 解决方案和评估系统简介 应用聚焦&#xff1a;高准确度数据采集器 结论 摘要 本文探讨了为仪器仪表应用设计高准确度设备所涉及的挑战&#xff0c;并介绍了由低INL SAR ADC、全集成式超低温漂精密基准电压源、四通道匹配电阻网络和零漂移低噪声放大器构建的…

基于springboot+vue+微信小程序的宠物领养系统

基于springbootvue微信小程序的宠物领养系统 一、介绍 本项目利用SpringBoot、Vue和微信小程序技术&#xff0c;构建了一个宠物领养系统。 本系统的设计分为两个层面&#xff0c;分别为管理层面与用户层面&#xff0c;也就是管理者与用户&#xff0c;管理权限与用户权限是不…

Termora 一个开源的 SSH 跨平台客户端工具

Termora 是一个终端模拟器和 SSH 客户端&#xff0c;支持 Windows&#xff0c;macOS 和 Linux。 功能特性 支持 SSH 和本地终端支持 SFTP 文件传输支持 Windows、macOS、Linux 平台支持 Zmodem 协议支持 SSH 端口转发支持配置同步到 Gist支持宏&#xff08;录制脚本并回放&…

TypeScript Jest 单元测试 搭建

NPM TypeScript 项目搭建 创建目录 mkdir mockprojectcd mockproject初始化NPM项目 npm init -y安装TypeScript npm i -D typescript使用VSCode 打开项目 创建TS配置文件tsconfig.json {"compilerOptions": {"target": "es5","module&…

sql模糊关联匹配

需求目标&#xff1a; 建立临时表 drop table grafana_bi.zbj_gift_2024;USE grafana_bi; CREATE TABLE zbj_gift_2024 (id INT AUTO_INCREMENT PRIMARY KEY,userName VARCHAR(255),giftName VARCHAR(255),giftNum INT,points INT,teacher VARCHAR(255),sendDate DATETIME,…

Web前端:JavaScript标识符与变量

JavaScript介绍 JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”&#xff0c;指的是它不具备开发操作系统的能力&#xff0c;而是只用来编写控制其他大型应用程序的“脚本”。 JavaScript 是一种嵌入式&#xff08;embedded&#xff09;语言。它本身提供的核心语法不算…

mac homebrew配置使用

本文介绍mac上homebrew工具的安装、配置过程。homebrew功能类似于centos的yum&#xff0c;用于软件包的管理&#xff0c;使用上有命令的差异。 本次配置过程使用mac&#xff0c;看官方文档&#xff0c;在linux上也可以用&#xff0c;但我没试过&#xff0c;有兴趣的同学可以试试…

对话新晋 Apache SeaTunnel Committer:张圣航的开源之路与技术洞察

近日&#xff0c;张圣航被推选为 Apache SeaTunnel 的 Committer成员。带着对技术的热情和社区的责任&#xff0c;他将如何跟随 Apache SeaTunnel 社区迈向新的高度&#xff1f;让我们一起来聆听他的故事。 自我介绍 请您简单介绍一下自己&#xff0c;包括职业背景、当前的工作…

智慧公厕大数据驱动下的公共卫生管理与优化

在快速发展的城市化进程中&#xff0c;公共卫生问题日益凸显&#xff0c;成为城市管理的重要议题。智慧公厕&#xff0c;作为公共卫生设施的一次革命性创新&#xff0c;正借助物联网技术的东风&#xff0c;引领公共卫生进入一个全新的生态时代。本文将深入探讨智慧公厕如何利用…

后盾人JS--JS值类型使用(终章)

数值类型转换技巧与NaN类型 什么是NaN呢&#xff1f;顾名思义就是&#xff0c;not a number <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width,…