最新教程下载:ThreadX NetXDUO网络协议栈教程更新记录贴,前11章已经发布(2022-01-03) - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz!
第11章 ThreadX NetXDUO之UDP客户端/服务器
本章节为大家讲解NetXDUO的UDP客户端实现,学习本章节前,务必要优先学习第10章UDP传输控制协议基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。
目录
11.2.2 函数nx_packet_pool_create
11.2.5 函数nx_ip_fragment_enable
11.2.9 函数nx_packet_data_retrieve
11.2.10 函数nx_packet_data_extract_offset
11.2.11 函数nx_udp_socket_receive
11.1 初学者重要提示
1、 学习本章节前,务必保证已经学习了第10章的基础知识。
2、 本章要掌握的函数稍多,可以先学会基本的使用,然后再深入了解这些函数使用时的注意事项,争取达到熟练使用。
3、 ThreadX NetXDUO的UDP Socket数据包申请和释放问题
- 函数nx_udp_socket_receive 会申请一个NX_PACKET数据包用于接收,如果用户不使用了必须使用函数nx_packet_release释放。
- 使用函数nx_udp_socket_send必须有申请好的NX_PACKET数据包,可以使用函数nx_packet_allocate申请,也可以使用nx_udp_socket_receive申请的。
特别要注意的地方来了,函数nx_udp_socket_send调用后会释放nx_packet_allocate或者nx_udp_socket_receive申请的数据包。无需用户再去调用函数nx_packet_release释放。
11.2 UDP的API函数
11.2.1 函数nx_system_initialize
函数原型:
VOID nx_system_initialize(VOID);
函数描述:
NetXDUO初始化,所有其它功能调用之前必须优先调用此函数。
11.2.2 函数nx_packet_pool_create
函数原型:
UINT nx_packet_pool_create(
                          NX_PACKET_POOL *pool_ptr,
                          CHAR *name,
                          ULONG payload_size,
                          VOID *memory_ptr,
                          ULONG memory_size);   函数描述:
此函数用于数据包内存池创建
函数参数:
- 第1个参数是内存池控制块的地址。
- 第2个参数是内存池名字。
- 第3个参数是内存池中每个数据包的字节数。 此值必须至少为 40 个字节,并且还必须可以被 4 整除。
- 第4个参数是内存池中数据地址,此地址必须ULONG对齐。
- 第5个参数是内存池大小。
- 返回值:
- NX_SUCCESS:(0x00) 创建内存池成功。
- NX_PTR_ERROR:(0x07) 第1个参数地址无效。
- NX_SIZE_ERROR:(0x09) 第5个参数内存池大小无效。
- NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
使用举例:
  /* 创建内存池 */
    status =  nx_packet_pool_create(&pool_0,                 /* 内存池控制块 */
                                    "NetX Main Packet Pool",/* 内存池名 */
               1536, /* 内存池每个数据包大小,单位字节此值必须至少为 40 个字节,并且还必须可以被 4 整除 */
             (ULONG*)(((int)packet_pool_area + 15) & ~15) ,/* 内存池地址,此地址必须ULONG对齐 */
               NX_PACKET_POOL_SIZE);                        /* 内存池大小 */   11.2.3 函数nx_ip_create
函数原型:
UINT nx_ip_create(
    NX_IP *ip_ptr, 
    CHAR *name, ULONG ip_address,
    ULONG network_mask, 
    NX_PACKET_POOL *default_pool,
    VOID (*ip_network_driver)(NX_IP_DRIVER *),
    VOID *memory_ptr, 
    ULONG memory_size,
    UINT priority);    函数描述:
此函数使用用户提供的 IP 地址,数据包内存内存池和网络驱动程序创建 IP 实例。注意,直到 IP任务执行之后,才会调用网络驱动。
函数参数:
- 第1个参数是创建IP实例的控制块指针。
- 第2个参数是IP实例的名字。
- 第3个参数是IP地址。
- 第4个参数是子网掩码
- 第5个参数是内存池地址。
- 第6个参数是网卡驱动地址。
- 第7个参数是IP任务栈地址
- 第8个参数是IP任务栈大小,单位字节。
- 第9个参数是IP任务优先级。
- 返回值
- NX_SUCCESS:(0x00) 创建 IP 实例成功。
- NX_NOT_IMPLEMENTED:(0x4A) 未正确配置 NetX Duo 库。
- NX_PTR_ERROR:(0x07) IP控制块地址、网络驱动函数指针、内存池地址或任务栈地址无效。
- NX_SIZE_ERROR:(0x09) 提供的任务栈大小太小。
- NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
- NX_IP_ADDRESS_ERROR:(0x21) 提供的 IP 地址无效。
- NX_OPTION_ERROR:(0x21) 提供的 IP 任务优先级无效。
使用举例:
/* 例化IP */
    status = nx_ip_create(&ip_0,                                                   /* IP实例控制块 */                                    
                            "NetX IP Instance 0",                                  /* IP实例名 */     
                            IP_ADDRESS(IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3),    /* IP地址 */
                            0xFFFFFF00UL,                                          /* 子网掩码 */
                            &pool_0,                                               /* 内存池 */
                        nx_driver_stm32h7xx,                                   /* 网卡驱动 */
                            (UCHAR*)AppTaskNetXStk,                                /* IP任务栈地址 */
                            sizeof(AppTaskNetXStk),                             /* IP任务栈大小,单位字节 */
                            APP_CFG_TASK_NETX_PRIO);                            /* IP任务优先级 */11.2.4 函数nx_arp_enable
函数原型:
UINT nx_arp_enable(
    NX_IP *ip_ptr, 
    VOID *arp_cache_memory,
    ULONG arp_cache_size);   函数描述:
此函数用于使能ARP地址解析。
函数参数:
- ip_ptr:IP实例地址。
- arp_cache_memory:ARP缓存地址。
- arp_cache_size:每个 ARP 条目均为 52 个字节,因此,ARP 条目总数是52字节整数倍。
- 返回值
- NX_SUCCESS:(0x00) 启用 ARP 成功。
- NX_PTR_ERROR:(0x07) IP实例地址或ARP缓存地址无效。
- NX_SIZE_ERROR:(0x09) 用户提供的 ARP 缓存内存太小。
- NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
- NX_ALREADY_ENABLED:(0x15) 此组件已启用。
使用举例:
int32_t tcp_sock;
tcp_sock = netTCP_GetSocket (tcp_cb_server);
    
if (tcp_sock > 0) 
{
res = netTCP_Listen (tcp_sock, PORT_NUM);
}
if(netTCP_SendReady(tcp_sock) == true )
{
}11.2.5 函数nx_ip_fragment_enable
函数原型:
UINT nx_ip_fragment_enable(NX_IP *ip_ptr);
函数描述:
此函数用于启用 IPv4 和 IPv6 数据包分段和重组功能。创建 IP 任务时,此服务会自动禁用。
函数参数:
- 第1个参数是IP实例地址。
- 返回值
- NX_SUCCESS:(0x00) 启用 IP 分段成功。
- NX_PTR_ERROR:(0x07) IP 实例地址无效。
- NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
- NX_NOT_ENABLED:(0x14) IP 分段功能未编译到 NetX Duo 中。
使用举例:
    /* 使能fragment */    
status = nx_ip_fragment_enable(&ip_0);11.2.6 函数nx_udp_enable
函数原型:
UINT nx_udp_enable(NX_IP *ip_ptr);
函数描述:
此函数用于使能UDP组件。
函数参数:
- 第1个参数是IP实例地址。
- 返回值
- NX_SUCCESS:(0x00) 启用 UDP 成功。
- NX_PTR_ERROR:(0x07) IP 指针无效。
- NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
- NX_ALREADY_ENABLED:(0x15) 此组件已启用。
使用举例:
    /* 使能UDP */
    status =  nx_udp_enable(&ip_0);11.2.7 函数nx_udp_socket_create
函数原型:
UINT nx_udp_socket_create(
    NX_IP *ip_ptr,
    NX_UDP_SOCKET *socket_ptr, 
    CHAR *name,
    ULONG type_of_service, 
    ULONG fragment,
    UINT time_to_live, 
    ULONG queue_maximum);函数描述:
此函数用于创建UDP Socket。
函数参数:
1、 第1个参数是IP实例指针。
2、 第2个参数是UDP Socket指针。
3、 第3个参数是UDP Socket名字。
4、 第4个参数是传输服务类型,支持的参数如下:
- NX_IP_NORMAL (0x00000000)
- NX_IP_MIN_DELAY (0x00100000)
- NX_IP_MAX_DATA (0x00080000)
- NX_IP_MAX_RELIABLE (0x00040000)
- NX_IP_MIN_COST (0x00020000)
5、 第5个参数是指定是否允许进行 IP 分段。 如果指定 NX_FRAGMENT_OKAY (0x0),则允许进行 IP 分段。如果指定 NX_DONT_FRAGMENT (0x4000),则会禁止进行 IP 分段。
6、 第6个参数是指定一个 8 位的值,用于定义此数据包在被丢弃之前可通过的路由器数目。 默认值由 NX_IP_TIME_TO_LIVE 指定。
7、 第7个参数是支持的UDP Socket报文最大数目。达到最大值后,接收到每个新数据包时,都会释放最早的 UDP 数据包。
8、 返回值
- NX_SUCCESS:(0x00) 创建 UDP Socket成功。
- NX_OPTION_ERROR:(0x0A) 服务类型、分段或生存时间选项无效。
- NX_PTR_ERROR:(0x07) IP 或Socket指针无效。
- NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
- NX_NOT_ENABLED (0x14) 尚未启用此组件。
使用举例:
    /* 创建UDP socket */
    ret = nx_udp_socket_create(&ip_0,                 /* IP实例控制块 */    
                                &UDPSocket,           /* UDP控制块 */ 
                                "UDP Server Socket",  /* UDP名 */ 
                                NX_IP_NORMAL,         /* IP服务类型 */ 
                                NX_FRAGMENT_OKAY,     /* 使能IP分段 */ 
                                NX_IP_TIME_TO_LIVE,   /*用于定义此数据包在被丢弃之前可通过的路由器数目 */ 
                                512);                 /* 支持的报文数 */11.2.8 函数nx_udp_socket_bind
函数原型:
UINT nx_udp_socket_bind(
    NX_UDP_SOCKET *socket_ptr, 
    UINT port,
    ULONG wait_option);函数描述:
此函数用于为创建的UDP Socket绑定端口。如果设置的端口号还不可用,可以设置等待时间。
函数参数:
1、第1个参数是UDP Socket指针。
2、第2个参数是绑定的端口,范围1 -65535。如果设置为NX_ANY_PORT(0x0000),则会搜索一个可用端口号。
3、第3个参数是端口号不可用时,等待时间定义:
- NX_NO_WAIT (0x00000000)
- NX_WAIT_FOREVER (0xFFFFFFFF)
- 等待时间:(0x00000001 到 0xFFFFFFFE),单位是ThreadX系统时钟节拍。
4、返回值
- NX_SUCCESS:(0x00) 绑定UDP Socket成功。
- NX_ALREADY_BOUND:(0x22) 此UDP Socket已与另一 UDP 端口绑定。
- NX_PORT_UNAVAILABLE:(0x23) 端口已与其他Socket绑定。
- NX_NO_FREE_PORTS:(0x45) 没有可用的端口。
- NX_WAIT_ABORTED:(0x1A) 已通过调用 tx_thread_wait_abort 中止请求。
- NX_INVALID_PORT:(0x46) 端口无效。
- NX_PTR_ERROR:(0x07) Socket指针无效。
- NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
- NX_NOT_ENABLED:(0x14) 此组件尚未启用。
使用举例:
    /* UDP Socket绑定端口 */
    ret = nx_udp_socket_bind(&UDPSocket, DEFAULT_PORT, TX_WAIT_FOREVER);
    if (ret != NX_SUCCESS)
    {
        Error_Handler(__FILE__, __LINE__);   
    }11.2.9 函数nx_packet_data_retrieve
函数原型:
UINT nx_packet_data_retrieve(
    NX_PACKET *packet_ptr,
    VOID *buffer_start,
    ULONG *bytes_copied);函数描述:
此函数用于将提供的数据包中的数据复制到提供的缓冲区。复制的实际字节数由形参bytes_copied 所指向的存储单元返回。
注意,此函数不会更改该数据包的内部状态。检索的数据仍存在于该数据包中。
函数参数:
- 第1个参数是指向源数据包的指针
- 第2个参数是目的数据包的地址。
- 第3个参数是最终复制的字节数存储地址。
- 返回值,返回以下几种状态值:
- NX_SUCCESS:(0x00)复制数据包数据成功。
- NX_INVALID_PACKET:(0x12) 数据包无效。
- NX_PTR_ERROR:(0x07) 形参地址无效。
注意事项:
目标缓冲区的大小必须足以容纳该数据包的内容。否则内存会损坏,导致不可预知的结果。
使用举例:
/* 获取客户端发来的数据 */
 nx_packet_data_retrieve(data_packet,    /* 接收到的数据包 */
                      data_buffer,    /* 解析出数据 */
                      &bytes_read);   /* 数据大小 */11.2.10 函数nx_packet_data_extract_offset
函数原型:
UINT nx_packet_data_extract_offset(
    NX_PACKET *packet_ptr,
    ULONG offset,
    VOID *buffer_start,
    ULONG buffer_length,
    ULONG *bytes_copied);函数描述:
此函数将NetX Duo 数据包中的数据复制到指定缓冲区中,可以指定要复制的数据偏移位置。实际复制的字节数在 bytes_copied 中返回。此函数不会从数据包中删除数据,也不会调整前置指针或其他内部状态信息。
函数参数:
- 第1个参数是数据包指针。
- 第2个参数是数据包的偏移地址。
- 第3个参数是复制后要保存的缓冲地址。
- 第4个参数是要复制的字节数。
- 第5个参数是实际复制的字节数
- 返回值,返回以下几种状态值:
- NX_SUCCESS:(0x00)复制数据包数据成功。
- NX_PACKET_OFFSET_ERROR (0x53)提供了无效的偏移值。
- NX_PTR_ERROR:(0x07) 形参地址无效。
注意事项:
目标缓冲区的大小必须足以容纳该数据包的内容。否则内存会损坏,导致不可预知的结果。
使用举例:
/* 将UDP数据包中的数据复制到缓冲data_buffer */
  nx_packet_data_extract_offset(RecPacket,            /* 数据包 */
                                0,                    /* 数据包地址偏移 */
                                data_buffer,          /* 目标缓冲 */
                                sizeof(data_buffer),  /* 目标缓冲大小 */
                                &bytes_read);         /* 数据复制的字节数 */11.2.11 函数nx_udp_socket_receive
函数原型:
UINT nx_udp_socket_receive(
    NX_UDP_SOCKET *socket_ptr,
    NX_PACKET **packet_ptr,
    ULONG wait_option);函数描述:
此函数用于从指定的Socket接收UDP数据,如果指定的Socket上没有已经排队的数据,则调用方会根据提供的等待选项参数挂起。
函数参数:
1、 第1个参数是UDP Socket指针
2、 第2个参数是UDP数据包指针。
3、 第3个参数是Socket队列上没有数据时的处理:
- NX_NO_WAIT (0x00000000)。
- NX_WAIT_FOREVER (0xFFFFFFFF)。
- 以时钟周期为单位的超时值(0x00000001 到 0xFFFFFFFE)。
4、 返回值:
- NX_SUCCESS:(0x00) 接收Socket数据成功。
- NX_NOT_BOUND:(0x24) Socket未绑定。
- NX_NO_PACKET:(0x01) 未收到任何数据。
- NX_WAIT_ABORTED:(0x1A) 通过调用 tx_thread_wait_abort 中止挂起。
- NX_PTR_ERROR:(0x07) Socket指针或返回数据包指针无效。
- NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
- NX_NOT_ENABLED:(0x14) 此组件尚未启用。
注意事项:
- 如果返回了 NX_SUCCESS,则应用程序负责:不再需要收到数据包时将其释放。
使用举例:
/* 接收数据 */
ret = nx_udp_socket_receive(&UDPSocket, &RecPacket, TX_WAIT_FOREVER);11.2.12 函数nx_udp_socket_send
函数原型:
UINT nx_udp_socket_send(
    NX_UDP_SOCKET *socket_ptr,
    NX_PACKET *packet_ptr,
    ULONG ip_address, 
    UINT port);函数描述:
此函数用于UDP Socket数据发送。注意,无论 UDP 数据报是否已成功发送,此服务都会立即返回。。
函数参数:
- 第1个参数是UDP Socket句柄。
- 第2个参数是UDP数据包指针。
- 第3个参数是目标地址。
- 第4个参数是目标端口号,范围1到65535。
- 返回值,返回以下几种状态值:
- NX_SUCCESS:(0x00) Socket发送成功。
- NX_NOT_BOUND:(0x24) Socket未与任何端口绑定。
- NX_NO_INTERFACE_ADDRESS:(0x50) 找不到合适的传出接口。
- NX_TX_QUEUE_DEPTH:(0x49) 已达到最大传输队列深度。
- NX_OVERFLOW:(0x03) 数据包追加指针无效。
- NX_UNDERFLOW:(0x02) 数据包前置指针无效。
- NX_PTR_ERROR:(0x07) 套接字指针无效。
- NX_CALLER_ERROR:(0x11) 此服务的调用方无效。
- NX_NOT_ENABLED:(0x14) 此组件尚未启用。
使用举例:
/* 发送数据包到UDP发送端 */
ret =  nx_udp_socket_send(&UDPSocket, TraPacket, source_ip_address, source_port);11.2.13 函数nx_packet_release
函数原型:
UINT nx_packet_release(NX_PACKET *packet_ptr);
函数描述:
此函数用于释放数据包,包括链接到指定数据包的任何其他数据包。如果有其他任务在等待这个数据包,则该任务会获得该数据包并继续执行。
函数参数:
1、 第1个参数是数据包地址。
2、 返回值,返回以下几种状态值:
- NX_SUCCESS:(0x00) 释放数据包成功。
- NX_PTR_ERROR:(0x07) 数据包指针无效。
- NX_UNDERFLOW:(0x02) 预置指针小于有效负载开始位置。
- NX_OVERFLOW:(0x03) 追加指针大于有效负载结束位置。
注意事项:
应用程序必须防止多次释放同一数据包,否则会导致不可预知的结果。
11.3 UDP的实现方法
11.3.1 NetXDUO初始化
创建UDP前,要初始化NetX,创建内存池,例化IP:
/*
*********************************************************************************************************
*	函 数 名: NetXTest
*	功能说明: TCPnet应用
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/    
void NetXTest(void)
{
    UINT status;
    UINT ret;
    ULONG socket_state;
    UINT old_priority;
    NX_PACKET *data_packet;
    ULONG bytes_read;
	
	ULONG peer_ip_address;
	ULONG peer_port;
    
	
    /* 初始化NetX */
    nx_system_initialize();
    /* 创建内存池 */
    status =  nx_packet_pool_create(&pool_0,                 /* 内存池控制块 */
                                     "NetX Main Packet Pool",/* 内存池名 */
               1536, /* 内存池每个数据包大小,单位字节此值必须至少为 40 个字节,并且还必须可以被 4 整除 */
		     (ULONG*)(((int)packet_pool_area + 15) & ~15) ,/* 内存池地址,此地址必须ULONG对齐 */
               NX_PACKET_POOL_SIZE);                        /* 内存池大小 */                  
          
    /* 检测创建是否失败 */
    if (status) error_counter++;
    /* 例化IP */
    status = nx_ip_create(&ip_0,                                                   /* IP实例控制块 */                                    
                            "NetX IP Instance 0",                                  /* IP实例名 */     
                            IP_ADDRESS(IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3),    /* IP地址 */
                            0xFFFFFF00UL,                                          /* 子网掩码 */
                            &pool_0,                                               /* 内存池 */
						nx_driver_stm32h7xx,                                   /* 网卡驱动 */
                            (UCHAR*)AppTaskNetXStk,                                /* IP任务栈地址 */
                            sizeof(AppTaskNetXStk),                             /* IP任务栈大小,单位字节 */
                            APP_CFG_TASK_NETX_PRIO);                            /* IP任务优先级 */
                            
            
    /* 检测创建是否失败 */
    if (status) error_counter++;
    /* 使能ARP,并提供ARP缓存 */
    status =  nx_arp_enable(&ip_0,               /* IP实例控制块 */
					 (void *)arp_space_area,  /* ARP缓存地址 */
		 sizeof(arp_space_area));   /* 每个 ARP 条目均为 52 个字节,因此,ARP 条目总数是52字节整数倍 */
    /* 使能fragment */    
    status = nx_ip_fragment_enable(&ip_0);
    /* 检测使能成功 */
    if (status) error_counter++;
    /* 使能TCP */
    status =  nx_tcp_enable(&ip_0);
    /* 检测使能成功 */
    if (status) error_counter++;
    /* 使能UDP  */
    status =  nx_udp_enable(&ip_0);
    /* 检测使能成功 */
    if (status) error_counter++;
    /* 使能ICMP */
    status =  nx_icmp_enable(&ip_0);
    /* 检测使能成功 */
    if (status) error_counter++;   
    
    /* NETX初始化完毕后,重新设置优先级 */
    tx_thread_priority_change(netx_thread_ptr, APP_CFG_TASK_NETX_PRIO1, &old_priority);
tx_thread_priority_change(&AppTaskNetXProTCB, APP_CFG_TASK_NetXPro_PRIO1, &old_priority);
    /* 省略 */
}
程序末尾务优先级做了特别处理,创建的时候先设置为低优先级,检测到网线正常连接并初始了网络后将优先级设置到正常水平。
11.3.2 UDP实现
下面是创建UDP:
/*
*********************************************************************************************************
*	函 数 名: NetXTest
*	功能说明: TCPnet应用
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/    
void NetXTest(void)
{
  
     /* 省略 */
    /* 创建UDP socket */
    ret = nx_udp_socket_create(&ip_0,                 /* IP实例控制块 */    
                                &UDPSocket,           /* UDP控制块 */ 
                                "UDP Server Socket",  /* UDP名 */ 
                                NX_IP_NORMAL,         /* IP服务类型 */ 
                                NX_FRAGMENT_OKAY,     /* 使能IP分段 */ 
                                NX_IP_TIME_TO_LIVE,   /* 用于定义此数据包在被丢弃之前可通过的路由器数目 */ 
                                512);                 /* 支持的报文数 */
    if (ret != NX_SUCCESS)
    {
        Error_Handler(__FILE__, __LINE__);   
    }
    
    /* UDP Socket绑定端口 */
    ret = nx_udp_socket_bind(&UDPSocket, DEFAULT_PORT, TX_WAIT_FOREVER);
    if (ret != NX_SUCCESS)
    {
        Error_Handler(__FILE__, __LINE__);   
    }
     /* 省略 */
}
11.3.3 UDP回环通信实现
回环的意思就是电脑端网络助手发送数据给板子后,板子再将数据返回。为了方便大家使用,本例子将接收数据包和发送数据包分别做了定义:
/*
*********************************************************************************************************
*	函 数 名: NetXTest
*	功能说明: TCPnet应用
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/    
void NetXTest(void)
{
  
     /* 省略 */
       /* 接收数据 */
        ret = nx_udp_socket_receive(&UDPSocket, &RecPacket, TX_WAIT_FOREVER);
        if (ret == NX_SUCCESS)
        {
            /* 将UDP数据包中的数据复制到缓冲data_buffer */
            nx_packet_data_extract_offset(RecPacket,            /* 数据包 */
                                          0,                    /* 数据包地址偏移 */
                                          data_buffer,          /* 目标缓冲 */
                                          sizeof(data_buffer),  /* 目标缓冲大小 */
                                          &bytes_read);         /* 数据复制的字节数 */
            /* 获取远程端口和IP  */
            nx_udp_source_extract(RecPacket, &source_ip_address, &source_port);
            /* 打印接收到数据 */
            PRINT_DATA(source_ip_address, source_port, data_buffer);
            /* 释放数据包 */
            nx_packet_release(RecPacket);
            /* 申请发送数据包 */
            ret = nx_packet_allocate(&pool_0, &TraPacket, NX_UDP_PACKET, TX_WAIT_FOREVER);
            if (ret)
            {
                Error_Handler(__FILE__, __LINE__);  
            }
            sprintf((char *)sendbuf, "sendbuf = %d\r\n", count++);
            /*将要发送的数据附加到TraPacket */
            ret = nx_packet_data_append(TraPacket, (VOID *)sendbuf, strlen((char *)sendbuf), 
&pool_0, TX_WAIT_FOREVER);
            if (ret)
            {
                Error_Handler(__FILE__, __LINE__);
            }
            /* 发送数据包到UDP发送端 */
            ret =  nx_udp_socket_send(&UDPSocket, TraPacket, source_ip_address, source_port);
        }
	}  	
     /* 省略 */
}
11.4 网络调试助手和板子的调试操作步骤
我们这里使用下面这款调试助手,当然,任何其它网络调试助手均可,不限制:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=1568 。
11.4.1 测试使用的DM916X网口并注意跳线帽
测试时,网线要插到DM916X网口上:

特别注意此处跳线帽的位置,要短接PG11:

11.4.2 RJ45网络变压器插座上绿灯和黄灯现象
各种网卡、交换机等网络设备都不一样,一般来讲:绿灯分为亮或不亮(代表网络速度),黄灯分为闪烁或不闪烁(代表是否有数据收发)。
绿灯:长亮代表100M; 不亮代表10M。
黄灯:长亮代表无数据收发; 闪烁代表有数据收发。
也有些千兆网卡的灯以颜色区分,不亮代表10M / 绿色代表100M / 黄色代表1000M。现在10M的网络基本看不到了,如果一个灯长亮,基本可以说明100M网络或更高,而另一个灯时而闪烁,那代表有数据收发,具体要看网络设备了。甚至有些低等网卡如TP-LINK,只有一个灯,亮代表连通,闪烁代表数据收发。
对于开发板上面的RJ45网络变压器插座上面的灯而言,绿灯代表数据收发,长亮的话表示无数据收发,闪烁代表有数据收发。黄灯代表网络速度,长亮代表100M,不亮代表10M。
11.4.3 第1步,设置板子IP地址
我们这里使用使用固定IP(或者说静态IP一个意思),设置也比较省事。我们这里以开发板和电脑直连的方式进行说明,即通过一根网线直接将开发板的网口和电脑端的网口连接起来即可。如果大家使用的是笔记本,强烈推荐测试期间将笔记本的WIFI网络禁止,各种代理软件和虚拟网卡也暂时关闭。等测试完毕了再逐一打开,查看是否有问题。
对于固定IP方式,也可以接到路由器或者交换机上面测试,特别注意板子设置的IP地址不要跟路由器或者交换机上其它设备的IP冲突了,测试阶段还是建议采用电脑直连方式,跑通了再跑其它方式。
在文件demo_dm9162_netx.h中设置IP地址,具体配置如下(大家更新自己的情况修改):
/*
*********************************************************************************************************
*                                        IP相关
*********************************************************************************************************
*/
#define DEFAULT_PORT                    1000    /* TCP服务器监听端口号 */
#define IP_ADDR0                        192
#define IP_ADDR1                        168
#define IP_ADDR2                        28
#define IP_ADDR3                        245 11.4.4 第2步,设置电脑IP地址
一定要将电脑端的IP地址设置到跟开发板在一个IP段,即都是192.168.28.X。第2步中已经将开发板的IP设置为192.168.28.245,我们这里就将电脑的IP设置为192.168.28.221。我这里是WIN7 64bit系统。
(1)右击桌面上的“网络”图标,选择属性。

(2)弹出的界面中选项“本地连接”

(3)选择“属性(P)”

(4)双击“Internet协议版本4(TCP/Ipv4)”选项。

(5)配置IP地址、子网掩码和默认网关,DNS无需配置,记得点击确定

(6)点击了“确定”按钮后,退回到之前的界面,这里的“确定”按钮不要忘了点击:

11.4.5 第3步,测试ping是否成功
下载例程到开发板,然后ping 192.168.28.245,查看是否连接上。
(1)WIN+R组合键打开“运行”窗口,输入cmd。

(2)输入ping 192.168.28.245后,回车,也是可以的。

收发相同,没有数据丢失,说明ping命令也是成功的。
11.4.6 第5步,网络调试助手创建UDP服务器
- 打开调试助手,点击左上角创建服务器:

- 弹出如下界面,指定IP设置为192.168.28.245,一定要跟第2步设置的板子端IP地址一致,端口号1000,最后点击确定:

- 创建后的界面效果如下:

11.4.7 第6步,连接板子端的UDP Socket
点击连接UDP Socket:

正常连接后效果:

11.4.8 第5步,UDP回环测试
板子和网络调试助手建立连接后就可以相互收发数据了。

板子端接收到字符做了个简单的展示(波特率115200,数据位8,奇偶校验位无,停止位1):

11.5 实验例程
配套例子:
V5-2404_ThreadX NetXDUO UDP
实验目的:
- 学习ThreadX NetXDUO UDP实现
实验内容:
- 共创建了如下几个任务,通过按下按键K1可以通过串口打印任务堆栈使用情况
======================================================
OS CPU Usage = 1.31%
=======================================================
任务优先级 任务栈大小 当前使用栈 最大栈使用 任务名
Prio StackSize CurStack MaxStack Taskname
2 4092 303 459 App Task Start
30 1020 303 303 App Task STAT
31 1020 87 71 App Task IDLE
5 4092 311 551 App Msp Pro
7 4092 303 719 App Task UserIF
6 4092 255 359 App NETX Pro
3 4092 415 535 NetX IP Instance 0
0 1020 191 235 System Timer Thread
串口软件可以使用SecureCRT或者H7-TOOL RTT查看打印信息。
App Task Start任务 :启动任务,这里用作BSP驱动包处理。
App Task MspPro任务 :消息处理。
App Task UserIF任务 :按键消息处理。
App Task COM任务 :这里用作LED闪烁。
System Timer Thread任务:系统定时器任务
操作说明:
1、由于程序使用了DWT时钟周期计数器,程序下载后,请将板子重新上电使用,防止DWT时钟周期计数器没有正常复位。
2、NetX网络协议栈操作:
(1) 默认IP地址192.168.28.245,在demo_dm9162_netx.c开头定义,用户可根据需要修改。
(2) 可以在电脑端用网络调试软件创建TCP服务器,端口号1001。
(3) 实现了一个简单的回环通信,用户使用上位机发送的数据,然后板子返回另外的数据。
串口打印信息方式(AC5,AC6和IAR):
波特率 115200,数据位 8,奇偶校验位无,停止位 1

11.6 总结
本章节就为大家讲解这么多,希望大家多做测试,争取可以熟练掌握这些API函数的使用。










