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

这篇具有很好参考价值的文章主要介绍了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”时,发送完数据且准备更新 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;
}

......省略后面的代码......
    

实验结果

TCP解帧解码、并发送有效数据到FPGA,ZYNQ&amp;FPGA实例,fpga开发,tcp/ip,网络协议,arm开发

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

TCP解帧解码、并发送有效数据到FPGA,ZYNQ&amp;FPGA实例,fpga开发,tcp/ip,网络协议,arm开发

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

以上代码只是一个雏形/模板,根据具体情况要更改很多的地方。如果这篇文章和正点原子的两个例程学明白了,那就自然会变通了。文章来源地址https://www.toymoban.com/news/detail-752875.html

到了这里,关于TCP解帧解码、并发送有效数据到FPGA的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ZYNQ通过AXI DMA实现PL发送连续大量数据到PS DDR

    硬件:ZYNQ7100 软件:Vivado 2017.4、Xilinx SDK 2017.4   ZYNQ PL 和 PS 的通信方式有 AXI GPIO、BRAM、DDR等。对于数据量较少、地址不连续、长度规则的情况,BRAM 比较适用。而对于传输速度要求高、数据量大、地址连续的情况,比如 ADC,可以通过 AXI DMA 来完成。 1、硬件设计 1.1 ZYNQ7

    2024年02月04日
    浏览(39)
  • 基于ZYNQ FPGA的8路ADC数据采集与存储实现

    基于ZYNQ FPGA的8路ADC数据采集与存储实现 概述: 在工程设计和科学研究中,数据采集与存储是一个重要的任务。为了满足高速、高精度和大容量的数据采集需求,本文将介绍如何基于ZYNQ FPGA平台实现8路ADC数据采集与存储。通过合理的硬件设计和软件开发,我们可以实现快速而

    2024年02月11日
    浏览(46)
  • FPGA + 图像处理(一)三种向zynq中DDR内导入图像数据的方法

    本文主要讲解三种本人已知的将图像数据传入ddr的方法(一些非图像数据也可以用),方便后续通过fpga对图像进行处理,在一些导入方法中,需要将图像转换成特定的格式,因此,需要用到matlab来实现图像的格式转换 这里先展示一下用到的图像,是一个ai随机绘制的图像 通

    2024年02月03日
    浏览(62)
  • 008-关于FPGA/ZYNQ直接处理图像传感器数据输出的若干笔记(裸板采集思路)

    最近也是未来需要考虑做的一件事情是,如何通过FPGA/ZYNQ去做显微镜图像观测下的图像采集传输与后续的处理。目前显微镜观测领域通常是以PC端连接工业相机接口,这个接口可以是USB3.0,可以是网口,也可以是其它传输方式。常常通过工业相机输出的为视频流数据,厂商会

    2024年01月23日
    浏览(56)
  • TCP发送数据、接受数据及TCP通信程序练习

    目录 一、TCP发送数据 二、TCP接收数据 三、TCP通信程序练习 Java中的TCP通信: Java对于基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信 Java为客户端提供了Socket类,为服务端提供了ServerSocket类 构造方法: 方法名

    2023年04月09日
    浏览(38)
  • java TCP发送数据

    TCP是一种可靠的网络协议 他在通信的两端都建立了Socke对象 从而形成了两端的虚拟链路 一旦建立了虚拟链路 两端就可以通过链路通信 TCP会将通信两端分为 客户端和服务端 客户端通过Socke实现 服务端通过ServerSocke实现 那么 我们就来实现一下发送数据的方法 我们创建一个测

    2024年02月07日
    浏览(29)
  • Zynq实现SDI视频解码PCIE传输 提供工程源码和QT上位机源码加技术支持

    PCIE(PCI Express)采用了目前业内流行的点对点串行连接,比起 PCI 以及更早期的计算机总线的共享并行架构,每个设备都有自己的专用连接,不需要向整个总线请求带宽,而且可以把数据传输率提高到一个很高的频率,达到 PCI 所不能提供的高带宽,是目前各行业高速接口的优

    2024年02月07日
    浏览(74)
  • 【QT实现TCP数据发送和接收】

    单客户端服务器实现代码: 在.pro文件添加 在头文件中添加 在源文件中添加

    2024年02月11日
    浏览(51)
  • Zynq和FPGA区别——快速认识Zynq开发

    ZYNQ包含了2个部分,双核的ARM和FPGA。根据Xilinx提供的手册,用ARM实现的模块被称为PS,而用FPGA实现的模块被称为PL。简单的说FPA更偏向于逻辑,不跑系统。 ZYNQ内部包含PS和PL两部分,ZYNQ开发有一下四种方式: ZYNQ是赛灵思公司(Xilinx)推出的新一代全可编程片上系统,它将处

    2024年02月16日
    浏览(52)
  • FPGA实现UART通信(1)---发送数据

    1、基本概念 通用异步收发传输器,是一种异步收发传输器,在发送数据通过将并行数据转换成串行数据进行传输,在接收数据时将串行数据转换成并行数据。 串行通信分为同步串行通信和异步串行通信。同步串行通信即需要时钟的参与,通信双方需要在同一时钟的控制下,

    2024年02月04日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包