【FPGA】串口通信讲解-状态机判断数据值

news2024/11/26 10:23:32

🎉欢迎来到FPGA专栏~串口通信讲解


  • ☆* o(≧▽≦)o *☆~我是小夏与酒🍹
  • 博客主页:小夏与酒的博客
  • 🎈该系列文章专栏:FPGA学习之旅
  • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
  • 📜 欢迎大家关注! ❤️
    FPGQ2

CSDN

🎉 目录-串口通信讲解

  • 一、效果演示
  • 二、串口通信基础知识
  • 三、基本通信代码编写
    • 3.1 异步信号分析
    • 3.2 USB
    • 3.3 RS232
    • 3.4 串口接收模块
    • 3.5 串口发送模块
  • 四、串口通信控制led亮灭

遇见未来

一、效果演示

🥝视频演示:

FPGA串口通信控制led点亮与熄灭

🥝RTL视图:
顶层模块:
顶层模块

状态机判断数据值:
FSM
状态转移

二、串口通信基础知识

参考【小月电子】大佬的博客:【小月电子】ALTERA FPGA开发板系统学习教程-LESSON7串口通信。

UART 是一种采用异步串行通信方式的通用异步收发传输器(universal asynchronous receiver-transmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。

UART 串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。UART 在发送或接收过程中的一帧数据由 4 部分组成,起始位、数据位、奇偶校验位和停止位。其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。校验位分为奇校验和偶校验,用于检验数据在传输过程中是否出错。奇校验时,发送方应使数据位中 1 的个数与校验位中 1 的个数之和为奇数;接收方在接收数据时,对 1 的个数进行检查,若不为奇数,则说明数据在传输过程中出了差错。同样,偶校验则检查 1 的个数是否为偶数。

串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是 bps( 位/秒),常用的波特率有 9600、19200、38400、57600 以及 115200 等。

在本篇文章中,串口通信所使用的波特率为9600,计算过程:
1、板载晶振为50MHz,时钟周期为20ns
2、波特率9600bps表示每秒传输9600bit
3、传输1bit的时间为:1/9600(秒)=104167ns
4、104167/20=5208.35,也就是5208个时钟周期,需要采集1次

在异步串口时序图中,空闲时为高电平,起始位为低电平,停止为高电平:
异步串口时序图
串口接收时序图:
串口接收时序图

串口发送时序图:
串口发送时序图

三、基本通信代码编写

3.1 异步信号分析

参考链接:高级FPGA设计技巧!多时钟域和异步信号处理解决方案。

亚稳态
触发器的建立时间和保持时间在时钟上升沿左右定义了一个时间窗口,如果触发器的数据输入端口上数据在这个时间窗口内发生变化(或者数据更新),那么就会产生时序违规。存在这个时序违规是因为建立时间要求和保持时间要求被违反了,此时触发器内部的一个节点(一个内部节点或者要输出到外部节点)可能会在一个电压范围内浮动,无法稳定在逻辑0或者逻辑1状态。换句话说,如果数据在上述窗口中被采集,触发器中的晶体管不能可靠地设置为逻辑0或者逻辑1对应的电平上。所以此时的晶体管并未处于饱和区对应的高或者低电平,而是在稳定到一个确定电平之前,徘徊在一个中间电平状态(这个中间电平或许是一个正确值,也许不是)。如下图所示,这就是所谓的亚稳态:
亚稳态

解决亚稳态的方法之一就是打两拍处理
打两拍
处理实现代码:

reg	rxd_ff1;
reg	rxd_ff2;
//打两拍操作-第一拍
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		rxd_ff1 <= 1'b0;
	else
		rxd_ff1 <= rxd;
end
//打两拍操作-第二拍
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		rxd_ff2 <= 1'b0;
	else
		rxd_ff2 <= rxd_ff1;
end

3.2 USB

硬件电路:
TTL

3.3 RS232

硬件电路:
RS232

3.4 串口接收模块

async_uart_rev.v:

///
//模块介绍:实现异步串口接收功能
///
module async_uart_rev(
	input				rst_n	,//复位信号,低电平有效
	input				clk,//时钟信号,50MHZ
	input				rxd,//串行接收数据
	output	reg	[7:0]	rev_data,//并行数据
	output	reg			rev_dvld //并行数据有效标志
	);
	
	parameter	baud_num	=5207;//1/9600*1000000000/20
	parameter	IDLE		=4'd0;
	parameter	START_ST    =4'd1;
	parameter	STOP_ST     =4'd2;
	
	reg	[12:0]	baud_cnt;
	reg			baud_cnt_en;
	wire		sample_en;
	reg	[3:0]	sample_num;
	reg			rxd_ff1;
	reg			rxd_ff2;
	reg	[3:0]	curr_st;
	
	//打两拍操作-第一拍
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			rxd_ff1 <= 1'b0;
		else
			rxd_ff1 <= rxd;
	end
	//打两拍操作-第二拍
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			rxd_ff2 <= 1'b0;
		else
			rxd_ff2 <= rxd_ff1;
	end
	
	//sample_en判断计数器是否到达计数值
	assign sample_en = (baud_cnt == baud_num[12:1])?1'b1:1'b0;
	
	//状态机跳转程序
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			curr_st<=IDLE;
		else case(curr_st)
			IDLE:begin
				if(rxd_ff2==0)
					curr_st<=START_ST;
				else
					;
			end
			START_ST:begin
				if(sample_num==8&&sample_en)
					curr_st<=STOP_ST;
				else
					;
			end
			STOP_ST:begin
				if(rxd_ff2==1&&sample_en)
					curr_st<=IDLE;
				else
					;
			end
			default:;
		endcase
	end
	
	//baud_cnt波特率计数器
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			baud_cnt<=0;
		else if(curr_st==START_ST||curr_st==STOP_ST)begin
			if(baud_cnt==baud_num)
				baud_cnt<=0;
			else 
				baud_cnt<=baud_cnt+1;
		end else
			baud_cnt<=0;
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)	
			sample_num<=0;
		else if(sample_en&&sample_num==9)
			sample_num<=0;
		else if(sample_en)
			sample_num<=sample_num+1;
		else
			;
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			rev_data<=0;
		else if(sample_en)
			case(sample_num)
			1:rev_data[0]<=rxd_ff2;
			2:rev_data[1]<=rxd_ff2;
			3:rev_data[2]<=rxd_ff2;
			4:rev_data[3]<=rxd_ff2;
			5:rev_data[4]<=rxd_ff2;
			6:rev_data[5]<=rxd_ff2;
			7:rev_data[6]<=rxd_ff2;
			8:rev_data[7]<=rxd_ff2;
			default:;
		endcase
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)	
			rev_dvld<=0;
		else if(sample_num==9&&sample_en)
			rev_dvld<=1;
		else
			rev_dvld<=0;
	end
	
endmodule

3.5 串口发送模块

async_uart_tran.v:

///
//模块介绍:实现异步串口发送功能
///
module async_uart_tran(
	input				rst_n	,//复位信号,低电平有效
	input				clk,//时钟,50MHZ
	input		[7:0]	tran_data,//输入的并行数据
	input				tran_dvld,//输入的并行数据有效标志
	output		reg		txd      //串行输出数据
	);
	
	parameter	baud_num	=5207;//1/9600*1000000000/20
	parameter	IDLE		=4'd0;
	parameter	DATA_ST    	=4'd1;
	parameter	START_ST    =4'd2;
	parameter	STOP_ST     =4'd3;
	
	reg	[12:0]		baud_cnt;
	reg				baud_cnt_en;
	reg	[3:0]		sample_num;
	reg	[3:0]		curr_st;
	
	wire		sample_en;

	assign	sample_en = (baud_cnt==baud_num)?1'b1:1'b0;
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			curr_st<=IDLE;
		else case(curr_st)
			IDLE:begin
				if(tran_dvld==1)
					curr_st<=START_ST;
				else
					;
			end
			START_ST:begin
				if(sample_en==1)
					curr_st<=DATA_ST;
			end
			DATA_ST:begin
				if(sample_en&&sample_num==8)
					curr_st<=STOP_ST;
				else
					;
			end
			STOP_ST:begin
				if(sample_en==1)
					curr_st<=IDLE;
				else
					;
			end
			default:;
		endcase
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			baud_cnt<=0;
		else if(curr_st==START_ST||curr_st==DATA_ST||curr_st==STOP_ST)begin
			if(baud_cnt==baud_num)
				baud_cnt<=0;
			else 
				baud_cnt<=baud_cnt+1;
		end else
			baud_cnt<=0;
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)	
			sample_num<=0;
		else if(curr_st==IDLE)
			sample_num<=0;
		else if(sample_en)
			sample_num<=sample_num+1;
		else
			;
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			txd<=1;
		else if(sample_en)
			case(sample_num)
			0:txd<=1'b0;
			1:txd<=tran_data[0];
			2:txd<=tran_data[1];
			3:txd<=tran_data[2];
			4:txd<=tran_data[3];
			5:txd<=tran_data[4];
			6:txd<=tran_data[5];
			7:txd<=tran_data[6];
			8:txd<=tran_data[7];
			9:txd<=1'b1;
			default:txd<=1;
		endcase
	end
	
endmodule

四、串口通信控制led亮灭

使用状态机判断数据并控制led亮灭

状态机学习参考链接:【FPGA零基础学习之旅#9】状态机基础知识。

主要根据状态跳转来编写代码:
状态转移

data.v:

module data(
	input 				clk,
	input 				rst_n,
	input 		[7:0]	rev_data,
	output reg 	[3:0]	led
);

	//定义LED的亮灭
	parameter led_light_all = 8'h11;//全部点亮
	parameter led_close_all = 8'h00;//全部熄灭
	parameter led_light_one = 8'hAA;//点亮led1
	parameter led_close_one = 8'hFF;//关闭led1
	
	localparam 
		IDLE 	 = 4'b0001,
		LIGHTALL = 4'b0010,
		ONELIGHT = 4'b0100,
		ONECLOSE = 4'b1000;
	
	reg [3:0]curr_st;
	
	//状态机主程序
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			curr_st <= IDLE;
		else begin
			case(curr_st)
				IDLE:begin
					if(rev_data == led_light_all)
						curr_st <= LIGHTALL;
					else if(rev_data == led_light_one)
						curr_st <= ONELIGHT;
					else
						;
				end
				LIGHTALL:begin
					if(rev_data == led_close_all)
						curr_st <= IDLE;
					else
						;
				end
				ONELIGHT:begin
					if(rev_data == led_close_one)
						curr_st <= ONECLOSE;
					else
						;
				end
				ONECLOSE:curr_st <= IDLE;
			default:curr_st <= IDLE;
			endcase
		end
	end
	
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			led <= 4'b1111;
		else begin
			case(curr_st)
				IDLE:		led <= 4'b1111;
				LIGHTALL:led <= 4'b0000;
				ONELIGHT:led <= 4'b1110;
				ONECLOSE:led <= 4'b0000;
			default:led <= led;
			endcase
		end
	end

endmodule

data.v的RTL视图:

FSM

本篇文章实现的顶层模块:

///
//模块介绍:顶层模块,例化接收和发送模块,控制LED状态
///
module async_uart_top(
	input			clk		,//时钟,50MHZ
	input			rst_n		,//复位信号,低电平有效
	input			rxd		,//串行接收数据
	output			txd		,//串行发送数据
	output	[3:0] 	led
	);
	
	wire	[7:0]	rev_data;
	wire			rev_dvld;
	
	async_uart_rev Uasync_uart_rev(
		.rst_n(rst_n),
		.clk(clk),
		.rxd(rxd),
		.rev_data(rev_data),
		.rev_dvld(rev_dvld)
	);
	
	async_uart_tran Uasync_uart_tran(
		.rst_n(rst_n),
		.clk(clk),
		.tran_data(rev_data),
		.tran_dvld(rev_dvld),
		.txd(txd)
	);
	
	data Udata(
		.clk(clk),
		.rst_n(rst_n),
		.rev_data(rev_data),
		.led(led)
	);
	
endmodule

顶层模块的RTL视图:

顶层模块

csdn

🧸结尾


  • ❤️ 感谢您的支持和鼓励! 😊🙏
  • 📜您可能感兴趣的内容:
  • 【Python】Python实现串口通信(Python+Stm32)
  • 【Verilog HDL】FPGA-testbench基础知识
  • 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
  • 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制
    遇见未来

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

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

相关文章

【ShaderToy中图形效果转译到UnityShaderlab案例分享,实现粒子场_ParticleField】

Shader"ShaderToy/ParticleField" {Properties{_iMouse("iMouse", Vector) = (0,0,0,0)}SubSha

怎么把PDF转为word?1分钟解决难题

PDF文件在我们的电脑上应用非常广泛&#xff0c;由于其较高的安全性和兼容性&#xff0c;得到了广泛的认可。然而&#xff0c;对于一些人来说&#xff0c;PDF文件不能直接进行编辑和修改可能是一个问题。因此&#xff0c;通常我们需要将其转换为Word格式&#xff0c;以便在Word…

uniapp 微信小程序 input详解 带小数点的input、可查看密码的输入框input

官网文档地址 1、template <!-- 本示例未包含完整css&#xff0c;获取外链css请参考上文&#xff0c;在hello uni-app项目中查看 --> <template><view><view class"uni-common-mt"><view class"uni-form-item uni-column"&g…

Mysql 主从复制、读写分离

目录 前言 一、主从复制原理 1.1 MySQL的复制类型 1.2 mysql主从复制的工作原理 1.3 MySQL主从复制延迟 1.4 MySQL四种同步方式 1.5 MySQL支持的复制类型 二、 MySQL应用场景 三、主从复制实验 3.1 MySQL主从服务器时间同步 3.1.1 安装ntp、修改配置文件 3.1.2 两台…

如何查看小程序的APPID和AppSecret

小程序APPID可以在手机上打开小程序后&#xff0c;点击右上角三点&#xff1a; 然后点击中间位置的小程序名称&#xff0c;进入小程序介绍页面&#xff1a; 点击“更多资料”后&#xff0c;进入页面就可以看到上方有APPID&#xff1a; 另一种方法&#xff1a; 在微信公众平台登…

安装:【vue】npm install -g @vue/cli出现错误

安装Vue脚手架&#xff0c;cli就是Command Line Interface 命令行接口 工具 进行到npm install -g vue/cli这一步出现错误&#xff0c;操作步骤如下&#xff1a; 1.通过 wins打开开始栏的搜索框&#xff0c;输入cmd&#xff0c;管理员身份运行 2.先下载node.js 不知道有没有…

数据库应用:Mycat+HAProxy+Keepalived实现高可用

目录 一、理论 1.高可用 2.部署高可用 二、实验 1.MycatHAProxyKeepalived实现高可用 三、问题 1.开启HA-Proxy失败 2. VMware 克隆linux 网卡UUID重复问题 3.keepalived状态报错 四、总结 一、理论 1.高可用 &#xff08;1&#xff09;概述 ①Mycat 在实际项目中…

蓝桥杯专题-真题版含答案-【加法变乘法】【三羊献瑞】【交换瓶子】【卡片换位】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

【专题速递】多模态数字人、多模态媒体大模型以及AI与AIGC对音视频的影响

// 随着AIGC内容的兴起&#xff0c;多模态媒体大模型逐渐进入大家的视野&#xff0c;而LLM的发展又让人们对未来音视频工具产生了新的思考。7月29日LiveVideoStackCon2023上海站AIGC与内容生产专场&#xff0c;集齐当下最热门的AIGC话题&#xff0c;与您共享。 AIGC与内容生产…

华为云使用 helm 模板

登录华为云上的 cce 进入对应的集群&#xff0c;选择模板管理 注意&#xff1a;华为云上不能配置 helm 仓库&#xff0c;只提供手动上传模板&#xff0c;模板的后缀为 tgz 下载模板的方式有很多种&#xff0c;这里用 xxl-job 举例 添加 helm 仓库更新仓库下载模板 helm rep…

基于linux下的高并发服务器开发(第二章)- 2.24 信号集及相关函数

1.用户通过键盘 Ctrl C, 产生2号信号SIGINT (信号被创建) 2.信号产生但是没有被处理 &#xff08;未决&#xff09; - 在内核中将所有的没有被处理的信号存储在一个集合中 &#xff08;未决信号集&#xff09; - SIGINT信号状态被存储在第二个标志位上 - 这个…

概率论和随机过程的学习和整理--番外15,如何计算N合1的合成数量问题?

目录 1 目标问题&#xff1a;多阶2合1的合成问题 1.1 原始问题 1.2 合成问题要注意&#xff0c;合成的数量 1.3 合成问题不能用马尔科夫链来解决 2 方案1&#xff1a;用合成公式合成多次能解决吗&#xff1f; --不能&#xff0c;解决不了递归的问题 3 方案2&#xff0c;…

微分流形2:流形上的矢量场和张量场

来了来了&#xff0c;切向量&#xff0c;切空间。流形上的所有的线性泛函的集合&#xff0c;注意是函数的集合。然后取流形上的某点p&#xff0c;它的切向量为&#xff0c;线性泛函到实数的映射。没错&#xff0c;是函数到实数的映射&#xff0c;是不是想到了求导。我们要逐渐熟…

uni-app 微信小程序:启用组件按需注入

uni-app 微信小程序&#xff1a;启用组件按需注入 文章目录 uni-app 微信小程序&#xff1a;启用组件按需注入一、官方文档按需注入注意事项 二、HBuilder X 设置三、效果 一、官方文档 https://developers.weixin.qq.com/miniprogram/dev/framework/ability/lazyload.html 按…

大数据面试题:HBase读写数据流程

面试题来源&#xff1a; 《大数据面试题 V4.0》 大数据面试题V3.0&#xff0c;523道题&#xff0c;679页&#xff0c;46w字 参考答案&#xff1a; 1、写数据流程 1&#xff09;Client先访问zookeeper&#xff0c;获取hbase:meta表位于哪个Region Server。 2&#xff09;访…

MySQL的主从复制练习

基本原理图如下&#xff1a; 主从复制&#xff08;也称 AB 复制&#xff09;允许将来自一个MySQL数据库服务器&#xff08;主服务器&#xff09;的数据复制到一个或多个MySQL数据库服务器&#xff08;从服务器&#xff09;。当主库进行更新的时候&#xff0c;会自动将数据复制到…

【youcans动手学模型】目标检测之 RCNN 模型

欢迎关注『youcans动手学模型』系列 本专栏内容和资源同步到 GitHub/youcans 【youcans动手学模型】目标检测之 RCNN 模型 1. R-CNN 目标检测1.1 论文摘要1.2 技术背景1.3 基本方法1.4 算法实现1.5 总结 2. 使用 PyTorch 实现 RCNN 目标检测2.1 训练 AlexNet 模型2.2 微调 Alex…

《个人理财》——第六章 理财规划计算工具与方法

考点1&#xff1a; 货币时间价值概述 货币时间价值 也叫资金的时间价值 • 货币在无风险条件下&#xff0c; 经历一定时间投资和再投资发生的增值 • 同等数量货币或现金流的未来价值更低 • 不同时间单位的货币不具有可比性 产生的原因• 现有货币用于投资&#xff0c; 可获得…

IDEA+SpringBoot +ssm+ Mybatis+easyui+Mysql求职招聘管理系统网站

IDEASpringBoot ssm MybatiseasyuiMysql求职招聘管理系统网站 一、系统介绍1.环境配置 二、系统展示1. 登录2.注册3.首页4.公司5.关于我们6.我的简历7.我投递的简历8.修改密码9. 管理员登录10.我的信息11.用户信息12.职位类别13. 职位列表14. 公司列表15. 日志列表 三、部分代码…