目录
- 准备模板
- LWIP文件夹
- 新建组
- 魔术棒
- 修改lwipopts.h
- 修改sys_arch.h
- 修改sys_arch.c
- 修改LWIP初始化函数
- 中断文件
- 主文件
- 测试
- 源码
在进行移植之前,大家需要学会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(count,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