FPGA之 串口UART通信

FPGA之串口UART通信

      • 1. UART发送模块(Transmitter)
      • 2. UART接收模块(Receiver)
      • 3. testbench
      • 4. 边沿检测电路

   串口(Universal Asynchronous Receiver/Transmitter,UART)是一种串行通信协议,用于异步通信。它广泛应用于计算机和嵌入式系统中,用于设备之间的数据传输。以下是UART的一些关键特性和工作原理:

  • 异步通信:UART通信是异步的,这意味着发送器和接收器使用不同的时钟信号,数据传输不需要同步时钟线。
  • 数据格式:UART通信可以配置不同的数据位(通常是7或8位)、停止位(1或2位)和奇偶校验位。
  • 波特率:UART通信的速率以波特率(Baud Rate)来衡量,表示每秒传输的信号单元数。常见的波特率有9600、19200、38400、115200等。
  • 串行数据传输:UART使用两条线进行通信:一条用于发送(TX),另一条用于接收(RX)。
  • 帧结构:一个UART数据帧通常包括起始位、数据位、奇偶校验位(可选)和停止位。
  • 起始位:每个数据帧开始时,发送器将信号线从高电平拉低,表示数据帧的开始。
  • 数据位:紧接着起始位,发送器发送数据帧的实际数据。
  • 奇偶校验位:如果启用,这是一个额外的位,用于检测数据中1的个数是奇数还是偶数。
  • 停止位:数据帧的最后是停止位,可以是1个或2个,将信号线拉高,表示数据帧的结束。
  • 流控制:UART可以支持硬件流控制(如RTS/CTS)或软件流控制(如XON/XOFF)。
  • 中断和DMA:UART通常可以通过中断或直接内存访问(DMA)与主机处理器通信,以减少处理器负载。
  • 配置和控制:UART设备通常有一组寄存器,用于配置波特率、数据格式、流控制等参数。

在嵌入式系统设计中,UART通常用于以下目的:

与计算机或其他设备进行串行通信。
调试和日志记录,通过UART发送调试信息。
控制台接口,用于输入和输出命令。
与其他串行设备(如传感器、调制解调器等)通信。
UART是一种成熟且广泛支持的通信协议,几乎所有的微控制器和计算机系统都提供UART接口。在软件层面,操作系统和编程语言通常提供UART通信的库和API,使得UART通信的实现变得简单。
RS232和DB9针脚定义

针脚功能描述
常用脚位为Pin2、3、5。

UART发送一个字节时序图
注意这里需要11个时钟才能够确认到是否已经完全收完数据。

UART(Universal Asynchronous Receiver/Transmitter,通用异步接收器/发送器)是一种全双工串行通信协议,用于异步通信。UART使用一对数据线(TX和RX)实现双向通信,无需共享时钟线,数据逐位传输。并且可以通过一个波特率设置端口支持不同的波特率。其本质为,将8位的并行数据通过一根信号线,在不同的时刻传输并行数据的不同位,通过多个时刻,最终将8位并行数据全部传出。串口通信通过控制端口的信号以1位的低电平标志串行传输的开始,待8位数据传输完成之后,再以1位的高电平标志传输的结束。当收到发送完成的响应信号时再开始下一个开始信号的发送。
UART串口模块设计的主要端口

对于串口接收电路,其实质为采样。通俗来讲,通过对一位数据进行多次采样,统计得到高电平出现的次数,次数多的就是该位的电平值。起始位检测,通过边沿检测电路。
接收端原理示意图

在Verilog中实现一个简单的UART模块,我们通常需要两个主要部分:发送模块(Transmitter)和接收模块(Receiver)。以下是这两个模块的基本实现示例:

1. UART发送模块(Transmitter)

module uart_tx(
    input wire clk,          // 时钟信号
    input wire rst_n,        // 复位信号(低电平有效)
    input wire [7:0] data,   // 要发送的数据
    input wire start,        // 开始发送信号
    input wire [4:0]band_set, //波特率
    output reg tx,           // UART发送线
    output reg busy          // 忙信号,表示发送是否完成
);
// 
// 300:10^9/300/20=166666=EDB6
//115200:8680ns/20ns=434次计数=1B2
reg [10:0] counter;  // 波特率计数器,主要用来分频
reg [3:0] bit_index; // 位索引,用于跟踪当前发送的位

reg [17:0] bps_DR;
always@(*)
	case(Baud_set)
		0:bps_DR=1000000000/9600/20;
		1:bps_DR=1000000000/19200/20;
		2:bps_DR=1000000000/38400/20;
		3:bps_DR=1000000000/57600/20;
		4:bps_DR=1000000000/115200/20;
		default:bps_DR=1000000000/9600/20;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        tx <= 1; // 复位时,将发送线置为高电平
        busy <= 0;
        counter <= 0;
        bit_index <= 0;
    end 
    else begin
        if (start && !busy) begin
            busy <= 1; // 标记为忙
            counter <= 0; // 重置计数器
            tx <= 0; // 发送起始位
            bit_index <= 0;
        end 
        else if (busy) begin
            if (counter >= (bps_DR-1)) begin 
                counter <= 0;
                if (bit_index < 8) begin
                    tx <= data[bit_index]; // 发送数据位
                    bit_index <= bit_index + 1;
                end 
                else begin
                    tx <= 1; // 发送停止位
                    bit_index <= 0;
                    if (data == 8'hFF) begin
                        busy <= 0; // 如果发送的数据是0xFF,表示发送完成
                    end
                end
            end 
            else begin
                counter <= counter + 1;
            end
        end
    end
end

endmodule

2. UART接收模块(Receiver)

module uart_rx(
    input wire clk,          // 时钟信号
    input wire rst_n,        // 复位信号(低电平有效)
    input wire rx,           // UART接收线
   input wire [4:0]band_set, //波特率控制
    output reg [7:0] data,  // 接收到的数据
    output reg busy,         // 忙信号,表示接收是否完成
    output reg start         // 开始接收信号
);

reg [10:0] counter;  // 波特率计数器
reg [3:0] bit_index; // 位索引,用于跟踪当前接收的位
reg [7:0] temp_data; // 临时数据寄存器

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data <= 8'b0;
        busy <= 0;
        start <= 0;
        counter <= 0;
        bit_index <= 0;
        temp_data <= 8'b0;
    end 
    else begin
        if (!start && rx == 0) begin
            start <= 1; // 检测到起始位
            counter <= 0;
            bit_index <= 0;
            temp_data <= 8'b0;
        end 
        else if (start && counter >= (bps_DR-1)) begin
            counter <= 0;
            if (bit_index < 8) begin
                temp_data[bit_index] <= rx; // 存储数据位
                bit_index <= bit_index + 1;
            end else begin
                data <= temp_data; // 更新接收到的数据
                start <= 0;
                busy <= 1; // 标记为忙
                if (rx == 1) begin
                    busy <= 0; // 停止位检测到,接收完成
                end
            end
        end else if (start) begin
            counter <= counter + 1;
        end
    end
end

endmodule

请注意,这两个模块都是基于一些假设实现的,例如波特率设置用户设置。在实际应用中,你可能需要根据具体的硬件和需求来调整这些参数。此外,这些模块没有实现奇偶校验。

3. testbench

为了测试上面给出的 UART 发送模块(uart_tx)和接收模块(uart_rx),需要编写一个测试平台(testbench),用于生成激励信号并观察模块的响应。下面是一个简单的 Verilog 测试平台示例:

`timescale 1ns / 1ps  //单位/精度,比如1.1

module uart_testbench;

    // 测试平台的参数
    parameter CLK_PERIOD = 20; // 时钟周期为20纳秒,对应频率为50MHz

    // 测试平台的信号
    reg clk;
    reg rst_n;
    reg [7:0] data_to_send;
    wire tx;
    wire busy_tx;
    wire tx_start;
    wire rx;
    wire busy_rx;
    wire rx_start;
    wire band_set;

    // 实例化 UART 发送模块
    uart_tx uut_tx(
        .clk(clk),
        .rst_n(rst_n),
        .data(data_to_send),
        .start(tx_start),
        .tx(tx),
        .busy(busy_tx),
        .band_set(band_set_tx)
    );

    // 实例化 UART 接收模块
    uart_rx uut_rx(
        .clk(clk),
        .rst_n(rst_n),
        .rx(rx),
        .data(rx_data),
        .busy(busy_rx),
        .start(rx_start),
        .band_set(band_set_rx)
    );

    // 时钟生成
    always #(CLK_PERIOD/2) clk = ~clk;

    // 初始化测试平台
    initial begin
        // 初始化信号
        clk = 0;
        rst_n = 0;
        data_to_send = 8'hAA; // 初始数据
        tx_start = 0;
        rx = 1; // 初始时,接收线为高电平(空闲状态)
        band_set_tx = 4;
        band_set_rx = 4;

        // 等待几个时钟周期以应用复位
        #100;
        rst_n = 1; // 释放复位

        // 等待直到发送模块准备好发送数据
        @(posedge busy_tx);
        #10;
        tx_start = 1; // 触发发送操作
        @(posedge busy_tx); // 等待发送完成

        // 检查接收模块是否接收到了正确的数据
        #10;
        if (rx_data == data_to_send) begin
            $display("Test Passed: Received data is correct.");
        end else begin
            $display("Test Failed: Received data is incorrect.");
        end

        // 结束仿真
        $finish;
    end

    // 监视变量的变化
    initial begin
        $monitor("Time = %t, rst_n = %b, data_to_send = %x, tx = %b, busy_tx = %b, rx = %b, rx_data = %x, busy_rx = %b, tx_start = %b, rx_start = %b",
                 $time, rst_n, data_to_send, tx, busy_tx, rx, rx_data, busy_rx, tx_start, rx_start);
    end

endmodule

这个测试平台(testbench)完成了以下任务:

  1. 定义了时钟周期和信号。
  2. 实例化了 UART 发送模块和接收模块。
  3. 生成了周期性的时钟信号。
  4. 初始化了所有信号,并应用了复位。
  5. 释放复位后,设置了要发送的数据,并触发了发送操作。
  6. 观察发送和接收模块的行为,并检查接收到的数据是否正确。
  7. 使用 $monitor 来监视信号的变化,使用 $display 来打印测试结果。
  8. 如果测试通过,则打印通过信息;如果失败,则打印失败信息,并指出接收到的数据不正确。
  9. 最后,使用 $finish 结束仿真。

4. 边沿检测电路

在UART通信中,起始位是一个逻辑“0”信号,它标志着一个数据帧的开始。通常,UART的数据线在空闲时保持高电平状态,因此起始位的检测可以通过检测数据线从高电平到低电平的边沿来实现。

以下是一个简单的Verilog代码示例,实现了起始位的边沿检测电路:

module uart_start_bit_detector(
    input wire clk,             // 时钟信号
    input wire rst_n,           // 复位信号(低电平有效)
    input wire rx,              // UART接收线
    output reg start_bit_detected // 起始位检测到的信号
);

// 用于存储上一个时钟周期的rx线状态
reg last_rx;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        // 同步复位
        last_rx <= 1'b1; // 假设数据线在复位时是空闲的(高电平)
        start_bit_detected <= 1'b0;
    end else begin
        // 捕获上一个周期的rx状态
        last_rx <= rx;
        // 检测从高到低的边沿,表示起始位的开始
        if (last_rx && !rx) begin
            start_bit_detected <= 1'b1; // 起始位检测到
        end else if (rx) begin
            start_bit_detected <= 1'b0; // 如果rx线再次变高,清除检测信号
        end
    end
end

endmodule

这段代码使用了一个寄存器last_rx来存储上一个时钟周期的rx线状态。在每个时钟上升沿,它会检查rx线是否从高电平变为低电平,如果是,就表示检测到了起始位的开始,将start_bit_detected信号置为高电平。如果rx线再次变为高电平,表示起始位已经结束,将start_bit_detected信号清除。

请注意,这个简单的边沿检测电路没有实现任何同步机制或滤波器,这在实际硬件设计中可能需要以避免误触发。此外,该电路假设rx信号在复位时是高电平,这应与系统的其他部分一致。在实际应用中,可能还需要进一步的逻辑来处理数据帧的其他部分,例如数据位、奇偶校验位和停止位。

如果需要将1位数据分成16段,舍弃前5段和后4段,取中间7段进行采样。即原本为一个波特率下的一位为一个bpsk,则现在需要16个bpsk,即,10^9 /9600/16 为一个采样的时间,则对应一个波特率的计数次数为10 ^ 9/9600/16 /20, 20ns为一个时钟周期

数据格式:UART通信的数据帧包括起始位、数据位、奇偶校验位(可选)、停止位。数据位通常为7或8位,停止位可以是1或2位。

波特率:数据传输速率以波特率(Baud Rate)表示,定义为每秒传输的位数。常见的波特率有9600、19200、38400、115200等。

起始位:数据传输开始时,发送器将信号线从高电平拉低,表示数据帧的开始。

数据位:起始位之后,发送器发送实际数据,可以是7位或8位,通常按照最低有效位(LSB)先发送的顺序。

奇偶校验位:如果启用,该位用于校验数据中1的个数,可以是奇校验或偶校验。

停止位:数据传输结束,发送器将信号线拉高,表示数据帧的结束。停止位可以是1位或2位,用于提供数据帧之间的间隔。

空闲状态:UART线上没有数据传输时,信号线保持高电平状态。

传输方向:数据可以从高位(MSB)开始传输,也可以从低位(LSB)开始传输。

帧间隔:数据帧与帧之间的间隔,可以以位或时间计量。

抗干扰能力:UART使用共模电压进行数据传输,抗干扰能力相对较差,因此传输距离有限。

配置参数:通信双方必须约定波特率、数据位宽、奇偶校验位、停止位等配置参数,以确保正确通信。

UART广泛应用于嵌入式系统和计算机之间,以及其他设备间的通信,因其简单、低成本而被广泛使用。在实际应用中,UART可以支持硬件流控制(如RTS/CTS)或软件流控制(如XON/XOFF),以提高数据传输的可靠性。
   

参考:
https://www.bilibili.com/video/BV1va411c7Dz?p=17&spm_id_from=pageDriver&vd_source=179014f1a2f3078fc78ff0659a14acb9

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

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

相关文章

大数据下的用户研究:深度洞察与精准决策的引擎

文章目录 一、大数据时代的用户研究新机遇二、大数据用户研究的流程与关键技术三、大数据用户研究的实践案例四、大数据用户研究的挑战与展望《用户研究方法&#xff1a;卓越产品和服务的用户研究技巧》内容及特色读者对象 在当今这个数据爆炸的时代&#xff0c;大数据已成为推…

仪表板展示|DataEase看中国:2023年中国新能源汽车经济运行情况分析

背景介绍 随着政府机构、企业和个人对环保和可持续发展的高度关注&#xff0c;“新能源汽车”在全球范围内成为了一个热门话题。新能源汽车是指使用非传统燃料&#xff08;如电能、氢能等&#xff09;作为动力源的汽车。 在中国市场&#xff0c;新能源汽车产业蓬勃发展&#…

2.贪心算法.基础

2.贪心算法.基础 基础知识题目1.分发饼干2.摆动序列3.最大子序和4.买股票的最佳时机24.2.买股票的最佳时机5.跳跃游戏5.1.跳跃游戏26.K次取反后最大化的数组和7.加油站8.分发糖果 基础知识 什么是贪心? 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪…

Codeforces Round #956 (Div. 2) and ByteRace 2024(A~D题解)

这次比赛也是比较吃亏的&#xff0c;做题顺序出错了&#xff0c;先做的第三个&#xff0c;错在第三个数据点之后&#xff0c;才做的第二个&#xff08;因为当时有个地方没检查出来&#xff09;所以这次比赛还是一如既往地打拉了 那么就来发一下题解吧 A. Array Divisibility …

Nifi 与 Kettle

01 Kettle简介 Kettle是一个开源的ETL&#xff08;Extract-Transform-Load&#xff09;工具&#xff0c;可以用于数据集成、数据转换和数据处理等任务。它提供了一组可视化的设计工具&#xff0c;使得用户可以通过简单的拖拽和连接来构建数据流程&#xff0c;并且还支持多种数据…

apache启动报错:the requested operation has failed

Apache24\bin cmd 回车 httpd -t 因为我重新压缩了&#xff0c;记住&#xff0c;重新压缩要使用原路径&#xff0c; 因为你安装的 时候使用的是原路径 还是不行就改个端口&#xff0c;切记修改配置文件httpd.conf先把Tomcat停了 Define SRVROOT "F:\Apache\Apache24&q…

人工智能和机器学习 (复旦大学计算机科学与技术实践工作站)20240703(上午场)人工智能初步、mind+人脸识别

前言 在这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经逐渐渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;无一不彰显着AI的强大潜力。而人脸识别技术作为AI领域的一项重要应用&#xff0c;更是以其高效、便捷的特点受到了…

什么是Kudu

Kudu是一个由Cloudera于2015年9月开源的分布式数据存储引擎&#xff0c;设计旨在结合Hadoop分布式文件系统&#xff08;HDFS&#xff09;和HBase的优势。Kudu提供了一种既支持高效随机访问又支持数据扫描的能力&#xff0c;适用于需要实时插入、更新和读取数据的场景&#xff0…

决策树算法简单介绍:原理和方案实施

决策树算法介绍&#xff1a;原理和方案实施 决策树&#xff08;Decision Tree&#xff09;是一种常用的机器学习算法&#xff0c;它既可以用于分类任务&#xff0c;也可以用于回归任务。由于其直观性和解释性&#xff0c;决策树在数据分析和模型构建中得到了广泛的应用。本文将…

python爬虫和用腾讯云API接口进行翻译并存入excel,通过本机的Windows任务计划程序定时运行Python脚本!

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a;定时爬取外网的某个页面&#xff0c;并将需要的部分翻译为中文存入excel 接下了的&#xff0c;没学过的最好看一下 基本爬虫的学习 【爬虫】requests 结合 BeautifulSoup抓取网页数据_requests beauti…

科普文:Linux服务器常用命令和脚本

Linux服务器常用的命令&#xff1a;find、grep、xargs、sort、uniq、tr、cut、paste、wc、sed、awk&#xff1b;提供的例子和参数都是最常用和最为实用的。 1.find 文件查找 查找txt和pdf文件 find . \( -name "*.txt" -o -name "*.pdf" \) -print 正…

朋友圈发文黄金时段揭秘,一文搞懂私域运营秘诀

猫头虎 &#x1f42f; 建联猫头虎&#xff0c;商务合作&#xff0c;产品评测&#xff0c;产品推广&#xff0c;个人自媒体创作&#xff0c;超级个体&#xff0c;涨粉秘籍&#xff0c;一起探索编程世界的无限可能&#xff01; 掌握朋友圈最佳发文时间&#xff0c;提升互动率&a…

matlab仿真 信道(下)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第四章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; 之前的内容还剩下simulink的仿真过程。 3.simulink中的AWGN模块仿真 系统框图如图所示&#xff0c;TX和RX 模块需要单独实现…

【C++ 】-vector:新时代动态数组的革新与未来

目录 1. vector的介绍及使用 1.1 vector的介绍 1.1.1 vector是什么 1.1.2 vector的存储机制 1.2 vector的使用 1.2.1 定义和构造函数 1.2.2 迭代器 1.2.3 容量相关操作 1.2.4 元素访问和修改 1.3 迭代器失效问题 2. vector深度剖析及模拟实现 2.1 std::vector的模拟…

新零售起盘案例「半藏酱酒」布局路径,半藏总院分院招商模式

在当前白酒市场中&#xff0c;一款名为半藏酒的酒品以其独特的新零售模式引起了广泛关注。这种模式不同于传统销售方式&#xff0c;通过多种创新玩法&#xff0c;实现了销售与品牌推广的双重目标&#xff0c;让我们一起来看看细节。 半藏酒的分级代理制度将代理商分为两个层级&…

DETR目标检测框架

概念&#xff1a;DETR&#xff08;Detection Transformer&#xff09;是一种基于Transformer架构的端到端目标检测框架。它与传统的基于区域提议的目标检测方法有所不同。传统方法通常依赖于手工设计的组件&#xff08;如锚框、非极大值抑制等&#xff09;&#xff0c;而DETR将…

Leetcode2542-最大子序列的分数

1.问题转换 首先明确题意&#xff0c;要选取的值和num1&#xff0c;num2两个数组都有关&#xff0c;但是num1中选取的是k个数&#xff0c;num2中选取的是1个数&#xff0c;显然num2中的数所占的权重较大&#xff08;对结果影响较大&#xff09;&#xff0c;所以我们就可以对nu…

【爬虫】Python实现,模拟天眼查登录验证获取token

模拟天眼查登录验证获取token 项目介绍逻辑思路效果演示部分代码展示源代码获取 项目介绍 注&#xff1a;本程序测试时期&#xff1a;2024.7.9&#xff0c;稳定可用 天眼查登录接口升级更新之后&#xff0c;后台接口login接口登录运用了4代极验gt&#xff0c;js逆向部分相当复…

基于网络编码的 tcp 变种-tcp/nc

tcp/nc 是指 “tcp with network coding”&#xff0c;是一种结合了网络编码技术的 tcp 变种&#xff0c;网上资源很少&#xff0c;我也不准备多介绍&#xff0c;只介绍它的核心。 传统 tcp 在演进过程中一直搞不定效率问题&#xff0c;网络带宽在增长&#xff0c;cpu 却没有变…

PHP全民投票微信小程序系统源码

&#x1f5f3;️【全民参与的力量】全民投票系统小程序&#xff0c;让决策更民主&#xff01; &#x1f310; 一键启动&#xff0c;全民参与 全民投票系统小程序&#xff0c;是连接每一个声音的高效桥梁。只需简单几步&#xff0c;即可在线发起投票活动&#xff0c;无论是社区…