【STM32F407】第11章 ThreadX NetXDUO之UDP客户端/服务器

最新教程下载:ThreadX NetXDUO网络协议栈教程更新记录贴,前11章已经发布(2022-01-03) - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz!

第11章       ThreadX NetXDUO之UDP客户端/服务器

本章节为大家讲解NetXDUO的UDP客户端实现,学习本章节前,务必要优先学习第10章UDP传输控制协议基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。

目录

11.1 初学者重要提示

11.2 UDP的API函数

11.2.1        函数nx_system_initialize

11.2.2        函数nx_packet_pool_create

11.2.3        函数nx_ip_create

11.2.4        函数nx_arp_enable

11.2.5        函数nx_ip_fragment_enable

11.2.6        函数nx_udp_enable

11.2.7        函数nx_udp_socket_create

11.2.8        函数nx_udp_socket_bind

11.2.9        函数nx_packet_data_retrieve

11.2.10   函数nx_packet_data_extract_offset

11.2.11   函数nx_udp_socket_receive

11.2.12   函数nx_udp_socket_send

11.2.13   函数nx_packet_release

11.3 UDP的实现方法

11.3.1        NetXDUO初始化

11.3.2        UDP实现

11.3.3        UDP回环通信实现

11.4 网络调试助手和板子的调试操作步骤

11.4.1 测试使用的DM916X网口并注意跳线帽

11.4.2 RJ45网络变压器插座上绿灯和黄灯现象

11.4.3 第1步,设置板子IP地址

11.4.4 第2步,设置电脑IP地址

11.4.5 第3步,测试ping是否成功

11.4.6 第5步,网络调试助手创建UDP服务器

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

11.4.8 第5步,UDP回环测试

11.5 实验例程

11.6 总结


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.   第1个参数是内存池控制块的地址。
  2.   第2个参数是内存池名字。
  3.   第3个参数是内存池中每个数据包的字节数。 此值必须至少为 40 个字节,并且还必须可以被 4 整除。
  4.   第4个参数是内存池中数据地址,此地址必须ULONG对齐。
  5.   第5个参数是内存池大小。
  6.   返回值:
  •   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.   第1个参数是创建IP实例的控制块指针。
  2.   第2个参数是IP实例的名字。
  3.   第3个参数是IP地址。
  4.   第4个参数是子网掩码
  5.   第5个参数是内存池地址。
  6.   第6个参数是网卡驱动地址。
  7.   第7个参数是IP任务栈地址
  8.   第8个参数是IP任务栈大小,单位字节。
  9.   第9个参数是IP任务优先级。
  10.   返回值
  •   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地址解析。

函数参数:

  1.   ip_ptr:IP实例地址。
  2.   arp_cache_memory:ARP缓存地址。
  3.   arp_cache_size:每个 ARP 条目均为 52 个字节,因此,ARP 条目总数是52字节整数倍。
  4.   返回值
  •   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.   第1个参数是IP实例地址。
  2.   返回值
  •  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.   第1个参数是IP实例地址。
  2.   返回值
  •   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.   第1个参数是指向源数据包的指针
  2.   第2个参数是目的数据包的地址。
  3.   第3个参数是最终复制的字节数存储地址。
  4.   返回值,返回以下几种状态值:
  •   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.   第1个参数是数据包指针。
  2.   第2个参数是数据包的偏移地址。
  3.   第3个参数是复制后要保存的缓冲地址。
  4.   第4个参数是要复制的字节数。
  5.   第5个参数是实际复制的字节数
  6.   返回值,返回以下几种状态值:
  •   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) 此组件尚未启用。

注意事项:

  1. 如果返回了 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.   第1个参数是UDP Socket句柄。
  2.   第2个参数是UDP数据包指针。
  3.   第3个参数是目标地址。
  4.   第4个参数是目标端口号,范围1到65535。
  5.   返回值,返回以下几种状态值:
  •   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

实验目的:

  1. 学习ThreadX NetXDUO UDP实现

实验内容:

  1. 共创建了如下几个任务,通过按下按键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函数的使用。

版权声明:本文为CSDN博主「Simon223」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Simon223/article/details/122562344

生成海报
点赞 0

Simon223

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

STM32+FreeRTOS+LWIP TCP Server多端口并发通信实例

1. 问题的提出 在LWIP的TCP Server功能的应用中,如何创建多个TCP服务器端口同时与多台客户端设备通信,或者如何在同一个TCP服务器端口下创建多个连接同时与多台客户端通信? 2. 设计

STM32 LWIP UDP获取对方IP及port

使用LWIP UDP通信时,需先定义本地IP和port。收到对端发来的信息后,可选择向固定IP及port发送响应信息,或者使用255.255.255.255广播发送。若对端只在本地IP网段内&#xff0