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) beginif (!rst_n) begintx <= 1; // 复位时,将发送线置为高电平busy <= 0;counter <= 0;bit_index <= 0;end else beginif (start && !busy) beginbusy <= 1; // 标记为忙counter <= 0; // 重置计数器tx <= 0; // 发送起始位bit_index <= 0;end else if (busy) beginif (counter >= (bps_DR-1)) begin counter <= 0;if (bit_index < 8) begintx <= data[bit_index]; // 发送数据位bit_index <= bit_index + 1;end else begintx <= 1; // 发送停止位bit_index <= 0;if (data == 8'hFF) beginbusy <= 0; // 如果发送的数据是0xFF,表示发送完成endendend else begincounter <= counter + 1;endendend
endendmodule

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) beginif (!rst_n) begindata <= 8'b0;busy <= 0;start <= 0;counter <= 0;bit_index <= 0;temp_data <= 8'b0;end else beginif (!start && rx == 0) beginstart <= 1; // 检测到起始位counter <= 0;bit_index <= 0;temp_data <= 8'b0;end else if (start && counter >= (bps_DR-1)) begincounter <= 0;if (bit_index < 8) begintemp_data[bit_index] <= rx; // 存储数据位bit_index <= bit_index + 1;end else begindata <= temp_data; // 更新接收到的数据start <= 0;busy <= 1; // 标记为忙if (rx == 1) beginbusy <= 0; // 停止位检测到,接收完成endendend else if (start) begincounter <= counter + 1;endend
endendmodule

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

3. testbench

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

`timescale 1ns / 1ps  //单位/精度,比如1.1module 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);endendmodule

这个测试平台(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) beginif (!rst_n) begin// 同步复位last_rx <= 1'b1; // 假设数据线在复位时是空闲的(高电平)start_bit_detected <= 1'b0;end else begin// 捕获上一个周期的rx状态last_rx <= rx;// 检测从高到低的边沿,表示起始位的开始if (last_rx && !rx) beginstart_bit_detected <= 1'b1; // 起始位检测到end else if (rx) beginstart_bit_detected <= 1'b0; // 如果rx线再次变高,清除检测信号endend
endendmodule

这段代码使用了一个寄存器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

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

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

相关文章

通义千问Qwen-VL-Chat大模型本地部署(一)

目录 前言 环境准备 软件安装 其它库安装启动项目 FASTAPI 小结 前言 人工智能大模型是一种能够利用大数据和神经网络来模拟人类思维和创造力的人工智能算法。它利用海量的数据和深度学习技术来理解、生成和预测新内容&#xff0c;通常情况下有数十亿乃至数百亿个参数&#xf…

什么?这动物图片可以上国家地理?

stable difussion中大部分的模型都是关于人的,今天交给大家一些不一样的:如何生成动物图片。在这篇文章中我们将会学到如何生成逼真的动物&#xff0c;可爱的动物&#xff0c;还有幻想中的动物。 准备工作 当然前提是你需要一个SD的软件&#xff0c;你可以用本地的SD webUI或…

[Spring] SpringBoot基本配置与快速上手

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

企业如何做好数据防泄密丨十个措施杜绝泄密事件

在当今数字化时代&#xff0c;信息泄露对企业构成了前所未有的威胁。企业的创新成果、专利技术和商业机密是其竞争力的核心。防止泄密可以确保这些关键资产不被非法复制或盗用。 客户数据是企业的宝贵资源。保护客户隐私不被泄露&#xff0c;不仅是法律要求&#xff0c;更是赢…

两位软件工程师创业,开发出一款软件质量保证自主AI代理,融资总额超3000万美元

编译整理&#xff5c;TesterHome社区 来源&#xff5c;TechCrunch 随着生成式人工智能&#xff08;generative AI&#xff09;的出现&#xff0c;人工智能应用正在改变和重塑各行各业&#xff0c;并改变人们的工作方式。软件开发也不例外。 总部位于旧金山和东京的初创公司Aut…

自动化立体仓库设计步骤:7步

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 完整版文件和更多学习资料&#xff0c;请球友到知识星球【智能仓储物流技术研习社】自行下载 这份文件是关于自动化立体仓库设计步骤的详细指南&#xff0c;其核心内容包括以下几个阶…

孟德尔随机化与痛风4

写在前面 今天阅读的文献是多种暴露与某结局的孟德尔随机化&#xff0c;算是以量取胜了。 The effect of metabolism-related lifestyle and clinical risk factors on digestive system cancers in East Asian populations: a two-sample Mendelian randomization analysis …

淘宝商品评论电商API接口:帮你轻松挑选优质商品

随着互联网的快速发展&#xff0c;电子商务应运而生&#xff0c;并在短时间内取得了巨大的成功。其中&#xff0c;淘宝作为我国最大的在线购物平台之一&#xff0c;每天都有数以亿计的商品交易发生。然而&#xff0c;面对海量的商品信息&#xff0c;如何挑选出优质商品成为了一…

800 元打造家庭版 SOC 安全运营中心

今天,我们开始一系列新的文章,将从独特而全面的角度探索网络安全世界,结合安全双方:红队和蓝队。 这种方法通常称为“紫队”,集成了进攻和防御技术,以提供对威胁和安全解决方案的全面了解。 在本系列的第一篇文章中,我们将指导您完成以 100 欧元约800元左右的预算创建…

无人机在交通管理方面的应用与潜力

随着智能化和数字化技术的发展&#xff0c;无人机已经成为智慧交通管理体系中的重要一环。无人机能够搭载各种专业设备&#xff0c;如超清摄像头、红外热成像摄像头、目标跟踪器等&#xff0c;从而完成多任务的数据采集和快速机动的任务执行。这些数据通过无线传输实时回传&…

LVS+Nginx高可用集群--基础篇(二)

1.虚拟主机-使用nginx为静态资源提供服务 静态资源服务器&#xff1a;主要包括两类资源&#xff0c;网页&#xff1b;图片&#xff0c;音频等&#xff1b; 也可以通过别名设置静态资源路径。 配置代码&#xff1a; server {listen 88;server_name localhost;locatio…

Kudu分区策略

Kudu表的分区策略主要有三种&#xff1a;范围分区&#xff08;Partition By Range&#xff09;、哈希分区&#xff08;Partition By Hash&#xff09;和高级分区&#xff08;Partition By Hash And Range&#xff09;。这些策略都要求分区字段必须包含在主键中。 范围分区&…

数据安全治理:从库级权限申请到表级权限申请

背景 随着数据安全意识的提高&#xff0c;企业越来越重视数据治理和权限管理。传统数仓大多对库级别进行读写授权&#xff0c;仅对人工标记的敏感库进行表级别授权&#xff0c;但由于敏感等级是由人为标记&#xff0c;错误率较高&#xff0c;故期望将权限申请流程细化到表级申…

树莓派_Pytorch学习笔记20:初步认识深度学习框架

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: ​ Python 版本3.7.3&#xff1a; ​ 本文很水&#xff0c;就介绍一下我以后的学习使用P…

【线程同步-1】

三大不安全案例 1、车站买票 package syn; ​ //不安全的买票 //线程不安全&#xff0c;有负数 public class UnsafeBuyTicket {public static void main(String[] args) {BuyTicket buyTicket new BuyTicket();new Thread(buyTicket,"xiaoming").start();new Thr…

STM32-按键及传感器模块

本内容是基于江协科技STM32视频整理而得。 1. 按键及传感器模块 1.1 按键简介 按键&#xff1a;常见的输入设备&#xff0c;按下导通&#xff0c;松手断开&#xff1b; 按键抖动&#xff1a;由于按键内部使用的是机械式弹簧片来进行通断的&#xff0c;所以在按下和松手的瞬间…

【代码随想录】【算法训练营】【第63天】 [卡码53]寻宝

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 63&#xff0c;周二&#xff0c;ding~ 题目详情 [卡码53] 寻宝 题目描述 卡码53 寻宝 解题思路 前提&#xff1a; 思路&#xff1a; 重点&#xff1a; 代码实现 C语言 prim算法 kruskal…

百望股份于港交所上市 用户量突破2300万

7月9日&#xff0c;百望股份&#xff08;股份代码&#xff1a;6657.HK&#xff09;正式于港交所上市&#xff0c;以32港元价格开盘&#xff0c;成为“电子发票第一股”。 招股书披露&#xff0c;百望股份成立于2015年&#xff0c;作为一家专注于企业数字化解决方案的提供商&…

C++ | Leetcode C++题解之第221题最大正方形

题目&#xff1a; 题解&#xff1a; class Solution { public:int maximalSquare(vector<vector<char>>& matrix) {if (matrix.size() 0 || matrix[0].size() 0) {return 0;}int maxSide 0;int rows matrix.size(), columns matrix[0].size();vector<…

在Mac上一键安装Mysql(解决所有安装问题)

重点强调安装mysql成功的关键在于安装的版本不能是最新&#xff01;&#xff01; 目录 一&#xff1a;下载mysql数据库安装部分到此结束 二&#xff1a;配置mysql数据库三&#xff1a;启动mysql数据库四&#xff1a;各类奇葩问题总结 一&#xff1a;下载mysql数据库 1.进入MyS…