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

news2024/11/15 19:48:00

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”时,发送完数据且准备更新 QSPI
    if (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);//clear

    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("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.coloradmin.cn/o/1273011.html

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

相关文章

基于springboot实现的在线考试系统

一、系统架构 前端&#xff1a;html | js | css | jquery | bootstrap 后端&#xff1a;springboot | springdata-jpa 环境&#xff1a;jdk1.7 | mysql | maven 二、 代码及数据库 三、功能介绍 01. 登录页 02. 管理员端-课程管理 03. 管理员端-班级管理 04. 管理员端-老师管理…

【CAN通信】CanIf模块详细介绍

目录 1.内容简介 2.CanIf详细设计 2.1 CanIf功能简介 2.2 一些关键概念 2.3依赖的上下层模块 2.4 功能详细设计 2.4.1 Hardware object handles 2.4.2 Static L-PDUs 2.4.3 Dynamic L-PDUs 2.4.4 Dynamic Transmit L-PDUs 2.4.5 Dynamic receive L-PDUs 2.4.6Physi…

微信小程序 - 开发版、体验版、正式版共享本地缓存

问题描述 最近突然发现一个大问题啊&#xff0c;小程序切换版本环境的时候发现数据被污染了&#xff0c;瞬间就怀疑不同环境版本的小程序本地缓存是否共享的&#xff1f;&#xff01; 果然是&#xff01; 解决方案 我们可能马上想到解决方案就是&#xff1a;给每一个环境版本…

不想花钱用aspera?这些免费的替代方案也同样快速哦

Aspera FASP是一款高速数据传输软件&#xff0c;被广泛应用于大文件的快速传输。然而&#xff0c;Aspera FASP并不便宜&#xff0c;对于一些小型企业或个人用户来说可能无法负担。因此&#xff0c;为了满足大家的需求&#xff0c;本文将介绍一些免费且同样快速的Aspera替代方案…

【meta】Scaling Speech Technology to 1,000+ Languages

nvidia-NeMo包含TTS的模型&#xff0c;开源数据 uroma转写工具介绍 uroman转写工具 N-to-M mapping 转写的规范&#xff0c;包含一些中文-拼音&#xff0c;拉丁文-读法的规则转换。字符串匹配规则下的查字典&#xff1b; 将字母对应到发音单元 转写规范 转写过程尽量做到可…

Android Studio初学者实例:Fragment学习--仿美团外卖界面

本次课程为Fragment为主题&#xff0c;课程的示例仿美团外卖界面&#xff0c;不同于底部导航栏的Fragment案例&#xff0c;此界面分为左侧切换与顶部切换。本文先是发布代码与效果&#xff0c;后续讲解将会在后续补充。先看看效果&#xff1a; 首先是布局文件代码&#xff1a;A…

GPT带我学Openpyxl操作Excel

注&#xff1a;以下文字大部分文字和代码由GPT生成 一、openpyxl详细介绍 Openpyxl是一个用于读取和编写Excel 2010 xlsx/xlsm/xltx/xltm文件的Python库。它允许您使用Python操作Excel文件&#xff0c;包括创建新的工作簿、读取和修改现有工作簿中的数据、设置单元格格式以及编…

Typora切换字体颜色

欢迎大家到我的博客浏览。Typora切换字体颜色 | YinKais Blog最近很无聊&#xff0c;突然想起来朋友的 Typora 可以修改字体颜色&#xff0c;而我的却不能&#xff0c;我就去研究了一下&#xff0c;于是有了这一篇教学文章。 可能会有人说&#xff0c;网上大佬早有教学了&…

提升Jmeter测试效率的9种参数化方法!

jmeter工具无论做接口测试还是性能测试&#xff0c;参数化都是一个必须掌握且非常有用的知识点。参数化的使用场景: 1&#xff09;多个请求都是同一个ip地址&#xff0c;若服务器地址更换了&#xff0c;则脚本需要更改每个请求的ip 2&#xff09;注册账号&#xff0c;不允许账…

群晖NAS配置之搭建WordPress个人博客站点

群晖NAS配置之搭建WordPress个人博客站点 之前写了一些ngrok和frp给群晖nas做内网穿透&#xff0c;今天分享一下在群晖nas下安装wordpress的教程。 WordPress是一个开源的内容管理系统&#xff08;CMS&#xff09;&#xff0c;最初是用来搭建博客的&#xff0c;但后来发展成为…

离线直线度测量仪的适用范围!

离线直线度测量仪虽是智能测量设备&#xff0c;但与在线检测设备相比&#xff0c;检测速度还是较慢&#xff0c;但非常适用于需要使用的圆形轧材抽检或全检&#xff0c;生产产线不适合安装在线仪器的的厂家。 离线直线度测量仪主要用于金属棒材、管材、陶瓷管材、压辊、轧辊等产…

浅谈集中控制式预付费抄表系统设计与应用

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201801 摘要:介绍一种由射频卡预付费和RS485总线组成的集中控制式预付费抄表系统,系统能准确及时地釆集处理电能计量数据并实现预付费功能。该系统简化用户电能表设计&#xff0c;使智能集中控制器具有多功能化&#xff0c;实现系统…

WP采集插件的进阶功能:输入关键词采集及定向采集实现精准筛选

WP采集插件教程&#xff1a;轻松实现全网文章采集 近年来&#xff0c;WordPress&#xff08;简称WP&#xff09;作为一款强大的网站建设工具&#xff0c;广受用户喜爱。然而&#xff0c;对于许多网站管理员来说&#xff0c;如何轻松而高效地获取全网各类文章内容成为了一个亟待…

PCB布线为什么不能走直角或锐角-笔记

PCB布线为什么不能走直角或锐角-笔记 摘要一.PCB走线在直角转弯的地方&#xff0c;信号前后部分相互影响这几个理由我们来一一分析一下传输线的直角带来的寄生电容从阻抗的角度来看直角的尖角产生放电或者电磁辐射走线直角的工艺问题 摘要 有一定熟悉画过PCB板的人或者PCB教学…

VMware通过ISO镜像安装window2016虚拟机

1.点文件->新建虚拟机 2.进入到下边页面 3.根据你的服务器硬件选择硬件兼容性 4.选择2016版本的windows(注&#xff1a;没有该版本的话选择最高版本) 5.根据你的需求选择引导设备( 启动过程&#xff1a; BIOS&#xff1a; 在计算机启动时&#xff0c;BIOS负责进行自检&#…

MySQL如何处理并发访问和高负载?

在当今互联网时代&#xff0c;面对日益增长的数据量和用户访问量&#xff0c;数据库的并发访问和高负载处理变得尤为重要。MySQL作为最流行的关系型数据库管理系统之一&#xff0c;具备许多关键技术和策略来处理并发访问和高负载&#xff0c;下面将对其进行深入探讨。 ​ 图片…

开关电源工作时,如何抑制纹波和减小高频噪声?

开关电源的纹波和噪声是一个本质问题&#xff0c;换而言之无论纹波和噪声多么小&#xff0c;也无法从根本上去除&#xff0c;再绝对的讲开关电源无论成本怎么提高&#xff0c;也无法完全达到线性电源的性能和特点。那么&#xff0c;通常抑制或减少它的做法有五种&#xff1a; …

Netty Review - 探索Pipeline的Inbound和Outbound

文章目录 概念Server CodeClient CodeInboundHandler和OutboundHandler的执行顺序在InboundHandler中不触发fire方法InboundHandler和OutboundHandler的执行顺序如果把OutboundHandler放在InboundHandler的后面&#xff0c;OutboundHandler会执行吗 概念 我们知道当boss线程监控…

SSD-FTL算法学习总结1

SSD核心技术&#xff1a;FTL算法。 1、什么是FTL? FTL是Flash Translation Layer&#xff08;闪存转换层&#xff09;&#xff0c;完成主机&#xff08;HOST&#xff09;逻辑地址空间到闪存Flash物理地址空间的翻译,或者说映射&#xff08;Mapping&#xff09;。 FTL算法分成…

发生这种情况 经常导致投资者的痛苦

在这个市场中&#xff0c;什么事会让人痛苦呢&#xff1f;有的投资者马上回答&#xff0c;因为亏损。说实话&#xff0c;如果经过刻意的练习&#xff0c;我们在一定程度上能克服亏损给人带来的痛感。但是有另一种情况也容易为投资者带来痛苦&#xff0c;下面我们就来讨论一下。…