zoukankan      html  css  js  c++  java
  • 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 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 <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
    ", 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
    ");
            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服务器,请稍等...........
    ");   
        while(1)
        { 
            printf("正在获取地址...
    ");
            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
    ",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
    ",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
    ",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
    ");
                break;
            }  
            OSTimeDly(50); //延时250ms
        }
        lwip_comm_dhcp_delete();//删除DHCP任务 
    }

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

    http://download.csdn.net/detail/dengrengong/8599063
  • 相关阅读:
    织梦DedeCms网站首页不生成html文件动态显示方法
    PHP7.0下安装DEDE织梦 出现 GD不支持的解决方法
    DEDECMS5.7支持伪静态的方法
    DEDECMS全站伪静态设置方法
    设置 SSH 通过密钥登录
    Windows安装OpenSSH服务
    VS Code远程开发工具错误-找不到SSH安装
    帝国CMS自定义列表的排序
    帝国CMS灵动标签e:loop的使用方法
    cisco 3750交换机堆叠后配置恢复方法
  • 原文地址:https://www.cnblogs.com/dengxiaojun/p/4433646.html
Copyright © 2011-2022 走看看