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

🎉欢迎来到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;localparambyte1 = "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)beginif(!Rst_n)cnt <= 1'b0;else if(Tx_Done)cnt <= cnt + 1'b1;else if(key_flag & !key_state)cnt <= 1'b0;else;endalways@(posedge Clk or negedge Rst_n)beginif(!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;elsesend_en <= 1'b0;endalways@(*)begincase(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;endcaseendendmodule

RTL视图:

RTL1

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

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

always@(posedge Clk or negedge Rst_n)beginif(!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)beginif(!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;elsesend_en <= 1'b0;
end

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

always@(*)begincase(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 20module 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 beginRst_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;endendmodule

其中,仿真模型key_model:

`timescale 1ns/1nsmodule key_model(press,key);input press;output reg key;reg [15:0]myrand;initial beginkey = 1'b1;endalways@(posedge press)press_key;task press_key;begin//50次随机时间按下抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key = ~key;endkey = 0;#25_000_000;//按下稳定//50次随机时间释放抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key = ~key;endkey = 1;#25_000_000;//释放稳定endendtask	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;localparambyte1 = "H",byte2 = "E",byte3 = "L",byte4 = "L",byte5 = "O",byte6 = "\n";always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt <= 1'b0;else if(Tx_Done)cnt <= cnt + 1'b1;else if(key_flag & !key_state)cnt <= 1'b0;else;endalways@(posedge Clk or negedge Rst_n)beginif(!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;elsesend_en <= 1'b0;endalways@(*)begincase(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;endcaseendendmodule

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)beginif(!Rst_n)cnt <= 4'd0;else if(key_flag & !key_state)cnt <= cnt + 1'd1;else if(cnt == 4'd10)cnt <= 4'd0;elsecnt <= cnt;end//--------<数据查找表>--------		always@(*)begincase(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;endcaseend//--------<发送“\n”的计数器>--------	always@(posedge Clk or negedge Rst_n)beginif(!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@(*)begincase(cnt_N)2'd0:Num_byte = Num_byte_r;2'd1:Num_byte = "\n";default:Num_byte = 0;endcaseend//--------<发送模块使能信号的处理>--------		always@(posedge Clk or negedge Rst_n)beginif(!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;elsesend_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.hqwc.cn/news/128851.html

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

相关文章

asp.net闲置物品购物网系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net闲置物品购物网系统是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语 言开发 asp.net 闲置物品购物网 二、功…

Euclid空间or欧式空间(定义、正交性、正交变换、对称变换)酉空间(定义、酉变换、Hermite变换、正规矩阵)

欧式空间的定义 ​​​​​例如&#xff1a; 再例如&#xff1a; 正交性 正交基与标准正交基 施密特正交化例题 正交变换与正交矩阵 ​​​​​​​对称变换与对称矩阵 正交变换与对称变换例题 酉空间介绍 ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​…

游戏软件开发与应用软件开发有什么不同呢?

游戏软件开发和应用软件开发是两种不同类型的软件开发&#xff0c;它们在许多方面都有不同之处。以下是它们之间的一些主要区别&#xff1a; 目标用户群体&#xff1a; 游戏软件开发的主要目标是提供娱乐和休闲体验&#xff0c;通常面向广大的游戏玩家群体。游戏软件的设计和开…

lua 中文字符的判断简介

一般在工作中会遇到中文字符的判断、截断、打码等需求&#xff0c;之前一直没有总结&#xff0c;虽然网上资料也多&#xff0c;今天在这里简单的总结一下。 1 .UTF-8简单描述 UTF-8 是 Unicode 的实现方式之一&#xff0c;其对应关系&#xff08;编码规则&#xff09;如下表所…

提高网站性能的10种方法:加速用户体验和降低服务器负担

在今天的数字时代&#xff0c;网站性能对于吸引和保留用户至关重要。一个快速加载的网站不仅提供更好的用户体验&#xff0c;还有助于降低服务器负担。以下是10种提高网站性能的方法&#xff0c;旨在加速页面加载速度和减少服务器的工作负荷。 压缩网页资源 利用压缩算法如gzi…

位移贴图和法线贴图的区别

位移贴图和法线贴图都是用于增强模型表面细节和真实感的纹理贴图技术&#xff0c;但是它们之间也存在着差异。 1、什么是位移贴图 位移贴图&#xff1a;位移贴图通过在模型顶点上定义位移值来改变模型表面的形状。该贴图包含了每个像素的高度值信息&#xff0c;使得模型的细节…

electron之进程间通信

Electron进程间通信 使用electron编写程序时经常遇到下面这种场景&#xff1a; 当用户点击一个按钮时&#xff0c;需要将页面输入的信息保存到本地电脑上&#xff1b; 或者是点击菜单时&#xff0c;需要页面窗口做出响应。 用户点击的按钮和窗口展示的内容是运行在渲染进程中&…

【JavaEE初阶】 线程的状态和转移

文章目录 &#x1f332;线程的状态&#x1f340;观察线程的所有状态&#x1f38d;线程状态和状态转移的意义&#x1f38b;观察线程的状态和转移&#x1f4cc;观察 1:&#x1f4cc;观察 2:&#x1f4cc;观察-3: ⭕总结 &#x1f332;线程的状态 &#x1f340;观察线程的所有状态…

怎样开始用selenium进行自动化测试?

如果您刚开始使用 Selenium 进行自动化测试&#xff0c;以下是建议的步骤。 1、安装 Selenium 首先&#xff0c;您需要安装 Selenium。Selenium 支持多种编程语言&#xff0c;如 Python、Java、C# 等。可以通过 pip 命令在 Python 中安装 Selenium&#xff1a; pip install …

化妆品用乙基己基甘油全球市场总体规模2023-2029

乙基己基甘油又名辛氧基甘油&#xff0c;分子式 C11H24O3&#xff0c;分子量 204.306&#xff0c;沸点 325℃&#xff0c;密度 0.962&#xff0c;无色液体&#xff0c;涂抹性能适中的润肤剂、保湿剂及润湿剂。它能够在提高配方滋润效果的同时又具有柔滑的肤感。加入在某些膏霜体…

深入理解强化学习——强化学习的基础知识

分类目录&#xff1a;《深入理解强化学习》总目录 在机器学习领域&#xff0c;有一类任务和人的选择很相似&#xff0c;即序贯决策&#xff08;Sequential Decision Making&#xff09;任务。决策和预测任务不同&#xff0c;决策往往会带来“后果”&#xff0c;因此决策者需要为…

Jmeter常用参数化技巧总结!

说起接口测试&#xff0c;相信大家在工作中用的最多的还是Jmeter。 JMeter是一个100&#xff05;的纯Java桌面应用&#xff0c;由Apache组织的开放源代码项目&#xff0c;它是功能和性能测试的工具。具有高可扩展性、支持Web(HTTP/HTTPS)、SOAP、FTP、JAVA 等多种协议。 在做…