LwIP带操作系统的移植

这篇具有很好参考价值的文章主要介绍了LwIP带操作系统的移植。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

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

 lwip移植,LWIP,嵌入式硬件,LwIP,网络通信,TCP/IP

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

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

 lwip移植,LWIP,嵌入式硬件,LwIP,网络通信,TCP/IP

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

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

lwip移植,LWIP,嵌入式硬件,LwIP,网络通信,TCP/IP

 该文件的源码如下:

/**
  ******************************************************************************
  * @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 路径下,该文 件夹结构如下图所示:

lwip移植,LWIP,嵌入式硬件,LwIP,网络通信,TCP/IP

lwip移植,LWIP,嵌入式硬件,LwIP,网络通信,TCP/IP

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

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

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

lwip移植,LWIP,嵌入式硬件,LwIP,网络通信,TCP/IP

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

lwip移植,LWIP,嵌入式硬件,LwIP,网络通信,TCP/IP

这里需要把这个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移植,LWIP,嵌入式硬件,LwIP,网络通信,TCP/IP

修改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文件文章来源地址https://www.toymoban.com/news/detail-732777.html

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


到了这里,关于LwIP带操作系统的移植的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32 CubeMX LwIP + freertOS 移植

    开发板: 官方 STM32F746  MCU型号:STM32F746NGH 网卡型号:LAN8742A  原理图如下 先用裸机测试LAN8742A的网卡驱动 使用CubeMX创建工程 系统时钟和时基定时器如下 无系统LWIP协议栈设置,静态IP地址,关闭DHCP 生产代码,下载进开发板。开发板网口与电脑网口通过网线直连,设置电脑本

    2024年01月16日
    浏览(83)
  • STM32F407+LWIP+DP83848以太网驱动移植

      最近有个项目上需要用到网络功能,于是开始移植网络相关代码。在移植的过程中感觉好难,网上找各种资料都没有和自己项目符合的,移植废了废了好的大劲。不过现在回头看看,其实移植很简单,主要是当时刚开始接触网络,各种新的知识和概念扑面而来,加上LWI

    2024年02月12日
    浏览(35)
  • 【正点原子FPGA连载】第三十一章基于lwip的echo server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

    随着物联网的兴起,万物互联需要一个强大而又灵活的协议体系,TCP/IP协议得天独厚,而在嵌入式网络设备中,由于硬件资源的限制,需要特殊的实现方式。LWIP作为TCP/IP协议的一种轻量级实现方式,满足了这一要求。本章我们利用VITIS软件自带的lwIP Echo Server例程模板,初步了

    2024年02月14日
    浏览(46)
  • 【正点原子FPGA连载】 第三十三章基于lwip的tftp server实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

    文件传输是网络环境中的一项基本应用,其作用是将一台电子设备中的文件传输到另一台可能相距很远的电子设备中。TFTP作为TCP/IP协议族中的一个用来在客户机与服务器之间进行文件传输的协议,常用于无盘工作站、路由器以及远程测控设备从主机上获取引导配置文件,实现

    2024年02月12日
    浏览(44)
  • 【LWIP】stm32用CubeMX(6.4版)配置LwIP+Ping+TCPclient+TCPserver发送信息到PC(操作部分)

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 这是我写的第一篇博客,欢迎大家给点鼓励和提出建议! 本人由于理想和爱好,辞去土木工作,于不到一个月前入职某科技公司开始从事嵌入式,专业能力和刚毕业的大学生一样都是很薄弱的。然后被

    2024年02月03日
    浏览(27)
  • STM32407 FreeRtos系统下lwip实现发送和接收数据包进行链路测试

    下面是一个基于STM32F407 FreeRTOS系统下lwIP实现发送和接收数据包进行链路测试的案例。 硬件配置和软件环境搭建 首先,需要准备好STM32F407开发板、以太网网线和一台电脑,然后在电脑上安装好STM32CubeMX工具和Keil MDK软件,创建一个新的工程,并按照以下步骤进行配置: 1)选择

    2024年02月12日
    浏览(33)
  • 【LWIP】(补充)STM32H743(M7内核)CubeMX配置LWIP并ping通

    之前我写了一个用CubeMX配置LWIP以太网通讯的博客: 【LWIP】stm32用CubeMX配置LwIP+Ping+TCPclient+TCPserver发送信息到PC(操作部分) 当时用的是F207、F407加上LAN8720、DP83848做了测试,效果都是很好的。但是当我第一次在STM32H743的时候突然傻眼了,H743用CubeMX配置以太网方法与其他内核的

    2024年02月11日
    浏览(33)
  • lwip:使用lwip UDP分包发送大量数据所遇问题以及解决方法(HPM6750、STM32)

    最近在调试HPM6750的项目时,考虑到调试方便需要将一些中间数据上传至PC分析,而且数据量又比较大,准备使用UDP分包发送,在调试的时候发现如下几个问题: sdk提供的lwip例程在使用时,分包会出现后一包的数据覆盖前一包数据的情况; 分包后,UDP首部校验和错误; sdk将

    2024年04月13日
    浏览(36)
  • 第二节 LwIP简介

    本专栏使用的是LwIP 2.1.2版本 ,官方下载链接:http://savannah.nongnu.org/projects/lwip/。 本专栏以LwIP 2. 1.2 为主要对象进行讲解,后续中出现的LwIP 如果没有特殊声明,均指2.1.2 版本。此时的LwIP 2. 1.2 为最新版本,可能当这本书写完的时候,LwIP 又被更新了,对于学习而言,大家其实

    2024年02月03日
    浏览(39)
  • 2.物联网LWIP网络

    注意:学习路径 1.如果想要深入了解,学习LWIP的ARM API(对底层操作) 2.如果想要快速开发,学习LWIP的Socket API(针对操作系统) 一。LWIP协议栈 (1)LWIP应用场景          上述是以太网DTU,通过这个器件可以很快捷的把数据传递到以太网上,即DTU就是硬件与以太网传递的中

    2024年02月13日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包