TI Cortex M3串口轉乙太網路常式上層應用的基礎是lwIP,版本是V1.3.2 。對於lwIP,陌生的同學可以到網上查查,它是是瑞士的Adam編寫的一個開源TCP/IP協議。既然串口轉乙太網路常式的基礎是lwIP,那麼還是看看lwIp是如何移植到TI的Cortex M3硬體中的吧。此為分割線-------
移植概述可以參看部落格的這篇文章,以下基本按照這個格式來看看具體的移植代碼。http://blog.csdn.net/zhzht19861011/article/details/6615965
1.cc.h檔案
這個檔案主要設定lwIP內部使用的資料類型,比如u8_t、u32_t等。lwIP可以移植到32位、16位甚至是8位構架的微控制器,由於移植的硬體平台以及編譯器的不同,這些資料類型是要移植者根據自己的硬體和編譯器特性來自行設定的。比如int類型變數,在8位和16位控制器中多表示2位元組,但在32位微處理器中卻表示4個位元組,若是連這些基礎資料型別 (Elementary Data Type)都沒有設定正確的話,就談不上移植了。下面看cc.h的原始碼:
#ifndef __CC_H__#define __CC_H__typedef unsigned char u8_t; //基礎資料型別 (Elementary Data Type)設定typedef signed char s8_t;typedef unsigned short u16_t;typedef signed short s16_t;typedef unsigned long u32_t;typedef signed long s32_t;typedef u32_t mem_ptr_t;#ifndef BYTE_ORDER#define BYTE_ORDER LITTLE_ENDIAN#endif#if defined(__arm__) && defined(__ARMCC_VERSION) //以下主要設定不同編譯器的結構體資料的對齊,lwIP需要 // // Setup PACKing macros for KEIL/RVMDK Tools // #define PACK_STRUCT_BEGIN __packed #define PACK_STRUCT_STRUCT #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x#elif defined (__IAR_SYSTEMS_ICC__) // // Setup PACKing macros for IAR Tools // #define PACK_STRUCT_BEGIN #define PACK_STRUCT_STRUCT #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x #define PACK_STRUCT_USE_INCLUDES#else // // Setup PACKing macros for GCC Tools // #define PACK_STRUCT_BEGIN #define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x#endif#ifdef DEBUGextern void __error__(char *pcFilename, unsigned long ulLine);#define LWIP_PLATFORM_ASSERT(expr) \{ \ if(!(expr)) \ { \ __error__(__FILE__, __LINE__); \ } \}#else#define LWIP_PLATFORM_ASSERT(expr)#endif#endif /* __CC_H__ */
2.乙太網路硬體初始化、與硬體密切相關的資料接收、發送函數
雖然Adam為便於lwIP協議棧的移植做了大量的工作,但因為網卡的多樣性和新網卡的不斷出現,Adam不可能為每一個網卡都寫一個驅動。因此,與網卡硬體相關的代碼就留給程式員來編寫了。其實Adam在lwIP協議棧中已經寫好了一個與硬體密切相關的移植代碼架構,它位於lwIP-1.3.2/src/netif/ethernetif.c中。Stellaris串口轉乙太網路移植代碼也基本上是參照這個代碼架構來編寫的。Stellais串口轉乙太網路模組與硬體密切相關的移植代碼位於stellarisif.c中。這裡面的代碼主要是三部分:lwIP協議棧和乙太網路硬體初始化函數、lwIP協議棧將資料發送到網路介面上的輸出函數以及從Stellaris乙太網路硬體讀取資料並送給lwIP協議棧的輸入函數。
2.1 lwIP協議棧和乙太網路硬體初始化
在移植代碼stellarisif.c中,對lwIP協議棧和乙太網路硬體初始化的函數是:
err_t stellarisif_init(structnetif *netif)
這個函數先是設定與協議棧有關的底層操作,指定底層接收回呼函數等,接著對實際網路介面晶片進行初始化,設定硬體的工作方式,開放中斷等。原始碼如下所示:
/** * Should be called at the beginning of the program to set up the * network interface. It calls the function stellarisif_hwinit() to do the * actual setup of the hardware. * 此在程式開始的時候被調用,用來設定網路介面.他調用stellarisif_hwinit()函數 * 來完成乙太網路硬體的設定. * This function should be passed as a parameter to netif_add(). * 這個函數作為一個參數傳遞給netif_add()函數. * @param netif the lwip network interface structure for this ethernetif * @return ERR_OK if the loopif is initialized * ERR_MEM if private data couldn't be allocated * any other err_t on error */err_tstellarisif_init(struct netif *netif){ LWIP_ASSERT("netif != NULL", (netif != NULL));#if LWIP_NETIF_HOSTNAME /* Initialize interface hostname */ netif->hostname = "lwip"; //初始化介面主機名稱字#endif /* LWIP_NETIF_HOSTNAME */ /* * Initialize the snmp variables and counters inside the struct netif. * The last argument should be replaced with your link speed, in units * of bits per second. */ NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 1000000); //初始化snmp變數 netif->state = &stellarisif_data; //指向乙太網路介面的私人資料,包括pbuf資料鏈和MAC地址 netif->name[0] = IFNAME0; netif->name[1] = IFNAME1; /* We directly use etharp_output() here to save a function call. * You can instead declare your own function an call etharp_output() * from it if you have to do some checks before sending (e.g. if link * is available...) */ netif->output = etharp_output; //IP層將一包資料發往網路介面時調用此函數 netif->linkoutput = stellarisif_output; //ARP模組將一包資料發往網路介面時調用此函數 stellarisif_data.ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); //初始化MAC地址 stellarisif_data.txq.qread = stellarisif_data.txq.qwrite = 0; //初始化pbuf資料鏈 stellarisif_data.txq.overflow = 0; /* initialize the hardware */ stellarisif_hwinit(netif); //初始化Stellaris乙太網路硬體 return ERR_OK;}
1. netif->output = etharp_output;用於將一包資料發送到網路介面,由IP層調用。這個函數最終會調用netif->linkoutput來將資料發送到網路介面。
2. netif->linkoutput = stellarisif_output;用於將一包資料發送到網路介面,有ARP模組調用。程式員需根據自己的硬體平台來編寫該函數。後面會講到該函數。
3.stellarisif_hwinit(netif):初始化乙太網路硬體,還是有必要看看該函數的,代碼如下所示,不再單獨解釋,可以看注釋。
/** * In this function, the hardware should be initialized. * Called from stellarisif_init(). * * @param netif the already initialized lwip network interface structure * for this ethernetif */static voidstellarisif_hwinit(struct netif *netif){ u32_t temp; //struct stellarisif *stellarisif = netif->state; /* 設定乙太網路硬體MAC地址長度 */ netif->hwaddr_len = ETHARP_HWADDR_LEN; /* 設定乙太網路硬體地址 */ EthernetMACAddrGet(ETH_BASE, &(netif->hwaddr[0])); /* 最大發送單元 */ netif->mtu = 1500; /* 使能網卡的功能,允許網卡廣播、ARP功能和允許硬體鏈路串連該網卡*/ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; /* Do whatever else is needed to initialize interface. */ /* 禁止所有乙太網路中斷 */ EthernetIntDisable(ETH_BASE, (ETH_INT_PHY | ETH_INT_MDIO | ETH_INT_RXER | ETH_INT_RXOF | ETH_INT_TX | ETH_INT_TXER | ETH_INT_RX)); temp = EthernetIntStatus(ETH_BASE, false); EthernetIntClear(ETH_BASE, temp); /* 初始化乙太網路控制器 */ EthernetInitExpClk(ETH_BASE, SysCtlClockGet()); /* * 配置乙太網路控制器正常運行. * - 使能 TX 全雙工系統模式 * - 使能 TX 填充 * - 使能 TX CRC 產生 * - 使能 RX 組播接收 */ EthernetConfigSet(ETH_BASE, (ETH_CFG_TX_DPLXEN |ETH_CFG_TX_CRCEN | ETH_CFG_TX_PADEN | ETH_CFG_RX_AMULEN)); /* 使能乙太網路控制器的發送器和接收器 */ EthernetEnable(ETH_BASE); /* 使能乙太網路中斷 */ IntEnable(INT_ETH); /* 使能乙太網路TX和RX資料包中斷 */ EthernetIntEnable(ETH_BASE, ETH_INT_RX | ETH_INT_TX);}
2.2 Stellaris乙太網路硬體底層資料發送函數
上面也已經講到了,ARP模組發送資料到網路介面會調用netif->linkoutput函數;IP層發送資料到網路介面會調用netif->output函數,但IP層實際的資料發送函數任然是netif->linkoutput,在乙太網路初始化中,已經為linkoutput指標賦值:netif->linkoutput=stellarisif_output;因此,移植lwIP時程式員需要編寫的Stellaris乙太網路硬體底層資料發送函數正是stellarisif_output()函數。先來看以下它的源碼:
/** * This function with either place the packet into the Stellaris transmit fifo, * or will place the packet in the interface PBUF Queue for subsequent * transmission when the transmitter becomes idle. * 這個函數要麼將資料包放到Stellaris發送緩衝FIFO中,要麼把資料包放到PBUF隊列,等待 * 發送器變空之後載發送。 * * @param netif the lwip network interface structure for this ethernetif * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) * @return ERR_OK if the packet could be sent * an err_t value if the packet couldn't be sent * */static err_tstellarisif_output(struct netif *netif, struct pbuf *p){ struct stellarisif *stellarisif = netif->state; SYS_ARCH_DECL_PROTECT(lev); /** * This entire function must run within a "critical section" to preserve * the integrity of the transmit pbuf queue. * */ SYS_ARCH_PROTECT(lev); /** * Bump the reference count on the pbuf to prevent it from being * freed till we are done with it. * */ pbuf_ref(p); /** * If the transmitter is idle, and there is nothing on the queue, * send the pbuf now. * */ if(PBUF_QUEUE_EMPTY(&stellarisif->txq) && ((HWREG(ETH_BASE + MAC_O_TR) & MAC_TR_NEWTX) == 0)) { stellarisif_transmit(netif, p); } /* Otherwise place the pbuf on the transmit queue. */ else { /* Add to transmit packet queue */ if(!enqueue_packet(p, &(stellarisif->txq))) { /* if no room on the queue, free the pbuf reference and return error. */ pbuf_free(p); SYS_ARCH_UNPROTECT(lev); return (ERR_MEM); } } /* Return to prior interrupt state and return. */ SYS_ARCH_UNPROTECT(lev); return ERR_OK;}
1. SYS_ARCH_DECL_PROTECT(lev);、SYS_ARCH_PROTECT(lev);、SYS_ARCH_UNPROTECT(lev);由於Stellaris乙太網路在發送資料的時候必須處在臨界區,既不能被中斷打擾,因此,上面幾個宏是用來關閉和開啟總中斷的。SYS_ARCH_DECL_PROTECT(lev)用來定義一個32位變數lev,這個變數用來儲存當前中斷使能資訊;SYS_ARCH_PROTECT(lev)用來關閉中斷;SYS_ARCH_UNPROTECT(lev)用來開啟中斷。
2. pbuf_ref(p);將參數結構體pbuf的ref域加一。這個域統計有多少個指標指向這個pbuf,這些指標可能是應用程式的指標、協議棧的指標或者資料鏈中的pbuf->next指標,只有ref為0時,才可以釋放這個pbuf。關於結構體pbuf的詳細介紹參見部落格:http://blog.csdn.net/zhzht19861011/article/details/6591252
3. stellarisif_transmit(netif, p):發送資料。
2.3 Stellaris乙太網路硬體底層資料過程
當網路上一包資料到達乙太網路控制器後,乙太網路控制器會置為接收中斷,在中斷服務函數中,調用
stellarisif_receive函數(需程式員根據具體硬體自行編寫)來接收一個資料包,再通過
ethernet_input函數分析接收到的資料包的類型,比如類型為0x8000,則為IP幀,會調用ip_input
函數來將收到的這個IP資料包送往lwIP協議棧的高層。Stellaris乙太網路硬體具體中斷函數分析見部落格
:http://blog.csdn.net/zhzht19861011/article/details/6221699
3.lwipopts.h檔案
...