现在,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对信号量、互斥量及邮箱的操作来实现这些函数。我们列举邮箱的各操作函数实现如下:

/*创建一个空的邮箱。*/
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{osMessageQDef(QUEUE, size, void *);*mbox = osMessageCreate(osMessageQ(QUEUE), NULL);#if SYS_STATS++lwip_stats.sys.mbox.used;if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) {lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;}
#endif /* SYS_STATS */if (*mbox == NULL)return ERR_MEM;return ERR_OK;
}/*重新分配一个邮箱。如果邮箱被释放时,邮箱中仍有消息,在lwIP中这是出现编码错误的指示,并通知开发人员。*/
void sys_mbox_free(sys_mbox_t *mbox)
{if( osMessageWaiting(*mbox) ){/* Line for breakpoint.  Should never break here! */portNOP();
#if SYS_STATSlwip_stats.sys.mbox.err++;
#endif /* SYS_STATS */}osMessageDelete(*mbox);#if SYS_STATS--lwip_stats.sys.mbox.used;
#endif /* SYS_STATS */
}/*发送消息到邮箱*/
void sys_mbox_post(sys_mbox_t *mbox, void *data)
{while(osMessagePut(*mbox, (uint32_t)data, osWaitForever) != osOK);
}/*尝试将消息发送到邮箱*/
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
err_t result;if ( osMessagePut(*mbox, (uint32_t)msg, 0) == osOK){result = ERR_OK;}else {// could not post, queue must be fullresult = ERR_MEM;#if SYS_STATSlwip_stats.sys.mbox.err++;
#endif /* SYS_STATS */}return result;
}/*阻塞进程从邮箱获取消息*/
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{osEvent event;uint32_t starttime = osKernelSysTick();;if(timeout != 0){event = osMessageGet (*mbox, timeout);if(event.status == osEventMessage){*msg = (void *)event.value.v;return (osKernelSysTick() - starttime);}else{return SYS_ARCH_TIMEOUT;}}else{event = osMessageGet (*mbox, osWaitForever);*msg = (void *)event.value.v;return (osKernelSysTick() - starttime);}
}/*尝试从邮箱获取消息*/
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{osEvent event;event = osMessageGet (*mbox, 0);if(event.status == osEventMessage){*msg = (void *)event.value.v;return ERR_OK;}else{return SYS_MBOX_EMPTY;}
}/*判断一个邮箱是否有效*/
int sys_mbox_valid(sys_mbox_t *mbox)
{     if (*mbox == SYS_MBOX_NULL)return 0;elsereturn 1;
}/*设置一个邮箱无效*/
void sys_mbox_set_invalid(sys_mbox_t *mbox)
{                                            *mbox = SYS_MBOX_NULL;
}                                             //  创建一个新的信号量。而 "count"参数指示该信号量的初始状态
err_t sys_sem_new(sys_sem_t *sem, u8_t count)
{osSemaphoreDef(SEM);*sem = osSemaphoreCreate (osSemaphore(SEM), 1);if(*sem == NULL){
#if SYS_STATS++lwip_stats.sys.sem.err;
#endif /* SYS_STATS */ return ERR_MEM;}if(count == 0)  // Means it can't be taken{osSemaphoreWait(*sem,0);}#if SYS_STATS++lwip_stats.sys.sem.used;if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) {lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;}
#endif /* SYS_STATS */return ERR_OK;
}

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

sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio)
{const osThreadDef_t os_thread_def = { (char *)name, (os_pthread)thread, (osPriority)prio, 0, stacksize};return osThreadCreate(&os_thread_def, arg);
}
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
{TaskHandle_t handle;#if( configSUPPORT_STATIC_ALLOCATION == 1 ) &&  ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )if((thread_def->buffer != NULL) && (thread_def->controlblock != NULL)) {handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),thread_def->buffer, thread_def->controlblock);}else {if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),&handle) != pdPASS)  {return NULL;}}
#elif( configSUPPORT_STATIC_ALLOCATION == 1 )handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),thread_def->buffer, thread_def->controlblock);
#elseif (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),&handle) != pdPASS)  {return NULL;}
#endifreturn handle;
}

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

4、结论

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

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

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

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

欢迎关注:

LwIP应用开发笔记之十:LwIP带操作系统基本移植相关推荐

  1. LwIP应用开发笔记之一:LwIP无操作系统基本移植

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

  2. LwIP应用开发笔记之八:LwIP无操作系统HTTP客户端

    前面我们实现了TCP服务器和客户端的简单应用,接下来我们实现一个基于TCP协议的应用协议,那就是HTTP超文本传输协议 1.HTTP协议简介 超文本传输协议(Hyper Text Transfer P ...

  3. LwIP应用开发笔记之七:LwIP无操作系统HTTP服务器

    前面我们实现了TCP服务器和客户端的简单应用,接下来我们实现一个基于TCP协议的应用协议,那就是HTTP超文本传输协议. 1.HTTP协议简介 超文本传输协议(Hyper Text Transfer ...

  4. LwIP应用开发笔记之四:LwIP无操作系统TFTP服务器

    前面我们已经实现了UDP的回环客户端和回环服务器的简单应用,接下来我们实现一个基于UDP的简单文件传输协议TFTP. 1.TFTP协议简介 TFTP是TCP/IP协议族中的一个用来在客户机与服务器之间 ...

  5. LwIP应用开发笔记之六:LwIP无操作系统TCP客户端

    上一篇我们基于LwIP协议栈的RAW API实现了一个TCP服务器的简单应用,接下来一节我们来实现一个TCP客户端的简单应用. 1.TCP简述 TCP(Transmission Control Pro ...

  6. LwIP应用开发笔记之五:LwIP无操作系统TCP服务器

    前面我们实现了UDP服务器及客户端以及基于其上的TFTP应用服务器.接下来我们将实现同样广泛应用的TCP协议各类应用. 1.TCP简述 TCP(Transmission Control Protoco ...

  7. LwIP应用开发笔记之三:LwIP无操作系统UDP客户端

    前一节我们实现了基于RAW API的UDP服务器,在接下来,我们进一步利用RAW API实现UDP客户端. 1.UDP协议简述 UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包 ...

  8. MLX90640开发笔记(十)成果展示-红眼睛相机

    最终的成果是一个微型的USB接口红外成像模块(微型红外成像仪30*30mm),可以连接到Android手机或者计算机的USB接口,实时显示热像视频,和手机相机差不多,只不过它是热红外成像,所以叫&qu ...

  9. MLX90640 红外热成像仪测温传感器模块开发笔记(十) 成果展示-红眼睛相机

    MLX90640 红外热成像仪测温传感器模块开发笔记(十) 成果展示-红眼睛相机 现在自己在做红外成像仪的越来越多了,两年前有个井下机电设备运行状态的科研项目,当时使用了 AMG8833(8*8 像素 ...

最新文章

  1. pybind11介绍
  2. TransG : A Generative Model for Knowledge Graph Embedding ACL 2016.Berlin, Germany.
  3. benchmark mysql_程序员的MySQL手册(二): 监控与benchmark
  4. 深入Android内存泄露
  5. 基础知识 一个工具给win7 win10的同学 或者MAC 可以跳过
  6. 【BZOJ1030】[JSOI2007] 文本生成器(AC自动机上跑DP)
  7. python pdfminer解析pdf文件的每一行,得到每一行的坐标与每个字符的坐标
  8. Android中Parcelable与Serializable接口用法
  9. python运算符及优先级顺序
  10. 微信小程序模拟器里面不能显示自己写的INDEX
  11. 三级网络技术--宽带接入技术--无线接入技术、光纤接入技术
  12. 计算机网络职业生涯规划书模板前言,计算机网络技术专业个人职业生涯规划书(参考模板).doc...
  13. Laravel artisan常用命令集锦
  14. 【简单利用函数实现多条件求和】
  15. BLE蓝牙连接不稳定以及突然断开的原因和解决方法
  16. TapTap 算法平台的 Serverless 探索之路
  17. 助力全球抗疫,腾讯加入Linux基金会公共卫生计划
  18. linux下文件重命名
  19. SLAM综述阅读笔记二:Simultaneous Localization and Mapping: A Survey of Current Trends in Autonomous(2017)
  20. 四川师范大学Java期末_四川师范大学2008-2009第一学期常微分方程期末试题A英文版(含答案)精选.pdf...

热门文章

  1. 二叉树介绍与代码实现
  2. case函数,replace函数
  3. 博弈论笔记--03--迭代剔除和中位选民定理
  4. DB2 错误代码 命令大全
  5. pidgin-qq可以使用QQ2012协议了
  6. CVS 客户端使用手册
  7. 于.net开发平台项目案例集锦
  8. 【计算机网络复习 数据链路层】3.5.3 CSMA协议
  9. 《动手学深度学习 PyTorch版》学习笔记(一):数据操作
  10. 第二章 数据的表示和运算 2.1.2 BCD码 [计算机组成原理笔记]