- #ifndef FREERTOS_CONFIG_H
 
- #define FREERTOS_CONFIG_H
 
- /*-----------------------------------------------------------
 
- * Application specific definitions.
 
- *
 
- * These definitions should be adjusted for your particular hardware and
 
- * application requirements.
 
- *
 
- * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 
- * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
 
- *
 
- * See http://www.freertos.org/a00110.html
 
- *----------------------------------------------------------*/
 
- /* Ensure stdint is only used by the compiler, and not the assembler. */
 
- #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
 
-     #include <stdint.h>
 
-     extern uint32_t SystemCoreClock;
 
- #endif
 
- #define configUSE_PREEMPTION            1
 
- #define configUSE_IDLE_HOOK             0//1
 
- #define configUSE_TICK_HOOK             0//1
 
- #define configCPU_CLOCK_HZ              ( SystemCoreClock )
 
- #define configTICK_RATE_HZ              ( ( TickType_t ) 100 )
 
- #define configMAX_PRIORITIES            ( 5 )
 
- #define configMINIMAL_STACK_SIZE        ( ( unsigned short ) 130 )
 
- #define configTOTAL_HEAP_SIZE           ( ( size_t ) ( 20 * 1024 ) )
 
- #define configMAX_TASK_NAME_LEN         ( 10 )
 
- #define configUSE_TRACE_FACILITY        1
 
- #define configUSE_16_BIT_TICKS          0
 
- #define configIDLE_SHOULD_YIELD         1
 
- #define configUSE_MUTEXES               1
 
- #define configQUEUE_REGISTRY_SIZE       8
 
- #define configCHECK_FOR_STACK_OVERFLOW  0//2
 
- #define configUSE_RECURSIVE_MUTEXES     1
 
- #define configUSE_MALLOC_FAILED_HOOK    0//1
 
- #define configUSE_APPLICATION_TASK_TAG  0
 
- #define configUSE_COUNTING_SEMAPHORES   1
 
- #define configGENERATE_RUN_TIME_STATS   0
 
- /* Co-routine definitions. */
 
- #define configUSE_CO_ROUTINES       0
 
- #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
 
- /* Software timer definitions. */
 
- #define configUSE_TIMERS                1
 
- #define configTIMER_TASK_PRIORITY       ( 2 )
 
- #define configTIMER_QUEUE_LENGTH        10
 
- #define configTIMER_TASK_STACK_DEPTH    ( configMINIMAL_STACK_SIZE * 2 )
 
- /* Set the following definitions to 1 to include the API function, or zero
 
- to exclude the API function. */
 
- #define INCLUDE_vTaskPrioritySet        1
 
- #define INCLUDE_uxTaskPriorityGet       1
 
- #define INCLUDE_vTaskDelete             1
 
- #define INCLUDE_vTaskCleanUpResources   1
 
- #define INCLUDE_vTaskSuspend            1
 
- #define INCLUDE_vTaskDelayUntil         1
 
- #define INCLUDE_vTaskDelay              1
 
- /* Cortex-M specific definitions. */
 
- #ifdef __NVIC_PRIO_BITS
 
-     /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
 
-     #define configPRIO_BITS             __NVIC_PRIO_BITS
 
- #else
 
-     #define configPRIO_BITS             4        /* 15 priority levels */
 
- #endif
 
- /* The lowest interrupt priority that can be used in a call to a "set priority"
 
- function. */
 
- #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         0xf
 
- /* The highest interrupt priority that can be used by any interrupt service
 
- routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
 
- INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
 
- PRIORITY THAN THIS! (higher priorities are lower numeric values. */
 
- #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5
 
- /* Interrupt priorities used by the kernel port layer itself.  These are generic
 
- to all Cortex-M ports, and do not rely on any particular library functions. */
 
- #define configKERNEL_INTERRUPT_PRIORITY         ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
 
- /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
 
- See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
 
- #define configMAX_SYSCALL_INTERRUPT_PRIORITY    ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
 
- /* Normal assert() semantics without relying on the provision of an assert.h
 
- header file. */
 
- //#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
 
- /* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
 
- standard names. */
 
- #define vPortSVCHandler SVC_Handler
 
- #define xPortPendSVHandler PendSV_Handler
 
- #define xPortSysTickHandler SysTick_Handler
 
- #endif /* FREERTOS_CONFIG_H */
1.5  打开lwipopts.h,修改NO_SYS宏,开启SYS。
 
1.6  注释掉一些中断服务函数,避免重复实现。
 
1.7  因为是在LWIP已经移植好的前提下,所以我们已经节约了大部分时间了,直接编译,会出现以下报错。
 
    这里提示空间不够,怎么办呢?我们发现不管是修改启动文件的堆栈大小还是修改编译优化等级,都无法解决这个问题,这样我们就需要考虑是不是FREERTOS本身的堆栈分配有误导致的,我们只需要修改FreeRTOS分配的固定堆栈大小就行了。打开FreeRTOSConfig.h,减少堆大小的配置即可。
 
这样就编译通过了。
 
1.8  关闭DHCP功能,只需注释掉main.h USE_DHCP即可,打开NETConfig.h,修改静态IP地址。
 
1.9  下载烧录代码到开发板,使用网线连接开发板和pc端,测试能否ping通。
 
正常ping通,移植成功。
2. NETCONN编程接口简介
2.1  netbuf 数据缓冲区
    netbuf 是 NETCONN API 编程接口使用的描述数据包的结构,我们可以使用 netbuf 来管理发送数据、接收数据的缓冲区。有关 netbuf 的详细描述在 netbuf.c 和 netbuf.h 这两个文件中,netbuf 是一个结构体,在 netbuf.h 中定义了这个结构体,代码如下,这里我们去掉了条件编译的代码。
- struct netbuf
 
- {
 
-     struct pbuf *p, *ptr;
 
-     ip_addr_t addr;
 
-     u16_t port;
 
- };
 
    不管是 TCP 连接还是 UDP 连接,接收到数据包后会将数据封装在一个 netbuf 中,然后将这个 netbuf 交给应用程序去处理。在数据发送时,根据不同的连接有不同的处理:对于 TCP 连接,用户只需要提供待发送数据的起始地址和长度,内核会根据实际情况将数据封装在合适大小的数据包中,并放入发送队列中,对于 UDP 来说,用户需要自行将数据封装在 netbuf 结构中,当发送函数被调用的时候内核直接将数据包中的数据发送出去。
    在 netbuf.c 中提供了几个操作 netbuf 的函数,如下表所示。
| 函数 |  | 
|  | 申请一个 netbuf 空间,但是不会分配任何数据空间(不指向任何的 pbuf),真正的数据存储区域需要调用  netbuf_alloc()函数来分配。 | 
|  | 释放一个 netbuf 的内存,如果 netbuf 中的 p 指针上还记录有数据,则相应的pbuf 也会被释放掉。 | 
|  | 为 netbuf 结构分配指定大小的数据空间,数据空间是通过 pbuf  的形式表现的,函数返回成功分配的数据空间起始地址(pbuf 的 payload  指向的地址) | 
|  | 释放 netbuf 中的 p 指向的  pbuf 数据空间内存。 | 
|  | 和 netbuf_alloc 类似,不过只是分配一个 pbuf 首部结构(首部结构中包含协议的首部空间),pbuf 中的 payload 指向参数  dataptr,这种描述数据包的方式在静态数据包的发送时经常用到。 | 
|  | 将参数 tail 的 pbuf 连接到参数  head 的 pbuf 的后面,调用此函数后参数 tail  结构会被删除掉。 | 
|  | 将 netbuf 结构中的 ptr 指针记录的  pbuf 数据起始地址填入参数 dataptr 中,同时将该  pbuf 中的数据长度填入到参数 len 中。 | 
|  | 将 netbuf 结构的 ptr 指针指向  pbuf 链表中的下一个 pbuf 结构。 | 
|  | 将 netbuf 结构的 ptr 指针指向  pbuf 链表中的第一个 pbuf 结构。 | 
2.2  netconn 连接结构
    我们前面在使用 RAW 编程接口的时候,对于 UDP 和 TCP 连接使用的是两种不同的编程函数:udp_xxx 和 tcp_xxx。NETCONN 对于这两种连接提供了统一的编程接口,用于可以使用同一的连接结构和编程函数,在 api.h 中定了了 netcon 结构体,代码如下。
- struct netconn
 
- {
 
-     enum netconn_type type; // 连接类型,TCP UDP 或者 RAW
 
-     enum netconn_state state; // 当前连接状态
 
-     union { // 内核中与连接相关的控制块指针
 
-         struct ip_pcb *ip; // IP 控制块
 
-         struct tcp_pcb *tcp; // TCP 控制块
 
-         struct udp_pcb *udp; // UDP 控制块
 
-         struct raw_pcb *raw; // RAW 控制块
 
-     } pcb;
 
-     err_t last_err; // 此连接上最新的错误
 
-     sys_sem_t op_completed; // 用于两部分 API 同步的信号量
 
-     sys_mbox_t recvmbox; // 接收数据的邮箱
 
-     #if LWIP_TCP
 
-     sys_mbox_t acceptmbox; // 用于 TCP 服务器端,连接请求的缓冲队列
 
-     #endif
 
-     #if LWIP_SOCKET
 
-     int socket; // socket 描述符,用于 socket API
 
-     #endif
 
-     #if LWIP_SO_SNDTIMEO
 
-     s32_t send_timeout; // 发送数据时的超时时间
 
-     #endif
 
-     #if LWIP_SO_RCVTIMEO
 
-     int recv_timeout; // 接收数据时的超时时间
 
-     #endif
 
-     #if LWIP_SO_RCVBUF
 
-     int recv_bufsize; // 接收消息队列长度
 
-     s16_t recv_avail; // 数据邮箱 recvmbox 中已缓存的数据长度
 
-     #endif
 
-     u8_t flags; // 标识符
 
-     #if LWIP_TCP
 
-     // 当调用 netconn_write 发送数据但缓存不足的时候,数据会暂时存放在 current_msg 中,等待下一次数据发送,write_offset 记录下一次发送时的索引
 
-     size_t write_offset;
 
-     struct api_msg_msg *current_msg;
 
-     #endif
 
-     netconn_callback callback; // 连接相关回调函数,实现 socket API 时使用
 
- };
- //枚举类型,用于描述连接类型
 
- enum netconn_type {
 
-     NETCONN_INVALID = 0, //无效类型
 
-     NETCONN_TCP = 0x10, //TCP
 
-     NETCONN_UDP = 0x20, //UDP
 
-     NETCONN_UDPLITE = 0x21, //UDPLite
 
-     NETCONN_UDPNOCHKSUM= 0x22, //无校验 UDP
 
-     NETCONN_RAW = 0x40 //原始链接
 
- };
 
- //枚举类型,用于描述连接状态,主要用于 TCP 连接中
 
- enum netconn_state
 
- {
 
-     NETCONN_NONE, //不处于任何状态
 
-     NETCONN_WRITE, //正在发送数据
 
-     NETCONN_LISTEN, //侦听状态
 
-     NETCONN_CONNECT, //连接状态
 
-     NETCONN_CLOSE //关闭状态
 
- };
2.3  netconn api 函数
    在文件 api_lib.c 中实现了 NETCONN 的各个函数,在 api_lib.c 中有很多 netconn 的函数,其中大部分是 LWIP 内部调用的,我们最后使用到的有 9 个函数,如下表所示。
| 函数 |  | 
|  | 这个函数其实就是一个宏定义,用来为新连接申请一个连接结构netconn 空间。 | 
|  | 该函数的功能是删除一个 netconn 连接结构。 | 
|  | 该函数用来获取一个 netconn 连接结构的源 IP 地址和源端口号或者目的 IP 地址和目的端口号。 | 
|  | 将一个连接结构与本地 IP 地址和端口号进行绑定。 | 
|  | 将一个连接结构与目的 IP 地址和端口号进行绑定。 | 
|  | 只能用在 UDP 连接结构中,用来断开与服务器的连接。 | 
|  | 这个函数就是一个宏定义,只在 TCP 服务器程序中使用,用来将连接结构  netconn 置为侦听状态。 | 
|  | 这个函数也只用与 TCP 服务器程序中,服务器调用此函数可以获得一个新的连接。 | 
|  | 从连接的 recvmbox 邮箱中接收数据包,可用于 TCP 连接,也可用于UDP 连接。 | 
|  |  | 
|  |  | 
|  |  | 
    - netconn_new()函数是函数 netconn_new_with_proto_and_callback()的宏定义,此函数用来为新连接申请一个 netconn 空间,参数为新连接的类型,常用的值是 NETCONN_UDP 和 NETCONN_TCP,分别代表 UDP 连接和 TCP 连接。
    - netconn_delete()函数用来删除一个 netconn 连接结构,如果函数调用时双方仍然处于连接状态,则相应连接将被关闭:对于 UDP 连接,连接立即被关闭,UDP 控制块被删除;对于 TCP连接,则函数执行主动关闭,内核完成剩余的断开握手过程。
    - netconn_getaddr()函数用来获取一个 netconn 连接结构的源 IP 地址和源端口号或者目的 IP地址和目的端口号,IP 地址保存在 addr 中,端口信息保存在 port 中,参数 local 指明是获取源地址还是目的地址,当 local 为 1 时表示本地地址,此函数原型如下。
- err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr,u16_t *port, u8_t local);
- err_t netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port);
- err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port);
- err_t netconn_disconnect (struct netconn *conn);
    - netconn_accept()函数也只用于 TCP 服务器程序,服务器调用此函数可以从 acceptmbox 邮箱中获取一个新建立的连接,若邮箱为空,则函数会一直阻塞,直到新连接的到来。服务器端调用此函数前必须先调用 netconn_listen()函数将连接设置为侦听状态,函数原型如下。
- err_t netconn_accept(struct netconn *conn, struct netconn **new_conn);
- err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf);
- err_t netconn_send(struct netconn *conn, struct netbuf *buf);
    - netconn_close()函数用来关闭一个 TCP 连接,该函数会产生一个 FIN 握手包的发送,成功后函数便返回,而后剩余的断开握手操作由内核自动完成,用户程序不用关心,该函数只是断开一个连接,但不会删除连接结构 netconn,但需要我们调用 netconn_delete()函数来删除连接结构,否则会造成内存泄漏,函数原型如下。
- err_t netconn_close(struct netconn *conn);
3. 编写NETCONN UDP实例
    接下来,我们就可以在这个基础上,开始使用netconn尝试编写一个udp server实例。
3.1  开启NETCONN编程
 
3.2  在lwipopts.h添加以下代码。
- // TCP/IP 线程名称
 
- #define TCPIP_THREAD_NAME              "TCP/IP"
 
- // TCP/IP 线程栈大小
 
- #define TCPIP_THREAD_STACKSIZE          1000
 
- // TCP/IP 邮箱大小
 
- #define TCPIP_MBOX_SIZE                 5
 
- // 默认 UDP 接收邮箱大小
 
- #define DEFAULT_UDP_RECVMBOX_SIZE       2000
 
- // 默认 TCP 接收邮箱大小
 
- #define DEFAULT_TCP_RECVMBOX_SIZE       2000
 
- // 默认接受邮箱大小
 
- #define DEFAULT_ACCEPTMBOX_SIZE         2000
 
- // 默认线程栈大小
 
- #define DEFAULT_THREAD_STACKSIZE        500
 
- // TCP/IP 线程优先级
 
- #define TCPIP_THREAD_PRIO               (8 - 2)
 
- // LWIP 兼容互斥量
 
- #define LWIP_COMPAT_MUTEX 1
 
- // 允许 LWIP 兼容互斥量
 
- #define LWIP_COMPAT_MUTEX_ALLOWED 0
 
- // LWIP TCP/IP 核心锁定
 
- #define LWIP_TCPIP_CORE_LOCKING 0
- #include "FreeRTOS.h"
 
- #include "lwip/opt.h"
 
- #include "task.h"
 
- #if LWIP_NETCONN
 
- #include "lwip/api.h"
 
- #include "lwip/sys.h"
 
- #define UDPECHO_THREAD_PRIO  ( tskIDLE_PRIORITY + 5 )
 
- static struct netconn *conn;
 
- static struct netbuf *buf;
 
- static struct ip4_addr *addr;
 
- static unsigned short port;
 
- /*-----------------------------------------------------------------------------------*/
 
- static void udpecho_thread(void *arg)
 
- {
 
-     err_t err, recv_err;
 
-     LWIP_UNUSED_ARG(arg);
 
-     conn = netconn_new(NETCONN_UDP);
 
-     if (conn!= NULL)
 
-     {
 
-         err = netconn_bind(conn, IP_ADDR_ANY, 6000);
 
-         if (err == ERR_OK)
 
-         {
 
-             while (1)
 
-             {
 
-                 recv_err = netconn_recv(conn, &buf);
 
-                 if (recv_err == ERR_OK)
 
-                 {
 
-                     addr = netbuf_fromaddr(buf);
 
-                     port = netbuf_fromport(buf);
 
-                     netconn_connect(conn, addr, port);
 
-                     buf->addr.addr = 0;
 
-                     netconn_send(conn,buf);
 
-                     netbuf_delete(buf);
 
-                 }
 
-             }
 
-         }
 
-         else
 
-         {
 
-             netconn_delete(conn);
 
-             printf("can not bind netconn");
 
-         }
 
-     }
 
-     else
 
-     {
 
-         printf("can create new UDP netconn");
 
-     }
 
- }
 
- /*-----------------------------------------------------------------------------------*/
 
- void udpecho_init(void)
 
- {
 
-     sys_thread_new("udpecho_thread", udpecho_thread, NULL, DEFAULT_THREAD_STACKSIZE,UDPECHO_THREAD_PRIO );
 
- }
 
- #endif /* LWIP_NETCONN */
    实现了一个简单的UDP回显服务器,使用FreeRTOS和LWIP协议栈。其主要功能是:
        1. 创建UDP连接:在 udpecho_thread 函数中,创建一个新的UDP netconn。
        2. 绑定端口:将该连接绑定到6000端口,监听来自任意IP地址的UDP数据包。
        3. 接收数据:进入一个无限循环,调用 netconn_recv 来接收数据。
        4. 回显数据:当收到数据包时,获取发送者的地址和端口,并使用 netconn_send 将数据包回传给发送者。
        5. 内存管理:处理完数据后,删除 netbuf 以释放内存。
    整体上,该代码实现了一个UDP回显服务,可以接收并回传数据,适用于网络通信测试。
3.4  编写main函数。
- int main(void)
 
- {
 
-     /* Init task */
 
-     xTaskCreate(Main_task, (const char *)"Main", configMINIMAL_STACK_SIZE * 2, NULL,MAIN_TASK_PRIO, NULL);
 
-     /* Start scheduler */
 
-     vTaskStartScheduler();
 
-     while(1);
 
- }
 
- void Main_task(void * pvParameters)
 
- {
 
-     char LCDDisplayBuf[100] = {0};
 
-     struct ip4_addr DestIPaddr;
 
-     uint8_t flag = 0;
 
-     /* User config the different system Clock */
 
- //    UserRCMClockConfig();
 
- //    /* Configure SysTick */
 
- //    ConfigSysTick();
 
-     USART_Init();
 
-     /* Configures LED2 and LED3 */
 
-     APM_BOARD_LEDInit(LED2);
 
-     APM_BOARD_LEDInit(LED3);
 
-     /* KEY init*/
 
-     APM_BOARD_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
 
-     APM_BOARD_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);
 
-     printf("This is a UDP NETCONN Demo! \r\n");
 
-     /* Configure ethernet (GPIOs, clocks, MAC, DMA) */
 
-     ConfigEthernet();
 
-     /* Initilaize the LwIP stack */
 
-     LwIP_Init();
 
- #ifndef USE_DHCP
 
-     /* Use Com printf static IP address*/
 
-     sprintf(LCDDisplayBuf,"TINY board Static IP address \r\n");
 
-     printf("%s",LCDDisplayBuf);
 
-     sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d \r\n",
 
-     IP_ADDR0,
 
-     IP_ADDR1,
 
-     IP_ADDR2,
 
-     IP_ADDR3);
 
-     printf("%s",LCDDisplayBuf);
 
-     sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d \r\n",
 
-     NETMASK_ADDR0,
 
-     NETMASK_ADDR1,
 
-     NETMASK_ADDR2,
 
-     NETMASK_ADDR3);
 
-     printf("%s",LCDDisplayBuf);
 
-     sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d \r\n",
 
-     GW_ADDR0,
 
-     GW_ADDR1,
 
-     GW_ADDR2,
 
-     GW_ADDR3);
 
-     printf("%s",LCDDisplayBuf);
 
-     printf("Connect port: %d", COMP_PORT);
 
- #endif
 
-     udpecho_init();
 
-     while(1)
 
-     {
 
-         /* check if any packet received */
 
-         if (ETH_CheckReceivedFrame())
 
-         {
 
-             /* process received ethernet packet */
 
-             LwIP_Pkt_Handle();
 
-         }
 
-         /* handle periodic timers for LwIP */
 
-         LwIP_Periodic_Handle(ETHTimer);
 
-     }
 
- }
    实现了一个基于FreeRTOS的UDP网络演示程序。主要功能包括:
        1. 任务创建:在 main 函数中创建了一个名为 Main_task 的任务,并启动了调度器。
        2. 初始化:在 Main_task 中,进行了多种硬件和网络的初始化,包括USART、LED、按键和以太网配置。
        3. LwIP堆栈:初始化LwIP(轻量级IP)堆栈,用于网络通信。
        4. 静态IP配置:如果未使用DHCP,代码会打印出静态IP地址、子网掩码和网关信息。
        5. UDP回显功能:调用 udpecho_init() 来初始化UDP回显功能。
        6. 主循环:在无限循环中,检查接收到的以太网帧,并处理它们,同时定期处理LwIP定时器。
    整体而言,该程序为设备提供了基本的网络通信能力,允许接收和处理UDP数据包。
    注意:这里一定要修改main.c中LWIP_Init()中的内存初始化函数。
 
    使用操作系统时,LWIP的初始化需要通过tcpip_init来处理网络协议栈的线程安全和事件管理。这是因为在多任务环境中,LWIP的操作可能需要在特定的上下文中进行,以确保线程间的同步和资源的正确管理。而在没有操作系统的情况下,初始化可以直接在主线程中完成,避免了复杂性。
3.5  实验现象
    使用网线连接PC和开发板后,使用网络调试助手,可达到回显的效果,如下图。
 
4. 总结
    在本文中,我们深入探讨了NETCONN在LWIP下的实现,并成功将现有的LWIP例程移植到了FreeRTOS操作系统,实现了NETCONN UDP例程。
    NETCONN编程相比于RAW编程更加高级层面,它提供了更抽象、更易用的接口,使网络编程更简单直观。NETCONN隐藏了底层协议细节,提供了更友好的API函数,使得建立连接和数据传输更加便捷。相比之下,RAW编程需要处理更多底层细节,如数据包构建和协议处理,需要更多的技术知识和精细操作。虽然NETCONN提供了便利,但有时可能会牺牲一些灵活性和性能优势,而RAW编程则更加灵活,可以更精细地控制数据流和处理过程。NETCONN适合快速开发和简单应用,RAW适合对网络细节有更深入了解和控制需求的人员。
    以上就是本文全部内容,上文只介绍了部分关键代码的实现,详细实现代码可见附件。
附件:
    1. LWIP移植FREERTOS Demo
 LWIP_FREERTOS.zip
(6.66 MB, 下载次数: 9)
LWIP_FREERTOS.zip
(6.66 MB, 下载次数: 9)
    2. NETCONN UDP Demo
 NETCONN_UDP.zip
(6.67 MB, 下载次数: 12)
NETCONN_UDP.zip
(6.67 MB, 下载次数: 12)