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个字节,若是连这些基本数据类型都没有设置正确的话,就谈不上移植了。下面看cc.h的源代码:
#ifndef __CC_H__ #define __CC_H__ typedef unsigned char u8_t; //基本数据类型设置 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 DEBUG extern 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_t stellarisif_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 void stellarisif_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); }
/** * 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_t stellarisif_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; }
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文件
...