前面小玩了一下ucos和lwip,但是都还不是真正的网络多任务,真正的网络多任务应该是什么样子的呢?应该是有一个专门的任务负责网络的通讯,他负责将数据发送出去,将数据接收回来,而其他的需要用到网络的任务与这个任务通讯,这才是能够极大提升网络效率的办法,lwip支持这种办法,不过移植起来就比较磨人了
首先第一步是修改lwip的配置文件,如下
#define NO_SYS 0 //使用UCOS操作系统
改了这个之后会发现卧槽一下子缺了好多东西,实际上是这样,lwip使用一系列的宏和方法封装了一个操作系统应该具备的属性,比如任务的创建,删除啦,任务的延时啦,邮箱信号量啦等等,所以其实也没那么困难,首先我们需要修改sys_arch.h文件
#ifndef __SYS_RTXC_H__ #define __SYS_RTXC_H__ #include <includes.h> #include "arch/cc.h" #include "includes.h" #include "malloc.h" #ifdef SYS_ARCH_GLOBALS #define SYS_ARCH_EXT #else #define SYS_ARCH_EXT extern #endif #define MAX_QUEUES 10 // 消息邮箱的数量 #define MAX_QUEUE_ENTRIES 20 // 每个消息邮箱的大小 //LWIP消息邮箱结构体 typedef struct { OS_EVENT* pQ; //UCOS中指向事件控制块的指针 void* pvQEntries[MAX_QUEUE_ENTRIES];//消息队列 MAX_QUEUE_ENTRIES消息队列中最多消息数 } TQ_DESCR, *PQ_DESCR; typedef OS_EVENT *sys_sem_t; //LWIP使用的信号量 typedef OS_EVENT *sys_mutex_t; //LWIP使用的互斥信号量 typedef PQ_DESCR sys_mbox_t; //LWIP使用的消息邮箱,其实就是UCOS中的消息队列 typedef INT8U sys_thread_t; //线程ID,也就是任务优先级 #endif /* __SYS_RTXC_H__ */
从这里的宏定义可以验证之前说的话了吧,但是这只是定义了数据类型,还有一些方法需要定义,列出了一个表如下所示
这些函数都是需要实现的,实现的代码如下,都有注释,看注释应该明白了
/* lwIP includes. */ #include "lwip/debug.h" #include "lwip/def.h" #include "lwip/sys.h" #include "lwip/mem.h" #include "includes.h" #include "delay.h" #include "arch/sys_arch.h" #include "malloc.h" //当消息指针为空时,指向一个常量pvNullPointer所指向的值. //在UCOS中如果OSQPost()中的msg==NULL会返回一条OS_ERR_POST_NULL //错误,而在lwip中会调用sys_mbox_post(mbox,NULL)发送一条空消息,我们 //在本函数中把NULL变成一个常量指针0Xffffffff const void * const pvNullPointer = (mem_ptr_t*)0xffffffff; //创建一个消息邮箱 //*mbox:消息邮箱 //size:邮箱大小 //返回值:ERR_OK,创建成功 // 其他,创建失败 err_t sys_mbox_new( sys_mbox_t *mbox, int size) { (*mbox)=malloc(sizeof(TQ_DESCR)); //为消息邮箱申请内存 mymemset((*mbox),0,sizeof(TQ_DESCR)); //清除mbox的内存 if(*mbox) //内存分配成功 { if(size>MAX_QUEUE_ENTRIES)size=MAX_QUEUE_ENTRIES; //消息队列最多容纳MAX_QUEUE_ENTRIES消息数目 (*mbox)->pQ=OSQCreate(&((*mbox)->pvQEntries[0]),size); //使用UCOS创建一个消息队列 LWIP_ASSERT("OSQCreate",(*mbox)->pQ!=NULL); if((*mbox)->pQ!=NULL)return ERR_OK; //返回ERR_OK,表示消息队列创建成功 ERR_OK=0 else { free((*mbox)); return ERR_MEM; //消息队列创建错误 } }else return ERR_MEM; //消息队列创建错误 } //释放并删除一个消息邮箱 //*mbox:要删除的消息邮箱 void sys_mbox_free(sys_mbox_t * mbox) { u8_t ucErr; sys_mbox_t m_box=*mbox; (void)OSQDel(m_box->pQ,OS_DEL_ALWAYS,&ucErr); LWIP_ASSERT( "OSQDel ",ucErr == OS_ERR_NONE ); free(m_box); *mbox=NULL; } //向消息邮箱中发送一条消息(必须发送成功) //*mbox:消息邮箱 //*msg:要发送的消息 void sys_mbox_post(sys_mbox_t *mbox,void *msg) { if(msg==NULL)msg=(void*)&pvNullPointer; //当msg为空时 msg等于pvNullPointer指向的值 while(OSQPost((*mbox)->pQ,msg)!=OS_ERR_NONE); //死循环等待消息发送成功 } //尝试向一个消息邮箱发送消息 //此函数相对于sys_mbox_post函数只发送一次消息, //发送失败后不会尝试第二次发送 //*mbox:消息邮箱 //*msg:要发送的消息 //返回值:ERR_OK,发送OK // ERR_MEM,发送失败 err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) { if(msg==NULL)msg=(void*)&pvNullPointer;//当msg为空时 msg等于pvNullPointer指向的值 if((OSQPost((*mbox)->pQ, msg))!=OS_ERR_NONE)return ERR_MEM; return ERR_OK; } //等待邮箱中的消息 //*mbox:消息邮箱 //*msg:消息 //timeout:超时时间,如果timeout为0的话,就一直等待 //返回值:当timeout不为0时如果成功的话就返回等待的时间, // 失败的话就返回超时SYS_ARCH_TIMEOUT u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) { u8_t ucErr; u32_t ucos_timeout,timeout_new; void *temp; sys_mbox_t m_box=*mbox; if(timeout!=0) { ucos_timeout=(timeout*OS_TICKS_PER_SEC)/1000; //转换为节拍数,因为UCOS延时使用的是节拍数,而LWIP是用ms if(ucos_timeout<1)ucos_timeout=1;//至少1个节拍 }else ucos_timeout = 0; timeout = OSTimeGet(); //获取系统时间 temp=OSQPend(m_box->pQ,(u16_t)ucos_timeout,&ucErr); //请求消息队列,等待时限为ucos_timeout if(msg!=NULL) { if(temp==(void*)&pvNullPointer)*msg = NULL; //因为lwip发送空消息的时候我们使用了pvNullPointer指针,所以判断pvNullPointer指向的值 else *msg=temp; //就可知道请求到的消息是否有效 } if(ucErr==OS_ERR_TIMEOUT)timeout=SYS_ARCH_TIMEOUT; //请求超时 else { LWIP_ASSERT("OSQPend ",ucErr==OS_ERR_NONE); timeout_new=OSTimeGet(); if (timeout_new>timeout) timeout_new = timeout_new - timeout;//算出请求消息或使用的时间 else timeout_new = 0xffffffff - timeout + timeout_new; timeout=timeout_new*1000/OS_TICKS_PER_SEC + 1; } return timeout; } //尝试获取消息 //*mbox:消息邮箱 //*msg:消息 //返回值:等待消息所用的时间/SYS_ARCH_TIMEOUT u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) { return sys_arch_mbox_fetch(mbox,msg,1);//尝试获取一个消息 } //检查一个消息邮箱是否有效 //*mbox:消息邮箱 //返回值:1,有效. // 0,无效 int sys_mbox_valid(sys_mbox_t *mbox) { sys_mbox_t m_box=*mbox; u8_t ucErr; int ret; OS_Q_DATA q_data; memset(&q_data,0,sizeof(OS_Q_DATA)); ucErr=OSQQuery (m_box->pQ,&q_data); ret=(ucErr<2&&(q_data.OSNMsgs<q_data.OSQSize))?1:0; return ret; } //设置一个消息邮箱为无效 //*mbox:消息邮箱 void sys_mbox_set_invalid(sys_mbox_t *mbox) { *mbox=NULL; } //创建一个信号量 //*sem:创建的信号量 //count:信号量值 //返回值:ERR_OK,创建OK // ERR_MEM,创建失败 err_t sys_sem_new(sys_sem_t * sem, u8_t count) { u8_t err; *sem=OSSemCreate((u16_t)count); if(*sem==NULL)return ERR_MEM; OSEventNameSet(*sem,"LWIP Sem",&err); LWIP_ASSERT("OSSemCreate ",*sem != NULL ); return ERR_OK; } //等待一个信号量 //*sem:要等待的信号量 //timeout:超时时间 //返回值:当timeout不为0时如果成功的话就返回等待的时间, // 失败的话就返回超时SYS_ARCH_TIMEOUT u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) { u8_t ucErr; u32_t ucos_timeout, timeout_new; if( timeout!=0) { ucos_timeout = (timeout * OS_TICKS_PER_SEC) / 1000;//转换为节拍数,因为UCOS延时使用的是节拍数,而LWIP是用ms if(ucos_timeout < 1) ucos_timeout = 1; }else ucos_timeout = 0; timeout = OSTimeGet(); OSSemPend (*sem,(u16_t)ucos_timeout, (u8_t *)&ucErr); if(ucErr == OS_ERR_TIMEOUT)timeout=SYS_ARCH_TIMEOUT;//请求超时 else { timeout_new = OSTimeGet(); if (timeout_new>=timeout) timeout_new = timeout_new - timeout; else timeout_new = 0xffffffff - timeout + timeout_new; timeout = (timeout_new*1000/OS_TICKS_PER_SEC + 1);//算出请求消息或使用的时间(ms) } return timeout; } //发送一个信号量 //sem:信号量指针 void sys_sem_signal(sys_sem_t *sem) { OSSemPost(*sem); } //释放并删除一个信号量 //sem:信号量指针 void sys_sem_free(sys_sem_t *sem) { u8_t ucErr; (void)OSSemDel(*sem,OS_DEL_ALWAYS,&ucErr ); if(ucErr!=OS_ERR_NONE)LWIP_ASSERT("OSSemDel ",ucErr==OS_ERR_NONE); *sem = NULL; } //查询一个信号量的状态,无效或有效 //sem:信号量指针 //返回值:1,有效. // 0,无效 int sys_sem_valid(sys_sem_t *sem) { OS_SEM_DATA sem_data; return (OSSemQuery (*sem,&sem_data) == OS_ERR_NONE )? 1:0; } //设置一个信号量无效 //sem:信号量指针 void sys_sem_set_invalid(sys_sem_t *sem) { *sem=NULL; } //arch初始化 void sys_init(void) { //这里,我们在该函数,不做任何事情 } extern OS_STK * TCPIP_THREAD_TASK_STK;//TCP IP内核任务堆栈,在lwip_comm函数定义 //创建一个新进程 //*name:进程名称 //thred:进程任务函数 //*arg:进程任务函数的参数 //stacksize:进程任务的堆栈大小 //prio:进程任务的优先级 sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio) { OS_CPU_SR cpu_sr; if(strcmp(name,TCPIP_THREAD_NAME)==0)//创建TCP IP内核任务 { OS_ENTER_CRITICAL(); //进入临界区 OSTaskCreate(thread,arg,(OS_STK*)&TCPIP_THREAD_TASK_STK[stacksize-1],prio);//创建TCP IP内核任务 OS_EXIT_CRITICAL(); //退出临界区 } return 0; } //lwip延时函数 //ms:要延时的ms数 void sys_msleep(u32_t ms) { OSTimeDly(ms/5); } //获取系统时间,LWIP1.4.1增加的函数 //返回值:当前系统时间(单位:毫秒) u32_t sys_now(void) { u32_t ucos_time, lwip_time; ucos_time=OSTimeGet(); //获取当前系统时间 得到的是UCSO的节拍数 lwip_time=(ucos_time*1000/OS_TICKS_PER_SEC+1);//将节拍数转换为LWIP的时间MS return lwip_time; //返回lwip_time; }
好的,这些函数有了,但是我们再想想,内核需要创建一个专用于网络通讯的任务,那么这个任务的优先级,使用的数据缓冲区都需要定义,消息邮箱,信号量的数量限制也需要定义,不然这个任务会乱来,所以接下来就要弄一下配置文件了,如下
//线程优先级 #ifndef TCPIP_THREAD_PRIO #define TCPIP_THREAD_PRIO 40 //定义内核任务的优先级为5 #endif #undef DEFAULT_THREAD_PRIO #define DEFAULT_THREAD_PRIO 38 #define SYS_LIGHTWEIGHT_PROT 1 //为1时使用实时操作系统的轻量级保护,保护关键代码不被中断打断 #define NO_SYS 0 //使用UCOS操作系统 #define MEM_ALIGNMENT 4 //使用4字节对齐模式 #define MEM_SIZE 16000 //内存堆heap大小 #define MEMP_NUM_PBUF 20 //MEMP_NUM_PBUF:memp结构的pbuf数量,如果应用从ROM或者静态存储区发送大量数据时,这个值应该设置大一点 #define MEMP_NUM_UDP_PCB 6 //MEMP_NUM_UDP_PCB:UDP协议控制块(PCB)数量.每个活动的UDP"连接"需要一个PCB. #define MEMP_NUM_TCP_PCB 10 //MEMP_NUM_TCP_PCB:同时建立激活的TCP数量 #define MEMP_NUM_TCP_PCB_LISTEN 6 //MEMP_NUM_TCP_PCB_LISTEN:能够监听的TCP连接数量 #define MEMP_NUM_TCP_SEG 15 //MEMP_NUM_TCP_SEG:最多同时在队列中的TCP段数量 #define MEMP_NUM_SYS_TIMEOUT 8 //MEMP_NUM_SYS_TIMEOUT:能够同时激活的timeout个数 //pbuf选项 #define PBUF_POOL_SIZE 20 //PBUF_POOL_SIZE:pbuf内存池个数 #define PBUF_POOL_BUFSIZE 512 //PBUF_POOL_BUFSIZE:每个pbuf内存池大小 #define LWIP_TCP 1 //使用TCP #define TCP_TTL 255 //生存时间 #undef TCP_QUEUE_OOSEQ #define TCP_QUEUE_OOSEQ 0 //当TCP的数据段超出队列时的控制位,当设备的内存过小的时候此项应为0 #undef TCPIP_MBOX_SIZE #define TCPIP_MBOX_SIZE MAX_QUEUE_ENTRIES //tcpip创建主线程时的消息邮箱大小 #undef DEFAULT_TCP_RECVMBOX_SIZE #define DEFAULT_TCP_RECVMBOX_SIZE MAX_QUEUE_ENTRIES //多任务的receive邮箱 #undef DEFAULT_ACCEPTMBOX_SIZE #define DEFAULT_ACCEPTMBOX_SIZE MAX_QUEUE_ENTRIES //多任务的accept邮箱 #define TCP_MSS (1500 - 40) //最大TCP分段,TCP_MSS = (MTU - IP报头大小 - TCP报头大小 #define TCP_SND_BUF (4*TCP_MSS) //TCP发送缓冲区大小(bytes). #define TCP_SND_QUEUELEN (2* TCP_SND_BUF/TCP_MSS) //TCP_SND_QUEUELEN: TCP发送缓冲区大小(pbuf).这个值最小为(2 * TCP_SND_BUF/TCP_MSS) #define TCP_WND (2*TCP_MSS) //TCP发送窗口 #define LWIP_ICMP 1 //使用ICMP协议 #define LWIP_DHCP 1 //使用DHCP,注意这个定义,在初始化网卡的时候有用 #define LWIP_UDP 1 //使用UDP服务 #define UDP_TTL 255 //UDP数据包生存时间 #define LWIP_STATS 0 #define LWIP_PROVIDE_ERRNO 1 //帧校验和选项,STM32F4x7允许通过硬件识别和计算IP,UDP和ICMP的帧校验和 //别的芯片不使用该功能,具体看平台 //#define CHECKSUM_BY_HARDWARE //定义CHECKSUM_BY_HARDWARE,使用硬件帧校验 #ifdef CHECKSUM_BY_HARDWARE //CHECKSUM_GEN_IP==0: 硬件生成IP数据包的帧校验和 #define CHECKSUM_GEN_IP 0 //CHECKSUM_GEN_UDP==0: 硬件生成UDP数据包的帧校验和 #define CHECKSUM_GEN_UDP 0 //CHECKSUM_GEN_TCP==0: 硬件生成TCP数据包的帧校验和 #define CHECKSUM_GEN_TCP 0 //CHECKSUM_CHECK_IP==0: 硬件检查输入的IP数据包帧校验和 #define CHECKSUM_CHECK_IP 0 //CHECKSUM_CHECK_UDP==0: 硬件检查输入的UDP数据包帧校验和 #define CHECKSUM_CHECK_UDP 0 //CHECKSUM_CHECK_TCP==0: 硬件检查输入的TCP数据包帧校验和 #define CHECKSUM_CHECK_TCP 0 #else //CHECKSUM_GEN_IP==1: 软件生成IP数据包帧校验和 #define CHECKSUM_GEN_IP 1 // CHECKSUM_GEN_UDP==1: 软件生成UDOP数据包帧校验和 #define CHECKSUM_GEN_UDP 1 //CHECKSUM_GEN_TCP==1: 软件生成TCP数据包帧校验和 #define CHECKSUM_GEN_TCP 1 // CHECKSUM_CHECK_IP==1: 软件检查输入的IP数据包帧校验和 #define CHECKSUM_CHECK_IP 1 // CHECKSUM_CHECK_UDP==1: 软件检查输入的UDP数据包帧校验和 #define CHECKSUM_CHECK_UDP 1 //CHECKSUM_CHECK_TCP==1: 软件检查输入的TCP数据包帧校验和 #define CHECKSUM_CHECK_TCP 1 #endif #define LWIP_NETCONN 1 //LWIP_NETCONN==1:使能NETCON函数(要求使用api_lib.c) #define LWIP_SOCKET 1 //LWIP_SOCKET==1:使能Sicket API(要求使用sockets.c) #define LWIP_COMPAT_MUTEX 1 #define LWIP_SO_RCVTIMEO 1 //通过定义LWIP_SO_RCVTIMEO使能netconn结构体中recv_timeout,使用recv_timeout可以避免阻塞线程 //有关系统的选项 #define TCPIP_THREAD_STACKSIZE 1000 //内核任务堆栈大小 #define DEFAULT_UDP_RECVMBOX_SIZE 2000 #define DEFAULT_THREAD_STACKSIZE 512 //LWIP调试选项 #define LWIP_DEBUG 0 //关闭DEBUG选项 #define ICMP_DEBUG LWIP_DBG_OFF //开启/关闭ICMPdebug #define ETHARP_SUPPORT_STATIC_ENTRIES 1 #endif /* __LWIPOPTS_H__ */
要注意的一点是我们使用的网卡是enc28j60,这个网卡不带硬件校验功能,所以需要选择软件校验,要是你使用的网卡带硬件校验,那么可以定义硬件校验,还能提升程序运行效率
又好像掉了点东西,临界段好像没有设置,没有临界段程序乱跑怎么办?所以在cc.h中需要定义临界段,如下
// cc.h属于LWIP TCP/IP协议栈一部分 // 作者: Adam Dunkels <[email protected]> //移植的时候该文件很重要 #ifndef __CC_H__ #define __CC_H__ #include "cpu.h" #include "stdio.h" #include "includes.h" //使用UCOS 要添加此头文件! //定义与平台无关的数据类型 typedef unsigned char u8_t; //无符号8位整数 typedef signed char s8_t; //有符号8位整数 typedef unsigned short u16_t; //无符号16位整数 typedef signed short s16_t; //有符号16位整数 typedef unsigned long u32_t; //无符号32位整数 typedef signed long s32_t; //有符号32位整数 typedef u32_t mem_ptr_t; //内存地址型数据 typedef int sys_prot_t; //临界保护型数据 //使用操作系统时的临界区保护,这里以UCOS II为例 //当定义了OS_CRITICAL_METHOD时就说明使用了UCOS II #if OS_CRITICAL_METHOD == 1 #define SYS_ARCH_DECL_PROTECT(lev) #define SYS_ARCH_PROTECT(lev) CPU_INT_DIS() #define SYS_ARCH_UNPROTECT(lev) CPU_INT_EN() #endif #if OS_CRITICAL_METHOD == 3 #define SYS_ARCH_DECL_PROTECT(lev) u32_t lev #define SYS_ARCH_PROTECT(lev) lev = OS_CPU_SR_Save() //UCOS II中进入临界区,关中断 #define SYS_ARCH_UNPROTECT(lev) OS_CPU_SR_Restore(lev) //UCOS II中退出A临界区,开中断 #endif //根据不同的编译器定义一些符号 #if defined (__ICCARM__) #define PACK_STRUCT_BEGIN #define PACK_STRUCT_STRUCT #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x #define PACK_STRUCT_USE_INCLUDES #elif defined (__CC_ARM) #define PACK_STRUCT_BEGIN __packed #define PACK_STRUCT_STRUCT #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x #elif defined (__GNUC__) #define PACK_STRUCT_BEGIN #define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x #elif defined (__TASKING__) #define PACK_STRUCT_BEGIN #define PACK_STRUCT_STRUCT #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x #endif //LWIP用printf调试时使用到的一些类型 #define U16_F "4d" #define S16_F "4d" #define X16_F "4x" #define U32_F "8ld" #define S32_F "8ld" #define X32_F "8lx" //宏定义 ,参数实时检查 #ifndef LWIP_PLATFORM_ASSERT #define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\r\n", x, __LINE__, __FILE__); } while(0) #endif #ifndef LWIP_PLATFORM_DIAG #define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0) #endif #endif /* __CC_H__ */
到这里移植基本上就弄完了,接下来就是使用了,通过前面的描述,这次协议栈初始化的时候必然会新建一个任务,也就是说,要在osinit系统初始化之后再初始化协议栈,如下
#include "mainInclude.h" //net 任务 //设置任务优先级 #define NET_TASK_PRIO 30 //开始任务的优先级设置为最低 //设置任务堆栈大小 #define NET_STK_SIZE 64 //创建任务堆栈空间 OS_STK NET_TASK_STK[NET_STK_SIZE]; //任务函数接口 void net_task(void *pdata) { while(1) { OSTimeDly(2);//延时2个时钟节拍 lwip_pkt_handle(); }; } //START 任务 //设置任务优先级 #define START_TASK_PRIO 10 //开始任务的优先级设置为最低 //设置任务堆栈大小 #define START_STK_SIZE 64 //创建任务堆栈空间 OS_STK START_TASK_STK[START_STK_SIZE]; //任务函数接口 void start_task(void *pdata) { OS_CPU_SR cpu_sr=0; pdata = pdata; OSStatInit(); //初始化统计任务.这里会延时1秒钟左右 OS_ENTER_CRITICAL(); //进入临界区(无法被中断打断) OSTaskCreate(led1_task,(void *)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO); OSTaskCreate(led2_task,(void *)0,(OS_STK*)&LED2_TASK_STK[LED2_STK_SIZE-1],LED2_TASK_PRIO); lwip_comm_dhcp_creat(); //创建DHCP任务 OSTaskCreate(net_task,(void *)0,(OS_STK*)&NET_TASK_STK[NET_STK_SIZE-1],NET_TASK_PRIO); OSTaskSuspend(START_TASK_PRIO); //挂起起始任务. OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断) } int main(void) { NVIC_Group_Init();//系统默认中断分组 Debug_Serial_Init(115200); Delay_Init(); Led_Init(); LCD_Init(); Beep_Init(); Key_Exti_Init(); while(SDRAM_Init()) { printf("sdram init failed\r\n"); Delay_Ms(1000); } MallocInit(); TIM6_Int_Init(1000,(72*5)-1); OSInit(); while(lwip_comm_init()) //lwip初始化 { LCD_ShowString(0,0,240,320,(u8*)"Lwip Init failed ! ",LCD_BLACK); //lwip初始化失败 Delay_Ms(200); LCD_ShowString(0,0,240,320,(u8*)" ",LCD_BLACK); //lwip初始化失败 Delay_Ms(200); } LCD_ShowString(0,0,240,320,(u8*)"Lwip Init success ! ",LCD_BLACK); //lwip初始化失败 OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务 OSStart(); }
这里面混进来了一个nettask,这个任务是干嘛的呢,理论上说,为了cpu效率的最大化,网卡最好是使用中断,在中断中获取数据,但是不好意思,enc28j60我没用中断,数据不能通过中断得到,没办法,只能创建一个任务来循环获取数据了,实现如下
//任务函数接口 void net_task(void *pdata) { while(1) { OSTimeDly(2);//延时2个时钟节拍 lwip_pkt_handle(); }; }
//定时的从网卡中获取数据 void lwip_pkt_handle(void) { if(ENC28J60_Read(EPKTCNT) !=0) { ethernetif_input(&lwip_netif); } }
这个时候我们用了一个lwip_comminit的函数,这个函数的实现如下
//LWIP初始化(LWIP启动的时候使用) //返回值:0,成功 // 1,内存错误 // 2,LAN8720初始化失败 // 3,网卡添加失败. u8 lwip_comm_init(void) { OS_CPU_SR cpu_sr; struct netif *Netif_Init_Flag; //调用netif_add()函数时的返回值,用于判断网络初始化是否成功 struct ip_addr ipaddr; //ip地址 struct ip_addr netmask; //子网掩码 struct ip_addr gw; //默认网关 if(lwip_comm_mem_malloc())return 1; //内存申请失败 tcpip_init(NULL,NULL); //初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务 ipaddr.addr = 0; netmask.addr = 0; gw.addr = 0; OS_ENTER_CRITICAL(); //进入临界区 Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,ðernetif_init,&tcpip_input);//向网卡列表中添加一个网口 OS_EXIT_CRITICAL(); //退出临界区 if(Netif_Init_Flag==NULL)return 3;//网卡添加失败 else//网口添加成功后,设置netif为默认值,并且打开netif网口 { netif_set_default(&lwip_netif); //设置netif为默认网口 netif_set_up(&lwip_netif); //打开netif网口 } return 0;//操作OK. }
这里面一个很重要的函数tcpip_init,这个函数实际上就创建了我们的底层网络数据通讯任务,剩下的就是设置网卡之类的巴拉巴拉
好了,到这里网络就能跑起来了,但是得事先一个功能啊,不然怎么叫demo,于是依然是先来dhcp,当然是一个独立任务,如下
//创建DHCP任务 void lwip_comm_dhcp_creat(void) { OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL(); //进入临界区 OSTaskCreate(lwip_dhcp_task,(void*)0,(OS_STK*)&LWIP_DHCP_TASK_STK[LWIP_DHCP_STK_SIZE-1],LWIP_DHCP_TASK_PRIO);//创建DHCP任务 OS_EXIT_CRITICAL(); //退出临界区 }
//DHCP处理任务 void lwip_dhcp_task(void *pdata) { u32 ip=0,netmask=0,gw=0; dhcp_start(&lwip_netif);//开启DHCP lwipdev.dhcpstatus=0; //正在DHCP printf("正在查找DHCP服务器,请稍等...........\r\n"); while(1) { printf("正在获取地址...\r\n"); ip=lwip_netif.ip_addr.addr; //读取新IP地址 netmask=lwip_netif.netmask.addr;//读取子网掩码 gw=lwip_netif.gw.addr; //读取默认网关 if(ip!=0) //当正确读取到IP地址的时候 { lwipdev.dhcpstatus=2; //DHCP成功 printf("通过DHCP获取到IP地址..............%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]); //解析通过DHCP获取到的子网掩码地址 lwipdev.netmask[3]=(uint8_t)(netmask>>24); lwipdev.netmask[2]=(uint8_t)(netmask>>16); lwipdev.netmask[1]=(uint8_t)(netmask>>8); lwipdev.netmask[0]=(uint8_t)(netmask); printf("通过DHCP获取到子网掩码............%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]); //解析出通过DHCP获取到的默认网关 lwipdev.gateway[3]=(uint8_t)(gw>>24); lwipdev.gateway[2]=(uint8_t)(gw>>16); lwipdev.gateway[1]=(uint8_t)(gw>>8); lwipdev.gateway[0]=(uint8_t)(gw); printf("通过DHCP获取到的默认网关..........%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]); break; }else if(lwip_netif.dhcp->tries>LWIP_MAX_DHCP_TRIES) //通过DHCP服务获取IP地址失败,且超过最大尝试次数 { lwipdev.dhcpstatus=0XFF;//DHCP失败. printf("dhcp failed\r\n"); break; } OSTimeDly(50); //延时250ms } lwip_comm_dhcp_delete();//删除DHCP任务 }
到此,基本上就算是将lwip正式移植到了ucos上,老规矩上代码
http://download.csdn.net/detail/dengrengong/8599063