• LwIP应用开发笔记之十:LwIP带操作系统基本移植


      现在,TCP/IP协议的应用无处不在。随着物联网的火爆,嵌入式领域使用TCP/IP协议进行通讯也越来越广泛。在我们的相关产品中,也都有应用,所以我们结合应用实际对相关应用作相应的总结。

    1、技术准备

      我们采用的开发平台是STM32F407和LwIP协议栈。在开始之前,我们需要做必要的准备工作。

      首先要获得LwIP的源码,在网上有很多,不同版本及不同平台的都有,不过我们还是建议直接从官方网站获得。其官方网站如下:

      http://savannah.nongnu.org/projects/lwip/

      其次,需要硬件平台,我们采用了STM32F407ZG+DM9161的网络接口方式,这并不是必须的,其他硬件平台也是一样的。

      最后,因为我们后面要在操作系统下移植,采用的操作系统是FreeRTOS,所以还需下载FreeRTOS的源码。同样简易从官网下载:

      https://www.freertos.org/index.html

    2LwIP简要说明

      LwIP是一款免费的TCP/IP协议栈,但它的功能趋势十分完备。LwIP 具有三种应用编程接口 (API):

    • Raw API:为原始的 LwIP API。它通过事件回调机制进行应用开发。该 API 提供了最好的性能和优化的代码长度,但增加了应用开发的复杂性。
    • Netconn API:为高层有序 API,需要实时操作系统 (RTOS)的支持 (提供进程间通讯的方法)。 Netconn API 支持多线程工作。
    • BSD Socket API:类似 Berkeley 的套接字 API (开发于 Netconn API 之上) 。

      对于以上三种接口,前一种只需要裸机即可调用,后两种需要操作系统才能调用。所以据此LwIP存在两种移植方式:一是,只移植内核,此时应用程序的编写只能基于RAW/Callback API进行。二是,移植内核和上层API,此时应用程序编写可以使用3种API,即:RAW/Callback API、Sequential API和Socket API。

    3LwIP的带操作系统基本移植

      带操作系统的移植首先是建立在无操作系统移植基础之上的。在无操作系统移植时,定义的数据类型和宏都是有效的,只需要对lwipopts.h配置文件做简单修改,并根据sys_arch.txt移植说明文件编写sys_arch.c和sys_arch.h两个文件以实现操作系统模拟层就可以了。

      操作系统模拟层的功能再以为协议栈提供邮箱、信号量、互斥量等机制,用以保证内核与上层API的通讯。这些操作系统模拟层函数均在sys.h中已经声明,我们一般在sys_arch.c文件中完成其定义。所以,我们很清楚,带操作系统的移植就是在无操作系统的基础上添加操作系统模拟层。在接下来我们就看看操作系统模拟层的编写。

      在操作系统已经正确移植的基础上,我们根据sys_arch.txt移植说明文件的描述,还需要移植的宏定义及函数等如下:

    名称

    属性

    功能

    sys_mbox_t

    数据类型

    指针类型,指向系统邮箱

    sys_sem_t

    数据类型

    指针类型,指向系统信号量

    sys_mutex_t

    数据类型

    指针类型,指向系统互斥量

    sys_thread_t

    数据类型

    系统任务标识

    SYS_MBOX_NULL

    邮箱指针指向的空值

    SYS_SEM_NULL

    信号量指针指向的空值

    sys_init

    函数

    初始化系统模拟层

    sys_sem_new

    函数

    生成一个信号量

    sys_sem_free

    函数

    删除一个信号量

    sys_sem_signal

    函数

    释放一个信号量

    sys_arch_sem_wait

    函数

    等待一个信号量

    sys_sem_valid

    函数

    判断一个信号量是否有效

    sys_sem_set_invalid

    函数

    将一个信号量置为无效

    sys_mutex_new

    函数

    生成一个新的互斥量

    sys_mutex_free

    函数

    删除一个互斥量

    sys_mutex_lock

    函数

    锁住一个互斥量

    sys_mutex_unlock

    函数

    解锁一个互斥量

    sys_mutex_valid

    函数

    判断一个互斥量是否有效

    sys_mutex_set_invalid

    函数

    将一个互斥量置为无效

    sys_mbox_new

    函数

    新建一个邮箱

    sys_mbox_free

    函数

    删除一个邮箱

    sys_mbox_post

    函数

    向邮箱投递消息,阻塞

    sys_mbox_trypost

    函数

    尝试向邮箱投递消息,不阻塞

    sys_arch_mbox_fetch

    函数

    从邮箱获取消息,阻塞

    sys_arch_mbox_tryfetch

    函数

    尝试从邮箱获取消息,不阻塞

    sys_mbox_valid

    函数

    判断一个邮箱是否有效

    sys_mbox_set_invalid

    函数

    将一个邮箱设置为无效

    sys_thread_new

    函数

    创建新进程

    sys_arch_protect

    函数

    临界区保护

    sys_arch_unprotect

    函数

    退出临界区保护

      从上表中我们可以发现,这些变量和函数主要是面向信号量、互斥量及邮箱,包括新建、删除、释放、获取等各类操作,我们需要根据操作系统的规定来实现这些函数,我们在这里使用的FreeRTOS,所以我根据FreeRTOS对信号量、互斥量及邮箱的操作来实现这些函数。我们列举邮箱的各操作函数实现如下:

      1 /*创建一个空的邮箱。*/
      2 err_t sys_mbox_new(sys_mbox_t *mbox, int size)
      3 {
      4   osMessageQDef(QUEUE, size, void *);
      5  
      6   *mbox = osMessageCreate(osMessageQ(QUEUE), NULL);
      7  
      8 #if SYS_STATS
      9       ++lwip_stats.sys.mbox.used;
     10       if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) {
     11          lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;
     12          }
     13 #endif /* SYS_STATS */
     14  if (*mbox == NULL)
     15   return ERR_MEM;
     16  
     17  return ERR_OK;
     18 }
     19  
     20 /*重新分配一个邮箱。如果邮箱被释放时,邮箱中仍有消息,在lwIP中这是出现编码错误的指示,并通知开发人员。*/
     21 void sys_mbox_free(sys_mbox_t *mbox)
     22 {
     23        if( osMessageWaiting(*mbox) )
     24        {
     25               portNOP();
     26 #if SYS_STATS
     27            lwip_stats.sys.mbox.err++;
     28 #endif /* SYS_STATS */
     29        }
     30  
     31        osMessageDelete(*mbox);
     32  
     33 #if SYS_STATS
     34      --lwip_stats.sys.mbox.used;
     35 #endif /* SYS_STATS */
     36 }
     37  
     38 /*发送消息到邮箱*/
     39 void sys_mbox_post(sys_mbox_t *mbox, void *data)
     40 {
     41   while(osMessagePut(*mbox, (uint32_t)data, osWaitForever) != osOK);
     42 }
     43  
     44 /*尝试将消息发送到邮箱*/
     45 err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
     46 {
     47     err_t result;
     48  
     49    if ( osMessagePut(*mbox, (uint32_t)msg, 0) == osOK)
     50    {
     51       result = ERR_OK;
     52    }
     53    else {
     54       result = ERR_MEM;
     55                     
     56 #if SYS_STATS
     57       lwip_stats.sys.mbox.err++;
     58 #endif /* SYS_STATS */
     59                     
     60    }
     61  
     62    return result;
     63 }
     64  
     65 /*阻塞进程从邮箱获取消息*/
     66 u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
     67 {
     68   osEvent event;
     69   uint32_t starttime = osKernelSysTick();;
     70  
     71   if(timeout != 0)
     72   {
     73     event = osMessageGet (*mbox, timeout);
     74    
     75     if(event.status == osEventMessage)
     76     {
     77       *msg = (void *)event.value.v;
     78       return (osKernelSysTick() - starttime);
     79     }
     80     else
     81     {
     82       return SYS_ARCH_TIMEOUT;
     83     }
     84   }
     85   else
     86   {
     87     event = osMessageGet (*mbox, osWaitForever);
     88     *msg = (void *)event.value.v;
     89     return (osKernelSysTick() - starttime);
     90   }
     91 }
     92  
     93 /*尝试从邮箱获取消息*/
     94 u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
     95 {
     96   osEvent event;
     97  
     98   event = osMessageGet (*mbox, 0);
     99  
    100   if(event.status == osEventMessage)
    101   {
    102     *msg = (void *)event.value.v;
    103     return ERR_OK;
    104   }
    105   else
    106   {
    107     return SYS_MBOX_EMPTY;
    108   }
    109 }
    110  
    111 /*判断一个邮箱是否有效*/
    112 int sys_mbox_valid(sys_mbox_t *mbox)         
    113 {     
    114   if (*mbox == SYS_MBOX_NULL)
    115     return 0;
    116   else
    117     return 1;
    118 }
    119  
    120 /*设置一个邮箱无效*/                                             
    121 void sys_mbox_set_invalid(sys_mbox_t *mbox)  
    122 {                                            
    123   *mbox = SYS_MBOX_NULL;                     
    124 }                                             
    125 
    126 //  创建一个新的信号量。而 "count"参数指示该信号量的初始状态
    127 err_t sys_sem_new(sys_sem_t *sem, u8_t count)
    128 {
    129   osSemaphoreDef(SEM);
    130  
    131   *sem = osSemaphoreCreate (osSemaphore(SEM), 1);
    132       
    133   if(*sem == NULL)
    134   {
    135 #if SYS_STATS
    136       ++lwip_stats.sys.sem.err;
    137 #endif /* SYS_STATS */ 
    138               return ERR_MEM;
    139   }
    140       
    141   if(count == 0)  // Means it can't be taken
    142   {
    143     osSemaphoreWait(*sem,0);
    144   }
    145  
    146 #if SYS_STATS
    147        ++lwip_stats.sys.sem.used;
    148       if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) {
    149               lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;
    150        }
    151 #endif /* SYS_STATS */
    152              
    153        return ERR_OK;
    154 }

      此外还有一些函数也是协议栈需要的函数,特别是sys_thread_new函数,不但协议栈在初始化是需要用到,在后续我们实现各类基于LwIP的应用时也需要用到,其实现如下:

     1 sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio)
     2 {
     3   const osThreadDef_t os_thread_def = { (char *)name, (os_pthread)thread, (osPriority)prio, 0, stacksize};
     4   return osThreadCreate(&os_thread_def, arg);
     5 }
     6 osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
     7 {
     8   TaskHandle_t handle;
     9  
    10 #if( configSUPPORT_STATIC_ALLOCATION == 1 ) &&  ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
    11   if((thread_def->buffer != NULL) && (thread_def->controlblock != NULL)) {
    12     handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
    13               thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
    14               thread_def->buffer, thread_def->controlblock);
    15   }
    16   else {
    17     if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
    18               thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
    19               &handle) != pdPASS)  {
    20       return NULL;
    21     }
    22   }
    23 #elif( configSUPPORT_STATIC_ALLOCATION == 1 )
    24  
    25     handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
    26               thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
    27               thread_def->buffer, thread_def->controlblock);
    28 #else
    29   if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
    30                    thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
    31                    &handle) != pdPASS)  {
    32     return NULL;
    33   }    
    34 #endif
    35  
    36   return handle;
    37 }

      至此,基于FreeRTOS操作系统的LwIP移植结算完成了,我们编译下载就可以对其进行验证。

    4、结论

      前面已经移植了基于操作系统的LwIP,那怎么知道我们的移植是否成功呢?接下来我们对它进行必要的验证。

      首先我们查看目标板在网络上的配置是否正确。我们打开命令行窗口,运行ipconfig命令,查看MAC地址和IP地址配置:

     

      我们配置的MAC地址00:08:E1:00:00:00和IP地址192.168.2.110显示正常。接下来我们采用ping命令测试网络链接:

     

      上图显示网络连接正常,经此测试,说明我们的LwIP在有操作系统情况下移植正常。

    欢迎关注:

  • 相关阅读:
    rancher 2.X 搭建小型web集群+mysql主从复制
    harbor 仓库搭建
    k8s 集群搭建
    oracle sql命令
    IIS实现反向代理
    高并发的大型网站架构设计
    .net core集成vue
    使用TFS玩转Docker自动化部署
    动态创建IIS站点
    网站架构设计(草稿)
  • 原文地址:https://www.cnblogs.com/foxclever/p/13461593.html
Copyright © 2020-2023  润新知