有什么网站可以做试题,wordpress 改为根目录,免费制作永久个人网站,广州天河区房价TCP解帧解码、并发送有效数据到FPGA 工程的功能#xff1a;使用TCP协议接收到网络调试助手发来的指令#xff0c;将指令进行解帧#xff0c;提取出帧头、有限数据、帧尾#xff1b;再将有效数据发送到FPGA端的BRAM上#xff0c;实现信息传递。 参考#xff1a;正点原子启…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(j0;j30;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; iBRAM_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中。
注
以上代码只是一个雏形/模板根据具体情况要更改很多的地方。如果这篇文章和正点原子的两个例程学明白了那就自然会变通了。