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

    前面小玩了一下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)_第1张图片

lwip移植到stm32上-enc28j60,103mcu(2)_第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),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,&ethernetif_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

 

你可能感兴趣的:(lwip移植到stm32上-enc28j60,103mcu(2))