LWIP:RTThread + LWIP

article/2025/8/25 17:40:59

1. 序言

  • 今天跟大家简单分享 LWIP + RTThread 的移植注意事项,记得以前刚接触 LWIP 那会,是跟着野火的教程一起走,而大部分 LWIP 移植教程都是以 freeRTOS 为主,本着支持 RTThread 的想法,在当时就想着移植 LWIP 到 RTThread 上,没想到翻车了,sockets 连接总是连接不上而 netconn 就可以,后面就放弃了…………,直到现在,把它捡起来!
  • 本文仅简单描述其中需要注意的事项,需要对 RTThread 和 LWIP 有一定的了解,详细的移植教程无法做到一一细说,还是建议大家去看看野火的教程《LwIP 应用开发实战指南》和 《RT-Thread 内核实现与应用开发实战指南》,都是不错的入门书籍(本文也引用了一些资料)。
  • 基于STM32 + RTThread Nano 3.1.3 + LWIP 2.1.3,当然直接使用全功能的RT-Thread IoT 就可以直接拥有丰富的网络协议,但是对于一些仅需要简单的TCP/UDP应用,也是一个不错的移植选择方案。

2. 实现

  1. 实现phy的底层驱动文件。
  2. 移植RTThread Nano 到工程。
  3. 移植LWIP文件到工程,使用STM32CubeMX勾选LWIP协议,参照生成的工程实现ethernetif.c文件。
  4. 移植sys_arch.c文件(非常重要)。
/** 主要实现的内容:内核邮箱、内核互斥量、内核信号量、内核线程创建、内核保护* 可参照STM32CubeMX生成的sys_arch.c*   */
#include <lwip/stats.h>
#include <lwip/debug.h>
#include <string.h>
#include "lwip/def.h"
#include "lwip/sys.h"
#include <lwip/opt.h>
#include <lwip/arch.h>
#include "tcpip.h"
#include "rtthread.h"/*------------------------IPC邮箱实现-------------------------------------------*/uint32_t mbox_count=0;		//用于对象容器名称计数err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{char s1[]="RT_MBX";char s2=mbox_count+'0';char *s3=s1+s2;mbox_count++;	*mbox = rt_mb_create(s3,(rt_ubase_t)size,RT_IPC_FLAG_PRIO);if(*mbox == NULL) {return ERR_MEM;}return ERR_OK;
}void sys_mbox_free(sys_mbox_t *mbox)
{rt_mb_delete(*mbox);
}void sys_mbox_post(sys_mbox_t *mbox, void *msg)
{rt_base_t ret;//由于RTThread邮箱大小为4个字节,所以这里传递的是指针指向的地址while(ERR_OK != rt_mb_send_wait(*mbox, (rt_uint32_t)msg, RT_WAITING_FOREVER));
}err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{rt_base_t ret;//由于RTThread邮箱大小为4个字节,所以这里传递的是指针指向的地址ret = rt_mb_send(*mbox, (rt_uint32_t)msg);if (ret == RT_EOK) {return ERR_OK;} else {return ERR_MEM;}
}err_t sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
{return sys_mbox_trypost(mbox, msg);
}u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout_ms)
{rt_base_t ret;void *msg_dummy;if (!msg) {msg = &msg_dummy;}if (!timeout_ms) {/* wait infinite */ret = rt_mb_recv(*mbox, (rt_uint32_t*)msg, RT_WAITING_FOREVER);} else {rt_tick_t timeout_ticks = timeout_ms;ret = rt_mb_recv(*mbox, (rt_uint32_t*)msg, timeout_ticks);if (ret != RT_EOK) {/* timed out */*msg = NULL;return SYS_ARCH_TIMEOUT;}}/* Old versions of lwIP required us to return the time waited.This is not the case any more. Just returning != SYS_ARCH_TIMEOUThere is enough. */return 1;
}u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{rt_base_t ret;void *msg_dummy;if (!msg) {msg = &msg_dummy;}ret = rt_mb_recv(*mbox, (rt_uint32_t*)&(*msg), 0);if (ret != RT_EOK) {*msg = NULL;return SYS_MBOX_EMPTY;}/* Old versions of lwIP required us to return the time waited.This is not the case any more. Just returning != SYS_ARCH_TIMEOUThere is enough. */return 1;
}int sys_mbox_valid(sys_mbox_t *mbox)
{if (*mbox == NULL)return 0;elsereturn 1;
}void sys_mbox_set_invalid(sys_mbox_t *mbox)
{*mbox = NULL;
}/*----------------------------IPC信号量实现---------------------------------*/uint32_t sem_count=0;		//用于对象容器名称计数err_t sys_sem_new(sys_sem_t *sem, u8_t initial_count)
{char s1[]="RT_SEM";char s2=sem_count+'0';char *s3=s1+s2;sem_count++;*sem = rt_sem_create(s3,1,RT_IPC_FLAG_PRIO);if(*sem == NULL) {return ERR_MEM;}if(initial_count == 0)		{rt_sem_trytake(*sem);}else if(initial_count == 1) {rt_base_t ret = rt_sem_release(*sem);}return ERR_OK;
}u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout_ms)
{rt_base_t ret;if(!timeout_ms) {/* wait infinite */ret = rt_sem_take(*sem, RT_WAITING_FOREVER);} else {rt_tick_t timeout_ticks = timeout_ms;ret = rt_sem_take(*sem, timeout_ticks);if (ret != RT_EOK) {/* timed out */return SYS_ARCH_TIMEOUT;}}/* Old versions of lwIP required us to return the time waited.This is not the case any more. Just returning != SYS_ARCH_TIMEOUThere is enough. */return 1;
}void sys_sem_signal(sys_sem_t *sem)
{rt_base_t ret;ret = rt_sem_release(*sem);
}void sys_sem_free(sys_sem_t *sem)
{rt_sem_delete(*sem);
}int sys_sem_valid(sys_sem_t *sem)
{if (*sem == NULL)return 0;elsereturn 1;
}void sys_sem_set_invalid(sys_sem_t *sem)
{*sem = NULL;
}/*----------------------------IPC互斥量---------------------------------*/#if LWIP_COMPAT_MUTEX == 0uint32_t mutex_count=0;		//用于对象容器名称计数err_t sys_mutex_new(sys_mutex_t *mutex)
{char s1[]="RT_MUTEX";char s2=mutex_count+'0';char *s3=s1+s2;mutex_count++;*mutex = rt_mutex_create(s3,RT_IPC_FLAG_PRIO);if(*mutex == NULL) {return ERR_MEM;}return ERR_OK;
}void sys_mutex_free(sys_mutex_t *mutex)
{rt_mutex_delete(*mutex);
}void sys_mutex_lock(sys_mutex_t *mutex)
{rt_base_t ret;ret = rt_mutex_take(*mutex, RT_WAITING_FOREVER);
}void sys_mutex_unlock(sys_mutex_t *mutex)
{rt_base_t ret;ret = rt_mutex_release(*mutex);
}#endif/*----------------------------IPC线程---------------------------------*/sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{rt_base_t ret;sys_thread_t lwip_thread;size_t rtos_stacksize;rtos_stacksize = (size_t)stacksize;lwip_thread = rt_thread_create(name,thread,arg,rtos_stacksize,prio,50);													 rt_thread_startup(lwip_thread);return lwip_thread;
}/*----------------------------保护内存申请等---------------------------------*///强烈建议在lwipopts.h将该宏打开,
//SYS_LIGHTWEIGHT_PROT宏实现可以用于保护内核申请内存时通过互斥量保护,
//可以有效避免由于内存申请出现的错误。#if SYS_LIGHTWEIGHT_PROT == 1sys_mutex_t lwip_sys_mutex;			//内存保护void sys_init(void)
{lwip_sys_mutex = rt_mutex_create("RT_MUTEX_PRO",RT_IPC_FLAG_PRIO);
}sys_prot_t sys_arch_protect(void)
{rt_base_t ret;ret = rt_mutex_take(lwip_sys_mutex, RT_WAITING_FOREVER);return 1;
}void sys_arch_unprotect(sys_prot_t pval)
{LWIP_UNUSED_ARG(pval);rt_base_t ret;ret = rt_mutex_release(lwip_sys_mutex);}
//void sys_arch_msleep(u32_t delay_ms)
//{
//  rt_thread_delay(delay_ms);
//}#endif/*-----------------------------LWIP时间计数-------------------------------------*/u32_t sys_now(void)
{return rt_tick_get();
}u32_t sys_jiffies(void)
{return rt_tick_get();
}
  1. 由于RTThread线程启动的特殊性,main函数是在一个线程里面被调用执行的,如果在main函数里面创建更高优先级的线程,就会马上执行高优先级线程,且一般我们会把网口的处理接收ethernetif_input单独作为一个线程来运行,通过ETH外设中断发送信号量使接收线程ethernetif_input运行,因此,我们需要在初始化LWIP内核和网口的初始化时,禁止中断和线程调度,避免LWIP内核未完成初始化时,ethernetif_input线程运行或发生中断过程中使用了内核信号量,但内核并没初始化出信号量,就会发生错误。
//伪代码
rt_base_t ret =rt_hw_interrupt_disable();
.....
LAN8720_Init();
tcpip_init(NULL,NULL);
....
rt_hw_interrupt_enable(ret);
  1. 一般使用的线程优先级,让LWIP内核线程反应更快。
    在这里插入图片描述
  2. 宏:SYS_LIGHTWEIGHT_PROT,开启后并用互斥量实现,能够保护内核在申请内存时安全。(sys_init初始化互斥量、sys_arch_protect申请互斥量、sys_arch_unprotect释放互斥量)
  3. 宏:LWIP_TCPIP_CORE_LOCKING,不开启时用户线程发送邮箱信息到tcpip线程,tcpip线程再来处理回调,如下图,这增加了系统开销(线程切换,从用户线程切换到tcpip线程),若开启后用户线程会申请互斥量,保护用户线程的运行,直接调用回调函数。建议开启。
    在这里插入图片描述
    在这里插入图片描述

http://chatgpt.dhexx.cn/article/dwWaaNBV.shtml

相关文章

基于rt thread smart构建EtherCAT主站

我把源码开源到到了gitee&#xff0c;https://gitee.com/rathon/rt-thread-smart-soem 有兴趣的去可以下载下来跑一下 软件工程推荐用vscode 打开。rt thread smart的教程可以参考官网资料&#xff0c;这里就不多阐述了。 用的是100ask的imx6ull开发板&#xff0c;雷赛的dm3e-5…

Rtthread线程源码分析

Rtthread线程源码分析 /*** This function will create a thread object and allocate thread object memory* and stack.** param name the name of thread, which shall be unique* param entry the entry function of thread* param parameter the parameter of thread ent…

rtthread套娃移植

和大家分享下将基于rtthread的项目移植到其他平台的经验。 背景 最近做了一个物联网项目移植。原先的项目使用的硬件平台为stm32f401sim800c(mcu 2G modem)&#xff0c;软件平台为rtthread 4.0.1。移植到的新平台为BC25(nb modem)&#xff0c;软件平台为BC25 opencpu sdk&am…

关于RT thread系统节拍时钟的配置

关于RT thread系统节拍时钟的配置 -----本文基于rt-thread-3.1.3版本编写 首先&#xff0c;使用RTthread OS时&#xff0c;要配置&#xff08;或者明白&#xff09;它的系统节拍rt_tick&#xff08;划重点&#xff09;。 系统节拍 系统节拍是特定的周期中断&#xff0c;可以…

rtthread学习

RT-Thread 内核实现与应用开发实战指南 1、数据类型rtdef.h 中的数据类型 在裸机系统中&#xff0c;他们统统放在一个叫栈的地方&#xff0c;栈是单片机 RAM 里面一段连续的内存空间&#xff0c;栈的大小一般在启动文件或者链接脚本里面指定&#xff0c; 最后由 C 库函数_m…

Rtthread 内存管理

Rtthread 堆内存管理 #define HEAP_MAGIC 0x1ea0 struct heap_mem {/* magic and used flag */rt_uint16_t magic; //魔数&#xff0c;固定值rt_uint16_t used; //使用标记&#xff0c;1为该内存已经被使用rt_size_t next, prev; //双向链表偏移 }; #define MIN_SIZE 12 //一…

RT-Thread学习

一、入门 RT-Thread官网  官网文档   Rt-thread学习文档  RT-Thread官方bilibili视频号   GD32官网 教你动手移植RT-Thread到国产MCU    如何移植RT-Thread到GD32单片机上&#xff08;非studio版&#xff09; 东方青讲RT-Thread  RT-Thread内核入门指南 RT-Thread…

RT Thread之ADC电压读取

官网连接&#xff1a;https://docs.rt-thread.org/#/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc 一、配置步骤&#xff1a; 1、用cubemx配置底层&#xff1b; 2、cubemx配置好的文件替换之前的配置文件&#xff1b; 3、修改Kconfig文件&…

rtthread mqtt

rtthread 以太网 (LAN8720A) 基于以太网的应用mqtt&#xff0c;在**rtthread 以太网 (LAN8720A)**中已经实现了tcp/ip通信正常&#xff0c;接下需要启用mqtt模块&#xff0c; 嵌入式mqtt设备 rtthread 启用mqtt 在rtthread中田间 pahomqtt 软件包&#xff0c;并右键详细配置…

【RTThread】修改Finsh打印串口波特率

这里需要注意得是一定要在hw_board_init初始化完成之后修改串口波特率。 /* 串口设备句柄 */static rt_device_t uart_device RT_NULL;/* 查找系统中的串口设备 */uart_device rt_device_find("uart1"); // 这里/* 串口配置结构体&#xff0c;使用serial.h的宏定义…

RT Thread之 Uart2 操作

官网连接&#xff1a;https://docs.rt-thread.org/#/rt-thread-version/rt-thread-standard/programming-manual/device/uart/uart 通过前面的学习&#xff0c;基本上RT Thread操作步骤都是&#xff0c;先配置单片机底层&#xff0c;然后再通过应用层映射到底层&#xff0c;最…

rtthread

链表 初始化双向链表 rt_inline void rt_list_init(rt_list_t *l) {l->next l->prev l; }插入 rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n) {l->next->prev n;n->next l->next;l->next n;n->prev l; }在NODE1后面插入节…

RT Thread根据开发板制作BSP方法

之前一直不懂怎么使用RT Thread的软件包&#xff0c;感谢网上的大神&#xff0c;看了你们的博客后大概了解一些&#xff0c;在此做下记录。用RT Thread软件包需要RT Thread的系统&#xff0c;但是RT Thread和RT Thread nano不一样&#xff0c;具体区别见 RT Thread官网&#xf…

rtthread开关中断

1 rtthread开关中断函数(cortex-m) /** rt_base_t rt_hw_interrupt_disable();*/ .global rt_hw_interrupt_disable .type rt_hw_interrupt_disable, %function rt_hw_interrupt_disable:MRS r0, PRIMASKCPSID IBX LR/** void rt_hw_interrupt_enable(rt_base_t le…

RTThread入门

RT-Thread入门 1.初识RT-Thread 嵌入式系统是一种完全嵌入在装置或设备内部&#xff0c;为满足特定需求而设计的计算机系统&#xff0c;譬如生活中常见的嵌入式系统就有&#xff1a;电视机顶盒、路由器、电冰箱、微波炉与移动电话等。 嵌入式操作系统是应用于嵌入式系统的软…

什么是RT-Thread?

一、RT-Thread的定义 RT-Thread&#xff0c;全称是 Real Time-Thread&#xff0c; 是一款主要由中国开源社区主导开发的开源实时操作系统&#xff08;许可证GPLv2&#xff09;&#xff0c;包含了实时、嵌入式系统相关的各个组件&#xff1a;TCP/IP协议栈、图形用户界面等。 相…

Redis启动失败的原因及解决方法

跑了近半年的Redis,今天早上来开启电脑运行程序的时候发现提示无法连接redis,暗想自己明明设置了开机自启的阿,以前也一直没问提,今天怎么就连不上了重启了下redis就提示如下错误 网上搜了好久都没找到解决办法,后来想起来去查看了下redis的日志文件 发现提示当前版本的redis无…

redis启动、获取密码及修改密码

一、启动redis服务的两种方式 查看密码是以redis服务已启动的前提下进行的&#xff0c;可直接在服务中右键启动redis或者安装根目录运行cmd输入《redis-server.exe》(不推荐不推荐不推荐&#xff0c;说三遍&#xff0c;命令行启动好像有bug&#xff0c;启动后redis能用&#x…

CentOS安装Redis及redis启动与关闭、配置(详细)

在项目使用redis过程中&#xff0c;在centos7上部署redis&#xff0c;查找相关资料并总结、记录&#xff0c;以备后续查看。 目录 一、Redis介绍 二、在CentOS上部署Redis 1、Redis安装包可以从官网上下载或者直接命令下载 升级到gcc 9.3&#xff1a; 3、Redis配置文件…

Redis启动和连接

一&#xff09;Redis简介 Redis不是简单的键值存储&#xff0c;它实际上是一个数据结构服务器&#xff0c;支持不同类型的值。 备注&#xff1a;由于我电脑是32位操作系统&#xff0c;所有就不提供redis软件下载地址了&#xff0c;请到官网下载使用。 软件解压之后&#xff0…