[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_SDK高级篇连载-19基于LWIP TCP的网络摄像头方案

文档创建者:FPGA课程
浏览次数:192
最后更新:2024-10-10
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 1_SDK应用方案(仅旗舰型号) » 2-SDK高级应用方案
本帖最后由 FPGA课程 于 2024-10-10 18:19 编辑

​ 软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!



1概述
本实验通过 uifdma_dbuf + uifdma IP 实现 PL PS 的数据通路部分,把摄像头数据传输到 PS DDR. PS ARM部分通过 lwip tcp 方式把 DDR 中数据发送到上位机,并且显示。
实验目的:
1:掌握 uifdmadbuf 配置成视频模式的情况下的参数设置
2:掌握 OV5640 摄像头数据如何通过 uifdmadbuf 写入到 uifdma ip
3:ps 实现 uifdmadbuf 中断到来,读取 DDR 缓存中的数据
4:设计基于 TCP 方式的摄像头传输方案
5:使用 lwip tcp 方式把采集的摄像头数据从 DDR 中发送出去
6:可以通过上位机获取开发板发过来的相机数据,并且显示图像
2系统构架
本方案中继续保留之前 HDMI 输出的部分,这部分功能没有用到,用户可以自己裁剪掉该功能。
fa99b711b4aa40a693dffab02a57cf51.jpg
3硬件电路分析
本方案使用到了FEP-GPIO-CEPX3模块,以及OV5640摄像头,模块的介绍请阅读“附录1”
4搭建SOC系统工程
4.1PL图形化编程
8094065c0e744676a42460d63d6a2654.jpg
上图中高亮部是FDMA的帧同步计数器,CAM0的帧同步计数器提供给CAM1和VDMA 输出 IP使用。单VDMA 输出 IP工作于SLAVE  circle 模式,所以CAM1和VDMA 输出都是从模式同步受控于CAM0的帧计数器,这样可以确保视频不出现撕裂。(本方案中可以优化掉VDMA部分,因为不需要显示功能)
另外,我们对CAM0和CAM1的IP在BD图像模块中做了层级封装,这样可以让复杂的BD图像设计,看起来更加简洁,如下图所示。
3b0cdbd1fc1e4185a3e45808855e017b.jpg

这个技巧如下,选中需要层级封装的IP,右击选择Create Hierarchy
69f236240cd24adcbd4c865210890acf.jpg
1:CAM0中FDMA IP设置
关键设置:
1`WBaseaddr 0x10000000 设置缓存的起始地址
2`WDsizebits 设置缓存的大小,2^23次方代表8MB大小
3`WBufsize 设置图像采用三缓存
4`WXsize设置行像素640
5`WXStride设置行Stride参数为1280,改参数用于2个视频在一个显存中显示
6`WYsize设置场像素为480
04228fc8530c4b7799d2db9ce1c03cc7.jpg
2:CAM1FDMA IP设置
1`WBaseaddr 0x1012CA00 设置缓存的起始地址,通过设置该地址可以让图像在指定的偏移位置显示
2`WDsizebits 设置缓存的大小,2^23次方代表8MB大小
3`WBufsize 设置图像采用三缓存
4`WXsize设置行像素640
5`WXStride设置行Stride参数为1280,改参数用于2个视频在一个显存中显示
6`WYsize设置场像素为480
02b72a6b40f94dbe85fc45459e82bf68.jpg
3:视频输出 VDMA IP 设置
20a7eb498cd94008b2170a5c9437a72b.jpg
以下设置视频输出 VDMA 为从模式,帧同步跟随主模式的 CAM0/uifdma_dbuf (本方案中可以优化掉 VDMA 部分,因为不需要显示功能)
a22f06172dbe40b4b0addbfc38b24b8d.jpg
4:多路视频同屏显示原理
30376186d1664782b90e1c0cdf1d3b47.jpg
为了把2个图像显示到1个显示器,首先得搞清楚以下关系:
hsize:每1行图像实际在内存中占用的有效空间,以32bit表示一个像素的时候占用内存大小为hsize*4
hstride:用于设置每行图像第一个像素的地址,以32bit 表示一个像素的时候h_cnt* hstride*4
vsize:有效的行
因此很容易得出cam0的每行第一个像素的地址也是h_cnt* hstride*4
同理如果我们需要把cam1在hsize和vsize空间的任何位置显示,我们只要关心cam1每一行图像第一个像素的地址,可以用以下公式h_cnt* hstride*4+offset
比如我们这里背景输出到显示器的分辨率为1280*720,cam1的分辨率是640*480需要移动上图的右下脚,offset=(1280-640)*4*(720-480)
4.2设置地址分配
以sccb方式初始化摄像头的地址空间截图
c31a0d5675e54b58a791061ce2ab5f02.jpg
4.3添加PIN约束
1:选中PROJECT  MANAGERà Add SourcesàAdd or create constraints,添加XDC约束文件。
9f7aeb6531fe41dfa2821a28363082aa.jpg
2:打开提供例程,复制约束文件中的管脚约束到XDC文件,或者查看原理图,自行添加管脚约束,并保存。
以下是添加配套工程路径下已经提供的pin脚文件。配套工程的pin脚约束文件在uisrc/04_pin路径
4.4编译并导出平台文件
1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。
2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。
3:生成Bit文件。
4:导出到硬件: FileàExport HardwareàInclude bitstream
5:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
d653fed804ab4491a01a5f262db61c50.jpg
5搭建Vitis-sdk工程
创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。
5.1创建SDK Platform工程
56afc2bee9b847eea6272e308f07ad6c.jpg
LWIP库的修改:
1:新版本系列工业级开发板板载网口芯片是RTL8211FDI,由于默认的驱动不支持,需要手动自己修改库文件。我们这里已经提供了修改好的库,解压到vivado的安装路径下的对于路径下:
38403f1640874a45a5b62fefe941f498.jpg
1ec05381e70c4a4483e405b1e15b7fcf.jpg
修改好后,需要关闭vitis-sdk然后重新打开sdk,否则无法识别修改的库
2:为了创建lwip工程需要先对zu_base中的board support package简称bsp设置lwip库的支持
368a2f9ad7bf4649896f9c32a60d5392.jpg
3:对lwip库参数修改以达到最佳性能。
本例程使用 RAW API,即函数调用不依赖操作系统。传输效率也比 SOCKET API 高,(具体可参考 xapp1026)。 将 use_axieth_on_zynq 和 use_emaclite_on_zynq 设为 0。如下图所示。
bfd7b613be2c47a99b7deb3ef3b687be.jpg
修改 lwip_memory_options 设置,将 mem_size,memp_n_pbuf,mem_n_tcp_pcb,memp_n_tcp_seg 这 4 个参数 值设大,这样会提高 TCP 传输效率。如下图所示。
2e4049f2a14847de869c6316ecb8c3a7.jpg
修改 pbuf_options 设置,将 pbuf_pool_size 设大,增加可用的 pbuf 数量,这样同样会提高 TCP 传输效率。如下 图所示。
8d59e56b792243b298243296b4414f82.jpg
修改 tcp_options 设置,将 tcp_snd_buf,tcp_wnd 参数设大,这样同样会提高 TCP 传输效率。如下图所示。
74e5c0e8b93a4d879190bfb2a525e46f.jpg
修改 temac_adapter_options 设置,将 n_rx_descriptors 和 n_tx_descriptors 参数设大。这样可以提高 zynq 内部 emac dma 的数据迁移效率,同样能提高 TCP 传输效率。如下图所示。
920f3e0719d14415924beeef765cd5a4.jpg
启用DHCP功能
18714a3aa22f40ddbff8b5dfc56a8459.jpg
修改完成后重新编译soc_base
5.2创建cam_lwip_tcp工程
e57c7aca19be4c28af67174d274403cc.jpg
6SDK程序分析
6.1FDMA数据接收原理
ea0dfbae73514786a5c4e27ce10a9c99.jpg
每当uifdmadbuf发送的中断后,该函数被调用,通过读取uifdmadbuf axi-lite的寄存器,获取当前哪一个缓存产生了中断(代表数据发送到PS DDR了)。
为了增加数据的吞吐能力,在中断中,不宜进行数据搬运,我们设计了要给结构体,可以用于标记已经写入DDR的数据。
1:PS_RX_intr_Handler
  1. void PS_RX_intr_Handler(void *param)
  2. {
  3. fdma_buf.record[fdma_buf.circle_cnt]= Xil_In32((UINTPTR)FDMA_DBUF_BASE_ADDR);
  4. if(fdma_buf.circle_cnt<2)
  5. fdma_buf.circle_cnt ++ ;
  6. else
  7. fdma_buf.circle_cnt = 0;
  8. fdma_buf.pkg_done_cnt++;
  9. }
复制代码
6.2数据包设计
Lwip ip作为轻量级的协议栈,不能一次性发送所有的图像数据,因此需要对图像数据分多次传输。本文中,传输的图像大小为1280*720*4 = 3600KB,数据设计为每包传输1024*16即16KB。因此传输完所有数据需要经过225次。
  1. #define TCP_PACKEG_SIZE
  2. 1024*16
  3. #define IMG_SIZE
  4. 1280*720*4
  5. #define TCP_SEND_TIMES
  6. IMG_SIZE/TCP_PACKEG_SIZE
  7. #define TCP_SEND_LAST_SIZE
  8. IMG_SIZE-(TCP_PACKEG_SIZE*TCP_SEND_TIMES)
  9. #define TCP_FIRST_SEND_SIZE
  10. HEADER_SIZE + TCP_PACKEG_SIZE
复制代码

6.3帧头设计
为了让上位机知道接收的数据的格式、大小、当前帧号、当前包号,设计了如下数据帧头:
  1. typedef struct packet_header
  2. {
  3. u32 ID0;//AA55AA55
  4. u32 ID1;//AA55AA55
  5. u16 framcnt;//1,2
  6. u16 hsize;//width
  7. u16 vsize;//height
  8. u16 offset;//
  9. u32 psize; //this packet saize
  10. u32 tsize; //total packet saize
  11. }packet_header;
复制代码
6.4主程序分析
main函数中完成中断资源的初始化,lwip的初始化,并且通过一个while循环完成, tcp连接监听、数据的接收函数调用、数据的发送函数调用。本文的实验只需要,tcp连接监听和数据的发送功能。
通过定时器,每间隔250ms会判断一次request_pcb->state的状态,如果以太网没有连接,则会创建一个新的TCP连接。
1:init_intr_sys()
该函数初始化中断,包括PL中断和以太网传输需要用到的定时器中断
f0445eff5c764da4a0f61e380be1fb1c.jpg
其中init_platform函数会对以太网定时器中断部分以及回调函数进行初始化。
可以关键看下platform_zynq.c中被调用的相关函数:
  1. void
  2. timer_callback(XScuTimer * TimerInstance)
  3. {
  4. /* we need to call tcp_fasttmr & tcp_slowtmr at intervals specified
  5. * by lwIP. It is not important that the timing is absoluetly accurate.
  6. */
  7. static int odd = 1;
  8. #if LWIP_DHCP==1
  9. static int dhcp_timer = 0;
  10. #endif
  11. TcpFastTmrFlag = 1;
  12. odd = !odd;
  13. #ifndef USE_SOFTETH_ON_ZYNQ
  14. ResetRxCntr++;
  15. #endif
  16. if (odd) {
  17. TcpSlowTmrFlag = 1;
  18. #if LWIP_DHCP==1
  19. dhcp_timer++;
  20. dhcp_timoutcntr--;
  21. dhcp_fine_tmr();
  22. if (dhcp_timer >= 120) {
  23. dhcp_coarse_tmr();
  24. dhcp_timer = 0;
  25. }
  26. #endif
  27. }
  28. /* For providing an SW alternative for the SI #692601. Under heavy
  29. * Rx traffic if at some point the Rx path becomes unresponsive, the
  30. * following API call will ensures a SW reset of the Rx path. The
  31. * API xemacpsif_resetrx_on_no_rxdata is called every 100 milliseconds.
  32. * This ensures that if the above HW bug is hit, in the worst case,
  33. * the Rx path cannot become unresponsive for more than 100
  34. * milliseconds.
  35. */
  36. #ifndef USE_SOFTETH_ON_ZYNQ
  37. if (ResetRxCntr >= RESET_RX_CNTR_LIMIT) {
  38. xemacpsif_resetrx_on_no_rxdata(&server_netif);
  39. ResetRxCntr = 0;
  40. }
  41. #endif
  42. XScuTimer_ClearInterruptStatus(TimerInstance);
  43. }
  44. void platform_setup_timer(void)
  45. {
  46. int Status = XST_SUCCESS;
  47. XScuTimer_Config *ConfigPtr;
  48. int TimerLoadValue = 0;
  49. ConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
  50. Status = XScuTimer_CfgInitialize(&TimerInstance, ConfigPtr,
  51. ConfigPtr->BaseAddr);
  52. if (Status != XST_SUCCESS) {
  53. xil_printf("In %s: Scutimer Cfg initialization failed...\r\n",
  54. __func__);
  55. return;
  56. }
  57. Status = XScuTimer_SelfTest(&TimerInstance);
  58. if (Status != XST_SUCCESS) {
  59. xil_printf("In %s: Scutimer Self test failed...\r\n",
  60. __func__);
  61. return;
  62. }
  63. XScuTimer_EnableAutoReload(&TimerInstance);
  64. /*
  65. * Set for 250 milli seconds timeout.
  66. */
  67. TimerLoadValue = XPAR_CPU_CORTEXA9_0_CPU_CLK_FREQ_HZ / 8;
  68. XScuTimer_LoadTimer(&TimerInstance, TimerLoadValue);
  69. return;
  70. }
  71. void platform_setup_interrupts(void)
  72. {
  73. /*
  74. * Connect the device driver handler that will be called when an
  75. * interrupt for the device occurs, the handler defined above performs
  76. * the specific interrupt processing for the device.
  77. */
  78. XScuGic_RegisterHandler(INTC_BASE_ADDR, TIMER_IRPT_INTR,
  79. (Xil_ExceptionHandler)timer_callback,
  80. (void *)&TimerInstance);
  81. /*
  82. * Enable the interrupt for scu timer.
  83. */
  84. XScuGic_EnableIntr(INTC_DIST_BASE_ADDR, TIMER_IRPT_INTR);
  85. return;
  86. }
  87. void platform_enable_interrupts()
  88. {
  89. /*
  90. * Enable non-critical exceptions.
  91. */
  92. Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
  93. XScuTimer_EnableInterrupt(&TimerInstance);
  94. XScuTimer_Start(&TimerInstance);
  95. return;
  96. }
  97. void init_platform()
  98. {
  99. platform_setup_timer();
  100. platform_setup_interrupts();
  101. return;
  102. }<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
2:lwip_init()
初始化lwip
3:xemac_add ()
添加以太网的MAC地址,MAC地址定义如下:
unsigned char mac_ethernet_address[] ={0x00,0x0a,0x35,0x00,0x01,0x02};
4:netif_set_default()
设置默认的以太网接口,这里定义了一个server_netif的全局变量,并且对其初始化。
struct netif server_netif;
netif = &server_netif;
netif_set_default(netif);
5:platform_enable_interrupts()
该函数会使能platform_zynq.c中的定时器,启动定时器,这样每间隔250msTcpFastTmrFlag变量就会设设置1,每间隔500ms TcpSlowTmrFlag变量就会设置1
6:启动DHCP配置
  1. dhcp_start(netif);
  2. dhcp_timoutcntr = 24;
  3. while (((netif->ip_addr.addr) == 0) && (dhcp_timoutcntr > 0))
  4. xemacif_input(netif);
  5. if (dhcp_timoutcntr <= 0) {
  6. if ((netif->ip_addr.addr) == 0) {
  7. xil_printf("ERROR: DHCP request timed out\r\n");
  8. assign_default_ip(&(netif->ip_addr),
  9. &(netif->netmask), &(netif->gw));
  10. }
  11. }
复制代码

7:start_appication()
该函数首先
  1. void start_application(void)
  2. {
  3. err_t err;
  4. ip_addr_t remote_addr;
  5. u32_t i;
  6. cam_init();/* 初始化摄像头 */
  7. first_trans_start = 0; /* 该变量判断是否第一次传输 */
  8. client_connected =0; /* 判断是否连接状态 */
  9. #if LWIP_IPV6==1
  10. remote_addr.type= IPADDR_TYPE_V6;
  11. err = inet6_aton(TCP_SERVER_IPV6_ADDRESS, &remote_addr);
  12. #else
  13. err = inet_aton(TCP_SERVER_IP_ADDRESS, &remote_addr); /* 设置服务器IP地址 */
  14. #endif /* LWIP_IPV6 */
  15. if (!err) {
  16. xil_printf("Invalid Server IP address: %d\r\n", err);
  17. return;
  18. }
  19. /* Create Client PCB */
  20. request_pcb = tcp_new_ip_type(IPADDR_TYPE_ANY); /* 创建一个客户端PCB */
  21. if (!request_pcb) {
  22. xil_printf("Error in PCB creation. out of memory\r\n");
  23. return;
  24. }
  25. err = tcp_connect(request_pcb, &remote_addr, TCP_CONN_PORT,
  26. tcp_client_connected); /* 设置客户端和主机连接上的回调函数 tcp_client_connected */
  27. if (err) {
  28. xil_printf("Error on tcp_connect: %d\r\n", err);
  29. tcp_client_close(request_pcb);
  30. return;
  31. }
  32. client.client_id = 0;
  33. return;
  34. }
复制代码
8:while循环
该循环中,每过250ms调用tcp_fasttmr(),每间隔500ms调用tcp_slowTmr()函数。tcp_fasttmr()每250ms处理延时发送的ack报文和fin报文,并且通知上层应用处理数据。tcp_slowTmr()每500ms调用,该函数负责超时重传以及移除TIME-WAIT 足够时间的 PCB,同时将PCB中unsent队列中的数据发送出去。一般使用tcp_write();写入数据后,数据不会马上发送,而是在定时任务中发送。
While循环中还会检测当前连接状体,如果当前连接状态不存在会每间隔250ms重新尝试连接一次。
最后当连接建立后,会调用transfer_data()完成数据从DDR到以太网的发送。
  1. while (1) {
  2. if (TcpFastTmrFlag) {
  3. if(request_pcb->state == CLOSED || (request_pcb->state == SYN_SENT && request_pcb->nrtx ==
  4. TCP_SYNMAXRTX))//check conditions for create new tcp connection
  5. {
  6. start_application();
  7. }
  8. tcp_fasttmr();
  9. TcpFastTmrFlag = 0;
  10. }
  11. if (TcpSlowTmrFlag) {
  12. tcp_slowtmr();
  13. TcpSlowTmrFlag = 0;
  14. }
  15. xemacif_input(netif);
  16. if(client_connected)
  17. transfer_data();
  18. }
复制代码

8:transfer_data()
该函数负责把DDR中摄像头的图像数据发送出去,是本方案的核心。该函数会调用tcp_send_perf_traffic()函数。
9:tcp_send_perf_traffic()函数
当FDMA摄像头的缓存中存在数据,首选发送帧头,然后连续发送TCP_SEND_TIMES次TCP_PACKEG_SIZE大小的数据包,直到所有数据完成发送。
  1. static err_t tcp_send_perf_traffic(void)
  2. {
  3. err_t err;
  4. u8_t apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
  5. if (c_pcb == NULL) {
  6. return ERR_CONN;
  7. }
  8. #ifdef __MICROBLAZE__
  9. /* Zero-copy pbufs is used to get maximum performance for Microblaze.
  10. * For Zynq A9, ZynqMP A53 and R5 zero-copy pbufs does not give
  11. * significant improvement hense not used. */
  12. apiflags = 0;
  13. #endif
  14. struct tcp_pcb *tpcb = c_pcb;
  15. if (!tpcb)
  16. return;
  17. if(first_trans_start==0)
  18. {
  19. first_trans_start =1;
  20. fdma_buf.circle_cnt=0;
  21. fdma_buf.next=0;
  22. fdma_buf.pkg_done_cnt=0;
  23. fdma_buf.pkg_cnt=0;
  24. fdma_buf.fram_cnt=0;
  25. pkg_psize =0;
  26. pkg_offset =0;
  27. XGpio_DiscreteWrite(&rstn_5640, 1, 0x1);
  28. }
  29. /*当fdma_buf.pkg_done_cnt 大于0标识数据已经从PL发送到DDR,当fdma_buf.pkg_done_cnt 大于2标识缓存溢出*/
  30. if(fdma_buf.pkg_done_cnt> 0 && fdma_buf.pkg_done_cnt<3) //1MB divide in to 64 times
  31. {
  32. /*当TCP发送缓冲区足够发送一包数据,则发送数据*/
  33. if (tcp_sndbuf(tpcb) > TCP_FIRST_SEND_SIZE)
  34. {
  35. /*transmit received data through TCP*/
  36. /*第一小包数据,在数据前面插入帧头*/
  37. if(fdma_buf.pkg_cnt==0)
  38. {
  39. bufaddr = (u8*)(RxBufferPtr[fdma_buf.record[fdma_buf.next]]);//获取地址
  40. header_p = (packet_header *)bufaddr;
  41. header_p->ID0
  42. = HEADER_ID0;
  43. header_p->ID1
  44. = HEADER_ID1;
  45. header_p->hsize
  46. = 1280;
  47. header_p->vsize
  48. = 720;
  49. header_p->tsize
  50. = IMG_SIZE;
  51. header_p->framcnt = fdma_buf.fram_cnt;
  52. header_p->psize
  53. = IMG_SIZE;
  54. header_p->offset = 0;
  55. /*确保cache一致性问题*/
  56. Xil_DCacheInvalidateRange((u32)bufaddr + HEADER_SIZE, TCP_PACKEG_SIZE);
  57. /*数据写入到TCP的发送缓存*/
  58. err = tcp_write(tpcb, bufaddr, TCP_FIRST_SEND_SIZE, apiflags);
  59. /*地址增加帧头偏移*/
  60. bufaddr = bufaddr + HEADER_SIZE;
  61. /*计算累计发送的大小*/
  62. pkg_offset = pkg_offset + TCP_PACKEG_SIZE;
  63. }
  64. else if(fdma_buf.pkg_cnt < TCP_SEND_TIMES) //发送剩余包数据
  65. {
  66. /*计算下一包数据数据首地址*/
  67. bufaddr = bufaddr + TCP_PACKEG_SIZE;
  68. Xil_DCacheInvalidateRange((u32)bufaddr, TCP_PACKEG_SIZE);
  69. err = tcp_write(tpcb, bufaddr, TCP_PACKEG_SIZE, apiflags);
  70. pkg_offset = pkg_offset + TCP_PACKEG_SIZE;
  71. }
  72. else if(TCP_SEND_LAST_SIZE>0) //发送剩余包数据
  73. {
  74. bufaddr = bufaddr + TCP_PACKEG_SIZE;
  75. Xil_DCacheInvalidateRange((u32)bufaddr, TCP_SEND_LAST_SIZE);
  76. err = tcp_write(tpcb, bufaddr, TCP_SEND_LAST_SIZE, apiflags);
  77. pkg_offset = pkg_offset + TCP_SEND_LAST_SIZE;
  78. }
  79. if (err != ERR_OK) {
  80. xil_printf("txperf: Error on tcp_write: %d\r\n", err);
  81. return;
  82. }
  83. err = tcp_output(tpcb);
  84. if (err != ERR_OK) {
  85. xil_printf("txperf: Error on tcp_output: %d\r\n",err);
  86. return;
  87. }
  88. fdma_buf.pkg_cnt++;/*小包计数器*/
  89. /*判断数据是否发送完毕*/
  90. if(pkg_offset == IMG_SIZE)
  91. {
  92. pkg_offset=0;
  93. fdma_buf.fram_cnt++;
  94. fdma_buf.pkg_done_cnt--;
  95. fdma_buf.pkg_cnt = 0;
  96. if(fdma_buf.next<2)
  97. fdma_buf.next++;
  98. else
  99. fdma_buf.next=0;
  100. }
  101. }
  102. }
  103. else if(fdma_buf.pkg_done_cnt > 2) //如果缓存不能处理,通过设置irst_trans_start = 0再次同步
  104. {
  105. xil_printf("error pkg_done_cnt = %d \r\n", fdma_buf.pkg_done_cnt);
  106. first_trans_start = 0;
  107. }
  108. /*
  109. if (client.end_time || client.i_report.report_interval_time) {
  110. u64_t now = get_time_ms();
  111. if (client.i_report.report_interval_time) {
  112. if (client.i_report.start_time) {
  113. u64_t diff_ms = now - client.i_report.start_time;
  114. u64_t rtime_ms = client.i_report.report_interval_time;
  115. if (diff_ms >= rtime_ms) {
  116. tcp_conn_report(diff_ms, INTER_REPORT);
  117. client.i_report.start_time = 0;
  118. client.i_report.total_bytes = 0;
  119. }
  120. } else {
  121. client.i_report.start_time = now;
  122. }
  123. }
  124. }*/
  125. return ERR_OK;
  126. }
复制代码

10:本地IP地址设置
在主程序tcp_lwip_test.c中定义
  1. #define DEFAULT_IP_ADDRESS
  2. "192.168.137.10"
  3. #define DEFAULT_IP_MASK
  4. "255.255.255.0"
  5. #define DEFAULT_GW_ADDRESS
  6. "192.168.1.1"<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
11:远程主机IP设置
在头文件tcp_client.h中
  1. #define TCP_SERVER_IP_ADDRESS "192.168.137.209"
  2. #define TCP_CONN_PORT 5001
复制代码
7方案演示
7.1硬件准备
e4c27616a8eb46d0be1855f5fad538ef.jpg
7fa3ca29578942c581c55338ccfe2fcf.jpg
7.2实验结果
e16c7a52d258493eb159ea1724773ce3.jpg














您需要登录后才可以回帖 登录 | 立即注册

本版积分规则