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

/*创建一个空的邮箱。*/
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在有操作系统情况下移植正常。

欢迎关注:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ThreadX应用开发笔记之一:移植ThreadX到STM32平台

现在一些小型系统中也往往有多任务处理的需求&#xff0c;这就为实时操作系统提供了用武之地。事实上国内外各种各样的RTOS有很多&#xff0c;而且基本都在走开源的路线&#xff0c;ThreadX也不例外&#xff0c;在这一篇中我们就来学习ThreadX初步应用并将其移植到STM32平台中。…

外设驱动库开发笔记12:TSEV01CL55红外温度传感器驱动

有时候我们需要检测一些无法直接接触的器件的温度。为了实现这一需求&#xff0c;我们通常会选择红外温度传感器来实现这一功能。考虑到复用的问题&#xff0c;我们一般会将操作元器件的代码抽象为驱动函数以备调用。这里我们就来设计并实现TSEV01CL55红外温度传感器的驱动。 …

FreeRTOS应用开发笔记之一:FreeRTOS在STM32的移植

FreeRTOS是如今在小型嵌入式领域应用比较广泛的一种实时操作系统。它是一种开源且免费的操作系统&#xff0c;而且移植和使用都非常的简单。在这里我们将学习并移植FreeRTOS。 1、必要的准备 工欲善其事&#xff0c;必先利其器&#xff0c;在开始学习和移植之前&#xff0c;相…

外设驱动库开发笔记13:MLX90614红外温度传感器驱动

红外温度传感器一般用于非接触式的温度检测。在我们的系统中经常会有这样的需求。所以我们将其设计为通用的驱动库以备复用。这一篇我们将讲述MLX90614红外温度传感器驱动的设计与实现。 1、功能概述 MLX90614是一种红外温度计&#xff0c;用于非接触式温度测量。红外测温是根…

Modbus协议栈应用实例之三:Modbus TCP客户端应用

自从开源了我们自己开发的Modbus协议栈之后&#xff0c;有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例&#xff0c;这一篇中我们将解说如何使用协议栈实现一个Modbus TCP客户端。 1、何为TCP客户端 Modbus…

Modbus协议栈应用实例之四:ModbusTCP服务器应用

自从开源了我们自己开发的Modbus协议栈之后&#xff0c;有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例&#xff0c;这一篇中我们来简述如何使用协议栈实现一个Modbus TCP服务器应用。 1、何为TCP服务器 Mo…

外设驱动库开发笔记14:DS18B20温度变送器驱动

在一时候我们需要相对简单的检测温度信号&#xff0c;而DS18B20就是一款功能和应用都相对简单的温度传感器&#xff0c;通过单线就可以实现检测温度信号的需求。这一篇我们就来实现操作DS18B20获取温度数据的驱动。 1、功能概述 DS18B20是常用的数字温度传感器&#xff0c;其…

Modbus协议栈应用实例之五:Modbus ASCII主站应用

自从开源了我们自己开发的Modbus协议栈之后&#xff0c;有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例&#xff0c;这一篇中我们来使用协议栈实现Modbus ASCII主站应用。 1、何为ASCII主站 我们知道Modbus…

Modbus协议栈应用实例之六:Modbus ASCII从站应用

自从开源了我们自己开发的Modbus协议栈之后&#xff0c;有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例&#xff0c;这一篇中我们来使用协议栈实现Modbus ASCII从站应用。 1、何为ASCII从站 我们知道Modbus…

外设驱动库开发笔记15:DHT11温湿度传感器驱动

与DS18B20一样DHT11也是采用单总线&#xff0c;但所不同的是DHT11可同时实现温度和湿度的检测。在我们的产品中经常使用它来检测环境的温湿度信息。这一篇我们将设计并封装DHT11的驱动程序&#xff0c;以方便重复使用。 1、功能概述 DHT11数字温湿度传感器是一款含有已校准数…

外设驱动库开发笔记16:MS5536C压力变送器驱动

压力检测也是经常会遇到的需求&#xff0c;比如环境压力或者低压气体等都会用到压力检测。这类检测压力都比较低&#xff0c;一般不会超过大气压&#xff0c;有时甚至是负压。这一篇我们要讨论的MS5536C就属于这类器件。接下来我们将设计并实现MS5536C的驱动。 1、功能概述 M…

外设驱动库开发笔记17:MS5803压力变送器驱动

很多时候我们需要检测被控对象的绝对压力&#xff0c;而且在我们的多款产品中也有这样的需求。当然检测绝对压力的传感器有很多&#xff0c;我们经常使用MS5803来实现压力检测。本篇中我们将设计并实现MS5803系列压力传感器的驱动。 1、功能概述 MS5803系列产品包含压阻传感器…

通讯接口应用笔记1:RS485通讯上下拉电阻的选择

RS485是一种常见的通讯接口方式&#xff0c;在我们的实际产品中也是多次使用。但我们平常并不会去过多考虑某一实现的细节问题&#xff0c;不过最近我们遇到了一个因如上下拉电阻的选择问题而造成的通讯故障&#xff0c;所以在这一片中我们来讨论一下RS485总线上下拉电阻的选择…

外设驱动库开发笔记18:MS5837压力变送器驱动

绝对压力的检测是常见的需求。在我们的系统中也常常会遇到。而MS5837压力传感器也是我们进场会采用的方案。在这篇里我们将讨论并实现MS5837压力传感器的驱动。 1、功能概述 MS5837压力传感器是一种可用于电路板上&#xff0c;适用于检测10-1200mbar压力范围的传感器&#xf…

外设驱动库开发笔记19:BMP280压力温度传感器驱动

压力和温度监测在嵌入式系统开发中是非常常见的需求&#xff0c;特别是对环境大气压力和温度的检测需求就更常见了。我们一般都会选择一些封装较小操作比较方便的压力传感器。BMP280就是满足这一要求的器件。在这一篇中我们将设计并实现BMP280的驱动。 1、功能概述 BMP280是一…

一个简单的空气质量数据监测站项目

大气质量数据监测站用于测试空气质量监测及数据采集&#xff0c;实现野外或者室内空气质量的检测。并通过网络将数据上传到OneNet​显示。​ 1、项目概述 本项目是一个定制项目&#xff0c;要求采集大气的压力、温度、湿度、PM25、位置等数据并上传到指定的后台服务器。但有时…

一个基于STM32实现的多组分气体分析仪项目

本篇将简要的总结一下一个基于STM32F412ZG实现的多组分气体分析仪的项目。简要描述该项目的软硬件设计及其验证。 一、项目概述 多组分气体分析仪是我公司近期研发的三个主要产品之一。采用模块化设计&#xff0c;可增减配置&#xff0c;可分析混合气体中的氧气、氢气、甲烷、…

外设驱动库开发笔记20:BME280压力湿度温度传感器驱动

嵌入式产品开发中&#xff0c;我们常常会有检测环境温度、压力、湿度的需求。如果有一个集成有这3个传感器的元件&#xff0c;无疑将是很方便的。博世的BME280就能实现这一要求。在这一篇中我们将讨论BME280的驱动设计与实现。 1、功能概述 BME280是一款专为移动应用而开发的…

外设驱动库开发笔记21:BME680环境传感器驱动

环境传感器是一类我们很常用的传感器。它可以方便我们获取压力、温度、湿度以及空气质量等数据。在这一篇中&#xff0c;我们将分析BME680环境传感器的功能&#xff0c;并设计和实现BME680环境传感器的驱动。 1、功能概述 BME680是一款专为移动应用和可穿戴设备开发的集成环境…

外设驱动库开发笔记22:ADXL345三轴数字加速度计驱动

移动设备的广泛应用增加对移动过程中各种参数的检测需求。ADXL345三轴数字加速度计可以用来检测加速度、进而测量倾斜角度等。在这一篇中&#xff0c;我们将讨论ADXL345三轴数字加速度计驱动程序的设计与实现。 1、功能概述 ADXL345是一款小而薄的超低功耗3轴加速度计&#x…