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)
    begin
        if(!sys_rst_n)
            begin
                uart_rxd_d0 <= 1'b1;
                uart_rxd_d1 <= 1'b1;
            end
        else 
             begin
                uart_rxd_d0 <= uart_rxd;
                uart_rxd_d1 <= uart_rxd_d0;
             end
    end
  
    //rx_flag
	always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!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_cnt
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!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;
//            end
              begin                               
                  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_cnt
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!sys_rst_n)
            rx_cnt <= 4'd0;
        else if(rx_flag == 1'b1)
            begin
                if(clk_cnt == uart_bps - 16'd1)
                    rx_cnt <= rx_cnt + 4'd1;
                else 
                    rx_cnt <= rx_cnt;
            end
        else
            rx_cnt <= 4'd0;
    end
    //rx_data
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!sys_rst_n)
            rx_data <= 8'd0;
        else if((rx_flag == 1'b1)&&(clk_cnt == uart_bps/2)) 
            begin
                case(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;
                endcase
            end
    end
    
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
         if(!sys_rst_n)
             begin
             	 uart_data <= 8'd0;
             	 uart_done <= 1'd0;
             end
         else if(rx_cnt == 4'd9)
             begin                       
             	 uart_data <= rx_data;    
         	     uart_done <= 1'd1;    
             end   
          else
             begin                 
             	 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_send

module 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)
    begin
        if(!sys_rst_n)
            begin
                uart_en_d0 <= 1'b1;
                uart_en_d1 <= 1'b1;
            end
        else 
             begin
                uart_en_d0 <= uart_en;
                uart_en_d1 <= uart_en_d0;
             end
    end
    
    //tx_data
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!sys_rst_n)
           tx_data <= 8'd0;          
        else if(en_flag == 1'b1) 
           tx_data <= uart_din;
        else 
           tx_data <= tx_data;
    end
    
    //tx_flag
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!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_cnt
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!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;
//            end
              begin                               
                  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_cnt
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!sys_rst_n)
            tx_cnt <= 4'd0;
        else if(tx_flag == 1'b1)
            begin
                if(clk_cnt == uart_bps - 16'd1)
                    tx_cnt <= tx_cnt + 4'd1;
                else 
                    tx_cnt <= tx_cnt;
            end
        else
            rx_cnt <= 4'd0;

    end
    
     //uart_txd
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(!sys_rst_n)
            uart_txd <= 1'b1;
        else if((tx_flag == 1'b1)&&(clk_cnt == 16'd0)) 
            begin
                case(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;
                endcase
            end
    end

endmodule

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

`timescale 1ns / 1ps
// Create Date: 2025/01/06 15:49:15
// Design Name: 
// Module Name: top_uart

module 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)
    begin
        if(!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_loop

module 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)
    begin
        if(!sys_rst_n)
            begin
                recv_done_d0 <= 1'b0;
                recv_done_d1 <= 1'b0;
            end
         else 
             begin                    
                 recv_done_d0 <= recv_done;
                 recv_done_d1 <= recv_done_d0;
             end                                                    
    end
    
    //判断接收完成信号,并在串口发送模块空闲时发送是使能信号
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
         if(!sys_rst_n)
             begin
                tx_ready  <= 1'b0;
                send_en   <= 1'b0;  
                send_data <= 8'd0;
             end
          else
              begin
                  if(recv_done_flag)   
                      begin
                          tx_ready  <= 1'b1;     
                          send_en <= 1'b0;       
                          send_data <= recv_data;                      
                      end     
                  else if(tx_ready&&(~tx_busy))
                      begin
                          tx_ready <= 1'b0;
                          send_en  <= 1'b1;
                      end
              end
    end   
       
endmodule

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ORACLE战报】2025.1月OCP | MySQL考试

2025.1月【最新考试成绩出炉】 OCP战报 MySQL 战报 部分学员成绩及证书

力扣经典练习题之198.打家劫舍

今天继续给大家分享一道力扣的做题心得今天这道题目是198.打家劫舍,这是一道非常经典的问题,在动态规划中经常考到这类问题,题目如下: 题目链接:198.打家劫舍 1,题目分析 首先此题就是给我们了一个数组,代表可以偷的房屋中的对应的金额,我们是一个小偷,一次可以偷很多…

【数据库】一、数据库系统概述

文章目录 一、数据库系统概述1 基本概念2 现实世界的信息化过程3 数据库系统内部体系结构4 数据库系统外部体系结构5 数据管理方式 一、数据库系统概述 1 基本概念 数据&#xff1a;描述事物的符号记录 数据库&#xff08;DB&#xff09;&#xff1a;长期存储在计算机内的、…

Redis有哪些常用应用场景?

大家好&#xff0c;我是锋哥。今天分享关于【Redis有哪些常用应用场景&#xff1f;】面试题。希望对大家有帮助&#xff1b; Redis有哪些常用应用场景&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Redis 是一个高性能的开源键值对&#xff08;Key-Va…

[uniapp] 实现扫码功能,含APP、h5、小程序

&#x1f680; 个人简介&#xff1a;某大型国企资深软件开发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

《异步编程之美》— 全栈修仙《Java 8 CompletableFuture 对比 ES6 Promise 以及Spring @Async》

哈喽&#xff0c;大家好&#xff01;在平常开发过程中会遇到许多意想不到的坑&#xff0c;本篇文章就记录在开发过程中遇到一些常见的问题&#xff0c;看了许多博主的异步编程&#xff0c;我只能说一言难尽。本文详细的讲解了异步编程之美&#xff0c;是不可多得的好文&#xf…

day07_Spark SQL

文章目录 day07_Spark SQL课程笔记一、今日课程内容二、Spark SQL函数定义&#xff08;掌握&#xff09;1、窗口函数2、自定义函数背景2.1 回顾函数分类标准:SQL最开始是_内置函数&自定义函数_两种 2.2 自定义函数背景 3、Spark原生自定义UDF函数3.1 自定义函数流程&#x…

presto不支持concat_ws

在sparksql/hive中&#xff0c;将一个数据集合已指定的分隔符拼接可以用concat_ws&#xff0c;但是在presto中没有这个函数&#xff0c;不过presto提供了一个集合方法array_join&#xff0c;来达到相同的目的 同样的对数据集去重可以用array_distinct 如果你不需要去重就直接…

【日常小记】Ubuntu启动后无图形界面且网络配置消失

【日常小记】Ubuntu启动后无图形界面且网络配置消失 解决方法&#xff1a; 1. 输入后恢复网络: #sudo dhclient 2. 重新安装ubuntu-desktop #sudo apt-get install ubuntu-desktop&#xff01;&#xff01;&#xff01;请关注是否能ping通某网站&#xff08;例如百度&…

01、kafka知识点综合

kafka是一个优秀大吞吐消息队列&#xff0c;下面我就从实用的角度来讲讲kafka中&#xff0c;“kafka为何有大吞吐的机制”&#xff0c;“数据不丢失问题”&#xff0c;“精准一次消费问题” 01、kafka的架构组织和运行原理 kafka集群各个节点的名称叫broker&#xff0c;因为kaf…

【ArcGIS微课1000例】0137:色彩映射表转为RGB全彩模式

本文讲述ArcGIS中,将tif格式的影像数据从色彩映射表转为RGB全彩模式。 参考阅读:【GlobalMapper精品教程】093:将tif影像色彩映射表(调色板)转为RGB全彩模式 文章目录 一、色彩映射表预览二、色彩映射表转为RGB全彩模式一、色彩映射表预览 加载配套数据包中的0137.rar中的…

Python教程丨Python环境搭建 (含IDE安装)——保姆级教程!

工欲善其事&#xff0c;必先利其器。 学习Python的第一步不要再加收藏夹了&#xff01;提高执行力&#xff0c;先给自己装好Python。 1. Python 下载 1.1. 下载安装包 既然要下载Python&#xff0c;我们直接进入python官网下载即可 Python 官网&#xff1a;Welcome to Pyt…

2025.1.13运算符重载和继承

作业 #include <iostream> #include <cstring> using namespace std; //在之前做的mystring类的基础上&#xff0c;将能够重载的运算符全部进行重载class mystring { private:char *str;int size;public://无参构造mystring():size(10){str new char[size];str[0…

慧集通(DataLinkX)iPaaS集成平台-业务建模之业务对象(二)

3.UI模板 当我们选择一条已经建好的业务对象点击功能按钮【UI模板】进入该业务对象的UI显示配置界面。 右边填写的是UI模板的编码以及对应名称&#xff1b;菜单界面配置以业务对象UI模板编码获取显示界面。 3.1【列表-按钮】 展示的对应业务对象界面的功能按钮配置&#xff1…

springboot使用Easy Excel导出列表数据为Excel

springboot使用Easy Excel导出列表数据为Excel Easy Excel官网&#xff1a;https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write 主要记录一下引入时候的pom&#xff0c;直接引入会依赖冲突 解决方法&#xff1a; <!-- 引入Easy Excel的依赖 -->&l…

计算机的错误计算(二百一十)

摘要 利用两个大模型计算 . 若可能&#xff0c;保留10位有效数字。实验表明&#xff0c;一个大模型给出了错误结果。另外一个大模型提供了 Python代码&#xff1b;运行代码后&#xff0c;输出中有2位错误数字。 例1. 计算 . 若可能&#xff0c;保留10位有效数字。 下面是一…

用vscode+ollama自定义Cursor AI编辑的效果

在vscode上搜索Continue 添加大语言模型 选择对应的本地模型版本 效果

基于微信小程序的汽车销售系统的设计与实现springboot+论文源码调试讲解

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

ANSYS Fluent学习笔记(七)求解器四部分

16.亚松弛因子 Controls面板里面设置&#xff0c;它能够稳定计算的过程。如果采用常规的迭代算法可能结果就会发生振荡的情况。采用亚松驰因子可以有助于残差的稳定。 他的取值范围是0-1&#xff0c;0代表没有亚松驰&#xff0c;1表示物理量变化很快&#xff0c;一般情况下取…

【MySQL数据库】基础总结

目录 前言 一、概述 二、 SQL 1. SQL通用语法 2. SQL分类 3. DDL 3.1 数据库操作 3.2 表操作 4. DML 5. DQL 5.1 基础查询 5.2 条件查询 5.3 聚合函数 5.4 分组查询 5.5 排序查询 5.6 分页查询 6. DCL 6.1 管理用户 6.2 权限控制 三、数据类型 1. 数值类…