目录

LwIP移植前期准备

LwIP移植流程

修改lwipopts.h

修改lwip_comm.c文件

修改ethernetif.c/h文件

修改ethernetif_input函数

修改ethernet.c文件

添加应用程序


LwIP是支持操作系统的,在操作系统的支持下我们可以使用LwIP提供的另外两种API编程接口编程。没有操作系统的时候,我们只能使用RAW编程,相较于其他两种API编程,RAW编程难度较大,需要用户对LwIP协议栈有较深的了解。使用操作系统之后,我们可以多任务运行,将LwIP作为任务来运行。

LwIP移植前期准备

在基于无操作系统的LwIP移植工程的基础之上,我们将FreeRTOS移植到工程中,首先需要将FreeRTOS的源码添加进工程中,然后工程新建分组,然后添加FreeRTOS源码进工程中。这里需要参考FreeRTOS的移植教程。

FreeRTOS源码下载地址:FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions

 

其中最主要的文件如上图所示,主要分为三种,一种是FreeRTOS的核心源码,include是头文件,portable是和移植相关的文件。

将对应的文件结构复制到无操作系统移植的LwIP工程中,在Middlewares文件夹下新建一个FreeRTOS的文件夹,再把对应文件加入到工程中,如下图所示:

 

这里需要注意的就是,添加源文件之后,需要添加头文件的路径,凡是有头文件的路径都添加到工程中。

LwIP移植流程

修改lwipopts.h

这个头文件是LwIP的配置裁剪文件,在前面提供的无操作系统移植博客里已经详细说明了该文件,这里我们可以去ST官方提供带RTOS 的参考例程中,复制一个lwipopts.h到工程中,覆盖原来的一个配置文件。C:\Users\STM32Cube\Repository\STM32Cube_FW_F4_V1.26.1\Projects\STM32469I_EVAL\Applications\LwIP\LwIP_HTTP_Server_Netconn_RTOS\Inc

 该文件的源码如下:

/**
  ******************************************************************************
  * @file    LwIP/LwIP_HTTP_Server_Netconn_RTOS/Inc/lwipopts.h
  * @author  MCD Application Team
  * @brief   lwIP Options Configuration.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2017 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under Ultimate Liberty license
  * SLA0044, the "License"; You may not use this file except in compliance with
  * the License. You may obtain a copy of the License at:
  *                             www.st.com/SLA0044
  *
  ******************************************************************************
  */
#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

/**
 * NO_SYS表示无操作系统模拟层,无操作系统为1,有操作系统为0,这个参数需要根据有误操作设置不同的值
*/
#define NO_SYS                  0        

/* ---------- 内存选项s ---------- */
/* 内存对齐,按照4字节对齐 */
#define MEM_ALIGNMENT           4

/* 堆内存的大小,如果需要设置更大的堆内存,那么设置高一点*/
#define MEM_SIZE                (10*1024)

/* 设置内存池的数量*/
#define MEMP_NUM_PBUF           10
/* UDP协议控制块的数量 */
#define MEMP_NUM_UDP_PCB        6
/* TCP的数量 */
#define MEMP_NUM_TCP_PCB        10
/* 监听TCP的数量 */
#define MEMP_NUM_TCP_PCB_LISTEN 5
/* 同时排队的TCP的数量段 */
#define MEMP_NUM_TCP_SEG        8
/* 超时模拟活动的数量*/
#define MEMP_NUM_SYS_TIMEOUT    10


/* ---------- Pbuf options ---------- */
/* 内存池中的每个内存块大小 */
#define PBUF_POOL_SIZE          8

/* pbuf池中每个pbuf的大小 */
#define PBUF_POOL_BUFSIZE       1524

/* ---------- TCP options ---------- */
#define LWIP_TCP                1
#define TCP_TTL                 255

/* 控制TCP是否应该对到达的段进行排队秩序,如果你的设备内存不足,定义为0 */
#define TCP_QUEUE_OOSEQ         0

/* TCP最大段大小. */
#define TCP_MSS                 (1500 - 40)	  /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */

/* TCP sender buffer space (bytes). */
#define TCP_SND_BUF             (4*TCP_MSS)

/*  TCP发送缓冲区空间,这必须是至少需要2*TCP_SND_BUF/TCP_MSS才能正常工作*/

#define TCP_SND_QUEUELEN        (2* TCP_SND_BUF/TCP_MSS)

/* TCP 接收窗口. */
#define TCP_WND                 (2*TCP_MSS)


/* ---------- ICMP options ---------- */
#define LWIP_ICMP                       1


/* ---------- DHCP options ---------- */
#define LWIP_DHCP               1


/* ---------- UDP options ---------- */
#define LWIP_UDP                1
#define UDP_TTL                 255


/* ---------- Statistics options ---------- */
#define LWIP_STATS 0

/* ---------- link callback options ---------- */
/* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
 * whenever the link changes (i.e., link down)
 */
#define LWIP_NETIF_LINK_CALLBACK        1


/* 
The STM32F4xx allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:
 - To use this feature let the following define uncommented.
 - To disable it and process by CPU comment the  the checksum.
*/
#define CHECKSUM_BY_HARDWARE 


#ifdef CHECKSUM_BY_HARDWARE
  /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
  #define CHECKSUM_GEN_IP                 0
  /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
  #define CHECKSUM_GEN_UDP                0
  /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
  #define CHECKSUM_GEN_TCP                0 
  /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
  #define CHECKSUM_CHECK_IP               0
  /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
  #define CHECKSUM_CHECK_UDP              0
  /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
  #define CHECKSUM_CHECK_TCP              0
  /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/  
  #define CHECKSUM_GEN_ICMP               0
#else
  /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
  #define CHECKSUM_GEN_IP                 1
  /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
  #define CHECKSUM_GEN_UDP                1
  /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
  #define CHECKSUM_GEN_TCP                1
  /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
  #define CHECKSUM_CHECK_IP               1
  /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
  #define CHECKSUM_CHECK_UDP              1
  /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
  #define CHECKSUM_CHECK_TCP              1
  /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/  
  #define CHECKSUM_GEN_ICMP               1
#endif


/*
   ----------------------------------------------
   ---------- Sequential layer options ----------
   ----------------------------------------------
*/
/**
 * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
 */
#define LWIP_NETCONN                    1

/*
   ------------------------------------
   ---------- Socket options ----------
   ------------------------------------
*/
/**
 * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
 */
#define LWIP_SOCKET                     0

/*
   ------------------------------------
   ---------- httpd options ----------
   ------------------------------------
*/
/** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the
 * file system (to prevent changing the file included in CVS) */
#define HTTPD_USE_CUSTOM_FSDATA   1

/*
   ---------------------------------
   ---------- OS options ----------
   ---------------------------------
*/

#define TCPIP_THREAD_NAME              "TCP/IP"
#define TCPIP_THREAD_STACKSIZE          1000
#define TCPIP_MBOX_SIZE                 6
#define DEFAULT_UDP_RECVMBOX_SIZE       6
#define DEFAULT_TCP_RECVMBOX_SIZE       6
#define DEFAULT_ACCEPTMBOX_SIZE         6
#define DEFAULT_THREAD_STACKSIZE        500
#define TCPIP_THREAD_PRIO               5



#endif /* __LWIPOPTS_H__ */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

这里和无操作系统的工程中的lwipopts.h文件最大的不同就是,添加了OS相关的配置,以及修改了操作系统需要修改的一些配置。

添加sys_arch.c/h文件

如果LwIP使用操作系统的话,sys_arch.c/h文件是LwIP和操作系统交互的一个文件,也可以说是它们是LwIP与操作系统的交接文件,显然sys_arch.c文件包含邮箱、信号量以及互斥锁等IPC策略都是由FreeRTOS操作系统提供的,注意:FreeRTOS操作系统没有邮箱的概念,可使用消息队列代替邮箱机制。

打开“contrib-2.1.0\ports\freertos\路径下的文件夹,该文件夹包含了与 FreeRTOS 相关的 sys_arch.c/.h 文件,笔者把这两个文件复制到本章实验的 Middlewares\lwip\arch 路径下,该文 件夹结构如下图所示:

 然后在工程中,添加对应的这两个文件。

这里还忘了说,添加FreeRTOS还需要添加一个FreeRTOS的配置头文件进USER文件夹中,它是对操作系统的配置和裁剪的一个文件,否则会报错。

这里还需要注意一点就是:..\MiddleWare\LwIP\src\api\err.c(50): error:  #20: identifier "ENOMEM" is undefined

 其实你只要跳转一下就会发现是没有定义这个宏,可以在LwIP中添加这个配置宏即可。

这里需要把这个4U去掉U,才不会出现报错:..\FreeRTOS\portable\RVDS\ARM_CM4F\port.c(483): error: A1586E: Bad operand types (UnDefOT, Constant),最后在把三个中断函数注释掉即可,以及sys_now函数在ethernetif.c中和我们添加的sys_arc.c中重复定义了该函数,注释掉其中的ethernetif.c中的即可。

修改lwip_comm.c文件

该文件主要修改网络配置的信息,比如默认IP的配置,LwIP的初始化以及DHCP等处理,下面我们对lwip_comm.c文件简单修改,如下所示:

 1、删除lwip轮询函数lwip_periodic_handle函数

2、修改lwip_comm_init函数,该函数如下

/**
 * @breif       LWIP初始化(LWIP启动的时候使用)
 * @param       无
 * @retval      0,成功
 *              1,内存错误
 *              2,以太网芯片初始化失败
 *              3,网卡添加失败.
 */
uint8_t lwip_comm_init(void)
{
    uint8_t retry = 0;
    struct netif *netif_init_flag;              /* 调用netif_add()函数时的返回值,用于判断网络初始化是否成功 */
    ip_addr_t ipaddr;                           /* ip地址 */
    ip_addr_t netmask;                          /* 子网掩码 */
    ip_addr_t gw;                               /* 默认网关 */

    if (ethernet_mem_malloc())return 1;         /* 内存申请失败*/

    lwip_comm_default_ip_set(&g_lwipdev);       /* 设置默认IP等信息 */

    while (ethernet_init())                     /* 初始化以太网芯片,如果失败的话就重试5次 */
    {
        retry++;

        if (retry > 5)
        {
            retry = 0;                          /* 以太网芯片初始化失败 */
            return 3;
        }
    }

    lwip_init();                                /* 初始化LWIP内核 */

#if LWIP_DHCP                                   /* 使用动态IP */
    ipaddr.addr = 0;
    netmask.addr = 0;
    gw.addr = 0;
#else   /* 使用静态IP */
    IP4_ADDR(&ipaddr, g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
    IP4_ADDR(&netmask, g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
    IP4_ADDR(&gw, g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
    printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n", g_lwipdev.mac[0], g_lwipdev.mac[1], g_lwipdev.mac[2], g_lwipdev.mac[3], g_lwipdev.mac[4], g_lwipdev.mac[5]);
    printf("静态IP地址........................%d.%d.%d.%d\r\n", g_lwipdev.ip[0], g_lwipdev.ip[1], g_lwipdev.ip[2], g_lwipdev.ip[3]);
    printf("子网掩码..........................%d.%d.%d.%d\r\n", g_lwipdev.netmask[0], g_lwipdev.netmask[1], g_lwipdev.netmask[2], g_lwipdev.netmask[3]);
    printf("默认网关..........................%d.%d.%d.%d\r\n", g_lwipdev.gateway[0], g_lwipdev.gateway[1], g_lwipdev.gateway[2], g_lwipdev.gateway[3]);
#endif  /* 向网卡列表中添加一个网口 */
    netif_init_flag = netif_add(&g_lwip_netif, (const ip_addr_t *)&ipaddr, (const ip_addr_t *)&netmask, (const ip_addr_t *)&gw, NULL, &ethernetif_init, &ethernet_input);


    if (netif_init_flag == NULL)
    {
        return 4;                             /* 网卡添加失败 */
    }
    else                                      /* 网口添加成功后,设置netif为默认值,并且打开netif网口 */
    {
        netif_set_default(&g_lwip_netif);     /* 设置netif为默认网口 */

        if (netif_is_link_up(&g_lwip_netif))
        {
            netif_set_up(&g_lwip_netif);      /* 打开netif网口 */
        }
        else
        {
            netif_set_down(&g_lwip_netif);
        }
    }

#if LWIP_DHCP                               /* 如果使用DHCP的话 */
    g_lwipdev.dhcpstatus = 0;                 /* DHCP标记为0 */
    dhcp_start(&g_lwip_netif);                /* 开启DHCP服务 */
#endif
    return 0;                               /* 操作OK. */
}

此函数主要修改三处地方,首先把lwip_init函数注释掉并添加tcp_init函数,该函数主要创建邮箱以及tcpip线程,该线程专门处理数据消息并把它往上层递交,然后我们修改netif_add函数的第七个形参,该形参不再指向ethernet_input函数而是指向tcpip_input函数,最后修改lwip_pkt_handle函数,该函数如下所示:

void lwip_pkt_handle(void)
{
    BaseType_t xHigherPriorityTaskWoken;
    /* 获取信号量 */
    xSemaphoreGiveFromISR(g_rx_semaphore,&xHigherPriorityTaskWoken);/* 释放二值信号量 */
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);                   /* 如果需要的话进行一次任务切换  */
}

当接收到数据时,该函数会释放一个信号量,该信号量会在ethernetif.c文件中创建,它是实现同步操作的(当有数据发来时,ETH中断会释放一个信号量,接收线程会获取这个信号量并把RX的buffer数据复制到pbuf中,然后程序把pbuf数据以邮箱的形式递交给tcpip线程处理)。

修改ethernetif.c/h文件

这些文件是网卡底层驱动文件,它主要负责接收数据包和处理数据包,下面我们在ethernetif.c文件修改两个函数即可,如下所示:

修改low_level_init函数

该函数主要初始化底层相关的信息,比如设置MAC地址以及初始化ETH的DMA描述符等操作,前面已将介绍过LwIP操作系统的方式是把接收以及协议处理分为两个线程或者任务,接收线程可以在这个函数中创建,该函数如下:

static void
low_level_init(struct netif *netif)
{
    netif->hwaddr_len = ETHARP_HWADDR_LEN; /*设置MAC地址长度,为6个字节*/
    /*初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复*/
    netif->hwaddr[0]=g_lwipdev.mac[0]; 
    netif->hwaddr[1]=g_lwipdev.mac[1]; 
    netif->hwaddr[2]=g_lwipdev.mac[2];
    netif->hwaddr[3]=g_lwipdev.mac[3];   
    netif->hwaddr[4]=g_lwipdev.mac[4];
    netif->hwaddr[5]=g_lwipdev.mac[5];
    
    netif->mtu=1500; /*最大允许传输单元,允许该网卡广播和ARP功能*/
    
    /* 创建一个信号量 */
    g_rx_semaphore = xSemaphoreCreateBinary();

    /* 创建处理ETH_MAC的任务 */
    sys_thread_new("eth_thread",
                   ethernetif_input,        /* 任务入口函数 */
                   netif,                   /* 任务入口函数参数 */
                   NETIF_IN_TASK_STACK_SIZE,/* 任务栈大小 */
                   NETIF_IN_TASK_PRIORITY); /* 任务的优先级 */
    
    /* 网卡状态信息标志位,是很重要的控制字段,它包括网卡功能使能、广播*/
    /* 使能、 ARP 使能等等重要控制位*/
    netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;   /*广播 ARP协议 链接检测*/
    
    HAL_ETH_DMATxDescListInit(&g_eth_handler,g_eth_dma_tx_dscr_tab,g_eth_tx_buf,ETH_TXBUFNB); /*初始化发送描述符*/
    HAL_ETH_DMARxDescListInit(&g_eth_handler,g_eth_dma_rx_dscr_tab,g_eth_rx_buf,ETH_RXBUFNB); /*初始化接收描述符*/
    HAL_ETH_Start(&g_eth_handler); /*开启MAC和DMA*/
}

此函数比无操作系统的 low_level_init 函数添加了两处地方,第一处是调用函数 xSemaphoreCreateBinary 创建信号量,起到同步的作用,第二处是调用函数 sys_thread_new 创 建接收任务,该任务函数为 ethernetif_input 函数以及任务函数的形参指向 netif。

修改ethernetif_input函数

该函数主要获取底层的数据包并递交给 tcpip 线程处理,该函数如下所示:

void
ethernetif_input(void *pParams)
{
    struct netif *netif;
    struct pbuf *p = NULL;
    netif = (struct netif *) pParams;
    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));

    while (1)
    {
        if (xSemaphoreTake( g_rx_semaphore, portMAX_DELAY ) == pdTRUE)
        {
            /* 将接收到的包移动到新的pbuf中 */
            taskENTER_CRITICAL();
REGAIN_PBUF:
            /* 调用low_level_input函数接收数据 */
            p = low_level_input(netif);
            taskEXIT_CRITICAL();

            /* 指向包有效负载,它从一个以太网报头开始 */
            if (p != NULL)
            {
                taskENTER_CRITICAL();

                /* 调用netif结构体中的input字段(一个函数)来处理数据包 */
                if (netif->input(p, netif) != ERR_OK)
                {
                    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
                    pbuf_free(p);
                    p = NULL;
                }
                else
                {
                    xSemaphoreTake( g_rx_semaphore, 0);
                    goto REGAIN_PBUF;
                }

                taskEXIT_CRITICAL();
            }
        }
    }

}  

此函数不再是 ethernetif_input(struct netif *netif)函数结构了,我们已经把该函数以任务的 形式展现出来,该任务函数首先获取信号量,该信号量是由当 ETH 中断释放的,如果有数据 进来,则 ETH 中断会释放一个信号量,此时这个函数获取信号量成功并在 low_level_input 函 数中获取数据包,最后该数据包在 netif->input 指针指向的函数以邮箱形式递交给 tcpip 线程处理。

 打开ethernetif.h文件,我们修改为以下源码:

#ifndef __ETHERNETIF_H__
#define __ETHERNETIF_H__
#include "lwip/err.h"
#include "lwip/netif.h"
err_t ethernetif_init(struct netif *netif); /* 网卡初始化函数 */
#endif

这里没什么好讲解的,我们就是把 void ethernetif_input(struct netif *netif)和 sys_now 函数 声明去除了而已。

修改ethernet.c文件

这个文件我们只修改ETH_IRQHandler中断服务函数,请把该函数内的while语句修改为if语句判断,该函数源码如下所示:

void ETH_IRQHandler(void)
{
 if (ethernet_get_eth_rx_size(g_eth_handler.RxDesc))
 {
 lwip_pkt_handle(); /* 处理以太网数据,即将数据提交给 LWIP */
 }
/* 清除 DMA 中断标志位 */
__HAL_ETH_DMA_CLEAR_IT(&g_eth_handler, ETH_DMA_IT_NIS); 
/* 清除 DMA 接收中断标志位 */
__HAL_ETH_DMA_CLEAR_IT(&g_eth_handler, ETH_DMA_IT_R); 
}

注意:由于 ETH 中断服务函数会调用 FreeRTOS 操作系统信号量相关的函数,所以 ETH 中断必须归 FreeRTOS 操作系统管理,这里笔者设置该中断优先级为 6,如下源码所示:

HAL_NVIC_SetPriority(ETH_IRQn, 6, 0); /* 网络中断优先级应该高一点 */

该函数在 ethernet.c 文件下的 HAL_ETH_MspInit 函数内调用的。至此我们已经修改完成, 编译工程应该不会报错了,下面我们来讲解一下带操作系统例程的工程结构,以后带操作系统 的例程都是以这样的结构编写代码的。

添加应用程序

移植好 lwIP 之后,当然要测试一下移植是否成功。

main文件

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./USMART/usmart.h"
#include "./BSP/KEY/key.h"
#include "./MALLOC/malloc.h"
#include "freertos_demo.h"
int main(void)
{
 HAL_Init(); /* 初始化 HAL 库 */
 sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
 delay_init(168); /* 延时初始化 */
 usart_init(115200); /* 串口初始化为 115200 */
 usmart_dev.init(84); /* 初始化 USMART */
 led_init(); /* 初始化 LED */
 lcd_init(); /* 初始化 LCD */
 key_init(); /* 初始化按键 */
 
my_mem_init(SRAMIN); /* 初始化内部 SRAM 内存池 */
my_mem_init(SRAMEX); /* 初始化外部 SRAM 内存池 */
 my_mem_init(SRAMCCM); /* 初始化内部 SRAMCCM 内存池 */
 freertos_demo(); /* 创建 lwIP 的任务函数 */
}

可以看到,在 main.c 文件中只包含了一个 main 函数,main 函数主要就是完成了一些外设 的初始化,如串口、LED、LCD、按键等,并在最后调用了函数 freertos_demo。

freertos_demo.c文件

#include "freertos_demo.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "lwip_comm.h"
#include "lwipopts.h"
#include "FreeRTOS.h"
#include "task.h"


/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO         5           /* 任务优先级 */
#define START_STK_SIZE          128         /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler;             /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 */

/* LWIP_DEMO 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define LWIP_DMEO_TASK_PRIO     11          /* 任务优先级 */
#define LWIP_DMEO_STK_SIZE      1024        /* 任务堆栈大小 */
TaskHandle_t LWIP_Task_Handler;             /* 任务句柄 */
void lwip_demo_task(void *pvParameters);    /* 任务函数 */

/* LED_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define LED_TASK_PRIO           10          /* 任务优先级 */
#define LED_STK_SIZE            128         /* 任务堆栈大小 */
TaskHandle_t LEDTask_Handler;               /* 任务句柄 */
void led_task(void *pvParameters);          /* 任务函数 */
/******************************************************************************************************/


/**
 * @breif       加载UI
 * @param       mode :  bit0:0,不加载;1,加载前半部分UI
 *                      bit1:0,不加载;1,加载后半部分UI
 * @retval      无
 */
void lwip_test_ui(uint8_t mode)
{
    uint8_t speed;
    uint8_t buf[30];
    
    if (mode & 1<< 0)
    {
        lcd_fill(5, 30, lcddev.width,110, WHITE);
        lcd_show_string(5, 30, 200, 16, 16, "STM32", RED);
        lcd_show_string(5, 50, 200, 16, 16, "lwIP ping Test", RED);
        lcd_show_string(5, 70, 200, 16, 16, "ATOM@ALIENTEK", RED);
    }
    
    if (mode & 1 << 1)
    {
        lcd_fill(5, 110, lcddev.width,lcddev.height, WHITE);
        lcd_show_string(5, 110, 200, 16, 16, "lwIP Init Successed", BLUE);
        
        if (g_lwipdev.dhcpstatus == 2)
        {
            sprintf((char*)buf,"DHCP IP:%d.%d.%d.%d",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);     /* 显示动态IP地址 */
        }
        else
        {
            sprintf((char*)buf,"Static IP:%d.%d.%d.%d",g_lwipdev.ip[0],g_lwipdev.ip[1],g_lwipdev.ip[2],g_lwipdev.ip[3]);    /* 打印静态IP地址 */
        }
        
        lcd_show_string(5, 130, 200, 16, 16, (char*)buf, BLUE);
        
        speed = ethernet_chip_get_speed();      /* 得到网速 */
        
        if (speed)
        {
            lcd_show_string(5, 150, 200, 16, 16, "Ethernet Speed:100M", BLUE);
        }
        else
        {
            lcd_show_string(5, 150, 200, 16, 16, "Ethernet Speed:10M", BLUE);
        }
    }
}

/**
 * @breif       freertos_demo
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
    /* start_task任务 */
    xTaskCreate((TaskFunction_t )start_task,
                (const char *   )"start_task",
                (uint16_t       )START_STK_SIZE,
                (void *         )NULL,
                (UBaseType_t    )START_TASK_PRIO,
                (TaskHandle_t * )&StartTask_Handler);

    vTaskStartScheduler(); /* 开启任务调度 */
}

/**
 * @brief       start_task
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void start_task(void *pvParameters)
{
    pvParameters = pvParameters;
    
    lwip_test_ui(1);    /* 加载后前部分UI */
    
    while (lwip_comm_init() != 0)
    {
        lcd_show_string(30, 110, 200, 16, 16, "lwIP Init failed!!", RED);
        delay_ms(500);
        lcd_fill(30, 50, 200 + 30, 50 + 16, WHITE);
        lcd_show_string(30, 110, 200, 16, 16, "Retrying...       ", RED);
        delay_ms(500);
        LED1_TOGGLE();
    }
    
    while (!ethernet_read_phy(PHY_SR))  /* 检查MCU与PHY芯片是否通信成功 */
    {
        printf("MCU与PHY芯片通信失败,请检查电路或者源码!!!!\r\n");
    }
    
    while ((g_lwipdev.dhcpstatus != 2)&&(g_lwipdev.dhcpstatus != 0XFF))  /* 等待DHCP获取成功/超时溢出 */
    {
        lwip_dhcp_process_handle();
        vTaskDelay(5);
    }
    
    lwip_test_ui(2);
    
    taskENTER_CRITICAL();           /* 进入临界区 */

    /* 创建lwIP任务 */
    xTaskCreate((TaskFunction_t )lwip_demo_task,
                (const char*    )"lwip_demo_task",
                (uint16_t       )LWIP_DMEO_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LWIP_DMEO_TASK_PRIO,
                (TaskHandle_t*  )&LWIP_Task_Handler);

    /* LED测试任务 */
    xTaskCreate((TaskFunction_t )led_task,
                (const char*    )"led_task",
                (uint16_t       )LED_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )LED_TASK_PRIO,
                (TaskHandle_t*  )&LEDTask_Handler);
                
    vTaskDelete(StartTask_Handler); /* 删除开始任务 */
    taskEXIT_CRITICAL();            /* 退出临界区 */
    
}

/**
 * @brief       lwIP运行例程
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void lwip_demo_task(void *pvParameters)
{
    pvParameters = pvParameters;
    
    uint8_t t = 0;
    
    while (1)
    {
        t ++;
        
        if ((t % 40) == 0)
        {
            LED0_TOGGLE();    /* 翻转一次LED0 */
        }
        
        vTaskDelay(5);
    }
}

/**
 * @brief       系统再运行
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void led_task(void *pvParameters)
{
    pvParameters = pvParameters;
    
    while (1)
    {
        LED1_TOGGLE();
        vTaskDelay(1000);
    }
}


Logo

鸿蒙生态一站式服务平台。

更多推荐