ZC706千兆网测试(ZYNQ,FreeRTOS,Echo,lwIP,TCP,RGMII)

(1)使用zc706开发板测试ps端网口(echo,lwip协议栈);
(2)配合操作pl端led(直接驱动和使用消息队列两种方式);
(3)ps端串口uart打印调试信息;
(4)qspi固化(dual quad spi parallel 8 bit模式)。
zc706中,mac 控制器与 phy 通过 rgmii(reduced gigabit media independent interface)接口进行连接,实现千兆网。
一、工程概述 1. 开发板配置 使用xilinx zynq开发板zc706,默认配置arm后即可使用ps端网口、串口和qspi,放置axi gpio的ip核驱动pl端的4个led。
arm端配置如下图所示,以5处的arm-a9为核心,使用1处的uart1打印调试信息,使用2处的网口0进行以太网通信,使用3处的axi gp(general port)master通用主设备接口连接pl端的axi gpio,最后使用4处的qspi固化程序,烧录boot文件。
2. sdk程序 上述工程综合、布局布线并生成bit流后,导出硬件。
新建应用工程application project,选择 os platform 平台为 freertos10_xilinx(vivado及sdk版本2018.2,低版本的可能是freertos9_xilinx),选择next,选中“freertos lwip echo server”。
新建完成后,即可进行最基础的网络通信了。这里注意,默认设置的是dchp动态主机配置协议,需要开发板和电脑都连接到一个路由器上。如果直接使用网线连接开发板和电脑,则启用 ipv4 协议,默认配置的ip地址为192.168.1.10,子网掩码255.255.255.0,网关196.128.1.1,如果想要更改默认配置,可以在main.c文件的main_thread()主线程中修改,如下所示:
xil_printf(error: dhcp request timed out); xil_printf(configuring default ip of 192.168.1.10); ip4_addr(&(server_netif.ip_addr), 192, 168, 1, 10); ip4_addr(&(server_netif.netmask), 255, 255, 255, 0); ip4_addr(&(server_netif.gw), 192, 168, 1, 1); lwip 是一个小型开源的 tcp/ip 协议栈,支持ipv4、ipv6、tcp、udp、dhcp等。
•igmp 协议,用于网络组管理,可以实现多播数据的接收
•internet 协议(ip),包括 ipv4 和 ipv6,支持 ip 分片与重装,包括通过多个网络接口的数据包转发
•用于网络维护和调试的 internet 控制消息协议(icmp)
•用户数据报协议(udp)
•传输控制协议(tcp)拥塞控制,往返时间(rtt)估计,快速恢复和重传
•dns,域名解析
•snmp,简单网络管理协议
•动态主机配置协议(dhcp) 
•以太网地址解析协议(arp)
•autoip,ip 地址自动配置
•ppp,点对点协议,支持
3. 网络设置 使用网线直接连接zc706开发板和计算机网口,配置计算机ip地址为192.168.1.11,子网掩码255.255.255.0,网关192.168.1.1,其中ip地址的最后一处可以更改为其他值,但是不能和开发板的相同。
4. 开启监听测试 使用securecrt软件监听,除此之外,使用其他网口助手也可以。
二、工程测试 1. 测试echo官方例程 先打开串口,波特率115200,下载官方例程到zc706开发板,连接securecrt_cn,初始化工程中串口打印信息如下:配置dchp动态主机协议超时,自动转为ipv4,将板子的ip地址配置为192.168.1.10,子网掩码255.255.255.0,网关192.168.1.1,使用端口7。
在securecrt_cn界面输入字符或字符串,回车,通过网口向开发板发送数据,开发板会返回同样的数据,测试正确。
2. 分析源码 2.1 main函数 打开main.c文件,找到main()函数。在main函数中创建了一个线程,传入的参数依次为线程名(调试用)、函数指针、函数需要的参数、需要的堆栈大小、优先级。
按照如下配置,调用了main_thread函数,不需要传参(用0或null),堆栈大小由#define定义为1024,优先级为2。
int main() { sys_thread_new(main_thrd, (void(*)(void*))main_thread, 0, thread_stacksize, default_thread_prio); vtaskstartscheduler(); while(1); return 0; }  
2.2 main_thread函数 此函数中实现的功能如下:
(1)初始化lwip协议栈;lwip_init();
(2)调用network_thread()创建线程;
(3)调用echo_application_thread()创建线程;
每500ms检测一次dhcp是否成功,若成功则创建echo应用线程,如果10秒还没有成功,则启用ipv4,配置ip地址、子网掩码和网关后,创建echo应用程序;创建成功后退出while,配置完成;
while (1) { vtaskdelay(dhcp_fine_timer_msecs / porttick_rate_ms); if (server_netif.ip_addr.addr) { xil_printf(dhcp request success); print_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask), &(server_netif.gw)); print_echo_app_header(); xil_printf(); sys_thread_new(echod, echo_application_thread, 0, thread_stacksize, default_thread_prio); break;   }   mscnt += dhcp_fine_timer_msecs; if (mscnt >= 10000) { xil_printf(error: dhcp request timed out); xil_printf(configuring default ip of 192.168.1.10); ip4_addr(&(server_netif.ip_addr), 192, 168, 1, 10); ip4_addr(&(server_netif.netmask), 255, 255, 255, 0); ip4_addr(&(server_netif.gw), 192, 168, 1, 1); print_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask), &(server_netif.gw)); /* print all application headers */ xil_printf(); xil_printf(%20s %6s %s, server, port, connect with..); xil_printf(%20s %6s %s, --------------------, ------, --------------------); print_echo_app_header(); xil_printf(); sys_thread_new(echod, echo_application_thread, 0, thread_stacksize, default_thread_prio); break; } }  
2.3 echo_application_thread函数 位置:echo.c文件。
(1)创建socket,绑定端口,监听;
(2)调用process_echo_request函数创建线程;
此函数需要传入参数。
while (1) { if ((new_sd = lwip_accept(sock, (struct sockaddr *)&remote, (socklen_t *)&size)) > 0) { sys_thread_new(echos,                     process_echo_request,                (void*)new_sd, thread_stacksize, default_thread_prio); } }  
2.4 process_echo_request函数 位置:echo.c文件,用户需要注意的最重要的函数,发送和接收的移植全部在这个函数。
(1)接收数据,最大数据长度2048,char 类型,存储在recv_buff 数组中,若接收出错,打印错误信息并退出while;
(2)若接收到的数据的前4个字符为quit,则退出while;
(3)将接收到的数据发送出去;
void process_echo_request(void *p) { int sd = (int)p; int recv_buf_size = 2048; char recv_buf[recv_buf_size]; int n, nwrote; while (1) { /* read a max of recv_buf_size bytes from socket */ if ((n = read(sd, recv_buf, recv_buf_size)) < 0) { xil_printf(%s: error reading from socket %d, closing socket, __function__, sd); break; } /* break if the recved message = quit */ if (!strncmp(recv_buf, quit, 4)) break; /* break if client closed connection */ if (n <= 0) break; /* handle request */ if ((nwrote = write(sd, recv_buf, n)) < 0) { xil_printf(%s: error responding to client echo request. received = %d, written = %d, __function__, n, nwrote); xil_printf(closing socket %d, sd); break; } } /* close connection */ close(sd); vtaskdelete(null); }
3. 测试网口发送数据 由2.4可知,在process_echo_request函数中更改发送即可。新增一个字符数组:
char tx_buf[16]={'h','e','l','l','o',',','w','o','r','l','d','',''}; 在发送完接收到的数据后,新增一个发送函数,即可发送tx_buf数组,长度为16:
write(sd, tx_buf, 16);
4. 测试网口接收数据并控制led 在向开发板发送数据时,规定一组特殊数据,如“led0”、“led5”、“led8”等,前3个字符“led”用于指示这部分数据是用于控制led的,第4个字符表示点亮组合,四个led使用二进制编码数据为0~15,注意,这里发送的是ascii字符,在控制led时需处理成数字(减 ’0’)。
接收到数据后,仿照函数中对quit字符串的处理方式,新增一个处理,将接收到的字符串与字符串“led”比较,如果收到的字符串的前3个字符是“led”,则使用第4个字符控制led的亮灭。
strncmp函数,字符串比较函数,字符串大小的比较以ascii 码表上的顺序来决定。函数声明为int strncmp ( const char * str1, const char * str2, size_t n ),把 str1 和 str2 进行比较,最多比较前 n 个字节,若str1与str2的前n个字符相同,则返回0;若s1大于s2,则返回大于0的值;若s1 小于s2,则返回小于0的值。
if (!strncmp(recv_buf, led, 3)) { xgpio_discretewrite(&gpio_led, 1, recv_buf[3]-'0'); xil_printf(led value = %d, recv_buf[3]-'0'); }
5. 测试led任务及消息队列 5.1 包含头文件,声明队列 #include freertos.h #include task.h #include queue.h #include timers.h queuehandle_t xqueue = null;  
5.2 在main函数中创建消息队列   传入两个参数,分别为队列长度和队列中每个元素的长度,xqueuecreate(1,1) 表示队列长度为1,队列中的每个元素时一个char类型数据,xqueuecreate(2,15) 表示队列长度为2,每个元素都是一个char[15]类型的字符数组。
xqueue = xqueuecreate(1,1); /* check the queue was created. 检查队列是否创建成功*/ configassert( xqueue );  
5.3 在main函数中创建led任务,接收队列消息 xtaskcreate( prvplledtask, ( const char * ) pl led, configminimal_stack_size, null, tskidle_priority + 1, null);   其中,调用的prvplledtask定义如下,每次从队列中读取一个char类型的数据,若队列为空则等待,若队列不为空则读出后控制led,注意这里的rece_led_value一定要加取地址符号&,表示传入指针,否则出错。
static void prvplledtask( void *pvparameters ) { const ticktype_t x1second = pdms_to_ticks( delay_1_second ); char rece_led_value; for( ;; ) { xil_printf( pl led task ); xqueuereceive( xqueue, /* the queue being read. */ &rece_led_value, /* data is read into this address. */ portmax_delay ); /* 延时 */ xil_printf( pl led task );    xil_printf( rece_led_value = %d, rece_led_value-'0' ); xgpio_discretewrite(&gpio_led, 1, rece_led_value-'0'); /* delay for 1 second. */ vtaskdelay( x1second ); } }  
5.4 在process_echo_request中添加发送队列消息 若满足条件,则将对led的控制信息写入队列,注意要加取地址符号&。
if (!strncmp(recv_buf, led, 3)) { xqueuesend( xqueue, &recv_buf[3], 0ul ); }
三、程序固化 1. 新建fsbl工程
2. 生成boot镜像文件 生成工程后,右键“create boot image”,依次添加fsbl工程的elf(默认已添加)、工程的bit文件(默认已添加)、需固化的程序elf(add找到路径添加),“create image”。
3. 烧录qspi flash   选择image和fsbl的路径,对flash,一定选择“qspi_dual_parallel”,若选择“qspi_single”也能下载成功,但是无法加载,zc706板载指示灯亮红灯。
4. 配置启动模式

云安全的新战场上,如何打破“云威胁”的阴霾?
ios10.3.2正式版怎么样?ios10.3.2也许是老设备的最后一个版本?你升级了吗
还不赶快投资捞一笔 2017不买比特币返贫,比特币暴涨23500人民币创历史新高
微机保护装置的特点说明
汽车传感器有哪些 智能传感器十大应用
ZC706千兆网测试(ZYNQ,FreeRTOS,Echo,lwIP,TCP,RGMII)
中国和意大利将在伊朗合作建设太阳能发电厂
fpga最小系统设计和原理图解析
NB-IoT网络商用还看智能抄表 智能抄表领头羊企业分析
手腕传感器的潜在应用包括哪些?
MSO5/6B系列示波器在智能汽车电源环路响应测试
ADI电源产品推陈出新 突破性能“老三篇
基于ArkUI request API实现下载进度获取及显示
苹果发布2019财年第四财季业绩,iPhone 11的定价策略很成功
RDAW263 高线性2.5-2.7 GHz WiMAX宽带
单片机跟plc哪个的区别是什么
关于开源Linux操作系统的veLinux深度解读
静止同步补偿器技术的简介和工作原理及其控制方式和应用现状
研华工控机内存条
中兴通讯PowerPilot节能方案,开启5G发展新航道