fpga实操训练(uart串口)

news2024/9/22 12:59:58

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        如果说led灯、按键、数码管这些都只能算是基础的话,那么学习fpga遇到的第一个门槛就是uart。要做好uart,首先需要了解串口的一些特性。

1、uart串口的基本特性

        1)波特率。所谓波特率,就是多长时间接收一个数据。这个速度可以快,可以慢。关键是双方要匹配。

        2)数据的构成。一般uart的数据,由起始位、数据位、校验位、停止位构成。校验位一般不用。所以串口一般是有10个数据构成。

2、开发方法

        1)状态机。前面编写的程序都比较简单,印象中除了按键消抖那一章用到了状态机,其他章节都没有用到。事实上,在fpga开发上,状态机用到的地方非常多,不管是底层模块,还是高层模块。

        2)开发顺序,可以按照先发送、后接收的方法一步一步来完成。为什么怎么做?主要是因为发送比较纯粹一些,等发送功能开发结束后,可以马上看到效果,提高自己的信心。而且,后续等接收功能开发完毕后,可以把数据回显到发送功能,马上可以进行调试。相反,如果先开发了接收功能,容易看不到效果,打击信心。

3、uart发送


module uart_send(clk, rst, finish_flag, recv_data,out);

input clk;
input rst;
input finish_flag;
input recv_data;
output out;

wire clk;
wire rst;
wire finish_flag;
wire[7:0] recv_data;
reg out;


`define SEND_INTERVAL 32'd49_9999
`define UART_INTERVAL (50*1000000/115200)

reg[31:0] internal_count;
reg[2:0] state;
reg[2:0] next_state;

reg[31:0] count;
reg[2:0] bitnum;
reg[7:0] send_data;

always@(posedge clk or negedge rst)
	if(!rst)
		send_data <= 8'h30;
	else if(finish_flag)
		send_data <= recv_data;

always@(posedge clk or negedge rst)
	if(!rst)
		internal_count <= 32'b0;
	else if(state == 3'b000) begin
		if(internal_count == `SEND_INTERVAL)
			internal_count <= 32'b0;
		else
			internal_count <= internal_count + 1'b1;
	end else
		internal_count <= 32'b0;
			
always@(posedge clk or negedge rst)
	if(!rst)
		state <= 3'b000;
	else
		state <= next_state;
		
always@(*)

	if(!rst) begin
		next_state <= 3'b000;
	end else
		case (state)
			3'b000:
			begin
				if(internal_count == `SEND_INTERVAL)
					next_state <= 3'b001;
				else
					next_state <= 3'b000;
			end		
			
			3'b001:
			begin
				if(count == `UART_INTERVAL)
					next_state <= 3'b010;
				else
					next_state <= 3'b001;
			end
			
			3'b010:
			begin
				if(count == `UART_INTERVAL && bitnum == 3'b111)
					next_state <= 3'b011;
				else
					next_state <= 3'b010;
			end
			
			3'b011:
			begin
				if(count == `UART_INTERVAL)
					next_state <= 3'b000;
				else
					next_state <= 3'b011;
			end
			
			default:
				next_state <= 3'b000;
		endcase


always @(posedge clk or negedge rst)
	if(!rst)
		count <= 32'b0;
	else if(state != 3'b000) begin
		if(count == `UART_INTERVAL)
			count <= 32'b0;
		else
			count <= count + 1'b1;
	end else
		count <= 32'b0;

always @(posedge clk or negedge rst)
	if(!rst)
		bitnum <= 3'b000;
	else if(state == 3'b010 && count == `UART_INTERVAL)
		bitnum <= bitnum + 1;

always @(posedge clk or negedge rst)
	if(!rst)
		out <= 1'b1;
	else begin
		case(state)
			3'b000:
				out <= 1'b1;
				
			3'b001:
				out <= 1'b0;
			
			3'b010:
				out <= send_data[bitnum];
			
			3'b011:
				out <= 1'b1;
			
			default:
				out <= 1'b1;	
		endcase
	end

endmodule

        整个数据的发送过程基本是按照状态机走的,每10ms发送一次。空闲状态是3'b000,准备发送数据后,状态跳转到3'b001,空闲状态下输出都是1。在这个状态下,首先发送一个bit的起始位,起始位是0。什么时候发送结束呢,就看这个count什么时候达到UART_INTERVAL了,而这个数值正是根据波特率算出来的。起始位发送结束后,状态跳转到3'b010。这个状态是数据位,总共有8位数据需要发送。等全部数据发送结束后,状态继续跳转到3'b011,即停止位数据的发送。这就是大概的步骤。

        整个代码中,使用到了一个小技巧,那就是bitnum。bitnum巧妙地利用了数据溢出效应,在所有的8位数据发送之后,重新reset为0。

4、uart接收


module uart_recv(clk, rst, in, finish_flag, recv_data, led);

input clk;
input rst;
input in;
output finish_flag;
output recv_data;
output led; // only for debug use

// baudrate setting

localparam RECV_INTERVAL= 50*1000000/115200;
localparam LED_INTERVAL = 32'd4999_9999;

wire clk;
wire rst;
wire in;
reg led;

reg[31:0] led_count;

reg tmp0;
reg tmp1;
wire diff;

// value for state machine

reg[2:0] state;
reg[2:0] next_state;
assign diff = tmp1 && ~tmp0;

reg[31:0] interval_cnt;
reg[2:0]  bit_cnt;

reg[7:0] recv_latch;
reg[7:0] recv_data;


// recv_data to store input data
always @(posedge clk or negedge rst)
	if(!rst)
		recv_data <= 8'b0;
	else if(state == 3'b100)
		recv_data <= recv_latch;

// finish flag
reg finish_flag;

always@(posedge clk or negedge rst)
	if(!rst)
		finish_flag <= 1'b0;
	else if(finish_flag)
		finish_flag <= 1'b0;
	else if(state == 3'b100)
		finish_flag <= 1'b1;

// count for led debug
always @(posedge clk or negedge rst)
	if(!rst)
		led_count  <= 32'b0;
	else if(led) begin
		if(led_count != LED_INTERVAL)
			led_count <= led_count + 1;
		else
			led_count <= 32'b0;
	end else
		led_count <= 32'b0;

// led for debug signal
always @(posedge clk or negedge rst)
	if(!rst)
		led <= 1'b0;
	else if(led && led_count == LED_INTERVAL)
		led <= 1'b0;
	else if(finish_flag)
		led <= 1'b1;

// state machine
always @(posedge clk or negedge rst)
	if(!rst)
		state <= 3'b000;
	else
		state <= next_state;
		
always @(*)
	if(!rst)
		next_state <= 3'b000;
	else
		case (state)
			3'b000:
				if(diff)
					next_state <= 3'b001;
				else
					next_state <= 3'b000;
					
			3'b001:
				if(interval_cnt == RECV_INTERVAL)
					next_state <= 3'b010;
				else
					next_state <= 3'b001;
					
			3'b010:
				if(interval_cnt == RECV_INTERVAL && bit_cnt == 3'b111)
					next_state <= 3'b011;
				else
					next_state <= 3'b010;
			
			3'b011:
				if(interval_cnt == RECV_INTERVAL/2 - 1)
					next_state <= 3'b100;
				else
					next_state <= 3'b011;
			
			3'b100: // make sure data has been received
				next_state <= 3'b000;
			
			default:
				next_state <= 3'b000;
		endcase


// interval count
always @(posedge clk or negedge rst)
	if(!rst)
		interval_cnt <= 32'b0;
	else if(state != 3'b000) begin
		if(interval_cnt == RECV_INTERVAL)
			interval_cnt <= 32'b0;
		else
			interval_cnt <= interval_cnt + 1'b1;
	end else
		interval_cnt <= 32'b0;

always @(posedge clk or negedge rst)
	if(!rst)
		bit_cnt <= 3'b000;
	else if(state == 3'b010) begin
		if(interval_cnt == RECV_INTERVAL)
			bit_cnt <= bit_cnt + 1'b1;
	end

// get data
always @(posedge clk or negedge rst)
	if(!rst)
		tmp0 <= 1'b0;
	else
		tmp0 <= in;
		
always @(posedge clk or negedge rst)
	if(!rst)
		tmp1 <= 1'b0;
	else
		tmp1 <= tmp0;

// recv data bit by bit
always @(posedge clk or negedge rst)
	if(!rst)
		recv_latch <= 8'b0;
	else
		case(state)
			3'b010: 
					if(interval_cnt == RECV_INTERVAL/2 -1)
						recv_latch[bit_cnt] <= tmp1;
			
			default:
				recv_latch <= recv_latch;
		endcase
		
endmodule

        uart的接收部分也使用到了状态机。其中3'b000代表空闲状态。什么时候开始接收数据呢,就看diff什么时候变更为1。接下来的步骤就和uart发送有点类似了,先是接收起始位,然后是有效数据位,最后是停止位。对于我们来说,真正有效的部分是数据位。状态机有两个地方需要注意下,第一是停止位在RECV_INTREVAL/2-1就可以停下来了,这主要为了防止后面新的数据丢失;第二添加了一个3'b100状态,这是为了可以让finish_flag和recv_data同时有效。

        另外还有一个小插曲。由于不怎么会用signal tap,就用led进行了调试。即,如果真的收到了数据,led亮起,延时1s。

// count for led debug
always @(posedge clk or negedge rst)
	if(!rst)
		led_count  <= 32'b0;
	else if(led) begin
		if(led_count != LED_INTERVAL)
			led_count <= led_count + 1;
		else
			led_count <= 32'b0;
	end else
		led_count <= 32'b0;

// led for debug signal
always @(posedge clk or negedge rst)
	if(!rst)
		led <= 1'b0;
	else if(led && led_count == LED_INTERVAL)
		led <= 1'b0;
	else if(finish_flag)
		led <= 1'b1;

        关于uart接收部分,还有一个想说的,就是真正数据接收的部分都是在RECV_INTERVAL/2-1来完成的。大家可以想想为什么。

5、测试总入口    


module uart_test(clk, rst, in, out, led);


input clk;
input rst;
input in;
output out;
output led;

wire clk;
wire rst;
wire in;
wire out;
wire led;

wire finish_flag;
wire[7:0] recv_data;

uart_recv uart_recv0(
	
	.clk(clk),
	.rst(rst),
	.in(in),
	.finish_flag(finish_flag),
	.recv_data(recv_data),
	.led(led)
	);

uart_send uart_send0(

	.clk(clk),
	.rst(rst),
	.finish_flag(finish_flag),
	.recv_data(recv_data),
	.out(out)
	);



endmodule

        这个测试总入口非常简单,就是分别把uart_send和uart_recv实例化,用finish_flag和recv_data把两者之间联系在一起。

6、pin绑定

         所有这些都准备好之后,就可以把信号和pin脚bind在一起了。生成sof文件,烧入开发板。

7、开始实验

        实验主要分成两部分。一部分是上电的时候;一部分是按下按键的时候。选择一个串口软件,这里用的putty。ax301开发板选择了usb转串口的芯片,所以本身相当于自带串口的。首先,需要以管理员身份打开putty。建议先打开putty,后烧入sof。

         接着,你会看到这样的窗口,

         设置好串口和波特率之后,直接单击Open,就可以看到相关的打印,

         这个时候如果按下数字1,就会发现打印的内容发生了变化,

         具体操作,还是希望大家多实践实践。

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

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

相关文章

Thymeleaf 预处理表达式__${表达式}__之国际化使用案例

目录一. 前期准备1.1 国际化项目获取类1.2 国际化配置文件类1.3 项目配置文件1.4 国际化资源文件二. __${表达式}__预处理表达式2.1 在Thymeleaf中使用Spring的Bean2.2 通过#{}获取国际化资源2.3 预处理表达式__${表达式}__的使用三. 效果一. 前期准备 1.1 国际化项目获取类 …

linux系统中RGBLCD的基本操作和实现方法

大家好&#xff0c;今天主要来聊一聊&#xff0c;如何控制RGBLCD屏的方法。 目录 第一&#xff1a;LCD基本简介 第二&#xff1a;LCD屏的要点 第三&#xff1a;LCD屏具体配置步骤 第四&#xff1a;LCD屏具体的代码实现 第一&#xff1a;LCD基本简介 LCD液晶屏是常用的外设&…

Linux常用命令,能解决工作中99%的Linux操作问题

目录 一、ls 二、pwd 三、cd 四、touch 五、mkdir 六、rmdir&rm 七、man 八、cp 九、mv 九、cat 十、move 十一、less 十二、head 十三、tail 十四、时间 十五、cal 十六、find 十七、grep 十八、zip/unzip 十九、tar 二十、计算器 二十一、uname 二…

SpringBoot 整合Netty自定义消息协议

本文主要介绍springboot项目&#xff0c;配置netty进行消息通信&#xff0c;自定义的netty消息协议&#xff0c;本文消息主要以表格中进行 消息头消息体长度加密标识&#xff08;可选&#xff09;加密类型&#xff08;可选&#xff09;消息体标识消息体校验码2字节2字节1字节&…

SAP ABAP——SAP简介(一)【SAP发展历程】

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

研究必备的 5 个外文文献检索网站

1. Google scholar 网址&#xff1a; https://scholar.google.com.hk/?hlzh-CN 如今搜索论文的首选&#xff0c;可以在这里查看论文统计和引用参考文献&#xff0c;还能通过关注作者或者论文获得新论文更新提醒&#xff0c;以及利用自动化推荐来提供一个基本库 2. DBLP 网址…

MSVC C++ UTF-8编程

除windows平台外大部分其他平台&#xff0c;编译器默认使用的编码都是UTF-8编码&#xff0c;最新版本的Clang编译器只支持UTF-8编码。如果程序需要在多个平台编译运行&#xff0c;则代码必须使用UTF-8。使用UTF-8可以更容易的在多字节字符串(char, std::string)和宽字符(wchar_…

Java+SSM汽车租赁系统汽车出租(含源码+论文+答辩PPT等)

项目功能简介: 该项目采用的技术实现如下 后台框架&#xff1a;Spring、SpringMVC、MyBatis UI界面&#xff1a;jQuery 、JSP 数据库&#xff1a;MySQL 系统功能 系统分为前台用户租车和后台系统管理&#xff1a; 1.前台用户租车 用户注册、用户登录、用户中心、浏览车辆、车辆…

Java项目:SSM在线二手图书交易商城网站平台

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 用户角色包含以下功能&#xff1a; 用户登录,查看商品详情,按分类查看,查看我的书架,上传二手书等功能。 由于本程序规模不大&#xff0c;可供课…

三、CAM可解释性分析——可解释性机器学习(DataWhale组队学习)

文章目录前言CAM算法的精妙之处相关工作CAM算法其它相关问题为什么不用池化操作&#xff1f;CAM的优点CAM算法的缺点扩展阅读和思考题前言 CAM算法奠定了可解释分析的基石 CAM算法的精妙之处 对深度学习实现可解释性分析、显著性分析可扩展性强&#xff0c;后续衍生出各种…

域名备案怎么查?怎么批量查询域名备案

ICP备案&#xff0c;是为了防止在网上从事非法的网站经营活动&#xff0c;打击不良互联网信息的传播&#xff0c;国家对互联网信息服务实行的备案制度。 备案的目的就是为了防止在网上从事非法的网站经营活动&#xff0c;打击不良互联网信息的传播&#xff0c;如果网站不备…

Android TP驱动模型框架分析

本文主要是对TP驱动框架的学习。 一、概述 1、触摸IC的工作原理 tp与主控间的通信接口一般是i2c&#xff0c;即scl、sda、gnd、vcc。在正常工作时需要加上rst、int脚。 整个过程是&#xff1a;通过点击屏幕&#xff0c;tp ic端会将int 脚电平拉低&#xff0c;等待主控的读取。…

【技术分享】Anaconda下载、安装、pip切换镜像源、conda切换镜像、conda创建指定Python版本虚拟环境教程

文章目录1.下载Anaconda1.1.下载最新版本Anaconda1.2.下载历史版本的Anaconda2.安装Anaconda3.conda切换镜像源4.pip切换镜像源5.conda创建指定版本Python环境1.下载Anaconda 1.1.下载最新版本Anaconda 步骤&#xff1a; 进入Anaconda官网&#xff0c;点击Download按钮下载最…

海量数据小内存!如何找到高频数

文章目录题目解答总结题目 如何在 20 亿个无符号整数中找到出现次数最多的那个数&#xff0c;在只提供 1 G 内存的条件下 解答 找到出现次数最多的数&#xff0c;通常的思维就是使用 HashMap 来统计这 20 亿个无符号整数中每个数出现的次数 已知只有 20 亿个数&#xff0c;…

b站黑马的Vue快速入门案例代码——【axios+Vue2】悦听player(音乐播放器)

目录 本文中修改的原代码中的BUG&#xff1a; 修改方法&#xff1a; 本文案例代码仍有的BUG&#xff1a;&#xff08;欢迎大家献计献策&#xff09; 目标效果&#xff1a; 悦音player案例——效果展示视频&#xff1a; 更换的新接口/参数&#xff1a; 1.歌曲搜索接口&…

实战讲解及分析Spring新建Bean的几种方式以及创建过程(图+文+源码)

1 缘起 作为一个应用开发人员而言&#xff0c;会使用某一个工具分为两个层次&#xff08;个人观点&#xff09;&#xff1a; 第一个层次&#xff0c;知道工具&#xff0c;会使用这个工具解决问题&#xff1b; 第二个层次&#xff0c;理解工具的实现原理。 关于Spring的学习&am…

Linux Centos7 磁盘的分区、挂载

1、前言 注&#xff1a;看不懂的同学可以直接跟着后面的步骤操作 一块新的磁盘放到电脑上&#xff0c;要经过分区-->给分区设置文件系统--->挂载才能用。 也就是说要想将磁盘挂载&#xff0c;必须完成给磁盘分区和给分区设置文件系统这两步。 分区的时候先分成主分区和扩…

【DBN分类】基于matlab深度置信网络DBN变压器故障诊断【含Matlab源码 2284期】

一、深度置信网络DBN变压器故障诊断简介 1 DBN模型 DBN是深度学习中最关键的一个多层网络架构&#xff0c;如图2所示&#xff0c;由多层RBM堆叠而成&#xff0c;前一层RBM的输出为后一层RBM的输入&#xff0c;最顶层采用Softmax分类器作为标签层&#xff0c;输出分类识别的结果…

AD-DA转换(PCF8591)

AD转换目录一、AD转换&#xff08;PCF8591&#xff09;①初始化函数②读取ADC值的函数二、DA转换&#xff08;PCF8591&#xff09;三、STC15系列单片机用户手册.pdf—第10章一、AD转换&#xff08;PCF8591&#xff09; 思路&#xff1a;&#xff08;66&#xff0c;两个地址0x90…

RNA-seq——上游分析练习2(数据下载+trim-galore+hisat2+samtools+featureCounts)

目录软件安装新建文件夹一、下载数据二、质控过滤1.数据质量检测2.数据质量控制3.对处理后的数据再次QC三、序列比对1.hisat2比对2.flagstat检查一下结果四、featureCounts定量写在前面——本文是转录组上游分析的实战练习。主要包含四个步骤&#xff1a; 数据下载&#xff08…