目录
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>© 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, ðernetif_init, ðernet_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);
}
}