LwIP带操作系统的移植

news2024/11/26 18:43:46

目录

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);
    }
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/83720.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用动态代理+Netty+Zookeeper+Protobuff手撸一个RPC框架

RPC是什么 RPC&#xff08;Remote Procedure Call&#xff09;远程过程调用&#xff0c;一种计算机之间的远程调用技术&#xff0c;客户端能够在不知道服务器底层的通信架构的情况下调用服务器端的方法&#xff0c;就像调用自身的方法一样。 举个例子&#xff1a; 老婆自己去…

Uni-app 实现md5加密

写下这篇文章&#xff0c;记录自己走过的坑 第一次尝试&#xff1a;参照博客uniapp使用md5_清雨小竹的博客-CSDN博客_uniapp md5 引入md5.js后&#xff0c;在main.js中import后&#xff0c;无法使用md5.hex_md5("需要加密的字符串")&#xff0c;vue页面无法打开&…

【捕风捉影】Vue项目报错,点击浏览器报错信息定位不到报错代码,该如何优雅地调试代码?

【捕风捉影】Vue项目如何优雅地调试代码一、背景二、调试时开启productionSourceMap三、devtool几种模式一、背景 通过vue-cli服务运行项目&#xff0c;项目运行一切正常。但打包后&#xff0c;通过nginx部署运行&#xff0c;大屏展示模块报echarts typeError 错误。但是点击浏…

如何使用Docker创建自定义网络

目录 网络模式 1.bridge模式(默认模式--桥接模式) 初识网络模式 查看桥接模式的特点 2.host模式&#xff08;仅主机模式&#xff09; 使用守护进程的方式创建并启动且进入容器 查看仅主机模式下的网络配置 端口映射 &#xff1a;​ 3.如何创建自定义网络 网络模式 Docker…

启发式算法 之 模拟退火原理及实践

一、初窥其貌 1.1 启发式算法和元启发式算法 启发式算法是求解优化问题的一类方法&#xff0c;因为经典优化方法存在局限性&#xff0c;有时无法得到最优解&#xff0c;只能得到一个可以接受的近似最优解&#xff0c;启发式算法就适合求解这类问题。启发式算法就是专家的推测…

Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩

大众点评项目 缓存穿透、缓存击穿、缓存雪崩需求&#xff1a;缓存穿透、缓存击穿、缓存雪崩处理策略缓存穿透处理缓存雪崩缓存击穿总结SpringCloud章节复习已经过去&#xff0c;新的章节Redis开始了&#xff0c;这个章节中将会回顾Redis实战项目 大众点评 主要依照以下几个原则…

吉时利Keithley静电计程控上位机软件-摩擦纳米发电机测试软件NS-EM

1、产品简介 NS-EM 静电计程控系统可实现对吉时利静电计的程控&#xff0c;通过此系统软件您可以单独程控静电计进行数据的采集的同时还可以利用告诉信号采集卡对测试获取的电压、电流等信号进行高频率采样并实时显示采集信号的波形图。 2、产品特点 ◆可远程进行仪器控制&am…

QF state machine 介绍

转型Qt小半年了&#xff0c;看到项目组用的Qt state machine signal和匿名函数满天飞&#xff0c;就想之前用的C#里的QF state machine 能不能做转到Qt平台。这样可以省去使用Qt状态机的信号&#xff0c;在这过程中学习借鉴了QF state machine 的鼻祖 QP框架&#xff0c;不知道…

编译原理笔记

第一课&#xff1a; 《编译原理求语法树的短语和直接短语等等》 二义性是什么&#xff1f; 如果最左推导和最右推导的结果不一致&#xff0c;那么说明文法有二义性 短语是什么&#xff1f; 找短语就是找能长叶子的结点&#xff0c;有五个如图圆圈标号1 2 3 4 5 直接短语&#x…

c语言:联合体—union

联合体一.基本认识1.一个联合体的基本样式2.内部成员的访问3.具体的内存分配二.大小端对联合体的影响三.一个问题一.基本认识 1.一个联合体的基本样式 看得出来其实跟我们定义结构体是一样的&#xff08;如果还不大了解结构体的可以看看这篇博客什么是结构体&#xff09;&…

[附源码]计算机毕业设计港口集团仓库管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis MavenVue等等组成&#xff0c;B/S模式…

MoveIT1 Assistant 总结

文章目录环境步骤备注故障问题解决参考接上一篇&#xff0c;生成URDF后&#xff0c;在MoveIT Assistant生成配置用于运动规划。https://blog.csdn.net/woshigaowei5146/article/details/128237105?spm1001.2014.3001.5501 环境 Ubuntu20.04&#xff1b;ROS1 Noetic;VMware 步…

生成模型(一):GAN

生成对抗网络 (GAN)在许多生成任务中显示出很好的结果&#xff0c;以复制真实世界的丰富内容&#xff0c;例如图像、文字和语音。它受到博弈论的启发&#xff1a;一个生成器和一个判别器&#xff0c;在互相竞争的同时让彼此变得更强大。然而&#xff0c;训练 GAN 模型相当具有挑…

一篇解析Linux paging_init

说明&#xff1a; Kernel版本&#xff1a;4.14ARM64处理器&#xff0c;Contex-A53&#xff0c;双核使用工具&#xff1a;Source Insight 3.5&#xff0c; Visio 1. 介绍 从详细讲解Linux物理内存初始化中&#xff0c;可知在paging_init调用之前&#xff0c;存放Kernel Image和…

java计算机毕业设计ssm幼儿英语学习平台的设计与实现yofnu(附源码、数据库)

java计算机毕业设计ssm幼儿英语学习平台的设计与实现yofnu&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支…

注册微信小程序

文章目录1. 项目结构2. 页面组成3. json配置文件4. 认识页面5. WXML6. WXSS7. js文件8. 宿主环境9. 组件10. API11. 协同工作与发布跟公众号平台不共用一个账号&#xff0c;需要用其它邮箱另行注册&#xff0c;填写身份证信息&#xff08;姓名、身份证号码&#xff09;&#xf…

[附源码]Node.js计算机毕业设计电商后台管理系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

我妈眼中我的房间

ONE This is Me in My room as a teenager according to My Mother. 在我老妈眼里&#xff0c;这就是十几岁待在房间里的我。 ✨ 评论区 1️⃣ It’s all true. 这都是真的。 2️⃣ As a father - yep that’s you. 作为一个父亲&#xff0c;没错就是你。 3️⃣ Looking…

基于C语言开发(控制台)通讯录管理程序【100010030】

通讯录程序设计 一、课程设计题目与要求 题目 &#xff1a;通讯录管理程序 1. 问题描述 ​ 编写一个简单的通讯录管理程序。通讯录记录有姓名&#xff0c;地址(省、市(县)、街道)&#xff0c;电话号码&#xff0c;邮政编码等四项。 2. 基本要求 程序应提供的基本基本管理…

Biotin-PEG-Pyrene,Pyrene-PEG-Biotin,芘丁酸-PEG-生物素peg化芘衍生物

聚乙二醇化芘衍生物之Pyrene-PEG-Biotin&#xff08;Biotin-PEG-Pyrene&#xff09;&#xff0c;其化学试剂的中文名为芘丁酸-聚乙二醇-生物素&#xff0c;此试剂可用于碳纳米管和石墨烯表面功能化。它所属分类为Biotin PEG Pyrene PEG。 peg试剂的分子量均可定制&#xff0c;…