野火FPGA跟练(四)——串口RS232、亚稳态
目录
- 简介
- 接口与引脚
- 通信协议
- 亚稳态
- RS232接收模块
- 模块框图
- 时序波形
- RTL 代码
- 易错点
- Testbench 代码
- 仿真
- RS232发送模块
- 模块框图
- 时序波形
- RTL 代码
- Testbench 代码
- 仿真
简介
- UART:Universal Asynchronous Receiver/Transmitter,异步串行通信接口。发送数据时并行转串行,接收数据时串行转并行。
- RS232:UART包括多种接口标准规范和总线标准规范,RS232为其一,还有RS499、RS423、RS422、RS485等。
接口与引脚


重点关注RXD、TXD即可。
通信协议
-
帧结构:包含8bit有效数据和起始位、停止位。空闲状态是高电平,起始位低电平,停止位高电平。低位数据先发,后发高位。

假设我要传输字符“1”,对应的ASCII码值是0x31,即“0011 0001” ,低位先发,那么我的这帧数据波形为 0 1000 1100 1 -
波特率:假设波特率为9600Bps,传输一个码元(一个码元在这里代表一个二进制数,就是1bit数据)需要1/9600秒。假设系统时钟50MHz(周期为20ns),那么传输1bit数据所需的时间为 (1/9600)/(20*e-9) = 5208.33 ≈ 5208 个时钟周期。
亚稳态
-
产生原因:
触发器采集输入信号时需要一定的建立时间和保持时间,如果在这一期间输入信号发生变化,那么触发器将无法稳定输出,导致输出信号在高低电平间快速振荡,即进入亚稳态。 -
不良影响:
如果处于亚稳态的信号直接接入组合逻辑电路,会导致亚稳态在整个系统中传递,从而影响整个电路的运行,导致不稳定。 -
解决方案:
单比特信号可以采用“打两拍”,多比特信号可以采用异步FIFO、格雷码、握手。
RS232接收模块
模块框图

输入输出描述:
- 时钟:50MHz,每10ns翻转一次
- 复位:高电平异步复位
- RX:接收的串口数据
- DATA:处理后的数据,并行输出
- FLAG:置高时代表已处理好一帧数据,只维持一个时钟周期
时序波形
示意时序图,以9600波特率为例。

时序图分析:
- RX_REG
- RX_REG1:是将 RX 同步到时钟信号上。
- RX_REG2、3:打两拍,避免亚稳态。
- START_FLAG 标志数据接收的开始、WORK_EN 标志数据接收的状态
- START_FLAG:这里使用的是组合方式赋值,通过 RX_REG2、3 检测 RX_REG 的下降沿。由于 RX_REG 的数据位中也有可能存在下降沿,所以 START_FLAG 置高的前提条件还应有 WORK_EN 为 0 。
- WORK_EN:WORK_EN 通过判断 START_FLAG 的状态而置高,通过判断 BIT_CNT、BIT_FLAG 的状态而置低,其余时刻保持不变。
- BAUD_CNT、BIT_FLAG
- BAUD_CNT:BAUD_CNT 只在 WORK_EN 为高时计数
- BIT_FLAG:在每比特数据传输时的中间时刻拉高
- RX_DATA
由于是低位数据先发,所以 RX_REG 的数据存在 RX_DATA 的高位,每次更新时 RX_DATA 右移,实现数据的串行转并行。RX_DATA 的值在 BAUD_CNT 为 1-8 并且 BIT_FLAG 为高时采集 RX_REG。
RTL 代码
module RS232_RX
#(parameter UART_BPS = 'd9600,parameter CLK_FREQ = 'd50_000_000
)
(input wire clk,input wire rst,input wire rx,output reg [7:0] data,output reg flag);parameter BAUD_CNT_MAX = CLK_FREQ / UART_BPS;reg rx_reg1;reg rx_reg2;reg rx_reg3;wire start_flag;reg work_en;reg [16:0] baud_cnt;reg bit_flag;reg [3:0] bit_cnt;reg [7:0] rx_data;reg data_flag;// 赋值rx_reg1always@(posedge clk or posedge rst) beginif(rst == 1'b1)rx_reg1 <= 1'b1;elserx_reg1 <= rx;end// 赋值rx_reg2always@(posedge clk or posedge rst) beginif(rst == 1'b1)rx_reg2 <= 1'b1;elserx_reg2 <= rx_reg1;end// 赋值rx_reg3always@(posedge clk or posedge rst) beginif(rst == 1'b1)rx_reg3 <= 1'b1;elserx_reg3 <= rx_reg2;end// 赋值start_flagassign start_flag = ((rx_reg2 == 1'b0) && (rx_reg3 == 1'b1));// 赋值work_enalways@(posedge clk or posedge rst) beginif(rst == 1'b1)work_en <= 1'b0;else if(start_flag == 1'b1)work_en <= 1'b1;else if((bit_flag == 1'b1) && (bit_cnt == 4'd8))work_en <= 1'b0;elsework_en <= work_en;end// 赋值baud_cntalways@(posedge clk or posedge rst) beginif(rst == 1'b1)baud_cnt <= 17'b0;else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))baud_cnt <= 17'b0;elsebaud_cnt <= baud_cnt + 1'b1;end // 赋值bit_flagalways@(posedge clk or posedge rst) beginif(rst == 1'b1)bit_flag <= 1'b0;else if(baud_cnt == BAUD_CNT_MAX / 2 - 1)bit_flag <= 1'b1;elsebit_flag <= 1'b0;end // 赋值bit_cntalways@(posedge clk or posedge rst) beginif(rst == 1'b1)bit_cnt <= 4'b0;else if((bit_flag == 1'b1) && (bit_cnt == 4'd8))bit_cnt <= 4'b0;else if(bit_flag == 1'b1)bit_cnt <= bit_cnt + 1'b1;elsebit_cnt <= bit_cnt;end// 赋值rx_dataalways@(posedge clk or posedge rst) beginif(rst == 1'b1)rx_data <= 8'b0;else if((bit_cnt >= 4'd1) && (bit_cnt <= 4'd8) && (bit_flag == 1'b1))rx_data <= {rx_reg3,rx_data[7:1]};elserx_data <= rx_data;end// 赋值data_flagalways@(posedge clk or posedge rst) beginif(rst == 1'b1)data_flag <= 1'b0;else if((bit_flag == 1'b1) && (bit_cnt == 4'd8))data_flag <= 1'b1;elsedata_flag <= 1'b0;end// 赋值dataalways@(posedge clk or posedge rst) beginif(rst == 1'b1)data <= 8'd0;else if(data_flag == 1'b1)data <= rx_data;elsedata <= data;end// 赋值flagalways@(posedge clk or posedge rst) beginif(rst == 1'b1)flag <= 1'b0;elseflag <= data_flag;end
endmodule
易错点
如果数据是多位的,赋值时需要注意其位宽大小。如果一个多位宽的数据赋值为“1’b0”,这将只赋值给这个数据的最低位,其他位将保留原值,与本意相悖。
Testbench 代码
`timescale 1ns / 1ps
module tb_RS232_RX();reg clk;reg rst;reg rx;wire [7:0] data;wire flag;// 初始化clk和rstinitial beginclk <= 1'b0;rst <= 1'b1;#60;rst <= 1'b0;end always #10 clk = ~clk;// 初始化rxinitial beginrx <= 1'b1;#100;rx_bit(8'b1001_0001);rx_bit(8'b0101_0010);rx_bit(8'b1110_0101);rx_bit(8'b0110_1000);rx_bit(8'b0110_1011);rx_bit(8'b0011_0110);rx_bit(8'b0001_1110);end// rx_bit函数,生成串行数据task rx_bit(input [7:0] rx_data);integer i;for(i = 0; i < 10; i = i + 1) begincase(i)0: rx <= 1'b0;1: rx <= rx_data[0];2: rx <= rx_data[1];3: rx <= rx_data[2];4: rx <= rx_data[3];5: rx <= rx_data[4];6: rx <= rx_data[5];7: rx <= rx_data[6];8: rx <= rx_data[7];9: rx <= 1'b1;endcase#(5208*20); end endtask// 实例化模块RS232_RX #(.UART_BPS(9600),.CLK_FREQ(50_000_000))tb_RS232_RX(.clk (clk ),.rst (rst ),.rx (rx ),.data (data ),.flag (flag ));
endmodule
仿真

RS232发送模块
模块框图

输入输出描述:
- 时钟:50MHz,每10ns翻转一次
- 复位:高电平异步复位
- DATA:待发送的并行数据
- FLAG:数据标志位
- TX:输出处理后的串行数据
时序波形
示意时序图,以9600波特率为例。

时序图分析:
- WORK_EN:WORK_EN 通过判断 START_FLAG 的状态而置高,通过判断 BIT_CNT、BIT_FLAG 的状态而置低,其余时刻保持不变。
- BAUD_CNT、BIT_FLAG
- BAUD_CNT:BAUD_CNT 只在 WORK_EN 为高时计数
- BIT_FLAG:在 BAUD_CNT 计数为1时刻拉高
RTL 代码
module RS232_TX
#(parameter UART_BPS = 'd9600,parameter CLK_FREQ = 'd50_000_000
)
(input wire clk,input wire rst,input wire [7:0] data,input wire flag,output reg tx);parameter BAUD_CNT_MAX = CLK_FREQ / UART_BPS;reg work_en;reg [16:0] baud_cnt;reg bit_flag;reg [3:0] bit_cnt;// 赋值work_enalways@(posedge clk or posedge rst) beginif(rst == 1'b1)work_en <= 1'b0;else if(flag == 1'b1)work_en <= 1'b1;else if((bit_flag == 1'b1) && (bit_cnt == 4'd10))work_en <= 1'b0;elsework_en <= work_en;end// 赋值baud_cntalways@(posedge clk or posedge rst) beginif(rst == 1'b1)baud_cnt <= 17'b0;else if((baud_cnt == BAUD_CNT_MAX - 1'b1) || (work_en == 1'b0))baud_cnt <= 17'b0;elsebaud_cnt <= baud_cnt + 1'b1;end// 赋值bit_flagalways@(posedge clk or posedge rst) beginif(rst == 1'b1)bit_flag <= 1'b0;else if(baud_cnt == 17'b1)bit_flag <= 1'b1;elsebit_flag <= 1'b0;end// 赋值bit_cntalways@(posedge clk or posedge rst) beginif(rst == 1'b1)bit_cnt <= 4'b0;else if((bit_flag == 1'b1) && (bit_cnt == 4'd10))bit_cnt <= 4'b0;else if(bit_flag == 1'b1)bit_cnt <= bit_cnt + 1'b1;elsebit_cnt <= bit_cnt;end// 赋值txalways@(posedge clk or posedge rst) beginif(rst == 1'b1)tx <= 1'b1;else begincase(bit_cnt)4'd1: tx <= 1'b0;4'd2: tx <= data[0];4'd3: tx <= data[1];4'd4: tx <= data[2];4'd5: tx <= data[3];4'd6: tx <= data[4];4'd7: tx <= data[5];4'd8: tx <= data[6];4'd9: tx <= data[7];4'd10: tx <= 1'b1;default: tx <= 1'b1;endcaseendend
endmodule
Testbench 代码
`timescale 1ns / 1ps
module tb_RS232_TX();reg clk; reg rst; reg [7:0] data;reg flag;wire tx; // 初始化clk和rstinitial beginclk <= 1'b0;rst <= 1'b1;#60;rst <= 1'b0;end always #10 clk = ~clk;// 初始化data和flaginitial begindata <= 8'd0;flag <= 1'd0;#200;// 数据1data <= 8'b0010_1011;flag <= 1'd1;#20;flag <= 1'd0;#(5208*12*20);// 数据2data <= 8'b0111_0101;flag <= 1'd1;#20;flag <= 1'd0;#(5208*12*20);// 数据3data <= 8'b1010_0001;flag <= 1'd1;#20;flag <= 1'd0;#(5208*12*20);end// 实例化模块RS232_TX #(.UART_BPS(9600),.CLK_FREQ(50_000_000))tb_RS232_TX(.clk (clk), .rst (rst), .data (data),.flag (flag), .tx (tx) );
endmodule
仿真

相关文章:
野火FPGA跟练(四)——串口RS232、亚稳态
目录 简介接口与引脚通信协议亚稳态RS232接收模块模块框图时序波形RTL 代码易错点Testbench 代码仿真 RS232发送模块模块框图时序波形RTL 代码Testbench 代码仿真 简介 UART:Universal Asynchronous Receiver/Transmitter,异步串行通信接口。发送数据时…...
Qt for Android 申请摄像头权限
步骤 1. 添加用户权限 方式1: AndroidManifest.xml 中新增(不添加后面申请选项时不弹窗) 或者再Qt Creator中直接添加 方式2: .pro 中引用multimedia 模块,编译时配置自动添加 <uses-permission android:name"android.permissi…...
kivy 百词斩项目 报错
AttributeError: FigureCanvasKivyAgg object has no attribute resize_event AttributeError: FigureCanvasKivyAgg object has no attribute resize_event 是一种常见的Python错误,当你试图访问一个对象(在这个例子中是 FigureCanvasKivyAgg 对象&am…...
ChatTTS 文字生成语言本地模型部署
ChatTTS部署 官方信息 [ChatTTS首页](https://chattts.com/)搭建步骤 1、下载源码 git clone https://github.com/2noise/ChatTTS.git 2、按照环境 pip install torch ChatTTS pip install -r requirements.txt 3、下载模型 git clone https://www.modelscope.cn/pzc163/ch…...
多曝光融合算法(三)cv2.createAlignMTB()多曝光图像融合的像素匹配问题
文章目录 1.cv2.createAlignMTB() 主要是计算2张图像的位移,假设位移移动不大2.多曝光图像的aline算法:median thresold bitmap原理讲解3.图像拼接算法stitch4.多曝光融合工具箱 1.cv2.createAlignMTB() 主要是计算2张图像的位移,假设位移移动…...
C/C++|类型推导中的模式匹配
在C11及以上的相关语法中,特别是在模版元编程的范式里,类型推导是了重中之重。 在 《Effective Modern C 》 中第一章主要就是讲各种类型推导。 当然了,谈到类型推导,我们不得不先搞懂类型推导中的模式匹配,这是基础&a…...
The 18th Northeast Collegiate Programming Contest(5/9/13)
心得 赛中ac:5,目前ac:9,题目总数:13 中档可做题还是很多的,可惜遇到了难绷的queueforces, 最后15min才判出来,oi赛制5wa4遗憾离场,赛后把几个题都给调过了࿰…...
Vue前端在线预览文件插件
Vue前端在线预览文件插件 一、使用场景 1.1.像文档资料等,只想让他人在线预览,但不能下载。此等场景需求可以用到此插件。 二、此文档介绍两种插件 1.view.xdocin插件 (上线后免费几天,然后收费,添加作者后,可以延…...
【ai】Audio2Face
Audio2Face 简介 Audio2Face是英伟达Omniverse平台的一部分,它使用先进的AI技术来生成基于音频输入的逼真面部动画。这个技术主要利用深度学习模型来解析人声,进而驱动一个三维模型的面部表情。下面是Audio2Face工作流程的详细说明: 预备阶段 在使用Audio2Face之前,需要准…...
2024.6.9 一
装饰器(Decorators) 装饰器是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,我们在用到装饰器时,常用到一个的符号,这个叫做语法糖,在函数定义前加上decorator_name, 那么后面的函数执行…...
地图之战争迷雾/地图算法/自动导航(一)
战争迷雾 TiledMap 创建黑色覆盖块,然后使用碰撞组件,控制黑色块的显示和隐藏 地图算法 在有些游戏中,地图需要随机生成,比如游戏中的迷宫等,这就需要地图生成的算法;在角色扮演类游戏中,角色…...
【wiki知识库】06.文档管理页面的添加--前端Vue部分
📝个人主页:哈__ 期待您的关注 目录 一、🔥今日目标 二、🐻前端Vue模块的改造 BUG修改 1.wangeditor无法展示问题 2.弹窗无法正常关闭问题 2.1 添加admin-doc.vue 2.1.1 点击admin-ebook中的路由跳转到admin-doc 2.2.2 进入…...
新电脑必装的7款软件,缺一不可
如果你买了新电脑或者是重装了新系统,那么这7款软件你一定要安装。 1、SpaceSniffer 如果你的C盘经常爆红,但是不知道是什么原因,那么你应该需要SpaceSniffer这款软件,它可以把你C盘中文件的空间占用情况,以大小方框…...
程序员学习Processing和TouchDesigner视觉编程相关工具
Proessing Processing 是一种用于视觉艺术和创意编程的开发环境和编程语言。它最初是为了帮助非专业程序员学习编程,特别是那些对于创意编程和视觉表达感兴趣的人。Processing 提供了简单易用的 API,使得绘制图形、创建动画和交互式应用变得相对容易。 …...
gitlabcicd-k8s部署gitlab
一.安装准备环境 存储使用nfs挂载持久化 k8s环境 helm安装 建议helm 3 二.部署gitlab-deploy.yaml nfs的ip是192.168.110.190 挂载目录是/data/data 注意所需要的目录需要创建:/data/data/gitlab/config ,/data/data/gitlab/logs ,/dat…...
浅谈JDBC
文章目录 一、什么是 JDBC?二、JDBC 操作流程三、JDBC代码例子 一、什么是 JDBC? JDBC是一种可用于执行SQL语句的JAVA API,是链接数据库和JAVA应用程序的纽带。JDBC一般需要进行3个步骤:与数据库建立一个链接、向数据库发送SQL语…...
【数据结构初阶】--- 顺序表
顺序表,好像学C语言时从来没听过,实际上就是给数组穿了层衣服,本质是一模一样的。 这里的顺序表实际是定义了一个结构体,设计各种函数来实现它的功能,比如说数组中的增删改查插入,这些基本操作其实平时就会…...
一个完整的java项目通常包含哪些层次(很全面)
1.View层(视图层) 职责:负责数据的展示和用户交互。在Web应用中,View层通常与HTML、CSS和JavaScript等技术相关。 技术实现:在Spring MVC中,View层可以使用JSP、Thymeleaf、FreeMarker等模板引擎来实现。…...
设置电脑定时关机
1.使用快捷键winR 打开运行界面 2.输入cmd ,点击确认,打开命令行窗口,输入 shutdown -s -t 100,回车执行命令,自动关机设置成功 shutdown: 这是主命令,用于执行关闭或重启操作。-s: 这个参数用于指定执行关…...
Java 编译报错:找不到符号? 手把手教你排查解决!
Java 编译报错:找不到符号? 手把手教你排查解决! 在 Java 开发过程中,我们经常会遇到编译器抛出 "找不到符号" 错误。这个错误提示意味着编译器无法在它所理解的范围内找到你所引用的类、变量或方法。这篇文章将带你一步…...
如何高效配置ClickHouse连接器:专业用户的完整指南
如何高效配置ClickHouse连接器:专业用户的完整指南 【免费下载链接】clickhouse-odbc ODBC driver for ClickHouse 项目地址: https://gitcode.com/gh_mirrors/cl/clickhouse-odbc ClickHouse ODBC驱动是连接ClickHouse数据库与各类数据分析工具的关键桥梁&a…...
从手动导入到自动溯源:Perplexity提问→Mendeley定位原文→高亮引用段落→一键生成BibTeX(全流程图解)
更多请点击: https://intelliparadigm.com 第一章:从手动导入到自动溯源:Perplexity提问→Mendeley定位原文→高亮引用段落→一键生成BibTeX(全流程图解) 科研写作中,文献溯源与引用管理长期面临“知其然不…...
Markdown元数据自动化管理:mdac-filler工具核心功能与实战指南
1. 项目概述:一个为Markdown文档自动填充元数据的工具如果你经常用Markdown写文档、博客或者项目README,肯定遇到过这样的场景:每次新建一个文件,都得手动去文件头部敲一堆“Front Matter”元数据,比如标题、日期、标签…...
淘金币自动化脚本:每天6分钟搞定淘宝全任务,彻底解放你的双手
淘金币自动化脚本:每天6分钟搞定淘宝全任务,彻底解放你的双手 【免费下载链接】taojinbi 淘宝淘金币自动执行脚本,包含蚂蚁森林收取能量,芭芭农场全任务,解放你的双手 项目地址: https://gitcode.com/gh_mirrors/ta/…...
macOS桌面歌词神器LyricsX:免费开源歌词同步工具完整指南
macOS桌面歌词神器LyricsX:免费开源歌词同步工具完整指南 【免费下载链接】Lyrics Swift-based iTunes plug-in to display lyrics on the desktop. 项目地址: https://gitcode.com/gh_mirrors/lyr/Lyrics LyricsX是一款专为macOS设计的开源桌面歌词显示工具…...
WaveTools:鸣潮玩家的终极优化工具箱,轻松解锁120FPS流畅体验
WaveTools:鸣潮玩家的终极优化工具箱,轻松解锁120FPS流畅体验 【免费下载链接】WaveTools 🧰鸣潮工具箱 项目地址: https://gitcode.com/gh_mirrors/wa/WaveTools 你是否曾经在《鸣潮》的激烈战斗中感受到画面卡顿?是否因为…...
保姆级避坑指南:用GGCNN源码处理Cornell抓取数据集,解决tiff文件生成失败问题
GGCNN源码实战:Cornell数据集预处理深度排错指南 第一次运行GGCNN的Cornell数据集预处理脚本时,我盯着毫无反应的终端窗口足足等了十分钟——没有进度条,没有错误提示,只有光标在无情地闪烁。这大概是每个复现论文的开发者都会经历…...
MobaXterm 全能终端神器:实战指南
写在前面:作为Windows下最全能的远程终端工具,MobaXterm 在 2026 年已迭代至 v26.0 版本。本文基于最新版,从工具选型对比、核心功能实战到效率提升技巧,带你真正掌握这款"瑞士军刀"。文末附赠快捷键大全和安全配置清单…...
流处理优化:提高实时数据处理性能
流处理优化:提高实时数据处理性能 一、流处理优化概述 1.1 流处理优化的定义 流处理优化是指通过优化流处理系统的性能、吞吐量和延迟,提高实时数据处理能力的过程。它涉及优化数据处理管道、资源配置和算法实现。 1.2 流处理优化的价值 低延迟ÿ…...
新手避坑指南:ICC LAB2 Design Planning 从加载设计到写出DEF的完整流程复盘
ICC LAB2 Design Planning全流程深度解析:从数据加载到DEF输出的实战避坑指南 当你第一次打开ICC工具面对LAB2的Design Planning任务时,是否感觉像被扔进了一个满是按钮的控制室?每个命令似乎都重要,但又不清楚它们如何串联成完整…...
