FPGA实现UART对应的电路和单片机内部配合寄存器实现的电路到底有何区别?

news2025/1/8 12:00:02

一、UART相关介绍

UART是我们常用的全双工异步串行总线,常用TTL电平标准,由TXD和RXD两根收发数据线组成。

那么,利用硬件描述语言实现UART对应的电路和51单片机内部配合寄存器实现的电路到底有何区别呢?接下来我们对照看一下。

二、FPGA实现UART异步串行收发方式

对于FPGA而言,利用verilog实现这个过程,其实最重要的就是安排好对应的计数器并配合移位寄存器串并转换即可完成。

FPGA发送端对应的代码如下:

module uart_tx(
	input wire clk ,
	input wire rst_n ,
	input wire [7:0] pi_data ,
	input wire pi_flag ,
	output reg tx 
	);
	reg [7:0] pi_data_reg;//用于寄存传进来的数据
	reg tx_flag ;
	reg [12:0] cnt_baud ;
	reg bit_flag1;
	reg [3:0] bit_cnt ;

	//在接收到pi_flag为高电平的时候寄存输入数?
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			pi_data_reg <= 'd0;
		else if(pi_flag == 1'b1)
			pi_data_reg <= pi_data ;
	end

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			tx_flag <= 1'b0;
		else if(bit_flag1 == 1'b1 && bit_cnt == 'd8)
			tx_flag <= 1'b0;
		else if(pi_flag == 1'b1)
			tx_flag <= 1'b1;
	end 

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt_baud <= 'd0;
		else if(tx_flag == 1'b0)
			cnt_baud <= 'd0;
		else if(cnt_baud == 'd5207 && tx_flag == 1'b1)
			cnt_baud <= 'd0;
		else if(tx_flag == 1'b1)
			cnt_baud <= cnt_baud + 1'b1;
	end

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			bit_flag1 <= 1'b0;
		else if(cnt_baud == 5206)
			bit_flag1 <= 1'b1;
		else 
			bit_flag1 <= 1'b0;
	end

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			bit_cnt <= 'd0;
		else if(bit_cnt == 'd8 && bit_flag1 == 1'b1)
			bit_cnt <= 'd0;
		else if(bit_flag1 == 1'b1)
			bit_cnt <= bit_cnt + 1'b1;
	end 

	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			tx <= 1'b1;//空闲状态
		else if(pi_flag == 1'b1)
			tx <= 1'b0;//起始?
		else if(bit_flag1 == 1'b1 && bit_cnt <= 'd7)
			tx <= pi_data_reg[bit_cnt];//8位数?
		else if(bit_cnt == 'd8 && bit_flag1 == 1'b1)
			tx <= 1'b1;//空闲?
	end
endmodule
FPGA接收端对应的代码如下:
module uart_rx(
	input wire clk ,
	input wire rst_n ,
	input wire rx ,
	output reg [7:0] po_data ,
	output reg po_flag
	);
	//用于打拍的寄存器
	reg rx1;
	reg rx2;
	reg rx2_reg ;
	reg rx_flag ;
	reg [12:0] cnt_baud ;
	reg bit_flag ;
	reg [3:0] bit_cnt ;

	//对接收到的外部信号进行打拍寄存
	always@(posedge clk)begin
		rx1 <= rx ;
		rx2 <= rx1 ;
		rx2_reg <= rx2 ;
	end

	//检测下降沿,拉高rx_flag
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			rx_flag <= 1'b0;
		end
		else if (rx2 == 1'b0 && rx2_reg == 1'b1) begin
			rx_flag <= 1'b1;
		end
		else if(bit_flag == 1'b1 && bit_cnt == 'd8)
			rx_flag <= 1'b0;
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) 
			cnt_baud <= 'd0;
		else if(rx_flag == 1'b0)
			cnt_baud <= 'd0;
		else if(cnt_baud == 'd5207 && rx_flag == 1'b1)//5207为波特率是9600时对应的计数值
			cnt_baud <= 'd0;
		else if(rx_flag == 1'b1)
			cnt_baud <= cnt_baud + 1'b1;
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) 
			bit_flag <= 1'b0;
		else if(cnt_baud == 'd2603 && rx_flag == 1'b1)
			bit_flag <= 1'b1;
		else 
			bit_flag <= 1'b0;
	end 

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) 
			bit_cnt <= 'd0;
		else if(bit_cnt == 'd8 && bit_flag == 1'b1)
			bit_cnt <= 'd0;
		else if(bit_flag == 1'b1 && rx_flag == 1'b1)
			bit_cnt <= bit_cnt + 1'b1;
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) 
			po_data <= 'd0;
		else if(bit_flag == 1'b1 && bit_cnt >= 'd1)
			po_data <= {rx2_reg,po_data[7:1]};
	end 

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) 
			po_flag <= 1'b0 ;
		else if(bit_flag == 1'b1 && bit_cnt == 'd8)
			po_flag <= 1'b1 ;
		else 
			po_flag <= 1'b0;
	end

endmodule
顶层代码如下:
module top_uart(
	input wire clk ,
	input wire rst_n ,
	input wire rx ,
	output wire tx
	);

	wire [7:0] po_data ;
	wire po_flag ;
	
	uart_rx inst_uart_rx (
		.clk(clk), 
		.rst_n(rst_n), 
		.rx(rx), 
		.po_data(po_data), 
		.po_flag(po_flag)
		);

	uart_tx inst_uart_tx (
		.clk(clk), 
		.rst_n(rst_n), 
		.pi_data(po_data), 
		.pi_flag(po_flag), 
		.tx(tx));

endmodule
经过综合后,生成的对应原理图如下:

顶层:

tx:可以发现,这里其实每一位寄存器都对应着一个D触发器,剩余的逻辑由LUT实现。

rx:接收端同理。

三、单片机内部UART异步串行收发实现方式

对于51单片机而言,它内部是CPU控制一些在物理层面已经连接好寄存器,通过对寄存器的控制配合电路的改变完成整个任务。

对于串口通信这个任务而言,它的整个实现过程总结如下:

整个实现可分为三大部分,定时器、中断系统及对应的缓冲寄存器完成,三大部分配合内部CPU便可完成收发任务,具体示意图如下:

红色区域是定时器部分,它外部连接着晶振,通过对TH1和TL1寄存器写入初始值可实现具体溢出频率的控制,假设晶振频率位12MHz(12T模式下1us计数一次),TH1和TL1填入0xF3(对应十进制243),一共256,则每隔256-243=13个数就会溢出一次,则溢出率为1/13us=0.07692MHz,当SMOD=1时,频率只经过16也就对应4800Hz,就会对应波特率4800Baud。配合控制器即可完成对波特率的控制。

蓝色部分是用于发送和接收数据的缓冲区;

黄色部分是串口中断器,这个中断器在接收数据的时候需要使用,TI和RI是中断标志。

在使用方式上,对涉及的寄存器进行初始化后,将数据写入SBUF或通过中断读取SBUF内的数据即可完成发送接收任务。

对应配置代码如下:

#include <REGX51.H>

/**
   *  @brief 串口初始化,4800bps@11.0592MHz
   *  @param 无	
   *  @retval 无
   */
void UART_Init()
{
	//对串行寄存器配置
	SCON=0x50;		//串行控制寄存器,涉及模式选择
	PCON &= 0x7F;		//电源控制寄存器(包括波特率)
	//对定时器0进行配置
	#11.0592M
	TMOD &= 0x0F;		//设置定时器模式,低4位保持不变
	TMOD |= 0x20;		//设置定时器模式,串口对应的模式需要是双8位模式
	TL1 = 0xFA;		//设定定时初值
	TH1 = 0xFA;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定定时器1
	
	//串口中断配置
	EA=1;//启动所有中断
	ES=1;//启动串口中断
}
/**
   *  @brief 串口发送1个字节数据,4800bps@11.0592MHz
   *  @param Byte 要发送的一个字节数据
   *  @retval 无
   */
void UART_SendByte(unsigned char Byte)
{
	SBUF=Byte;//将数据写入发送缓冲器即可
	while(TI==0);//是否有中断请求位
	TI=0;//软件置0
}

//串口中断函数的模板,需要用的时候复制到主函数中
/*
//注意:一个函数不能既在主函数中出现,又在中断函数中出现
void UART_Routine() interrupt 4 //串口中断对应的中断号
{
	if(RI==1)//接收到了串口中断
	{
		P2=~SBUF;
		UART_SendByte(SBUF);
		RI=0;//需要由软件清0
	}
	
}
*/



主函数代码如下:

#include <REGX51.H>
#include "Delay.h"
#include "UART.h"

void main()
{
	UART_Init();
	
	while(1)
	{
		
    }
}


//注意:一个函数不能既在主函数中出现,又在中断函数中出现
void UART_Routine() interrupt 4 //串口中断对应的中断号
{
	if(RI==1)//接收到了串口中断
	{
		P2=~SBUF;
		UART_SendByte(SBUF);
		RI=0;//需要由软件清0
	}
	
}

四、总结

均由寄存器加逻辑电路组成,只不过二者工作模式及灵活性差别很大,单片机依赖冯诺依曼架构需要控制器控制且电路固定(使得定时器、寄存器等资源有限),FPGA的并行及灵活性在这里体现的淋漓尽致。

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

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

相关文章

patchwork++地面分割学习笔记

参考资料&#xff1a;古月居 - ROS机器人知识分享社区 https://zhuanlan.zhihu.com/p/644297447 patchwork算法一共包含四部分内容&#xff1a;提出了以下四个部分&#xff1a;RNR、RVPF、A-GLE 和 TGR。 1&#xff09;基于 3D LiDAR 反射模型的反射噪声消除 (RNR)&#xff…

【VScode】设置代理,通过代理连接服务器

文章目录 VScode编辑器设置代理1.图形化界面1.1 进入proxy设置界面1.2 配置代理服务器 2.配置文件&#xff08;推荐&#xff09;2.1 打开setting.json 文件2.2 配置代理 VScode编辑器设置代理 根据情况安装nmap 1.图形化界面 1.1 进入proxy设置界面 或者使用快捷键ctrl , 。…

【HarmonyOS】鸿蒙应用点9图的处理(draw9patch)

【HarmonyOS】鸿蒙应用点9图的处理&#xff08;draw9patch&#xff09; 一、前言&#xff1a; 首先在鸿蒙中是不支持安卓 .9图的图片直接使用。只有类似拉伸的处理方案&#xff0c;鸿蒙提供的Image组件有与点九图相同功能的API设置。 可以通过设置resizable属性来设置Resiza…

光伏仿真与设计系统应用架构深度剖析

在光伏产业蓬勃发展的时代背景下&#xff0c;绿虫光伏仿真与设计系统成为推动其高效发展的核心力量。其应用架构涵盖多个关键步骤&#xff0c;每个环节都紧密相扣&#xff0c;共同构建起精准且高效的设计体系。 气象分析作为开篇之笔&#xff0c;起着基石般的重要作用。系统全…

k8s dashboard离线部署步骤

确定k8s版本&#xff0c;以1.23为例。 部署metrics-server服务&#xff0c;最好用v0.5.2。 用v0.6.0&#xff0c;可能会报以下错误&#xff1a; nodekubemaster:~/Desktop/metric$ kubectl top nodes Error from server (ServiceUnavailable): the server is currently unabl…

05-Linux系统编程之进程(下)

一、子进程资源回收 1.概述 在每个进程退出的时候&#xff0c;内核释放该进程所有的资源&#xff0c;包括一些存储在栈区、全局区的数据、打开的文件、占用的内存等。但是仍有一部分信息没有释放&#xff0c;这些信息主要指进程控制块 PCB 的信息&#xff08;包括进程号、退出…

HDFS异构存储和存储策略

一、HDFS异构存储类型 1.1 冷、热、温、冻数据 通常&#xff0c;公司或者组织总是有相当多的历史数据占用昂贵的存储空间。典型的数据使用模式是新传入的数据被应用程序大量使用&#xff0c;从而该数据被标记为"热"数据。随着时间的推移&#xff0c;存储的数据每周…

【51单片机】02LED流水灯实验

点亮你的LED 一、点亮第一个LED1.GPIO介绍2.P1、P2、P3端口 二、LED实验2.尝试点亮LED3.LED流水灯 一、点亮第一个LED 1.GPIO介绍 这块内容这里可以做简单的了解&#xff0c;与数电知识强相关。后续可以再回过头来学习 GPIO (general purpose input output) 通用输入输出端口…

springboot 集成 etcd

springboot 集成 etcd 往期内容 ETCD 简介docker部署ETCD 前言 好久不见各位小伙伴们&#xff0c;上两期内容中&#xff0c;我们对于分布式kv存储中间件有了简单的认识&#xff0c;完成了docker-compose 部署etcd集群以及可视化工具 etcd Keeper&#xff0c;既然有了认识&a…

云安全相关博客阅读(一)

2024-03-04 Cloudflare announces Firewall for AI 关注问题&#xff1a; 传统的WAF功能能够保护web和api安全&#xff0c;但是随着LLM等AI模型等出现&#xff0c;保护这些AI相关应用等安全是一个新出现的问题虽然AI应用是新的场景&#xff0c;但是以往的攻击方法也能够直接用…

2025年01月07日Github流行趋势

项目名称&#xff1a;khoj 项目地址url&#xff1a;https://github.com/khoj-ai/khoj项目语言&#xff1a;Python历史star数&#xff1a;20105今日star数&#xff1a;363项目维护者&#xff1a;debanjum, sabaimran, MythicalCow, aam-at, shantanuSakpal项目简介&#xff1a;你…

从零手写线性回归模型:PyTorch 实现深度学习入门教程

系列文章目录 01-PyTorch新手必看&#xff1a;张量是什么&#xff1f;5 分钟教你快速创建张量&#xff01; 02-张量运算真简单&#xff01;PyTorch 数值计算操作完全指南 03-Numpy 还是 PyTorch&#xff1f;张量与 Numpy 的神奇转换技巧 04-揭秘数据处理神器&#xff1a;PyTor…

【python】matplotlib(radar chart)

文章目录 1、功能描述和原理介绍2、代码实现3、效果展示4、完整代码5、多个雷达图绘制在一张图上6、参考 1、功能描述和原理介绍 基于 matplotlib 实现雷达图的绘制 一、雷达图的基本概念 雷达图&#xff08;Radar Chart&#xff09;&#xff0c;也被称为蛛网图或星型图&…

数据库环境安装(day1)

网址&#xff1a;MySQL 下载&#xff08;环境准备&#xff09;&#xff1a; &#xff08;2-5点击此处&#xff0c;然后选择合适的版本&#xff09; 1.linux在线YUM仓库 下载/安装: wget https://repo.mysql.com//mysql84-community-release-el9-1.noarch.rpm rpm -i https://r…

Fabric链码部署测试

参考链接&#xff1a;运行 Fabric 应用程序 — Hyperledger Fabric Docs 主文档 (hyperledger-fabric.readthedocs.io) &#xff08;2&#xff09;fabric2.4.3部署运行自己的链码 - 知乎 (zhihu.com) Fabric2.0测试网络部署链码 - 辉哥哥~ - 博客园 (cnblogs.com) 1.启动测试…

数据结构与算法之二叉树: LeetCode 107. 二叉树的层序遍历 II (Ts版)

二叉树的层序遍历 II https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/description/ 描述 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&a…

Python插件化开发实战:开发个图片浏览器

在本篇教程中&#xff0c;我将详细介绍如何使用Python开发一个基于插件架构的图片浏览器。这个项目将展示如何实现插件系统、如何处理图片显示,以及如何使用wxPython构建GUI界面。 “C:\pythoncode\pythonplugin\your_project\main_app.py” 项目概述 我们将开发一个具有以下…

根据python代码自动生成类图的实现方法[附带python源码]

概述 利用python库抽象语法树(AST)和类图描述语言(PlantUML),实现自动将python代码生成类图的目的。 环境 windowsvscodepythonplantuml ✒️网上好像大部分都是用Pyreverse库来实现的&#xff0c;但是我实际测试发现只能在一个文件中才能行&#xff0c;当然应该有解决方法…

下载b站高清视频

需要使用的edge上的一个扩展插件&#xff0c;所以选择使用edge浏览器。 1、在edge浏览器上下载 强力视频下载合并 扩展插件 2、在edge上打开b站&#xff0c;登录自己账号&#xff08;登录后才能下载到高清&#xff01;&#xff01;&#xff09;。打开一个视频&#xff0c;选择自…

flutter 专题二十四 Flutter性能优化在携程酒店的实践

Flutter性能优化在携程酒店的实践 一 、前言 携程酒店业务使用Flutter技术开发的时间快接近两年&#xff0c;这期间有列表页、详情页、相册页等页面使用了Flutter技术栈进行了跨平台整合&#xff0c;大大提高了研发效率。在开发过程中&#xff0c;也遇到了一些性能相关问题和…