LWIP:RTThread + LWIP

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_TIMEOUT
     here 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_TIMEOUT
     here is enough. */
  return 1;
}

int sys_mbox_valid(sys_mbox_t *mbox)
{
  if (*mbox == NULL)
    return 0;
  else
    return 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_TIMEOUT
     here 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;
  else
    return 1;
}

void sys_sem_set_invalid(sys_sem_t *sem)
{
  *sem = NULL;
}


/*----------------------------IPC互斥量---------------------------------*/

#if LWIP_COMPAT_MUTEX == 0

uint32_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 == 1

sys_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线程),若开启后用户线程会申请互斥量,保护用户线程的运行,直接调用回调函数。建议开启。
    在这里插入图片描述
    在这里插入图片描述
  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值