- #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;
- };
我们可以从上面的代码中看出,p 和 ptr 指向 pbuf 链表,其中 p 和 ptr 都指向 pbuf 链表,不同的是 p 一直指向 pbuf 链表的第一个 pbuf 结构,而 ptr 可能指向链表中其他的位置,netbuf_next()和 netbuf_first()操作 ptr 字段。addr 和 port 字段用来记录数据发送方的 IP 地址和端口号,netbuf_fromaddr 和 netbuf_fromport 这两个宏定义用于返回 addr 和 port 这两个字段。netbuf和 pbuf 之间的关系如下图所示。
不管是 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 时使用
- };
在 api.h 文件中还定义了连接状态和连接类型,这两个都是枚举类型。
- //枚举类型,用于描述连接类型
- 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);
- netconn_bind()函数将一个连接结构与本地 IP 地址 addr 和端口号 port 进行绑定,服务器端程序必须执行这一步,服务器必须与指定的端口号绑定才能接受客户端的连接请求,函数原型如下。
- err_t netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port);
- netconn_connect()函数的功能是连接服务器,将指定的连接结构与目的 IP 地址 addr 和目的端口号 port 进行绑定,当作为 TCP 客户端程序时,调用此函数会产生握手过程,函数原型如下。
- err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port);
- netconn_disconnect()函数只能使用在 UDP 连接中,功能是断开与服务器的连接。对于 UDP连接来说就是将 UDP 控制块中的 remote_ip 和 remote_port 字段值清零,函数原型如下。
- err_t netconn_disconnect (struct netconn *conn);
- netconn_listen()函数只有在 TCP 服务器程序中使用,将一个连接结构 netconn 设置为侦听状态,既将 TCP 控制块的状态设置为 LISTEN 状态。
- netconn_accept()函数也只用于 TCP 服务器程序,服务器调用此函数可以从 acceptmbox 邮箱中获取一个新建立的连接,若邮箱为空,则函数会一直阻塞,直到新连接的到来。服务器端调用此函数前必须先调用 netconn_listen()函数将连接设置为侦听状态,函数原型如下。
- err_t netconn_accept(struct netconn *conn, struct netconn **new_conn);
- netconn_recv()函数是从连接的 recvmbox 邮箱中接收数据包,可用于 TDP 连接,也可用于UDP 连接,函数会一直阻塞,直到从邮箱中获得数据消息,数据被封装在 netbuf 中。如果从邮箱中接收到一条空消息,表示对方已经关闭当前的连接,应用程序也应该关闭这个无效的连接,函数原型如下。
- err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf);
- netconn_send()函数用于在 UDP 连接上发送数据,参数 conn 指出了要操作的连接,参数 buf为要发送的数据,数据被封装在 netbuf 中。如果 IP 层分片功能未使能,则 netbuf 中的数据不能太长,不能超过 MTU 的值,最好不要超过 1000 字节。如果 IP 层分片功能使能的情况下就可以忽略此细节,函数原型如下。
- err_t netconn_send(struct netconn *conn, struct netbuf *buf);
- netconn_write()函数用于在稳定的 TCP 连接上发送数据,参数 dataptr 和 size 分别指出了待发送数据的起始地址和长度,函数并不要求将数据封装在 netbuf 中,对于数据长度也没有限制,内核会直接处理这些数据,将他们封装在 pbuf 中,并挂接到 TCP 的发送队列中。
- 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
3.3 新建udpecho.c,实现udp的回显功能。
- #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)
2. NETCONN UDP Demo
NETCONN_UDP.zip
(6.67 MB, 下载次数: 10)