当前位置: 首页 > article >正文

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'b0;
                uart_en_d1 <= 1'b0;
            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视图:

另注意:

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

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


http://www.kler.cn/a/469745.html

相关文章:

  • 计算机网络——数据链路层—局域网和广域网
  • 华为OD机试E卷 --羊、狼、农夫过河--24年OD统一考试(Java JS Python C C++)
  • 探秘前沿科技:RFID 与 NFC,开启智能识别新篇
  • ARP(地址解析协议)攻击;TCP SYN Flood(SYN洪流)攻击
  • git 常用命令和本地合并解决冲突
  • 关于单片机的基础知识(一)
  • Linux系统操作笔记
  • LeetCode-合并两个有序链表(021)
  • 《从零到一:深入浅出解析支持向量机的对偶问题》
  • Java Web开发进阶——Spring Boot基础
  • 配置管理工具和k8s功能重叠部分的优势比较
  • GitHub - riscv-software-src/riscv-isa-sim: Spike, a RISC-V ISA Simulator
  • QT----------文件系统操作和文件读写
  • Java解析PDF数据库设计文档
  • MyBatis 配置文件全解析
  • 发电厂冷水降电导 超纯水的制备和应用 抛光树脂
  • LLM - FlashAttention 的 Safe-Softmax 与 One-Pass Tiling 计算 教程
  • SolidWorks进行热力学有限元分析三、有限元计算
  • 【RISC-V CPU debug 专栏 4.1 -- RV Debug Vector Address 介绍】
  • 解决cryptoJS.AES默认参数加密,java无法解密的问题