TCP解帧解码、并发送有效数据到FPGA

TCP解帧解码、并发送有效数据到FPGA

工程的功能:使用TCP协议接收到网络调试助手发来的指令,将指令进行解帧,提取出帧头、有限数据、帧尾;再将有效数据发送到FPGA端的BRAM上,实现信息传递。

参考:正点原子启明星ZYNQ之嵌入式SDK开发指南_V2.0:第三十九章 基于 TCP 协议的远程更新 QSPI Flash 实验 和 第十五章 基于 BRAM 的 PS 和 PL 的数据交互

TCP接收、解帧功能的实现

在正点原子提供的“基于 TCP 协议的远程更新 QSPI Flash 实验”例程中,是使用TCP协议实现远程更新 QSPI 的功能。在本项目中,将其改为接收并且解帧的功能。

  • 如何实现?

先分析一下正点原子的源代码:

在“qspi_remote_update.c”代码中,以下这段代码:

//接收回调函数
static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{struct pbuf *q;if (!p){tcp_close(tpcb);tcp_recv(tpcb, NULL);xil_printf("tcp connection closed\r\n");return ERR_OK;}q = p;//当接收到的数据长度为 6 且内容为“update”时,发送完数据且准备更新 QSPIif (q->tot_len == 6 && !(memcmp("update", p->payload, 6))){start_update_flag = 1;sent_msg("\r\nStart QSPI Update\r\n");//向网络调试助手发送数据}//当接收到的数据长度为 5 且内容为“clear”时,清除先前发送的数据else if (q->tot_len == 5 && !(memcmp("clear", p->payload, 5))){start_update_flag = 0;total_bytes = 0;sent_msg("Clear received data\r\n");xil_printf("Clear received data\r\n");}//对于除此之外接收到的信息,将写入到 rxbuffer中,rxbuffer是一个大小为 MAX_FLASH_LEN 的数组,用于存放发送方发送的 BOOT.bin 文件数据else {xil_printf("tot_len=%d    len=%d\r\n",q->tot_len,q->len);while (q->tot_len != q->len){//tot_len:表示客户端发送数据的总字节数//len:表示服务器端接收客户端发过来的有效字节数//字符串复制函数。从q->payload中复制q->len个字节到&  rxbuffer[total_bytes]中memcpy(&rxbuffer[total_bytes], q->payload, q->len);total_bytes += q->len;//更新总字节数q = q->next;}memcpy(&rxbuffer[total_bytes], q->payload, q->len);total_bytes += q->len;}tcp_recved(tpcb, p->tot_len);//当程序处理完数据后一定要调用这个函数,通知内核更新接收窗口pbuf_free(p);return ERR_OK;
}

代码中我加的注释很详细,这里再简单分析一下。在这之前的代码中,ZYNQ将接收到的信息(BOOT.bin文件)写入到了QSPI中。

上面的这段代码是一个判断:

  • 当接收数据长度为 6 且内容为“update”时,发送完数据且准备更新 QSPI;

  • 当接收到的数据长度为 5 且内容为“clear”时,清除先前发送的数据;

  • 除此之接收的信息,一律全写入rxbuffer中。这个rxbuffer就是用于存储BOOT.bin文件的。

所以,我们可以这样改:前面的两个判断全部不要了,所有来的信息我全部都接收,存入rxbuffer中,之后再来判断。

//接收回调函数
static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{struct pbuf *q;if (!p){tcp_close(tpcb);tcp_recv(tpcb, NULL);xil_printf("tcp connection closed\r\n");return ERR_OK;}q = p;receive_flag = 1;	//receive_flag是接收数据的标志位//接收到的信息,将写入到 rxbuffer 中,rxbuffer是一个大小为 MAX_FLASH_LEN 的数组while(q->tot_len != q->len)	//tot_len == len 时,表明已经传输到最后一个了。{xil_printf("tot_len=%d    len=%d\r\n",q->tot_len,q->len);//tot_len:表示客户端发送数据的总字节数//len:表示服务器端接收客户端发过来的有效字节数//memcpy:字符串复制函数。从q->payload中复制q->len个字节到&  rxbuffer[total_bytes]中memcpy(&rxbuffer[total_bytes], q->payload, q->len);total_bytes += q->len;//更新总字节数q = q->next;}memcpy(&rxbuffer[total_bytes], q->payload, q->len);  //对最后一个进行接收total_bytes += q->len;tcp_recved(tpcb, p->tot_len);//当程序处理完数据后一定要调用这个函数,通知内核更新接收窗口pbuf_free(p);return ERR_OK;
}

改为以上代码,这样,不管网络调试助手发送什么数据,我能全部接收,并存入rxbuffer中,方便后面的解析。

如何解帧?

关于帧头、帧尾、有效数据的概念,这里不再赘述。总之,我们要实现的功能是:只有按照特定的帧头、帧尾发送数据,这个数据才是有效的,才能被我使用;按照其他格式发送的数据一律无效

假设我的帧头是:AAAA5555

帧尾是:5555AAAA

需要发送的有效数据是8个字节、32位(如 12345678)

    if(receive_flag == 1){sent_msg("TCP recv\r\n");receive_flag = 0;//帧头frame_header = (rxbuffer[0]<<24)+(rxbuffer[1]<<16)+(rxbuffer[2]<<8)+rxbuffer[3];xil_printf("rxbuffer[0] = %x\r\n",rxbuffer[0]);xil_printf("rxbuffer[1] = %x\r\n",rxbuffer[1]);xil_printf("rxbuffer[2] = %x\r\n",rxbuffer[2]);xil_printf("rxbuffer[3] = %x\r\n",rxbuffer[3]);xil_printf("rxbuffer[4] = %x\r\n",rxbuffer[4]);xil_printf("rxbuffer[5] = %x\r\n",rxbuffer[5]);xil_printf("rxbuffer[6] = %x\r\n",rxbuffer[6]);xil_printf("rxbuffer[7] = %x\r\n",rxbuffer[7]);xil_printf("rxbuffer[8] = %x\r\n",rxbuffer[8]);xil_printf("rxbuffer[9] = %x\r\n",rxbuffer[9]);xil_printf("rxbuffer[10] = %x\r\n",rxbuffer[10]);xil_printf("rxbuffer[11] = %x\r\n",rxbuffer[11]);xil_printf("frame_header = %x\r\n",frame_header);if(frame_header == 0xAAAA5555){//帧尾frame_end = (rxbuffer[8]<<24)+(rxbuffer[9]<<16)+(rxbuffer[10]<<8)+rxbuffer[11];xil_printf("frame_end = %x\r\n",frame_end);if(frame_end == 0x5555AAAA){//有效数据vaild_data = (rxbuffer[4]<<24)+(rxbuffer[5]<<16)+(rxbuffer[6]<<8)+rxbuffer[7];xil_printf("vaild_data = %x\r\n",vaild_data);total_bytes = 0;	//如果帧头,帧尾都正确,指针清零}else{xil_printf("frame_end detection is error!\r\n");total_bytes = 0;	//如果帧尾不正确,指针清零}}else{xil_printf("frame_header detection is error!\r\n");total_bytes = 0;	//如果帧头不正确,指针清零}}

代码解释:在代码最前面,定义了 u32 frame_header = 0; u32 frame_end = 0; u32 vaild_data = 0;

用来存储帧头、帧尾和有效数据。

frame_header = (rxbuffer[0]<<24)+(rxbuffer[1]<<16)+(rxbuffer[2]<<8)+rxbuffer[3];

解释:假如我发送的信息是“AAAA5555123456785555AAAA”那么,rxbuffer[0]是“AA”、rxbuffer[1]是“AA”、rxbuffer[2]是“55”依此类推。rxbuffer[i]的数据是8位的,需要将它们拼成32位的帧头、帧尾、有效数据。这行代码就实现了这个功能。

中间的xil_printf函数可以帮助理解代码。

并且如果帧头或帧尾不正确,则将total_bytes清零,即把rxbuffer清零,这组数据无效。

PS_PL传输数据

按照正点原子 第十五章 基于 BRAM 的 PS 和 PL 的数据交互 实验,硬件工作不再赘述。

将软件的代码移植到qspi_remote_update.c文件中即可。

#define PL_BRAM_START			PL_BRAM_RD_S00_AXI_SLV_REG0_OFFSET	//RAM读开始寄存器地址
#define PL_BRAM_START_ADDR		PL_BRAM_RD_S00_AXI_SLV_REG1_OFFSET	//RAM起始寄存器地址
#define PL_BRAM_LEN				PL_BRAM_RD_S00_AXI_SLV_REG2_OFFSET	//PL读 RAM的深度
#define	PL_BRAM_BASE			XPAR_PL_BRAM_RD_0_S00_AXI_BASEADDR	//PL_RAM_RD基地址#define START_ADDR			0	//RAM起始地址范围:0~1023
#define BRAM_DATA_BYTE		4	//BRAM数据字节个数char ch_data[1024];		//写入BRAM的字符数组
int  ch_data_len;		//写入BRAM的字符个数......省略中间的代码......//接收回调函数
static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{struct pbuf *q;u16 i;u16 j;int wr_cnt = 0;int read_data = 0;int ch_data_len = 8;if (!p){tcp_close(tpcb);tcp_recv(tpcb, NULL);xil_printf("tcp connection closed\r\n");return ERR_OK;}q = p;receive_flag = 1;//接收到的信息,将写入到 rxbuffer中,rxbuffer是一个大小为 MAX_FLASH_LEN 的数组while(q->tot_len != q->len)	//tot_len == len 时,表明已经传输到最后一个了。{xil_printf("tot_len=%d    len=%d\r\n",q->tot_len,q->len);//tot_len:表示客户端发送数据的总字节数//len:表示服务器端接收客户端发过来的有效字节数//memcpy:字符串复制函数。从q->payload中复制q->len个字节到&  rxbuffer[total_bytes]中memcpy(&rxbuffer[total_bytes], q->payload, q->len);total_bytes += q->len;//更新总字节数q = q->next;}memcpy(&rxbuffer[total_bytes], q->payload, q->len);  //对最后一个进行接收total_bytes += q->len;for(j=0;j<30;j++);Xil_Out32(XPAR_BRAM_0_BASEADDR,0);//clearif(receive_flag == 1){sent_msg("TCP recv\r\n");receive_flag = 0;//帧头frame_header = (rxbuffer[0]<<24)+(rxbuffer[1]<<16)+(rxbuffer[2]<<8)+rxbuffer[3];xil_printf("frame_header = %x\r\n",frame_header);if(frame_header == 0xAAAA5555){//帧尾frame_end = (rxbuffer[8]<<24)+(rxbuffer[9]<<16)+(rxbuffer[10]<<8)+rxbuffer[11];xil_printf("frame_end = %x\r\n",frame_end);if(frame_end == 0x5555AAAA){//有效数据vaild_data = (rxbuffer[4]<<24)+(rxbuffer[5]<<16)+(rxbuffer[6]<<8)+rxbuffer[7];xil_printf("vaild_data = %x\r\n",vaild_data);//将有效数据写入BRAM,每次循环向 BRAM中写入 1 个字符;vaild_data的长度是4个字节for(i = 0; i<(START_ADDR + ch_data_len)*BRAM_DATA_BYTE; i+=BRAM_DATA_BYTE){XBram_WriteReg(XPAR_BRAM_0_BASEADDR,i,vaild_data[wr_cnt]);wr_cnt++;}//配置PL_BRAM_RD起始地址PL_BRAM_RD_mWriteReg(PL_BRAM_BASE,PL_BRAM_START_ADDR,START_ADDR*BRAM_DATA_BYTE);//配置PL_BRAM_RD长度PL_BRAM_RD_mWriteReg(PL_BRAM_BASE,PL_BRAM_LEN,ch_data_len*BRAM_DATA_BYTE);//配置PL_BRAM_RD开始读信号,产生一个上升沿PL_BRAM_RD_mWriteReg(PL_BRAM_BASE,PL_BRAM_START,1);PL_BRAM_RD_mWriteReg(PL_BRAM_BASE,PL_BRAM_START,0);total_bytes = 0;	//如果帧头,帧尾都正确,指针清零}else{xil_printf("frame_end detection is error!\r\n");total_bytes = 0;	//如果帧尾不正确,指针清零}}else{xil_printf("frame_header detection is error!\r\n");total_bytes = 0;	//如果帧头不正确,指针清零}}tcp_recved(tpcb, p->tot_len);//当程序处理完数据后一定要调用这个函数,通知内核更新接收窗口pbuf_free(p);return ERR_OK;
}......省略后面的代码......

实验结果

在这里插入图片描述

上面是UART串口打印出的数据,上面显示了帧头:AAAA5555;帧尾:5555AAAA;有效数据:12345678

在这里插入图片描述

上面是FPGA ILA抓取的波形。可以看到,有效数据12345678被存入到了BRAM中。

以上代码只是一个雏形/模板,根据具体情况要更改很多的地方。如果这篇文章和正点原子的两个例程学明白了,那就自然会变通了。

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

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

相关文章

Apache Hive3.1.3 遇到DATE_FORMAT转换2021年12月格式的问题

比如&#xff1a;需要将时间2021-12-28 00:00:00转换成2021-12的格式&#xff0c;用date_format会将2021-12转换成2022-12的问题。 解决方法&#xff1a; 方式一&#xff1a;大写的‘Y’换成‘y’ 方式二&#xff1a;字符串截取&#xff0c;substr 本博主推荐方式一&#xf…

Linux地址空间随机化

ASLR(Address Space Layout Randomization)在2005年被引入到Linux的内核 kernel 2.6.12 中&#xff0c;早在2004年就以补丁的形式引入。内存地址的随机化&#xff0c;意味着同一应用多次执行所使用内存空间完全不同&#xff0c;也意味着简单的缓冲区溢出攻击无法达到目的。 1.…

Oracle(2-8)Configuring the Database Archiving Mode

文章目录 一、基础知识1、Redo Log History2、NOARCHIVELOG Mode 非归档模式3、ARCHIVELOG Mode 归档模式4、Changing the Archiving Mode 更改归档模式![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d6a09f9a6de24de7bbcdad90b8d6b9ca.png)5、Auto and Manual Ar…

MybtisPlus快速开发(从controller到mapper)

创建新项目 写好配置文件 server:port: 8905#配置MP控制台打印日志 mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplspring:datasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:m…

联想M7400W激光打印机加粉清零方法

基本参数 产品定位&#xff1a;多功能商用一体机 产品类型&#xff1a;黑白激光多功能一体机 涵盖功能&#xff1a;打印、复印、扫描 最大处理幅面&#xff1a;A4 耗材类型&#xff1a;鼓粉分离 耗材容量&#xff1a;硒鼓LD2451 12000页&#xff0c;墨粉LT2451 1500页、L…

每日一练2023.11.30——谁先倒【PTA】

题目链接&#xff1a;谁先倒 题目要求&#xff1a; 划拳是古老中国酒文化的一个有趣的组成部分。酒桌上两人划拳的方法为&#xff1a;每人口中喊出一个数字&#xff0c;同时用手比划出一个数字。如果谁比划出的数字正好等于两人喊出的数字之和&#xff0c;谁就输了&#xff0…

PyCharm安装教程(详细步骤)

一、软件简介 PyCharm是一款Python IDE&#xff0c;其带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具&#xff0c;比如&#xff0c; 调试、语法高亮、Project管理、代码跳转、智能提示、自动完成、单元测试、版本控制等等。此外&#xff0c;该IDE提供了一些高…

在Linux上搭建JavaWeb项目运行环境

文章目录 安装JDK安装Tomcat安装数据库 安装JDK 安装Oracle官方的JDK比较麻烦&#xff0c;我们在此处选择安装开源社区维护的openjdk。他们俩的差别不大且兼容。 安装Tomcat 我们把本地下载好的 tomcat.zip 包拖到Linux页面上&#xff0c;让Linux也有一个zip包&#xff0c;再…

Python 计算图像差分的三种方式(cv2,torchvision,numpy)

前言&#xff1a;最近在可视化图像残差时&#xff0c;发现几种不同的差分方法&#xff0c;下面分别给出每种差分方法的实现方式&#xff0c;并比较不同方法之间的差异。 目录 1️⃣ cv22️⃣ PIL & torchvision3️⃣ PIL & numpy 目标&#xff1a;对于给定的下述两张图…

MacOS + Android Studio 通过 USB 数据线真机调试

环境&#xff1a;Apple M1 MacOS Sonoma 14.1.1 软件&#xff1a;Android Studio Giraffe | 2022.3.1 Patch 3 设备&#xff1a;小米10 Android 13 一、创建测试项目 安卓 HelloWorld 项目: 安卓 HelloWorld 项目 二、数据线连接手机 1. 手机开启开发者模式 参考&#xff1…

代码随想录算法训练营第四十八天【动态规划part09】 | 198.打家劫舍、213.打家劫舍II、337.打家劫舍III

198.打家劫舍 题目链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 求解思路&#xff1a; 当前房屋偷与不偷取决于前一个房屋是否被偷了 动规五部曲 确定dp数组及其下标含义&#xff1a;考虑下标i&#xff08;包括i&#xff09…

Qt串口助手

QT5 串口助手 ​ 由于C课程作业的需要&#xff0c;用QT5写了个简陋的串口助手。只作为一个简单的案例以供参考&#xff0c;默认读者具有C基础和了解简单的Qt操作。 功能展示 【用QT写了个简单的串口助手】 准备工作 Qt自带有<QSerialPort> 库, 可以方便地配置和调用…

lv11 嵌入式开发 轮询与中断13

1 CPU与硬件的交互方式 轮询 CPU执行程序时不断地询问硬件是否需要其服务&#xff0c;若需要则给予其服务&#xff0c;若不需要一段时间后再次询问&#xff0c;周而复始 中断 CPU执行程序时若硬件需要其服务&#xff0c;对应的硬件给CPU发送中断信号&#xff0c;CPU接收到中…

python爬取robomaster论坛数据,作为后端数据

一. 内容简介 python爬取robomaster论坛数据&#xff0c;作为后端数据 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 三.主要流程 3.1 接口分析 # 接口分析 # 全部数据 # https://bbs.robomaster.com/forum.php?modforumdisplay&fid63 2…

Ubuntu systemd-analyze命令(系统启动性能分析工具:分析系统启动时间,找出可能导致启动缓慢的原因)

文章目录 Ubuntu systemd-analyze命令剖析目录简介systemd与systemd-analyze工作原理 安装和使用命令参数详解用例与示例显示启动时间&#xff08;systemd-analyze time&#xff09;列出启动过程中各个服务的启动时间&#xff08;systemd-analyze blame&#xff09;显示系统启动…

使用opencv实现图像滤波

1 图像滤波介绍 滤波是信号和图像处理中的基本任务之一&#xff0c;其旨在有选择地提取图像的某些特征&#xff0c;可以用于在给定应用程序的上下文中传达重要信息&#xff0c;例如&#xff0c;去除图像中的噪声、提取所需的视觉特征、图像重采样等。 1.1 图像滤波理论 图像…

厦门城市建设与建筑结构健康监测系统的重要性与作用

厦门&#xff0c;这座美丽的海滨城市&#xff0c;随着经济的快速发展和城市化的不断推进&#xff0c;城市建设已成为人们关注的焦点。其中&#xff0c;建筑结构健康监测系统对于保障城市建设和建筑的安全具有举足轻重的地位。 WITBEE万宾针对建筑的动态平衡&#xff0c;温湿度&…

shell编程系列(7)-使用wc进行文本统计

文章目录 前言wc命令的使用wc命令的参数说明&#xff1a;统计字数统计行数打印文本行号 结语 前言 统计功能也是我们在shell编程中经常碰到的一个需求&#xff0c;wc命令可以适用于任何需要统计的数据&#xff0c;不只是统计文本&#xff0c;配合ls命令我们可以统计文件的个数…

java gc垃圾回收机制(垃圾收集 Garbage Collection)

一文带你深入JAVA GC&#xff08;垃圾回收机制&#xff09;面试讲解&#xff01;&#xff01;&#xff01; - 知乎 gc常用算法 gc常用算法有&#xff1a;标记-清除算法&#xff0c;标记-压缩算法&#xff0c;复制算法&#xff0c;分代收集算法。 复制算法&#xff08;Java中…

java--子类构造器的特点

1.子类构造器的特点 子类的全部构造器&#xff0c;都会先调用父类的构造器&#xff0c;再执行自己。 2.子类构造器是如何实现调用父类构造器的 ①默认情况下&#xff0c;子类全部构造器的第一行代码都是super()(写不写都有)&#xff0c;它会调用父类的无参数构造器。 ②如果…