(1)使用ZC706開(kāi)發(fā)板測(cè)試PS端網(wǎng)口(Echo,lwIP協(xié)議棧);
(2)配合操作PL端LED(直接驅(qū)動(dòng)和使用消息隊(duì)列兩種方式);
(4)QSPI固化(Dual Quad SPI Parallel 8 bit模式)。
ZC706中,MAC 控制器與 PHY 通過(guò) RGMII(Reduced Gigabit Media Independent Interface)接口進(jìn)行連接,實(shí)現(xiàn)千兆網(wǎng)。
一、工程概述
1. 開(kāi)發(fā)板配置
使用Xilinx ZYNQ開(kāi)發(fā)板ZC706,默認(rèn)配置ARM后即可使用PS端網(wǎng)口、串口和QSPI,放置AXI GPIO的IP核驅(qū)動(dòng)PL端的4個(gè)LED。
ARM端配置如下圖所示,以5處的ARM-A9為核心,使用1處的UART1打印調(diào)試信息,使用2處的網(wǎng)口0進(jìn)行以太網(wǎng)通信,使用3處的AXI GP(General Port)Master通用主設(shè)備接口連接PL端的AXI GPIO,最后使用4處的QSPI固化程序,燒錄Boot文件。
2. SDK程序
上述工程綜合、布局布線并生成bit流后,導(dǎo)出硬件。
新建應(yīng)用工程Application Project,選擇 OS Platform 平臺(tái)為 freertos10_xilinx(Vivado及SDK版本2018.2,低版本的可能是freertos9_xilinx),選擇Next,選中“FreeRTOS lwIP Echo Server”。
新建完成后,即可進(jìn)行最基礎(chǔ)的網(wǎng)絡(luò)通信了。這里注意,默認(rèn)設(shè)置的是DCHP動(dòng)態(tài)主機(jī)配置協(xié)議,需要開(kāi)發(fā)板和電腦都連接到一個(gè)路由器上。如果直接使用網(wǎng)線連接開(kāi)發(fā)板和電腦,則啟用 IPv4 協(xié)議,默認(rèn)配置的IP地址為192.168.1.10,子網(wǎng)掩碼255.255.255.0,網(wǎng)關(guān)196.128.1.1,如果想要更改默認(rèn)配置,可以在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 是一個(gè)小型開(kāi)源的 TCP/IP 協(xié)議棧,支持IPv4、IPv6、TCP、UDP、DHCP等。
?IGMP 協(xié)議,用于網(wǎng)絡(luò)組管理,可以實(shí)現(xiàn)多播數(shù)據(jù)的接收
?Internet 協(xié)議(IP),包括 IPv4 和 IPv6,支持 IP 分片與重裝,包括通過(guò)多個(gè)網(wǎng)絡(luò)接口的數(shù)據(jù)包轉(zhuǎn)發(fā)
?用于網(wǎng)絡(luò)維護(hù)和調(diào)試的 Internet 控制消息協(xié)議(ICMP)
?用戶數(shù)據(jù)報(bào)協(xié)議(UDP)
?傳輸控制協(xié)議(TCP)擁塞控制,往返時(shí)間(RTT)估計(jì),快速恢復(fù)和重傳
?DNS,域名解析
?SNMP,簡(jiǎn)單網(wǎng)絡(luò)管理協(xié)議
?動(dòng)態(tài)主機(jī)配置協(xié)議(DHCP)
?以太網(wǎng)地址解析協(xié)議(ARP)
?AUTOIP,IP 地址自動(dòng)配置
?PPP,點(diǎn)對(duì)點(diǎn)協(xié)議,支持
3. 網(wǎng)絡(luò)設(shè)置
使用網(wǎng)線直接連接ZC706開(kāi)發(fā)板和計(jì)算機(jī)網(wǎng)口,配置計(jì)算機(jī)IP地址為192.168.1.11,子網(wǎng)掩碼255.255.255.0,網(wǎng)關(guān)192.168.1.1,其中IP地址的最后一處可以更改為其他值,但是不能和開(kāi)發(fā)板的相同。
4. 開(kāi)啟監(jiān)聽(tīng)測(cè)試
使用SecureCRT軟件監(jiān)聽(tīng),除此之外,使用其他網(wǎng)口助手也可以。
二、工程測(cè)試
1. 測(cè)試Echo官方例程
先打開(kāi)串口,波特率115200,下載官方例程到ZC706開(kāi)發(fā)板,連接SecureCRT_CN,初始化工程中串口打印信息如下:配置DCHP動(dòng)態(tài)主機(jī)協(xié)議超時(shí),自動(dòng)轉(zhuǎn)為IPv4,將板子的IP地址配置為192.168.1.10,子網(wǎng)掩碼255.255.255.0,網(wǎng)關(guān)192.168.1.1,使用端口7。
在SecureCRT_CN界面輸入字符或字符串,回車(chē),通過(guò)網(wǎng)口向開(kāi)發(fā)板發(fā)送數(shù)據(jù),開(kāi)發(fā)板會(huì)返回同樣的數(shù)據(jù),測(cè)試正確。
2. 分析源碼
2.1 main函數(shù)
打開(kāi)main.c文件,找到main()函數(shù)。在main函數(shù)中創(chuàng)建了一個(gè)線程,傳入的參數(shù)依次為線程名(調(diào)試用)、函數(shù)指針、函數(shù)需要的參數(shù)、需要的堆棧大小、優(yōu)先級(jí)。
按照如下配置,調(diào)用了main_thread函數(shù),不需要傳參(用0或NULL),堆棧大小由#define定義為1024,優(yōu)先級(jí)為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函數(shù)
此函數(shù)中實(shí)現(xiàn)的功能如下:
(1)初始化lwip協(xié)議棧;lwip_init();
(2)調(diào)用network_thread()創(chuàng)建線程;
(3)調(diào)用echo_application_thread()創(chuàng)建線程;
每500ms檢測(cè)一次DHCP是否成功,若成功則創(chuàng)建echo應(yīng)用線程,如果10秒還沒(méi)有成功,則啟用IPv4,配置IP地址、子網(wǎng)掩碼和網(wǎng)關(guān)后,創(chuàng)建echo應(yīng)用程序;創(chuàng)建成功后退出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函數(shù)
位置:echo.c文件。
(1)創(chuàng)建socket,綁定端口,監(jiān)聽(tīng);
(2)調(diào)用process_echo_request函數(shù)創(chuàng)建線程;
此函數(shù)需要傳入?yún)?shù)。
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函數(shù)
位置:echo.c文件,用戶需要注意的最重要的函數(shù),發(fā)送和接收的移植全部在這個(gè)函數(shù)。
(1)接收數(shù)據(jù),最大數(shù)據(jù)長(zhǎng)度2048,char 類(lèi)型,存儲(chǔ)在recv_buff 數(shù)組中,若接收出錯(cuò),打印錯(cuò)誤信息并退出while;
(2)若接收到的數(shù)據(jù)的前4個(gè)字符為quit,則退出while;
(3)將接收到的數(shù)據(jù)發(fā)送出去;
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. 測(cè)試網(wǎng)口發(fā)送數(shù)據(jù)
由2.4可知,在process_echo_request函數(shù)中更改發(fā)送即可。新增一個(gè)字符數(shù)組:
char tx_buf[16]={‘H’,‘e’,‘l’,‘l’,‘o’,‘,’,‘W’,‘o’,‘r’,‘l’,‘d’,‘
’,‘
’};
在發(fā)送完接收到的數(shù)據(jù)后,新增一個(gè)發(fā)送函數(shù),即可發(fā)送tx_buf數(shù)組,長(zhǎng)度為16:
write(sd, tx_buf, 16);
4. 測(cè)試網(wǎng)口接收數(shù)據(jù)并控制LED
在向開(kāi)發(fā)板發(fā)送數(shù)據(jù)時(shí),規(guī)定一組特殊數(shù)據(jù),如“l(fā)ed0”、“l(fā)ed5”、“l(fā)ed8”等,前3個(gè)字符“l(fā)ed”用于指示這部分?jǐn)?shù)據(jù)是用于控制LED的,第4個(gè)字符表示點(diǎn)亮組合,四個(gè)LED使用二進(jìn)制編碼數(shù)據(jù)為0~15,注意,這里發(fā)送的是ASCII字符,在控制LED時(shí)需處理成數(shù)字(減 ’0’)。
接收到數(shù)據(jù)后,仿照函數(shù)中對(duì)quit字符串的處理方式,新增一個(gè)處理,將接收到的字符串與字符串“l(fā)ed”比較,如果收到的字符串的前3個(gè)字符是“l(fā)ed”,則使用第4個(gè)字符控制LED的亮滅。
strncmp函數(shù),字符串比較函數(shù),字符串大小的比較以ASCII 碼表上的順序來(lái)決定。函數(shù)聲明為int strncmp ( const char * str1, const char * str2, size_t n ),把 str1 和 str2 進(jìn)行比較,最多比較前 n 個(gè)字節(jié),若str1與str2的前n個(gè)字符相同,則返回0;若s1大于s2,則返回大于0的值;若s1 小于s2,則返回小于0的值。
if (!strncmp(recv_buf, “l(fā)ed”, 3)) { XGpio_DiscreteWrite(&Gpio_Led, 1, recv_buf[3]-‘0’); xil_printf(“Led Value = %d
”, recv_buf[3]-‘0’); }
5. 測(cè)試LED任務(wù)及消息隊(duì)列
5.1 包含頭文件,聲明隊(duì)列
#include “FreeRTOS.h” #include “task.h” #include “queue.h” #include “timers.h” QueueHandle_t xQueue = NULL;
5.2 在main函數(shù)中創(chuàng)建消息隊(duì)列
傳入兩個(gè)參數(shù),分別為隊(duì)列長(zhǎng)度和隊(duì)列中每個(gè)元素的長(zhǎng)度,xQueueCreate(1,1) 表示隊(duì)列長(zhǎng)度為1,隊(duì)列中的每個(gè)元素時(shí)一個(gè)char類(lèi)型數(shù)據(jù),xQueueCreate(2,15) 表示隊(duì)列長(zhǎng)度為2,每個(gè)元素都是一個(gè)char[15]類(lèi)型的字符數(shù)組。
xQueue = xQueueCreate(1,1); /* Check the queue was created. 檢查隊(duì)列是否創(chuàng)建成功*/ configASSERT( xQueue );
5.3 在main函數(shù)中創(chuàng)建LED任務(wù),接收隊(duì)列消息
xTaskCreate( prvPlLedTask, ( const char * ) “PL Led”, configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL);
其中,調(diào)用的prvPlLedTask定義如下,每次從隊(duì)列中讀取一個(gè)char類(lèi)型的數(shù)據(jù),若隊(duì)列為空則等待,若隊(duì)列不為空則讀出后控制LED,注意這里的rece_led_value一定要加取地址符號(hào)&,表示傳入指針,否則出錯(cuò)。
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 ); /* 延時(shí) */
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中添加發(fā)送隊(duì)列消息
若滿足條件,則將對(duì)LED的控制信息寫(xiě)入隊(duì)列,注意要加取地址符號(hào)&。
if (!strncmp(recv_buf, “l(fā)ed”, 3)) { xQueueSend( xQueue, &recv_buf[3], 0UL ); }
三、程序固化
1. 新建FSBL工程
2. 生成Boot鏡像文件
生成工程后,右鍵“Create Boot Image”,依次添加FSBL工程的elf(默認(rèn)已添加)、工程的bit文件(默認(rèn)已添加)、需固化的程序elf(Add找到路徑添加),“Create Image”。
3. 燒錄QSPI Flash
選擇Image和FSBL的路徑,對(duì)Flash,一定選擇“qspi_dual_parallel”,若選擇“qspi_single”也能下載成功,但是無(wú)法加載,ZC706板載指示燈亮紅燈。
4. 配置啟動(dòng)模式
原文標(biāo)題:ZC706千兆網(wǎng)測(cè)試(ZYNQ,F(xiàn)reeRTOS,Echo,lwIP,TCP,RGMII)
文章出處:【微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
責(zé)任編輯:haq
-
網(wǎng)絡(luò)
+關(guān)注
關(guān)注
14文章
7485瀏覽量
88543 -
開(kāi)發(fā)板
+關(guān)注
關(guān)注
25文章
4898瀏覽量
97061
原文標(biāo)題:ZC706千兆網(wǎng)測(cè)試(ZYNQ,F(xiàn)reeRTOS,Echo,lwIP,TCP,RGMII)
文章出處:【微信號(hào):zhuyandz,微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論