本帖最后由 正点原子运营 于 2020-11-19 10:29 编辑
第三十五章 基于UDP协议的远程更新QSPI Flash实验
上一实验我们利用TCP协议实现了远程更新QSPI。为了满足不同的网络需求,本次实验我们将上一章实验使用的TCP协议替换成UDP协议。本章包括以下几个部分: 37.1 简介 37.2 实验任务 37.3 硬件设计 37.4 软件设计 37.5 下载验证
1.1 简介UDP由于本身的尽最大能力交付问题,即不稳定可靠,在传输文件上并不合适,在网络性能较好时,能够不丢失数据,当网络性能较差时,会面临数据丢失的问题,可靠性无法保证。UDP更适合于传输音视频这种对实时性要求高而又能容忍丢失数据的场合。当然了,也可以在UDP协议之上,编写一个能够确保可靠性的应用层协议,如TFTP协议。 本实验只是将《基于TCP协议的远程更新QSPI Flash实验》中的TCP协议实现改为UDP协议实现,了解如何使用UDP协议实现远程更新QSPI。如果在实际需求中确实需要使用UDP协议进行远程更新,可以在该实验的基础上增加确认重传机制,同时需要编写一个上位机使该确认重传机制能够正常工作;或者结合《基于lwip的TFTP服务器实验》使用TFTP协议以确保文件传输的可靠性。 1.2 实验任务本章的实验任务是使用LWIP协议栈的UDP协议实现远程更新QSPI的功能,当输入“update”命令时更新QSPI并反馈信息,当输入“clear”命令时之前传输的数据无效。 1.3 硬件设计本次实验的硬件设计与《基于TCP协议的远程更新QSPI Flash实验》相同。 1.4 软件设计上一章的实验的软件设计部分,为了提高数据传送的效率,我们对lwip进行相应设置。其实官方提供的模板中有优化好的设置,我们上一章的设置就是参考官方的设置。本章我们使用官方优化好的设置,避免自己手动优化。 我们删除上一实验的应用工程和BSP工程,新建一个名为“qspi_update_udp”的工程,如图 37.4.1所示。点击“Next>”,在选择模板界面中,选择“lwipUDP Perf Server”,如图37.4.2所示:
图 37.4.1 新建应用工程
图 37.4.2 选择“lwip UDP Perf Server” 我们将应用工程“qspi_update_udp”的src目录下所有的.c和.h文件删除,只保留lscript.ld、README.txt和Xilinx.spec三个文件即可。因为我们只需要其BSP工程已经优化好的设置,应用工程用我们自己设计好的源文件。从提供的例程中拷贝SDK的源文件,需拷贝的文件如下:
图 37.4.3 源文件 以上拷贝的源文件相对于《基于TCP协议的远程更新QSPI Flash实验》只是修改了qspi_remote_update.c文件,其他文件无特殊需求可保持不变。 现对qspi_remote_update.c文件中修改的函数简单的做个讲解。 使用UDP协议则需要创建UDP服务,所以需要将start_application()中的TCP服务的创建改写成UDP服务的创建。创建UDP服务的start_application()实现如下: - void start_application()
- {
- struct udp_pcb *pcb;
- err_t err;
- //Qspi初始化
- err = qspi_init();
- if (err != XST_SUCCESS) {
- xil_printf("QSPIinit Failed\r\n");
- }
- xil_printf("Successfullyinit QSPI\r\n");
- //创建新的UDP PCB
- pcb = udp_new();
- if (!pcb) {
- xil_printf("Errorcreating PCB. Out of Memory\r\n");
- return;
- }
- //绑定端口
- err = udp_bind(pcb, IP_ADDR_ANY, SER_PORT);
- if (err != ERR_OK) {
- xil_printf("Unableto bind to port %d; err %d\r\n",
- SER_PORT, err);
- udp_remove(pcb);
- return;
- }
- //设置接收回调函数
- udp_recv(pcb, (udp_recv_fn) recv_callback, NULL);
- xil_printf("UDPserver started @ port %d\n\r", SER_PORT);
- }
复制代码代码第157行首先对QSPI进行了初始化,随后调用的三个函数便完成了UDP服务的创建。从start_application函数可以看到lwip中使用UDP协议很简单。首先通过udp_new函数创建一个新的用于UDP 的PCB,然后调用udp_bind函数绑定端口号和本地IP地址,IP_ADDR_ANY表明为任意本地地址,SER_PORT是在qspi_remote_update.h宏定义的端口号,其值为6789,即UDP服务的默认端口。最后调用udp_recv函数设置接收回调函数就完成了UDP服务的创建,服务端的功能由回调函数实现。回调函数代码如下: - //UDP接收回调函数
- static void recv_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p,
- ip_addr_t *ip, u16_t port)
- {
- struct pbuf *q;
- q = p;
- upcb->remote_ip = *ip;
- upcb->remote_port = port;
- c_pcb = upcb;
- if (q->tot_len == 6 && !(memcmp("update", p->payload, 6))) {
- start_update_flag = 1;
- sent_msg("StartQSPI Update\r\n");
- } else if (q->tot_len == 5 && !(memcmp("clear", p->payload, 5))) {
- start_update_flag = 0;
- total_bytes = 0;
- sent_msg("Clearreceived data\r\n");
- xil_printf("Clearreceived data\r\n");
- } else {
- while (q->tot_len != q->len) {
- 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;
- }
- pbuf_free(p);
- }
复制代码代码第130~146行的主要部分与TCP协议的无差别。有差别的第126~128行用于UDP的发送包文函数udp_send,以向远程客户端发送包文,该函数在sent_msg()函数中调用。 - //向客户端回送信息
- void sent_msg(const char *msg)
- {
- static struct pbuf *pbuf2sent;
- pbuf2sent = pbuf_alloc(PBUF_TRANSPORT, strlen(msg), PBUF_POOL);
- if (!pbuf2sent)
- xil_printf("Errorallocating pbuf\r\n");
- memcpy(pbuf2sent->payload, msg, strlen(msg));
- //发送报文
- if (udp_send(c_pcb, pbuf2sent) != ERR_OK)
- xil_printf("UDPsend error\r\n");
- //释放pbuf
- pbuf_free(pbuf2sent);
- }
复制代码该函数的主要作用就是实时的向客户端回送更新进度信息和命令应答信息。以上就是我们使用UDP协议时需要修改的三个函数。 如果想查看模板为提高数据传送的效率而进行的优化设置,可右键点击bsp工程qspi_update_udp_bsp,在弹出的菜单中选择“Board Support Package Settings”。 在打开的界面中,点击standalone下的lwip202,查看右侧界面的选项设置。 以lwip_memory_options选项为例。可以看到mem_size已经设置为524288;memp_n_pbuf已经设置为1024,与我们在上一章设置的相同,如下图所示。
图 37.4.4 lwip_memory_options的设置选项 1.5 下载验证首先我们将下载器与领航者底板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用Mini USB连接线将USB UART接口与电脑连接,用于串口通信。使用网线一端连接领航者开发板的以太网接口,另一端与电脑或路由器连接。最后连接开发板的电源,并打开电源开关。 step6:板级验证 6-1 在SDK软件的下方的SDK Terminal窗口中点击右上角的加号连接串口。 6-2 下载程序。下载完成后,可以看到串口打印的结果如下:
图 37.5.1 显示打印结果 如果接到路由器,因为有DHCP服务器,可自动获取IP 给开发板;如果没有DHCP 服务器,则当领航者开发板DHCP超时时使用默认IP 地址:192.168.1.10,端口号为设置的6789。图 36.5.1中红框圈起来的,表示QSPI初始化成功。 6-3 远程更新QSPI 打开网络调试助手,在网络调试助手发送区设置里选择“启用文件数据源”,选择需要发送的BOOT.bin 文件,这里我们选择《程序固化实验》生成的BOOT.bin 文件,然后点击发送,如下图所示:
图 37.5.2 加载BOOT.bin 文件 传输完成后,输入更新QSPI命令“update”。输入更新QSPI命令“update”后,启动QSPI更新,更新信息实时通过网络传送回发送方,显示在网络调试助手中,如下图所示:
图 37.5.3 输入更新QSPI命令“update” 通过传送回的文件大小,可以了解到传送过程中有没有丢包。更新进度信息中的Elapsed time表明每个操作(擦除、写入、校验)所花费的时间。 此时,接收方也会通过串口实时输出更新信息,如下图所示:
图 37.5.4 接收方通过串口实时输出更新信息 校验成功后,关闭电源开关。将领航者核心板上的启动模式开关左边拨到上面(置为1),右边拨到下面(置为0),即设置为由QSPIFlash启动,然后再次打开电源开关。 电源开关打开后,核心板上PL配置完成的指示灯点亮。然后每次按下底板上PL_KEY0,可以改变核心板上LED2的显示状态,说明远程更新QSPI成功,本次实验在领航者ZYNQ开发板上面下载验证成功。
|