lwip移植到stm32上-enc28j60,103mcu(2)

时间:2021-09-10 10:20:18

前面小玩了一下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移植到stm32上-enc28j60,103mcu(2)

lwip移植到stm32上-enc28j60,103mcu(2)

这些函数都是需要实现的,实现的代码如下,都有注释,看注释应该明白了

/* 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),,sizeof(TQ_DESCR)); //清除mbox的内存
if(*mbox) //内存分配成功
{
if(size>MAX_QUEUE_ENTRIES)size=MAX_QUEUE_ENTRIES; //消息队列最多容纳MAX_QUEUE_ENTRIES消息数目
(*mbox)->pQ=OSQCreate(&((*mbox)->pvQEntries[]),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!=)
{
ucos_timeout=(timeout*OS_TICKS_PER_SEC)/; //转换为节拍数,因为UCOS延时使用的是节拍数,而LWIP是用ms
if(ucos_timeout<)ucos_timeout=;//至少1个节拍
}else ucos_timeout = ;
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*/OS_TICKS_PER_SEC + ;
}
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,);//尝试获取一个消息
} //检查一个消息邮箱是否有效
//*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,,sizeof(OS_Q_DATA));
ucErr=OSQQuery (m_box->pQ,&q_data);
ret=(ucErr<&&(q_data.OSNMsgs<q_data.OSQSize))?:;
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!=)
{
ucos_timeout = (timeout * OS_TICKS_PER_SEC) / ;//转换为节拍数,因为UCOS延时使用的是节拍数,而LWIP是用ms
if(ucos_timeout < )
ucos_timeout = ;
}else ucos_timeout = ;
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*/OS_TICKS_PER_SEC + );//算出请求消息或使用的时间(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 )? :;
} //设置一个信号量无效
//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)==)//创建TCP IP内核任务
{
OS_ENTER_CRITICAL(); //进入临界区
OSTaskCreate(thread,arg,(OS_STK*)&TCPIP_THREAD_TASK_STK[stacksize-],prio);//创建TCP IP内核任务
OS_EXIT_CRITICAL(); //退出临界区
}
return ;
} //lwip延时函数
//ms:要延时的ms数
void sys_msleep(u32_t ms)
{
OSTimeDly(ms/);
} //获取系统时间,LWIP1.4.1增加的函数
//返回值:当前系统时间(单位:毫秒)
u32_t sys_now(void)
{
u32_t ucos_time, lwip_time;
ucos_time=OSTimeGet(); //获取当前系统时间 得到的是UCSO的节拍数
lwip_time=(ucos_time*/OS_TICKS_PER_SEC+);//将节拍数转换为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 <adam@sics.se>
//移植的时候该文件很重要
#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()
#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()
{
OSTimeDly();//延时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=;
pdata = pdata;
OSStatInit(); //初始化统计任务.这里会延时1秒钟左右
OS_ENTER_CRITICAL(); //进入临界区(无法被中断打断)
OSTaskCreate(led1_task,(void *),(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-],LED1_TASK_PRIO);
OSTaskCreate(led2_task,(void *),(OS_STK*)&LED2_TASK_STK[LED2_STK_SIZE-],LED2_TASK_PRIO);
lwip_comm_dhcp_creat(); //创建DHCP任务
OSTaskCreate(net_task,(void *),(OS_STK*)&NET_TASK_STK[NET_STK_SIZE-],NET_TASK_PRIO);
OSTaskSuspend(START_TASK_PRIO); //挂起起始任务.
OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断)
} int main(void)
{
NVIC_Group_Init();//系统默认中断分组
Debug_Serial_Init();
Delay_Init();
Led_Init();
LCD_Init();
Beep_Init();
Key_Exti_Init();
while(SDRAM_Init())
{
printf("sdram init failed\r\n");
Delay_Ms();
}
MallocInit(); TIM6_Int_Init(,(*)-);
OSInit();
while(lwip_comm_init()) //lwip初始化
{
LCD_ShowString(,,,,(u8*)"Lwip Init failed ! ",LCD_BLACK); //lwip初始化失败
Delay_Ms();
LCD_ShowString(,,,,(u8*)" ",LCD_BLACK); //lwip初始化失败
Delay_Ms();
}
LCD_ShowString(,,,,(u8*)"Lwip Init success ! ",LCD_BLACK); //lwip初始化失败
OSTaskCreate(start_task,(void *),(OS_STK *)&START_TASK_STK[START_STK_SIZE-],START_TASK_PRIO );//创建起始任务
OSStart();
}

这里面混进来了一个nettask,这个任务是干嘛的呢,理论上说,为了cpu效率的最大化,网卡最好是使用中断,在中断中获取数据,但是不好意思,enc28j60我没用中断,数据不能通过中断得到,没办法,只能创建一个任务来循环获取数据了,实现如下

//任务函数接口
void net_task(void *pdata)
{
while()
{
OSTimeDly();//延时2个时钟节拍
lwip_pkt_handle();
};
}
//定时的从网卡中获取数据
void lwip_pkt_handle(void)
{
if(ENC28J60_Read(EPKTCNT) !=)
{
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 ; //内存申请失败
tcpip_init(NULL,NULL); //初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务
ipaddr.addr = ;
netmask.addr = ;
gw.addr = ;
OS_ENTER_CRITICAL(); //进入临界区
Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,&ethernetif_init,&tcpip_input);//向网卡列表中添加一个网口
OS_EXIT_CRITICAL(); //退出临界区
if(Netif_Init_Flag==NULL)return ;//网卡添加失败
else//网口添加成功后,设置netif为默认值,并且打开netif网口
{
netif_set_default(&lwip_netif); //设置netif为默认网口
netif_set_up(&lwip_netif); //打开netif网口
}
return ;//操作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*),(OS_STK*)&LWIP_DHCP_TASK_STK[LWIP_DHCP_STK_SIZE-],LWIP_DHCP_TASK_PRIO);//创建DHCP任务
OS_EXIT_CRITICAL(); //退出临界区
}
//DHCP处理任务
void lwip_dhcp_task(void *pdata)
{
u32 ip=,netmask=,gw=;
dhcp_start(&lwip_netif);//开启DHCP
lwipdev.dhcpstatus=; //正在DHCP
printf("正在查找DHCP服务器,请稍等...........\r\n");
while()
{
printf("正在获取地址...\r\n");
ip=lwip_netif.ip_addr.addr; //读取新IP地址
netmask=lwip_netif.netmask.addr;//读取子网掩码
gw=lwip_netif.gw.addr; //读取默认网关
if(ip!=) //当正确读取到IP地址的时候
{
lwipdev.dhcpstatus=; //DHCP成功
printf("通过DHCP获取到IP地址..............%d.%d.%d.%d\r\n",lwipdev.ip[],lwipdev.ip[],lwipdev.ip[],lwipdev.ip[]);
//解析通过DHCP获取到的子网掩码地址
lwipdev.netmask[]=(uint8_t)(netmask>>);
lwipdev.netmask[]=(uint8_t)(netmask>>);
lwipdev.netmask[]=(uint8_t)(netmask>>);
lwipdev.netmask[]=(uint8_t)(netmask);
printf("通过DHCP获取到子网掩码............%d.%d.%d.%d\r\n",lwipdev.netmask[],lwipdev.netmask[],lwipdev.netmask[],lwipdev.netmask[]);
//解析出通过DHCP获取到的默认网关
lwipdev.gateway[]=(uint8_t)(gw>>);
lwipdev.gateway[]=(uint8_t)(gw>>);
lwipdev.gateway[]=(uint8_t)(gw>>);
lwipdev.gateway[]=(uint8_t)(gw);
printf("通过DHCP获取到的默认网关..........%d.%d.%d.%d\r\n",lwipdev.gateway[],lwipdev.gateway[],lwipdev.gateway[],lwipdev.gateway[]);
break;
}else if(lwip_netif.dhcp->tries>LWIP_MAX_DHCP_TRIES) //通过DHCP服务获取IP地址失败,且超过最大尝试次数
{
lwipdev.dhcpstatus=0XFF;//DHCP失败.
printf("dhcp failed\r\n");
break;
}
OSTimeDly(); //延时250ms
}
lwip_comm_dhcp_delete();//删除DHCP任务
}

到此,基本上就算是将lwip正式移植到了ucos上,老规矩上代码

http://download.csdn.net/detail/dengrengong/8599063