STM32F103VET6基于ENC28J60移植LWIP1.4.1(标准库,FreeRTOS)

在进行移植之前,大家需要学会FreeRTOS的基本使用与无OS移植LWIP的经验,因为基于FreeRTOS移植LWIP需要准备一个FreeRTOS模板项目以及无OS移植LWIP成功的项目。

不知道什么是FreeRTOS的应该不会点开这篇博客。

没有无OS移植LWIP项目的一定要准备一个,LWIP相关文件就直接复制过来。可以点击查看我的上一篇博客来学习。

硬件与引脚连接也是看上一篇博客。

准备模板

准备一个FreeRTOS模板项目,启用计数量、消息队列
在这里插入图片描述

启动文件把堆栈都设置大点
在这里插入图片描述

LWIP文件夹

复制无OS移植LWIP项目中的LWIP文件夹,粘贴到本项目下
在这里插入图片描述

新建组

打开空STM32项目、新建组lwip-api、lwip-arch、lwip-netif、lwip-core、lwip-core-ipv4

lwip-arch添加cc.h、lwipopts.h、perf.h、网卡驱动文件,arch目录下新建两个文件sys_arch.h与sys_arch.c并加入分组
在这里插入图片描述

lwip-netif添加ethernetif.c、etharp.c、slipif.c
在这里插入图片描述

lwip-core添加core全部文件,lwip-core-ipv4添加core/ipv4全部文件
在这里插入图片描述在这里插入图片描述

lwip-api添加api下的全部文件
在这里插入图片描述

魔术棒

添加头文件路径
在这里插入图片描述

Misc Control里面输入–diag_suppress=1,1295,174,167,111,128,177,550
规避部分警报
在这里插入图片描述

修改lwipopts.h

NO SYS 设置为0
LWIP_SOCKET与LWIP_NETCONN设置为1

新添加以下宏:

//以太网首部填充:
#define ETH_PAD_SIZE	0
//TCPIP任务以及用到的队列大小定义,默认为0所以必须要改
#define TCPIP_THREAD_STACKSIZE          512
#define TCPIP_THREAD_PRIO               1
#define TCPIP_MBOX_SIZE                 1024
#define DEFAULT_RAW_RECVMBOX_SIZE       1024
#define DEFAULT_UDP_RECVMBOX_SIZE       1024
#define DEFAULT_TCP_RECVMBOX_SIZE       1024
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

#define NO_SYS                  0
#define LWIP_SOCKET             1
#define LWIP_NETCONN            1

#define MEM_ALIGNMENT           4  
#define MEM_SIZE                10*1024

#define TCP_SND_BUF             4000
#define TCP_MSS                 1000

#define ETHARP_DEBUG 	LWIP_DBG_ON
#define ICMP_DEBUG    LWIP_DBG_ON

//新加部分
#define ETH_PAD_SIZE						0

#define TCPIP_THREAD_STACKSIZE          512
#define TCPIP_THREAD_PRIO               1
#define TCPIP_MBOX_SIZE                 1024
#define DEFAULT_RAW_RECVMBOX_SIZE       1024
#define DEFAULT_UDP_RECVMBOX_SIZE       1024
#define DEFAULT_TCP_RECVMBOX_SIZE       1024
#endif /* __LWIPOPTS_H__ */

修改sys_arch.h

实现部分要求的类型以及函数定义(部分用宏定义代替)

LWIP要求实现的类型有三个:sys_sem_t(信号量)、sys_mbox_t(邮箱)、sys_thread_t(任务),其实还有一个互斥量也要实现,但是可以通过设置宏LWIP_COMPAT_MUTEX为1,使得互斥量等价于信号量

这里信号量、邮箱、任务分别用FreeRTOS的信号量、消息队列与任务来实现。

LWIP还要我们实现一些函数,部分过于简单的函数直接在这个头文件里面用宏定义代替。

#ifndef __SYS_ARCH_H__
#define __SYS_ARCH_H__

#define LWIP_COMPAT_MUTEX 					1

#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#include "task.h"

typedef xSemaphoreHandle 						sys_sem_t;
#define SYS_SEM_NULL 								NULL
#define sys_sem_valid(sema) 				((*sema)!=NULL)
#define sys_sem_set_invalid(sema) 	((*sema)=NULL)

typedef xQueueHandle		 						sys_mbox_t;
#define SYS_MBOX_NULL 							NULL
#define sys_mbox_valid(mbox) 				((*mbox)!=NULL)
#define sys_mbox_set_invalid(mbox) 	((*mbox)=NULL)

typedef xTaskHandle sys_thread_t;
#endif

修改sys_arch.c

实现部分要求实现的函数,是关于信号量、邮箱以及创建新任务的。

要求实现的函数较多,这里直接贴代码。实现的思路其实就是封装FreeRTOS对应功能的函数。

如果你不实现这些函数的话编译会报错

#include "sys_arch.h"
#include "sys.h"
#include "cc.h"
#include "err.h"
err_t sys_sem_new(sys_sem_t *sem, u8_t count){
		xSemaphoreHandle *new_sem = NULL;
		new_sem = xSemaphoreCreateCounting(1,count);
		if (new_sem!=NULL){
				*sem = (void*) new_sem;
				return ERR_OK;
		}
		*sem = SYS_SEM_NULL;
		return ERR_MEM;
}

void sys_sem_free(sys_sem_t *sem){
		vSemaphoreDelete(*sem);
}

void sys_sem_signal(sys_sem_t *sem){
		if (xSemaphoreGive(*sem) != pdTRUE) printf("signal give fail\r\n");
}

u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout){
		portTickType StartTime=0,EndTime=0,Elapsed=0;
		StartTime = xTaskGetTickCount();
		if (timeout == 0){
				xSemaphoreTake(*sem,portMAX_DELAY);
				EndTime = xTaskGetTickCount();
				Elapsed = EndTime - StartTime;
				return Elapsed;
		}
		else{
				if (xSemaphoreTake(*sem, timeout) == pdTRUE){
						EndTime = xTaskGetTickCount();
						Elapsed = EndTime - StartTime;
						return (Elapsed);
				}
				else
				{
					return SYS_ARCH_TIMEOUT;
				}
		}
}

err_t sys_mbox_new(sys_mbox_t *mbox, int size){
	*mbox = xQueueCreate( size, sizeof( void * ) );
	if(*mbox!=NULL) return ERR_OK;
	return ERR_BUF;
}

void sys_mbox_free(sys_mbox_t *mbox){
	vQueueDelete(*mbox);
}
	
void sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
	while ( xQueueSendToBack(*mbox, &msg, portMAX_DELAY ) != pdTRUE ){sys_msleep(1);}
}

err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
	 err_t result;
   if ( xQueueSend( *mbox, &msg, 0 ) == pdPASS )
   {
      result = ERR_OK;
   }
   else {
      result = ERR_MEM;		
   }
	 return result;
}

u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
	void *dummyptr;
	portTickType StartTime, EndTime, Elapsed;
	StartTime = xTaskGetTickCount();
	if ( msg == NULL ) msg = &dummyptr;
	if ( timeout != 0 )
	{
		if (pdTRUE == xQueueReceive(*mbox, &(*msg), timeout))
		{
			EndTime = xTaskGetTickCount();
			Elapsed = (EndTime - StartTime);	
			return ( Elapsed );
		}
		else
		{
			*msg = NULL;
			return SYS_ARCH_TIMEOUT;
		}
	}
	else
	{
		while( pdTRUE != xQueueReceive( *mbox, &(*msg), portMAX_DELAY ) ){sys_msleep(1);} // time is arbitrary
		EndTime = xTaskGetTickCount();
		Elapsed = (EndTime - StartTime);
		return ( Elapsed );	
	}
}
 
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
	void *dummyptr;
	if ( msg == NULL ) msg = &dummyptr;
  if ( pdTRUE == xQueueReceive( *mbox, &(*msg), 0 ) ) return ERR_OK;
  else return SYS_MBOX_EMPTY;
}

sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{
		xTaskHandle CreatedTask;
		int result;
    result = xTaskCreate( thread, ( portCHAR * ) name, stacksize, arg, prio, &CreatedTask );
	  if(result == pdPASS){ LWIP_PLATFORM_DIAG("tcpip_thread ok\r\n"); return CreatedTask;}
	  else { LWIP_PLATFORM_DIAG("tcpip_thread no ok\r\n"); return NULL;}
}

sys_prot_t sys_arch_protect(void)
{
	vPortEnterCritical();
	return 1;
}
 
void sys_arch_unprotect(sys_prot_t pval)
{
	( void ) pval;
	vPortExitCritical();
}

void sys_assert( const char *msg )
{	
	( void ) msg;
   vPortEnterCritical(  );
   for(;;)
   ;
}

void sys_init(void){
	printf("init ok\r\n");
}

修改LWIP初始化函数

原来使用lwip_init,现在把它改成tcpip_init

netif_add最后一个input参数原来是ethernetif_input,现在改成tcpip_input,要引入头文件lwip/tcpip.h

static void LWIP_Init(void){
	struct ip_addr ipaddr,netmask,gw;
	tcpip_init(NULL,NULL);
	IP4_ADDR(&gw,192,168,1,1);
	IP4_ADDR(&ipaddr,192,168,1,114);
	IP4_ADDR(&netmask,255,255,255,0);
	netif_add(&enc28j60_netif,&ipaddr,&netmask,&gw,NULL,ethernetif_init,tcpip_input);
	netif_set_default(&enc28j60_netif);
	netif_set_up(&enc28j60_netif);
}

中断文件

写一个sys_now函数返回Tick(FreeRTOS有这种变量,就不用自己实现了)。再根据ENC28J60引脚连接图,编写对应的外部中断函数

unsigned int sys_now(void){
		return xTaskGetTickCount();
}

void EXTI1_IRQHandler(){
	if(EXTI_GetITStatus(EXTI_Line1) != RESET) {
		EXTI_ClearITPendingBit(EXTI_Line1);
	}
}

主文件

FreeRTOS启动前初始化外设,调用LWIP初始化函数(此时会创建TCPIP进程),然后创建一个任务循环调用ethernetif_input(相当于无OS时的主函数)

ethernetif_input函数会调用tcpip_input将数据包发送至初始化时自动创建的消息队列中,而自动创建的TCPIP进程会读取邮箱处理数据。

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "bsp_led.h"
#include "psd_usart.h"

#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/tcpip.h"
#include "lwip/init.h"
#include "lwip/timers.h"
#include "netif/etharp.h"
#include "enc28j60.h"

static TaskHandle_t AppTaskCreate_Handle = NULL;
static TaskHandle_t LWIP_Task_Handle = NULL;

struct netif enc28j60_netif;
err_t ethernetif_init(struct netif* netif);
void ethernetif_input(struct netif *netif);

static void LWIP_Init(void){
	struct ip_addr ipaddr,netmask,gw;
	tcpip_init(NULL,NULL);
	IP4_ADDR(&gw,192,168,1,1);
	IP4_ADDR(&ipaddr,192,168,1,114);
	IP4_ADDR(&netmask,255,255,255,0);
	netif_add(&enc28j60_netif,&ipaddr,&netmask,&gw,NULL,ethernetif_init,tcpip_input);
	netif_set_default(&enc28j60_netif);
	netif_set_up(&enc28j60_netif);
}

static void BSP_Init(void){
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	Usart_1_Config();
  Enc28j60_Init();
	LWIP_Init();
}

static void LWIP_Task(void* parameter){
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
			ethernetif_input(&enc28j60_netif);
			vTaskDelayUntil(&xLastWakeTime,500);
	}
}


static void AppTaskCreate(void){
	BaseType_t xReturn = pdPASS;
	taskENTER_CRITICAL();
	xReturn = xTaskCreate(
		(TaskFunction_t) LWIP_Task,
		(const char*)    "LWIP_Task",
		(uint32_t)       128,
		(void*)				NULL,
		(UBaseType_t)    4,
		(TaskHandle_t*)  &LWIP_Task_Handle
	);
	if (pdPASS == xReturn) 
		printf("LWIP_Task created successfully\r\n");
	else 			
		printf("LWIP_Task created failed\r\n");

	vTaskDelete(AppTaskCreate_Handle);
	taskEXIT_CRITICAL();
}


int main(void){
	BaseType_t xReturn = pdPASS;
	BSP_Init();
	xReturn = xTaskCreate(
			(TaskFunction_t) AppTaskCreate,
			(const char*)    "AppTaskCreate",
			(uint32_t)       128,
			(void*)					 NULL,
			(UBaseType_t)    3,
			(TaskHandle_t*)  &AppTaskCreate_Handle
	);
											
	if (xReturn == pdPASS) 
		vTaskStartScheduler();
	else 
		printf("error\r\n");
	while(1);	
}

测试

连接好硬件,以太网IP与单片机网卡设置为同一网段内,ping单片机的IP地址:
在这里插入图片描述测试成功。

源码

链接:https://pan.baidu.com/s/1s8-XFTKBtEbpjMT6Iu_hxw?pwd=4869
提取码:4869

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
lwip是瑞士计算机科学院的一个开源的TCP/IP协议栈实现.   lwIP是TCP/IP协议栈的一个实现。lwIP协议栈主要关注的是怎么样减少内存的使用和代码的大小,这样就可以让lwIP适用于资源有限的小型平台例如嵌入式系统。为了简化处理过程和内存要求,lwIP对API进行了裁减,可以不需要复制一些数据。   LwIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。LwIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用,一般它只需要几百字节的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的嵌入式系统中使用。   其主要特性如下:   ①支持多网络接口下的IP转发;   ②支持ICMP协议;   ③包括实验性扩展的UDP(用户数据报协议);   ④包括阻塞控制、RTT 估算、快速恢复和快速转发的TCP(传输控制协议);   ⑤提供专门的内部回调接口(Raw API),用于提高应用程序性能;   ⑥可选择的Berkeley接口API (在多线程情况下使用) 。   (1) 信号量   LwIP中需要使用信号量进行通信,所以在sys_arch中应实现相应的信号量结构体 struct sys_semt和处理函数sys_sem_new() 、sys_sem_free() 、sys_sem_signal ( ) 和sys_arch_sem_wait ( ) 。由于μC/OS已经实现了信号量OSEVENT的各种操作,并且功能和LwIP上面几个函数的目的功能是完全一样的,所以只要把μC/OS的函数重新包装成上面的函数,就可直接使用。   (2) 消息队列   LwIP 使用消息队列来缓冲、传递数据报文,因此要实现消息队列结构sys_mbox_t ,以及相应的操作函数:sys_mbox_new() 、sys_mbox_free () 、sys_mbox _post () 和sys_arch_mbox_fetch() 。μC/OS实现了消息队列结构及其操作,但是μC/OS没有对消息队列中的消息进行管理,因此不能直接使用,必须在μC/OS的基础上重新实现。具体实现时,对队列本身的管理利用μC/OS自己的OSQ操作完成,然后使用μC/OS中的内存管理模块实现对消息的创建、使用、删除和回收,两部分综合起来形成了LwIP的消息队列功能。   (3) 定时器函数   LwIP中每个和TCP/IP相关的任务的一系列定时事件组成一个单向链表,每个链表的起始指针存在lwip_timeouts 的对应表项中,如图2所示。移植时需要实现struct sys_timeouts * sys_arch_timeouts (void) 函数,该函数返回目前正处于运行态的线程所对应的timeout 队列指针。   (4) 创建新线程函数   在μC/OS 中,没有线程(thread) 的概念,只有任务(Task) 。它提供了创建新任务的系统API调用OSTaskCreate,因此只要把OSTaskCreate封装一下,就可以实现 sys_thread_new。需要注意的是LwIP中的thread并没有μC/OS 中优先级的概念,实现时要由用户事先为LwIP中创建的线程分配好优先级。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值