FPGA串口接收解帧、并逐帧发送有效数据——1

news2024/9/24 1:17:52

FPGA串口接收解帧、并逐帧发送有效数据

工程实现的功能:FPGA串口接收到串口调试助手发来的数据,将其数据解帧。判断到正确的帧头和帧尾之后,将有效数据存入rx_data中;另一方面发送端将有效数据逐帧发送出去。

参考:正点原子官方FPGA串口通信实验

模块构成:

在这里插入图片描述

在原子哥的基础上改的代码。添加了接收状态机模块:rx_state_machine修改了串口发送模块:uart_send。其余部分代码基本不变(只加了例化,修改数据位宽)

接收状态机模块rx_state_machine——进行解帧处理,接收有效数据

假设:帧头为AA,帧尾为55,有效数据为32bit

思路:使用三段式状态机

接收状态机标志位是什么?

module uart_recv(
    input			  sys_clk,                  //系统时钟
    input             sys_rst_n,                //系统复位,低电平有效
    
    input             uart_rxd,                 //UART接收端口
    output  reg       uart_done,                //接收一帧数据完成标志
    output  reg       rx_flag,                  //接收过程标志信号
    output  reg [ 3:0] rx_cnt,                  //接收数据计数器
    output  reg [ 7:0] rxdata,
    output  reg [7:0] uart_data,                 //接收的数据
	output  reg [31:0] rx_data
    );

在uart_recv中,有一个uart_done信号,是接收一帧数据完成标志,当uart_done拉高,表明接收了一帧数据(8bit),并存到了 uart_data 中。

我们可以用uart_done的上升沿作为状态机跳转的使能信号。

边沿检测如下:

//边沿检测 uart_done 信号
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)begin
		uart_done_prev1 <= 0;
		uart_done_prev2 <= 0;
	end
	else begin
		uart_done_prev1 <= uart_done;	
		uart_done_prev2 <= uart_done_prev1;  //延迟两拍
	end
end
//上升沿检测
assign uart_done_rise = uart_done_prev1 & ~uart_done_prev2;

检测到uart_done的上升沿时,uart_done_rise拉高一个时钟周期。当uart_done_rise拉高时(表明接收了一帧数据,并存到了 uart_data 中),状态机进入跳转判断。

状态机设计

在上面的假设中,帧头为AA,帧尾为55,有效数据为4帧(32bit)。

所以状态机的状态有:

  • 起始状态IDLE

  • 如果接收到AA,则进入DATA_RX状态

  • 如果接收的数据>=4帧 ,进入下一个状态,即帧尾检测状态

  • 如果检测到了帧尾,则进入DONE状态,表明解帧完成

在接收帧头或帧尾的状态中,只要不是AA或55,则重新进入起始状态。

localparam 	SOF		= 8'haa;	//帧头
localparam 	EOF		= 8'h55;	//帧尾

localparam	IDLE		= 4'd0;	
localparam	DATA_SOF	= 4'd1;
localparam	DATA_RX		= 4'd2;
localparam 	DATA_EOF	= 4'd3;
localparam 	DONE		= 4'd4;

//时序逻辑状态切换
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) 
		c_status <= 4'd0;
	else
		c_status <= n_status;
end

//组合逻辑状态切换
always @(*) begin
	case(c_status)
		IDLE : begin 					//起始状态
			if(uart_done_rise) begin
				if(uart_data == SOF)
					n_status = DATA_RX;	//如果接收到AA,则进入DATA_RX状态
				else
					n_status = IDLE;
				end
			else 
				n_status = IDLE;
		end
		DATA_RX: begin					//接收有效数据状态
			if(uart_done_rise) begin
				if(r_bytecnt >= 3'd3)
					n_status = DATA_EOF;
				else
					n_status = DATA_RX;
			end
			else n_status = DATA_RX;
		end
		DATA_EOF: begin					//接收帧尾状态
			if(uart_done_rise) begin
				if(uart_data == EOF)
					n_status = DONE;
				else
					n_status = IDLE;
			end
			else n_status = DATA_EOF;
		end
		DONE: begin 					//解帧完成
			n_status = IDLE;
			end
		default: begin
			n_status = IDLE;
		end
	endcase
end
//接收4个有效数据的计数器
always @(posedge sys_clk) begin
	if(c_status == DATA_RX) begin
		if(uart_done_rise) 
			r_bytecnt <= r_bytecnt+1;
		else ;
	end
	else r_bytecnt <= 3'b0;
end

代码解释:首先定义帧头SOF= 8’haa;(start of flag),帧尾EOF= 8’h55;(end of flag),以及5个状态(在后面设计状态机时发现DATA_SOF状态用不到)

  • 第一个always语句块,时序逻辑状态切换,保证了当前状态c_status和下个状态n_status的切换。

  • 第二个always语句块,4个状态所执行的操作。

    • c_status为IDLE,uart_done_rise拉高时,状态机进入跳转判断。

      如果此时uart_data中的数据是帧头SOF,则准备进入下一个状态,进行有效数据的接收DATA_RX状态;

      否则继续停留在IDLE状态。

    • c_status为DATA_RX,uart_done_rise拉高时,状态机进入跳转判断。

      在DATA_RX状态中,如果接收的字节数r_bytecnt>= 3'd3,说明已经完整地接收了4帧的有效数据,准备进入下个状态,进行帧尾检测DATA_EOF;

      否则,说明数据还没有接收完,继续停留在DATA_RX状态接收数据。

    • c_status为DATA_EOF,uart_done_rise拉高时,状态机进入跳转判断。

      如果此时uart_data中的数据是帧尾EOF,则进入解帧完成状态DONE;

      否则,说明帧尾不正确,该组数据无效,状态进入IDLE,重新解帧新的数据

  • 第三个always语句中,是接收4个有效数据的计数器。c_status处于有效数据的接收状态时,每次uart_done_rise拉高(表明接收了一帧数据),计数器r_bytecnt加1;


上面的三个always语句块,实现了状态的跳转和判断。接下来需要计算数据,并判断什么情况下进行输出。

//将所有接收到的值赋给寄存器 reg_rx_data
always @(posedge sys_clk) begin
	if((c_status == DATA_RX) && uart_done_rise) begin
		case(r_bytecnt)
			3'd0: reg_rx_data[31:24] <= uart_data;
			3'd1: reg_rx_data[23:16] <= uart_data;
			3'd2: reg_rx_data[15:8] <= uart_data;
			3'd3: reg_rx_data[7:0] <= uart_data;		
			default: ;
		endcase
	end
end

首先,将有效数据存入寄存器reg_rx_data中。

c_status = DATA_RX状态下,一共接收了4帧的数据,现在把这4帧数据组合成一个32bit的数据。

当r_bytecnt为0时,这时候接收的是最高位,存入reg_rx_data[31:24],

当r_bytecnt为1时,将此时刻接收的数据存入reg_rx_data[23:16],

依此类推。

//判断数据是否有效
always @(posedge sys_clk) begin
	if((c_status == DONE)) begin
		rx_data <= reg_rx_data;
		rx_vaild <= 1'b1;
		rx_error <= 1'b0;
	end
	else if((c_status == DATA_EOF) && (uart_data != EOF)) begin
		rx_data <= rx_data;
		rx_error <= 1'b1;
		rx_vaild <= 1'b0;
	end
	else begin
		rx_data <= rx_data;
		rx_vaild <= 1'b0;
		rx_error <= 1'b1;
	end	
end

接下来,判断什么情况下进行输出 / 判断该组数据是否有效

  • c_status == DONE时,表明完整地解帧完了一组数据,rx_data <= reg_rx_data;将寄存器reg_rx_data的值赋给输出rx_data;同时给两个标志位rx_vaild、rx_error赋值。
  • 当解帧的帧尾不等于EOF时,接收数据无效,rx_data保持不变。

注释:至于为什么这里只判断了帧尾,没有判断帧头,是因为帧头的判断在状态机里就实现了:如果帧头不正确,则直接回到起始状态,进行不到接收有效数据的状态。但是帧尾还需要再判断,是因为进行到帧尾的状态时,就已经接收完有效数据了,并已经存入了reg_rx_data中。所以,还要再对帧尾进行判断,如果不符合,则rx_data不更新,认为这个数据无效。

接收解帧结果

ila_0 ila_rx (
	.clk(sys_clk), // input wire clk

	.probe0(uart_done), // input wire [0:0]  probe0  
	.probe1(uart_data), // input wire [7:0]  probe1 
	.probe2(rx_vaild), // input wire [0:0]  probe2 
	.probe3(rx_data), // input wire [31:0]  probe3 
	.probe4(uart_done_rise), // input wire [0:0]  probe4 
	.probe5(c_status), // input wire [2:0]  probe5 
	.probe6(r_bytecnt) // input wire [2:0]  probe6
);

在这里插入图片描述

在串口调试助手中,我发送的是aa1234567855,结果如上图,有效数据12345678存入了rx_data。


在下一节继续写逐帧发送部分

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

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

相关文章

嵌入式门槛高吗?

今日话题&#xff0c;嵌入式门槛高吗&#xff1f;在嵌入式领域&#xff0c;门槛因公司和职位的不同而异。普通的嵌入式岗位门槛相对较低&#xff0c;通常要求掌握一些C语言编程和单片机相关知识&#xff0c;可以制作简单的电子产品&#xff0c;但相应的工资较低。然而&#xff…

HNU-电路与电子学-未知年份(不含解析)

【写在前面】 电路与电子学好像是从2020级开设的课程&#xff0c;故实际上目前只有2020与2021两个年级考过期末考试。 这门课程主要由所谓的“数电”与“模电”组成。而且先学的“模电”后学的“”数电&#xff0c;故期中考试主要以“模电”为主&#xff0c;期末考试主要以“…

编译WSL内核,用于操作usb读卡器

wsl2默认不能操作usb读卡器&#xff0c;但是对于嵌入式linux开发来说&#xff0c;需要经常对tf卡进行操作&#xff0c;随时都会使用到usb读卡器的访问。下面讲述如何开启wsl2的usb读卡器的访问&#xff0c;主要涉及到以下2个步骤&#xff1a; wsl2本质是一个虚拟机&#xff0c…

【GO】protobuf在golang中的测试用例

上篇文章介绍了如何安装protobuf环境&#xff0c;文章链接如下 【Go】protobuf介绍及安装-CSDN博客 本节介绍protobuf在gRPC中具体如何使用&#xff0c;并编写测试用例 一、Protobuf是如何工作的 .proto文件是protobuf一个重要的文件&#xff0c;它定义了需要序列化数据的结…

Ubuntu 20.0 + mysql 8.0 用户和密码修改

第一步 下载&#xff08;简单,注意联网&#xff09;Ubuntu 终端输入以下两行命令 (1) 数据库的服务端及客户端数据库的开发软件包 sudo apt-get install mysql-server mysql-client (2) 数据库的开发软件包 sudo apt-get install libmysqlclient-dev 第二步 查看是否安装成功 …

深度分析电动工具的发展趋势,盘点几个极具潜力的科技功能点

纵观市场发展规律&#xff0c;人类的每一次能源转型&#xff0c;都会带来大量红利商机&#xff0c;也会催生整个产业链的彻底革新。 一、电动工具的惊人爆发力 比如说电动工具这个大品类&#xff0c;在近两年意想不到地成为全球隐形增长冠军。主要原因在于海外市场有大量 DIY…

HNU-电路与电子学-2019期末A卷(不含解析)

【写在前面】 电路与电子学好像是从2020级开设的课程&#xff0c;故实际上目前只有2020与2021两个年级考过期末考试。 这门课程主要由所谓的“数电”与“模电”组成。而且先学的“模电”后学的“”数电&#xff0c;故期中考试主要以“模电”为主&#xff0c;期末考试主要以“数…

持续集成交付CICD:GitLab Webhook触发Jenkins流水线

目录 一、实验 1.Jenkins远程下载GiaLab仓库代码 2.curl远程触发Jenkins流水线 3.GitLab Webhook触发Jenkins流水线 二、问题 1.GitLab配置Webhook时报错 一、实验 1.Jenkins远程下载GiaLab仓库代码 (1) Jenkins添加选项参数 (2)添加字符参数 (3)查看构建参数情况 (4)添…

孩子都能学会的FPGA:第二十一课——用线性反馈移位寄存器实现伪随机序列

&#xff08;原创声明&#xff1a;该文是作者的原创&#xff0c;面向对象是FPGA入门者&#xff0c;后续会有进阶的高级教程。宗旨是让每个想做FPGA的人轻松入门&#xff0c;作者不光让大家知其然&#xff0c;还要让大家知其所以然&#xff01;每个工程作者都搭建了全自动化的仿…

神经网络常用归一化和正则化方法解析(一)

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

CRM:提升营销效果的关键

一场成功的营销活动&#xff0c;可以帮助企业扩大知名度&#xff0c;获取大量的优质商机。作为专业的管理软件&#xff0c;CRM系统同样具备营销管理的能力&#xff0c;帮助企业实现营销活动的规划、执行和监控&#xff0c;提高营销效果。下面说说&#xff0c;CRM营销自动化对企…

职场人的年底总结,年初规划,又要开始啦!

在2023年&#xff0c;你可能错过了某些重要的职业发展机会&#xff0c;或者错失了一些与家人和朋友共度的时光。也可能经历了企业的降本增效&#xff0c;面临了被否定和裁员的风险&#xff1b;或者是得到了企业的重用和提拔&#xff0c;一个人撑起了整个业务部门。这些经历不仅…

Python中函数添加超时时间,Python中signal使用

from time import time, sleepimport signal# 模拟要删除5条数据,中间有超时的i 5# 超时后执行的方法def timeout_handler(signal, frame):# 引发异常raise TimeoutError("删除第" str(i) "条,超时!")# 或者执行其他操作,不往外抛异常(超时的函数不会被…

【排障记录】Oracle自动归档清理任务无法进行(windows平台),原因居然是?

前言 接用户求助&#xff0c;生产业务上的一套数据库存在归档文件占用过多磁盘空间的问题&#xff08;约四万个&#xff09;&#xff0c;需要清理&#xff0c;最好设置成定期自动清理&#xff0c;以减少人工干预。 处置过程 由于Oracle搭建在windows操作系统之上&#xff0c…

spring boot mybatis TypeHandler 源码如何初始化及调用

目录 概述使用TypeHandler使用方式在 select | update | insert 中加入 配置文件中指定 源码分析配置文件指定Mapper 执行query如何转换 结束 概述 阅读此文 可以达到 spring boot mybatis TypeHandler 源码如何初始化及如何调用的。 spring boot 版本为 2.7.17&#xff0c;my…

编译原理词法分析:NFA转DFA(原理+完整代码+可视化实现)

NFA转换为DFA 【本文内容摘要】 什么是DFA通过子集构造法将NFA转换为DFA生成DFA的dot文件并且形成可视化。 如果本文对各位看官有用的话&#xff0c;请记得给一个免费的赞哦&#xff08;收藏也不错&#xff09;&#xff01; 文章目录 NFA转换为DFA一、什么是DFA二、NFA转换为…

java表达式、java中jexl3的使用,java中jexl3如何自定义函数方法,jexl3自定义函数怎么传集合数组列表

引入jexl3 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-jexl3</artifactId><version>3.2.1</version> </dependency> 基本用法 //引入对应包 import org.apache.commons.jexl3.*;public class …

【STM32入门】3.OLED屏幕

1.OLED引脚 OLED屏幕的接线按图所示&#xff0c;本例中用的是4管脚OLED屏幕 2.驱动程序 配套的驱动程序是“OLED.c"&#xff0c;主要由以下函数构成&#xff1a;1、初始化&#xff1b;2、清屏&#xff1b;3、显示字符&#xff1b;4、显示字符串&#xff1b;5、显示数字…

杀虫剂市场分析:2022年市场规模突破100亿元

杀虫剂是杀死害虫的一种药剂&#xff0c;目前杀虫剂主要应用在农业种植领域中&#xff0c;其中水稻的应用占比最大。杀虫剂的分类繁多&#xff0c;目前市场上主要以有机磷杀虫剂等为主流。 杀虫剂是指被用于杀死昆虫或防止昆虫进行破坏性行为的化学物质&#xff0c;杀虫剂的种类…

C盘爆满,python pip无法安装应用

解决方法1 C盘扩容 从其他盘压缩空间&#xff0c;C盘使用压缩的空间进行扩容&#xff0c;治标不治本&#xff0c;以后C盘还会越来越大 解决方法2 转移pip安装目录 1. 获取显示pip安装目录 C:\Users\biewang>pip show pip Name: pip Version: 23.3.1 Summary: The PyPA r…