正点原子 发表于 2023-5-18 09:30:35

《ATK-DFPGL22G之FPGA开发指南_V1.0》第三十六章

本帖最后由 正点原子 于 2023-5-18 09:30 编辑

1)实验平台:正点原子 DFZU2EG_4EV MPSoC开发板
2)购买链接:https://item.taobao.com/item.htm?&id=692368045899
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-340252-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子FPGA交流群:994244016




第三十六章基于UDP协议的远程更新QSPI Flash实验
上一章实验我们利用TCP协议实现了远程更新QSPI Flash。为了满足不同的网络需求,本次实验我们将上一章实验使用的TCP协议替换成UDP协议。本章包括以下几个部分:
36.1简介
36.2实验任务
36.3硬件设计
36.4软件设计
36.5下载验证


36.1简介
UDP由于本身的尽最大能力交付问题,即不稳定可靠,在传输文件上并不合适,在网络性能较好时,能够不丢失数据,当网络性能较差时,会面临数据丢失的问题,可靠性无法保证。UDP更适合于传输音视频这种对实时性要求高而又能容忍丢失数据的场合。当然了,也可以在UDP协议之上,编写一个能够确保可靠性的应用层协议,如TFTP协议。
本实验只是将《基于TCP协议的远程更新QSPI Flash实验》中的TCP协议实现改为UDP协议实现,了解如何使用UDP协议实现远程更新QSPI。如果在实际需求中确实需要使用UDP协议进行远程更新,可以在该实验的基础上增加确认重传机制,同时需要编写一个上位机使该确认重传机制能够正常工作;或者结合《基于lwip的TFTP服务器实验》使用TFTP协议以确保文件传输的可靠性。
36.2实验任务
本章的实验任务是使用LWIP协议栈的UDP协议实现远程更新QSPI Flash的功能,当输入“update”命令时更新QSPI Flash并反馈信息,当输入“clear”命令时之前传输的数据无效,当输入“erase”命令时对Flash进行擦除,如果此时Flash中有程序则会被擦除掉。
36.3硬件设计
本次实验的硬件设计与《基于TCP协议的远程更新QSPI Flash实验》相同。将“qspi_update_tcp”工程另存为本章实验的工程,即名为“qspi_update_udp”的工程,导出硬件平台文件到vitis文件夹,然后打开Vitis软件。
36.4软件设计
上一章的实验的软件设计部分,为了提高数据传送的效率,我们对lwip进行相应设置。其实官方提供的模板中有优化好的设置,我们上一章的设置就是参考官方的设置。本章我们使用官方优化好的设置,避免自己手动优化。
在打开的Vitis软件中,依次点击“File->New->Application Project”,新建一个名为“qspi_update_udp”的工程,如图 36.4.1所示。点击“Next >”,添加应用平台文件,添加完成后,接下来依次点击“Next>”,直到弹出选择模板界面,选择“lwip UDP Perf Server”,然后点击“Finish”,如图 36.4.2所示:
图 36.4.1 新建应用工程
图 36.4.2 选择“lwip UDP Perf Server”
我们将应用工程“qspi_update_udp”的src目录下所有的.c和.h文件删除,只保留lscript.ld和README.txt两个文件即可。因为我们只需要其板级支持包(BSP)已经优化好的设置,应用工程用我们自己设计好的源文件。从提供的例程中拷贝源文件,需拷贝的文件如下:
图 36.4.3 源文件
以上拷贝的源文件相对于《基于TCP协议的远程更新QSPI Flash实验》只是修改了qspi_remote_update.c文件,其他文件无特殊需求可保持不变。
现对qspi_remote_update.c文件中修改的函数简单的做个讲解。
使用UDP协议则需要创建UDP服务,所以需要将start_application()中的TCP服务的创建改写成UDP服务的创建。创建UDP服务的start_application()实现如下:
167 int start_application()
168 {
169   struct udp_pcb *pcb;
170   err_t err;
171
172   //Qspi初始化
173   err = qspi_init();
174   if (err != XST_SUCCESS) {
175         xil_printf("QSPI init Failed\r\n");
176   }
177   xil_printf("Successfully init QSPI\r\n");
178
179   //创建UDP PCB
180   pcb = udp_new();
181   if (!pcb) {
182         xil_printf("Error creating PCB. Out of Memory\n\r");
183         return;
184   }
185
186   //绑定端口号
187   err = udp_bind(pcb, IP_ADDR_ANY, SER_PORT);
188   if (err != ERR_OK) {
189         xil_printf("Unable to bind to port %d; err %d\r\n",
190                  SER_PORT, err);
191         udp_remove(pcb);
192         return;
193   }
194
195   //设置接收回调函数
196   udp_recv(pcb, (udp_recv_fn) recv_callback, NULL);
197
198   xil_printf("UDP server started @ port %d\n\r", SER_PORT);
199 }
代码第173行首先对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服务的创建,服务端的功能由回调函数实现。回调函数代码如下:
132 //UDP接收回调函数
133 static void recv_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p,
134         ip_addr_t *ip, u16_t port)
135 {
136   struct pbuf *q;
137   q = p;
138   upcb->remote_ip = *ip;
139   upcb->remote_port = port;
140   c_pcb = upcb;
141
142   if (q->tot_len == 6 && !(memcmp("update", p->payload, 6))) {
143         start_update_flag = 1;
144         sent_msg("Start QSPI Update\r\n");
145   } else if (q->tot_len == 5 && !(memcmp("clear", p->payload, 5))) {
146         start_update_flag = 0;
147         total_bytes = 0;
148         sent_msg("Clear received data\r\n");
149         xil_printf("Clear received data\r\n");
150   } else if(q->tot_len == 5 && !(memcmp("erase", p->payload, 5))){
151         start_erase_flag = 1;
152         sent_msg("\r\nQspi Erase\r\n");
153         xil_printf("\r\nQspi Erase\r\n");
154   } else {
155         while (q->tot_len != q->len) {
156             memcpy(&rxbuffer, q->payload, q->len);
157             total_bytes += q->len;
158             q = q->next;
159         }
160         memcpy(&rxbuffer, q->payload, q->len);
161         total_bytes += q->len;
162   }
163
164   pbuf_free(p);
165 }
代码第142~162行的主要部分与TCP协议的无差别。有差别的第138~140行用于UDP的发送包文函数udp_send,以向远程客户端发送包文,该函数在sent_msg()函数中调用。
113 //向客户端回送信息
114 void sent_msg(const char *msg)
115 {
116   static struct pbuf *pbuf2sent;
117
118   pbuf2sent = pbuf_alloc(PBUF_TRANSPORT, strlen(msg), PBUF_POOL);
119   if (!pbuf2sent)
120         xil_printf("Error allocating pbuf\r\n");
121
122   memcpy(pbuf2sent->payload, msg, strlen(msg));
123
124   //发送报文
125   if (udp_send(c_pcb, pbuf2sent) != ERR_OK)
126         xil_printf("UDP send error\r\n");
127
128   //释放pbuf
129   pbuf_free(pbuf2sent);
130 }
该函数的主要作用就是实时的向客户端回送更新进度信息和命令应答信息。以上就是我们使用UDP协议时需要修改的三个函数。
如果想查看模板为提高数据传送的效率而进行的优化设置,可点击应用工程下的qspi_update_udp.prj,然后在右侧页面中找到“Options”标签页,点击“Navigate to BSP Settings”。在弹出的页面中点击“Modify BSP Settings”,打开板级支持包设置界面。
在打开的界面中,点击standalone下的lwip211,查看右侧界面的选项设置。
以lwip_memory_options选项为例。可以看到mem_size已经设置为524288; memp_n_pbuf已经设置为1024,与我们在上一章设置的相同,如下图所示。
图 36.4.4 lwip_memory_options的设置选项
36.5下载验证
首先将下载器一端与开发板上的JTAG接口连接,下载器另外一端与电脑连接。再使用USB连接线将USB_UART(开发板PS PORT)接口与电脑连接,用于串口通信。使用网线一端连接开发板的PS以太网接口(PS_ETH),另一端与电脑或路由器连接。最后连接开发板的电源,给开发板上电。
step6:板级验证
6-1 打开Vitis Terminal终端,设置并连接串口。
6-2 下载程序。下载完成后,可以看到串口打印的结果如下:
图 36.5.1 显示打印结果
如果接到路由器,因为有DHCP服务器,可自动获取IP 给开发板;如果没有DHCP 服务器,则当MPSOC开发板DHCP超时时使用默认IP 地址:192.168.1.10,端口号为设置的6789。上图中红框圈起来的,表示QSPI初始化成功。
6-3 远程更新QSPI
打开网络调试助手,在网络调试助手发送区设置里选择“启用文件数据源”,选择需要发送的BOOT.bin 文件,这里我们选择《程序固化实验》生成的BOOT.bin 文件,然后点击发送,如下图所示:
图 36.5.2 加载BOOT.bin 文件
在上面的本地主机端口栏里,本次设置成52789。另外也可以在windows系统的命令提示符窗口中输入“netstat -an”命令来查询可用端口。
传输完成后,输入更新命令“update”。输入更新QSPI命令“update”后,启动QSPI更新,更新信息实时通过网络传送回发送方,显示在网络调试助手中,如下图所示:
图 36.5.3 输入更新QSPI命令“update”
通过传送回的文件大小,可以了解到传送过程中有没有丢包。更新进度信息中的Elapsed time表明每个操作(擦除、写入、校验)所花费的时间。
此时,接收方也会通过串口实时输出更新信息,如下图所示:
图36.5.4 接收方通过串口实时输出更新信息
校验成功后,断开开发板电源。将MPSOC开发板上的启动模式开关设置为ON_ON_OFF_ON(也就是开发板上丝印标注的0010 SPI_32),即设置为由QSPI Flash启动,然后再次连接开发板电源。
连接电源后,开发板上绿色的PL配置完成指示灯点亮。然后每次按下开发板上PL_KEY1,可以改变PS_LED1的显示状态,说明远程更新QSPI Flash成功,本次实验在MPSOC开发板上面下载验证成功。
这里有一点需要注意,当bin文件发送完成后,发送update命令更新flash时,网络助手接收窗口中关于接收到的bin文件大小显示为0字节时,可能是巨型帧设置的问题,如下图所示:
图36.5.5 接收的文件大小为0字节
巨型帧设置步骤是,首先找到网络和Internet设置中的更改适配器选项并打开,如下图所示,
图 36.5.6更改适配器选项
图 36.5.7打开更改适配器选项
然后右击以太网属性,点击配置选项,打开高级标签页,下拉属性选项栏找到巨型帧选项,如下图红色圆圈1所示,然后将其值设置为关闭,如圆圈2所示,这样开发板就能正常接收数据包了。
图36.5.8 巨型帧设置
页: [1]
查看完整版本: 《ATK-DFPGL22G之FPGA开发指南_V1.0》第三十六章