最新教程下载:/d/file/titlepic/forum.php RL-TCPnet之创建多个TCP客户端 本章节为大家讲解RL-TCPnet的TCP多客户端实现,因为多客户端在实际项目中用到的地方还挺多,所以我们也专门开启一个章节做讲解。另外,学习本章节前,务必要优先学习第12章TCP客户端。学会创建一个TCP客户端了,创建多个客户端是一样的。 目录 第13章 RL-TCPnet之创建多个TCP客户端 13.1 初学者重要提示 13.2 创建多个TCP客户端连接服务器 13.3 系统配置说明(Net_Config.c) 13.4 TCP配置说明(Net_Config_TCP.h) 13.5 以太网配置说明(Net_Config_ETH_0.h) 13.6 网络调试说明(Net_Debug.c) 13.7 TCP客户端的实现方法 13.7.1 创建三个TCP客户端 13.7.2 TCP数据发送 13.7.3 TCP数据接收 13.8 网络调试助手和板子的调试操作步骤 13.8.1 获取板子IP地址 13.8.2 获取电脑的IP地址 13.8.3 在程序中配置要访问的远程IP地址和端口 13.8.4 网络调试助手创建TCP服务器 13.8.5 TCP客户端发送数据 13.8.6 TCP客户端接收数据 13.9 实验例程说明(RTX5) 13.10 实验例程说明(FreeRTOS) 13.11 总结 本章节为大家讲解一个多TCP客户端连接服务器的实例,因为实际项目中,这种情况还比较多,所以也作为一期教程进行专门的讲解。 有了上期教程的基础,本期教程也比较好实现,用户仅需多创建几个TCP客户端,并配置Net_Config_TCP.h中可以创建的TCP Socket个数即可。本章节配套的例子是创建了三个TCP客户端跟电脑端的服务器连接通信。 RL-TCPnet的系统配置工作是通过文件Net_Config.c实现。在MDK工程中打开文件Net_Config.c,可以看到下图所示的工程配置向导:13.1 初学者重要提示
学习本章节前,务必保证已经学习了第11章的TCP基础知识和第12章的TCP客户端。 相比前面章节的TCP服务器,TCP客户端的测试要稍麻烦些,例子中默认访问的TCP服务器端IP地址是192.168.1.2,端口号1001。大家测试时要根据自己电脑的实际IP地址设置app_tcpnet_lib.c文件中远程IP和端口。具体测试方法详看本章节的13.7小节。13.2 创建多个TCP客户端连接服务器
13.3 系统配置说明(Net_Config.c)
Network System Settings
Local Host Name局域网域名。
这里起名为armfly,使用局域网域名限制为15个字符。
Memory Pool size参数范围1536-262144字节。
内存池大小配置,单位字节。
Start System Services开启系统服务。如果使能了此选项(打上对勾表示使能),系统将自动开启系统服务,比如HTTP, FTP, TFTP rver等。如果没有使能,需要用户调用专门的API使能。
OS Resource SettingsCore Thread Stack SizeRL-TCPnet内核任务需要的栈大小,单位字节,范围512到65535。
NET_THREAD_PRIORITYRL-TCPnet内核任务的优先级。
这个选择在配置向导里面没有展示,需要大家点击上图左下角的Text Editor按钮查看宏定义修改。
TCP配置文件:
TCP Sockets
Number of TCP Sockets范围1-20。
用于配置可创建的TCP Sockets数量。
Number of Retries范围0-20。
用于配置重试次数,TCP数据传输时,如果在设置的重试时间内得不到应答,算一次重试失败,这里就是配置的最大重试次数。
Retry Timeout in conds范围1-10,单位秒。
重试时间。如果发送的数据在时间内得不到应答,将重新发送数据。
Default Connect Timeout in conds范围1-600,单位秒。
用于配置默认的保持连接时间,即我们常说的Keep Alive时间,如果时间到了将断开连接。常用于HTTP Server,Telnet Server等。
Maximum Segment Size范围536-1440,单位字节。
MSS定义了TCP数据包能够传输的最大数据分段。
Receive Window Size范围536-65535,单位字节。
TCP接收窗口大小。
以太网涉及到的配置选项比较多:
Ethernet Network Interface 0
Connect to hardware via Driver_ETH#
用于指定驱动号,这个一般不需要用户去设置,比如RTE创建的文件名是Net_Config_ETH_0.h,就会自动将此参数设置为0。
VLAN
虚拟局域网。
ETH0_VLAN_IDVLAN的ID号,12bit数值,范围1到4093。
MAC Address
局域网内可以随意配置,只要不跟局域网内其它设备的MAC地址冲突即可。
注意,MAC地址的第1个字节的最后一个bit一定要是0。
IP Address
IP地址。
Subnet mask
子网掩码。
Default Gateway
默认网关。
Primary DNS Server
首选DNS服务器地址。
Secondary DNS Server
备选DNS服务器地址。
IP Fragmentation
使用发送IP报文的分片处理和接收IP报文的重组。
MTU Size范围576-1500字节。
最大的传输单元。
ARP Address Resolution
地址解析协议
Cache Table sizeARP Cache表大小。
Cache Timeout in condsCache表超时时间。
Number of Retries尝试解析IP地址的次数
Rend Timeout in conds每次解析请求的时间间隔。
Send Notification on Address changes启用此选项后,嵌入式主机将在启动时或设备IP地址已更改时发送ARP通知。
IGMP Group Management
IGMP分组管理。
Membership Table size此主机可以加入的分组数。
NetBIOS Name Service
NetBIOS局域网域名服务,这里打上对勾就使能了。这样我们就可以通过Net_Config.c文件配置的Local Host Name局域网域名进行访问,而不需要通过IP地址访问了。
Dynaminc Host Configuration
即DHCP,这里打上对勾就使能了。使能了DHCP后,RL-TCPnet就可以从外接的路由器上获得动态IP地址。
Vendor Class Identifier厂商ID,如果设置了的话,会将其加到DHCP的请求消息中,用于识别网络设备的不同厂商。
Bootfile Name从DHCP 服务器获取的引导文件名。
NTP Servers从DCHP服务器获得NTP服务器列表。
RL-TCPnet的调试功能是通过配置文件Net_Debug.c实现。在MDK工程中打开文件Net_Debug.c,可以看到如下图所示的工程配置向导:
Print Time Stamp
勾选了此选项的话,打印消息时,前面会附带时间信息。
其它所有的选项
默认情况下,所有的调试选项都是关闭的,每个选项有三个调试级别可选择,这里我们以Memory Management为例,点击下拉列表,可以看到里面有Off,Errors only和Full debug三个调试级别可供选择,每个调试选项里面都是这三个级别。
Off:表示关闭此选项的调试功能。
Errors only:表示仅在此选项出错时,将其错误打印出来。
Full debug:表示此选项的全功能调试。
具体测试,我们这里就不做了,大家可以按照第9章讲解的调试方法进行测试。
有了本章前面小节的配置后,剩下的问题就是TCP客户端的创建和TCP客户端数据收发的实现。
TCP服务器的创建比较简单,调用函数netTCP_GetSocket即可(此函数的使用方法和注意事项在第12章有讲解),为了更好的管理这三个TCP客户端,专门为每个TCP客户端单独做一个C文件:
app_tcpnet_lib.c,app_tcpclient1.c和app_tcpclient2.c分别是TCP客户端1,2和3。每个客户端都是通过单独创建一个任务实现:
/*********************************************************************************************************** 函 数 名: AppTaskMsgPro* 功能说明: TCPnet应用任务* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal2 **********************************************************************************************************/void AppTaskMsgPro(void *argument){ while(1) { TCPnetTest(); } }/*********************************************************************************************************** 函 数 名: AppTaskClient1* 功能说明: TCPnet应用任务* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal2 **********************************************************************************************************/void AppTaskClient1(void *argument){ while(1) { TCPnetClient1(); } }/*********************************************************************************************************** 函 数 名: AppTaskClient2* 功能说明: TCPnet应用任务* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal2 **********************************************************************************************************/void AppTaskClient2(void *argument){ while(1) { TCPnetClient2(); } }
下面以TCP客户端1为例说明,即任务AppTaskMsgPro里面创建的客户端:
/*********************************************************************************************************** 宏定义,远程服务器的IP和端口**********************************************************************************************************//* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */#define IP1 192#define IP2 168#define IP3 1#define IP4 2#define PORT_NUM 1001/* 这个是本地端口 */#define LocalPort_NUM 1024/*********************************************************************************************************** 函 数 名: TCPnetClient1* 功能说明: TCPnet应用* 形 参: 无* 返 回 值: 无**********************************************************************************************************/ void TCPnetClient1(void){ int32_t iCount; uint8_t *ndbuf; uint32_t maxlen; netStatus res; const uint16_t usMaxBlockTime = 2; /* 延迟周期 */ uint32_t EvtFlag; tcp_sock1 = netTCP_GetSocket (tcp_cb_client1); if (tcp_sock1 > 0) { /* 使能TCP_TYPE_KEEP_ALIVE,会一直保持连接 */ netTCP_SetOption (tcp_sock1, netTCP_OptionKeepAlive, 1); } while (1) { EvtFlag = osThreadFlagsWait(0x0000000FU, osFlagsWaitAny, usMaxBlockTime); /* 按键消息的处理 */ switch (EvtFlag) { 省略未写 /* 接收到摇杆OK键按下,连接远程服务器 */ ca KEY4_BIT3: if (tcp_sock1 > 0) { if(netTCP_GetState(tcp_sock1) != netTCP_StateESTABLISHED) { res = netTCP_Connect (tcp_sock1, (NET_ADDR *)&addr, LocalPort_NUM); printf_debug("%s\r\n", ReVal_Table1[res]); } } break; /* 其他的键值不处理 */ default: break; } }}
代码比较好理解,由函数netTCP_GetSocket创建,再通过netTCP_Connect连接服务器。
TCP Socket的数据发送一定要注意各个函数调用顺序和使用方法,非常重要!否则,数据发送很容易失败。数据发送所用到函数的使用方法和注意事项在第12章有讲解。下面的代码中对数据发送专门做了处理,支持任意字节大小的数据发送,仅需修改计数变量iCount的初始值即可,初始值是多少,就是发送多少字节。
/*********************************************************************************************************** 函 数 名: TCPnetTest* 功能说明: TCPnet应用* 形 参: 无* 返 回 值: 无**********************************************************************************************************/ void TCPnetTest(void){ int32_t iCount; uint8_t *ndbuf; uint32_t maxlen; netStatus res; const uint16_t usMaxBlockTime = 2; /* 延迟周期 */ uint32_t EvtFlag; tcp_sock = netTCP_GetSocket (tcp_cb_client); if (tcp_sock > 0) { /* 使能TCP_TYPE_KEEP_ALIVE,会一直保持连接 */ netTCP_SetOption (tcp_sock, netTCP_OptionKeepAlive, 1); } while (1) { EvtFlag = osThreadFlagsWait(0x0000000FU, osFlagsWaitAny, usMaxBlockTime); /* 按键消息的处理 */ switch (EvtFlag) { /* 接收到K1键按下,给远程TCP客户端发送4096字节数据 */ ca KEY1_BIT0: iCount = 4096; if(netTCP_GetState(tcp_sock) == netTCP_StateESTABLISHED) { do { if(netTCP_SendReady(tcp_sock) == true ) { maxlen = netTCP_GetMaxSegmentSize (tcp_sock); iCount -= maxlen; if(iCount < 0) { /* 这么计算没问题的 */ maxlen = iCount + maxlen; } ndbuf = netTCP_GetBuffer (maxlen); ndbuf考研各科分数[0] = '1'; ndbuf[1] = '2'; ndbuf[2] = '3'; ndbuf[3] = '4'; ndbuf[4] = '5'; ndbuf[5] = '6'; ndbuf[6] = '7'; ndbuf[7] = '8'; /* 必须使用申请的内存空间 */ netTCP_Send (tcp_sock, ndbuf, maxlen); } }while(iCount > 0); } break; /* 其他的键值不处理 */ default: break; } }}
TCP数据接收主要是通过函数netTCP_GetSocket的回调函数实现(RTX5和FreeRTOS):
/*********************************************************************************************************** 函 数 名: tcp_cb_client* 功能说明: TCP Socket的回调函数* 形 参: socket 句柄* event 事件类型* addr NET_ADDR类型变量,记录IP地址,端口号。* buf ptr指向的缓冲区记录着接收到的TCP数据。* len 记录接收到的数据个数。* 返 回 值: **********************************************************************************************************/uint32_t tcp_cb_client (int32_t socket, netTCP_Event event, const NET_ADDR *addr, const uint8_t *buf, uint32_t len) { switch (event) { /* 远程客户端连接消息 1、数组ptr存储远程设备的IP地址,par中存储端口号。 2、返回数值1允许连接,返回数值0禁止连接。 */ ca netTCP_EventConnect: return (1); /* Socket远程连接已经建立 */ ca netTCP_EventEstablished: printf_debug("Socket is connected to remote peer\r\n"); break; /* 连接断开 */ ca netTCP_EventClod: printf_debug("Connection has been clod\r\n"); break; /* 连接终止 */ ca netTCP_EventAborted: break; /* 发送的数据收到远程设备应答 */ ca netTCP_EventACK: break; /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */ ca netTCP_EventData: printf_debug("Data length = %d\r\n", len); printf ("%.*s\r\n",len, buf); break; } return (0);}
TCP服务器的数据接收主要是通过回调函数的TCP_EVT_DATA消息实现,进入消息后,指针变量buf是接收数据缓冲区首地址,变量len记录接收到的数据长度,单位字节。
我们这里使用下面这款调试助手,任何其它网络调试助手均可,不限制:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=1568 。
重要提示,操作的过程中务必要优先在电脑端创建TCP服务器并开启,然后再操作板子进行连接。因为本章节配套的实例在按键按下后调用函数netTCP_Connect只进行一次连接,如果在Net_Config_TCP.c文件中配置的重连次数范围内无法连接上,就不会再进行连接了,需要再次点击按键进行连接。
(说明,对于TCP客户端实验,这步已经不需要了,不过大家还可以进行测试)
首先,强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址,而且在前面的配置中使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址。测试方法如下:
WIN+R组合键打开“运行”窗口,输入cmd。 弹出的命令窗口中,输入ping armfly。 输入ping armfly后,回车。获得IP地址是192.168.1.6。
获取电脑IP地址的方法很多,可以在网上邻居获取,也可以通过输入命令ipconfig获取,方法跟上面13.8.1小节中的方式一样:
WIN+R组合键打开“运行”窗口,输入cmd。弹出的命令窗口中,输入ipconfig。输入ipconfig后,回车。获得电脑的IP地址是192.168.1.2。
据前面13.8.2小节获取的电脑端IP地址,需要大家配置程序中app_tcpnet_lib.c文件开头的宏定义,其中IP地址填前面获取的192.168.1.2,大家要根据电脑实际的IP地址填写。而端口号,我们这里随意配置一个即可,配置为1001,后面电脑端使用网络调试助手创建TCP服务器时,务必要跟这个端口号统一:
/*********************************************************************************************************** 宏定义,远程服务器的IP和端口**********************************************************************************************************//* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */#define IP1 192#define IP2 168#define IP3 1#define IP4 2#define PORT_NUM 1001
如果开发板下载了TCP客户端的程序,并且开发板已经上电,按下板子上面的K1按键,可以看到三个TCP客户端已经加入:
板子和网络调试助手建立连接后就可以互相收发数据了。对于发送数据,三个TCP客户端都可以给服务器发送数据。
摇杆上键按下,TCP客户端1发送4096字节,每次发送数据包的前8个字节设置了字符a到字符h,后面都未做设置。 摇杆左键按下,TCP客户端2发送4096字节,每次发送数据包的前8个字节设置了字符a到字符h,后面都未做设置。 摇杆右键按下,TCP客户端3发送4096字节,每次发送数据包的前8个字节设置了字符a到字符h,后面都未做设置。TCP服务器接收数据的测试也比较方便,我们这里通过网络调试助手给板子发送1到5,共5个字符。
TCP客户端1数据接收测试。点击发送后,可以看到串口软件打印出接收到的5个字符:
测试也是没问题的。
TCP客户端2数据接收测试。点击发送后,可以看到串口软件打印出接收到的5个字符:
测试也是没问题的。
TCP客户端3数据接收测试。点击发送后,可以看到串口软件打印出接收到的5个字符:
测试也是没问题的。
配套例子:
V5-1010_RL-TCPnet V7.X实验_多个TCP客户端连接(RTX5)
实验目的:
学习RTX5 + RL-TCPnet的多个TCP客户端制作。实验内容:
强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要根据实际情况重新配置,如果不会配置,看本例程对应的教程即可):#define IP1 192
#define IP2 168
#define IP3 1
#define IP4 2
#define PORT_NUM 1001
本例程可以创建三个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,三个TCP Client的端口号分别是1024,1025和1026。用户可以在电脑端用网络调试软件创建TCP Server跟这三个客户端建立连接。执行下面5–8步的操作时,优先将电脑端的TCP Server建立起来!!摇杆OK键按下,创建三个TCP客户端,端口号分别是1024,1025和1026。按键K1按下,TCP客户端1发送4096字节数据给服务器。按键K2按下,TCP客户端2发送4096字节数据给服务器。按键K3按下,TCP客户端3发送4096字节数据给服务器。实验操作:
详见本章节13.8小节。
系统配置说明(Net_Config.c):
详见本章节13.3小节。
TCP配置说明(Net_Config_TCP.h):
详见本章节13.4小节。
以太网配置说明(Net_Config_ETH_0.h):
详见本章节13.5小节。
网络调试说明(Net_Debug.c):
详见本章节13.6小节。
RTX5配置:
RTX5配置向导详情如下:
System Configuration
系统配置
Global Dynamic Memory size全局动态内存大小,单位字节。
当前配置为20480字节。
Kernel Tick Frequency内核滴答时钟频率。
当前配置为1KHz
Round-Robin Thread switching使能时间片调度,并把时间片设置为5个,即5ms。
OS_ISR_FIFO_QUEUE中断服务程序里面调用RTX5的API,需要用到这个FIFO队列,当前FIFO大小设置为16个。
OS_ISR_FIFO_QUEUE中断服务程序里面调用RTX
Thread Configuration
任务配置。
Default Thread Stack size默认的任务栈大小,单位字节。
Idle Thread Stack size空闲任务栈大小,单位字节。
Idle Thread Stack size空闲任务栈大小,单位字节。
Stack overrun checking使能栈溢出检测。
Stack usage watermark栈使用率。
Privileged mode使能特权模式。
RTX5任务调试信息:
RL-TCPnet协议栈调试信息:
程序设计:
任务分配:
AppTaskUrIF任务 : 按键消息处理。
AppTaskLED任务 : LED闪烁。
AppTaskMsgPro任务 : TCPnet应用任务,TCP客户端1。
AppTaskClient1任务 : TCP客户端2。
AppTaskClient2任务 : TCP客户端3。
AppTaskEthCheck : 网线插拔状态检测。
AppTaskStart任务 : 启动任务,也是最高优先级任务,这里用作BSP驱动包处理。
netCore_Thread任务 : TCPnet内核任务。
netEth0_Thread任务 : TCPnet以太网接口任务。
osRtxTimerThread任务: 定时器任务,TCPnet时间基准。
系统栈大小分配:
RTX5初始化
/*********************************************************************************************************** 函 数 名: main* 功能说明: 标准c程序入口。* 形 参: 无* 返 回 值: 无**********************************************************************************************************/int main (void) { /* HAL库,MPU,Cache,时钟等系统初始化 */ System_Init(); /* 内核开启前关闭HAL的时间基准 */ HAL_SuspendTick(); /* 内核初始化 */ osKernelInitialize(); /* 创建启动任务 */ ThreadIdStart = osThreadNew(AppTaskStart, NULL, &ThreadStart_Attr); /* 开启多任务 */ osKernelStart(); while(1);}
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*********************************************************************************************************** 函 数 名: System_Init* 功能说明: 系统初始化,主要是MPU,Cache和系统时钟配置* 形 参:无* 返 回 值: 无**********************************************************************************************************/void System_Init(void){ /* STM32F407 HAL 库初始化,此时系统用的还是F407自带的16MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIV优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到168MHz - 切换使用HSE。 - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。 - 默认不开启,如果要使能此选项,务必看V5开发板用户手册第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并开启 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart();#endif}/*********************************************************************************************************** 函 数 名: bsp_Init* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次* 形 参: 无* 返 回 值: 无**********************************************************************************************************/void bsp_Init(void){ bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */ bsp_InitTimer(); /* 初始化滴答定时器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitLed(); /* 初始化LED */ }
RTX任务创建:
/*********************************************************************************************************** 函 数 名: AppTaskCreate* 功能说明: 创建应用任务* 形 参: 无* 返 回 值: 无**********************************************************************************************************/static void AppTaskCreate (void){ ThreadIdTaskEthCheck = osThreadNew(AppTaskEthCheck, NULL, &ThreadEthCheck_Attr); ThreadIdTaskLED = osThreadNew(AppTaskLED, NULL, &ThreadLED_Attr); ThreadIdTaskUrIF = osThreadNew(AppTaskUrIF, NULL, &ThreadUrIF_Attr); }
几个RTX任务的实现:
/*********************************************************************************************************** 函 数 名: AppTaskUrIF* 功能说明: 按键消息处理 * 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal (数值越小优先级越低,这个跟uCOS相反)**********************************************************************************************************/void AppTaskUrIF(void *argument){ uint8_t ucKeyCode; while(1) { ucKeyCode = bsp_GetKey(); if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { /* K1键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit0 */ ca KEY_DOWN_K1: printf("K1键按下,直接发送事件标志给任务ThreadIdTaskMsgPro,bit0被设置\r\n"); osThreadFlagsSet(ThreadIdTaskMsgPro, KEY1_BIT0); break; /* K2键按下,直接发送事件标志给任务AppTaskClient1,设置bit0 */ ca KEY_DOWN_K2: printf("K2键按下,直接发送事件标志给任务AppTaskClient1,bit0被设置\r\n"); osThreadFlagsSet(ThreadIdTaskClient1, KEY1_BIT0); break; /* K3键按下,直接发送事件标志给任务AppTaskClient2,设置bit0 */ ca KEY_DOWN_K3: printf("K3键按下,直接发送事件标志给任务AppTaskClient2,bit0被设置\r\n"); osThreadFlagsSet(ThreadIdTaskClient2, KEY1_BIT0); break; /* 摇杆OK键按下,直接发送事件标志给三个TCP任务,设置bit3 */ ca JOY_DOWN_OK: printf("OK键按下,直接发送事件标志给三个TCP任务,bit3被设置\r\n"); osThreadFlagsSet(ThreadIdTaskMsgPro, KEY4_BIT3); osThreadFlagsSet(ThreadIdTaskClient1, KEY4_BIT3); osThreadFlagsSet(ThreadIdTaskClient2, KEY4_BIT3); break; /* 其他的键值不处理 */ default: break; } } osDelay(20); }}/*********************************************************************************************************** 函 数 名: AppTaskLED* 功能说明: LED闪烁。* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal1 **********************************************************************************************************/void AppTaskLED(void *argument){ const uint16_t usFrequency = 200; /* 延迟周期 */ uint32_t tick; /* 获取当前时间 */ tick = osKernelGetTickCount(); while(1) { bsp_LedToggle(2); /* 相对延迟 */ tick += usFrequency; osDelayUntil(tick); }}/*********************************************************************************************************** 函 数 名: AppTaskMsgPro* 功能说明: TCPnet应用任务* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal2 **********************************************************************************************************/void AppTaskMsgPro(void *argument){ while(1) { TCPnetTest(); } }/*********************************************************************************************************** 函 数 名: AppTaskClient1* 功能说明: TCPnet应用任务* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal2 **********************************************************************************************************/void AppTaskClient1(void *argument){ while(1) { TCPnetClient1(); } }/*********************************************************************************************************** 函 数 名: AppTaskClient2* 功能说明: TCPnet应用任务* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal2 **********************************************************************************************************/void AppTaskClient2(void *argument){ while(1) { TCPnetClient2(); } }/*********************************************************************************************************** 函 数 名: AppTaskEthCheck* 功能说明: 检查网线插拔状态。* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal3 **********************************************************************************************************/void AppTaskEthCheck(void *argument){ /* 初始化变量 */ ThreadIdTaskMsgPro = NULL; ThreadIdTaskClient1 = NULL; ThreadIdTaskClient2 = NULL; g_ucEthLinkStatus = 0; /* 初始化网络 */ netInitialize(); while(1) { /* 网线插拔处理,方便移植,大家也可以根据需要发送任务事件标志做处理 */ switch (g_ucEthLinkStatus) { /* 插拔临时状态,无需处理 */ ca 0: ca 1: break; /* 网线插入,创应用任务 */ ca 2: if(ThreadIdTaskMsgPro == NULL) { printf_taskdbg("网线插入,创建应用任务\r\n"); ThreadIdTaskMsgPro = osThreadNew(AppTaskMsgPro, NULL, &ThreadMsgPro_Attr); ThreadIdTaskClient1 = osThreadNew(AppTaskClient1, NULL, &ThreadTCPClient1_Attr); ThreadIdTaskClient2 = osThreadNew(AppTaskClient2, NULL, &ThreadTCPClient2_Attr); } break; /* 网线拔掉,复位网络,删除应用任务 */ ca 3: printf_taskdbg("网线拔掉,复位网络,删除应用任务\r\n"); /* 释放所有网络资源,含TCPnet内核任务和ETH接口任务 */ netUninitialize(); printf_taskdbg("netUninitialize\r\n"); /* 删除TCPnet应用任务 */ osThreadTerminate(ThreadIdTaskMsgPro); osThreadTerminate(ThreadIdTaskClient1); osThreadTerminate(ThreadIdTaskClient2); ThreadIdTaskMsgPro = NULL; g_ucEthLinkStatus = 0; /* 重新初始化 */ netInitialize(); printf_taskdbg("netInitialize\r\n"); /* 其他的键值不处理 */ default: break; } osDelay(10); } }/*********************************************************************************************************** 函 数 名: AppTaskStart* 功能说明: 启动任务,这里用作BSP驱动包处理。* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal4 **********************************************************************************************************/void AppTaskStart(void *argument){ const uint16_t usFrequency = 1; /* 延迟周期 */ uint32_t tick; /* 初始化外设 */ HAL_ResumeTick(); bsp_Init(); /* 创建任务 */ AppTaskCreate(); /* 获取当前时间 */ tick = osKernelGetTickCount(); while(1) { /* 需要周期性处理的程序,对应裸机工程调用的SysTick_ISR */ bsp_ProPer1ms(); /* 相对延迟 */ tick += usFrequency; osDelayUntil(tick); }}
RL-TCPnet功能测试
三个TCP客户端的创建是类似的,这里以TCP客户端1的创建为例进行说明,在文件app_tcpnet_lib.c里面实现。
/*********************************************************************************************************** 宏定义,远程服务器的IP和端口**********************************************************************************************************//* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */#define IP1 192#define IP2 168#define IP3 0#define IP4 102#define PORT_NUM 1001/* 这个是本地端口 */#define LocalPort_NUM 1024/*********************************************************************************************************** 变量**********************************************************************************************************/NET_ADDR4 addr = { NET_ADDR_IP4, PORT_NUM, IP1,IP2,IP3,IP4};int32_t tcp_sock;/* TCPnet API的返回值 */static const char * ReVal_Table[]= { "netOK: Operation succeeded", "netBusy: Process is busy", "netError: Unspecified error", "netInvalidParameter: Invalid parameter specified", "netWrongState: Wrong state error", "netDriverError: Driver error", "netServerError: Server error", "netAuthenticationFailed: Ur authentication failed", "netDnsResolverError: DNS host resolver failed", "netFileError: File not found or file r/w error", "netTimeout: Operation timeout",};/*********************************************************************************************************** 函 数 名: tcp_cb_client* 功能说明: TCP Socket的回调函数* 形 参: socket 句柄* event 事件类型* addr NET_ADDR类型变量,记录IP地址,端口号。* buf ptr指向的缓冲区记录着接收到的TCP数据。* len 记录接收到的数据个数。* 返 回 值: **********************************************************************************************************/uint32_t tcp_cb_client (int32_t socket, netTCP_Event event, const NET_ADDR *addr, const uint8_t *buf, uint32_t len) { switch (event) { /* 远程客户端连接消息 1、数组ptr存储远程设备的IP地址,par中存储端口号。 2、返回数值1允许连接,返回数值0禁止连接。 */ ca netTCP_EventConnect: return (1); /* Socket远程连接已经建立 */ ca netTCP_EventEstablished: printf_debug("Socket is connected to remote peer\r\n"); break; /* 连接断开 */ ca netTCP_EventClod: printf_debug("Connection has been clod\r\n"); break; /* 连接终止 */ ca netTCP_EventAborted: break; /* 发送的数据收到远程设备应答 */ ca netTCP_EventACK: break; /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */ ca netTCP_EventData: printf_debug("Data length = %d\r\n", len); printf ("%.*s\r\n",len, buf); break; } return (0);}/*********************************************************************************************************** 函 数 名: TCPnetTest* 功能说明: TCPnet应用* 形 参: 无* 返 回 值: 无**********************************************************************************************************/ void TCPnetTest(void){ int32_t iCount; uint8_t *ndbuf; uint32_t maxlen; netStatus res; const uint16_t usMaxBlockTime = 2; /* 延迟周期 */ uint32_t EvtFlag; tcp_sock = netTCP_GetSocket (tcp_cb_client); if (tcp_sock > 0) { /* 使能TCP_TYPE_KEEP_ALIVE,会一直保持连接 */ netTCP_SetOption (tcp_sock, netTCP_OptionKeepAlive, 1); } while (1) { EvtFlag = osThreadFlagsWait(0x0000000FU, osFlagsWaitAny, usMaxBlockTime); /* 按键消息的处理 */ switch (EvtFlag) { /* 接收到K1键按下,给远程TCP客户端发送4096字节数据 */ ca KEY1_BIT0: iCount = 4096; if(netTCP_GetState(tcp_sock) == netTCP_StateESTABLISHED) { do { if(netTCP_SendReady(tcp_sock) == true ) { maxlen = netTCP_GetMaxSegmentSize (tcp_sock); iCount -= maxlen; if(iCount < 0) { /* 这么计算没问题的 */ maxlen = iCount + maxlen; } ndbuf = netTCP_GetBuffer (maxlen); ndbuf[0] = '1'; ndbuf[1] = '2'; ndbuf[2] = '3'; ndbuf[3] = '4'; ndbuf[4] = '5'; ndbuf[5] = '6'; ndbuf[6] = '7'; ndbuf[7] = '8'; /* 必须使用申请的内存空间 */ netTCP_Send (tcp_sock, ndbuf, maxlen); } }while(iCount > 0); } break; /* 接收到摇杆OK键按下,连接远程服务器 */ ca KEY4_BIT3: if (tcp_sock > 0) { if(netTCP_GetState(tcp_sock) != netTCP_StateESTABLISHED) { res = netTCP_Connect (tcp_sock, (NET_ADDR *)&addr, LocalPort_NUM); printf_debug("%s\r\n", ReVal_Table[res]); } } break; /* 其他的键值不处理 */ default: break; } }}
配套例子:
V5-1011_RL-TCPnet V7.X实验_多个TCP客户端连接(FreeRTOS)
实验目的:
学习FreeROS + RL-TCPnet的多个TCP客户端制作。实验内容:
强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c开头,测试的时候板子要连接这个IP和端口(下面是默认配置,一定要根据实际情况重新配置,如果不会配置,看本例程对应的教程即可):#define IP1 192
#define IP2 168
#define IP3 1
#define IP4 2
#define PORT_NUM 1001
本例程可以创建三个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,三个TCP Client的端口号分别是1024,1025和1026。用户可以在电脑端用网络调试软件创建TCP Server跟这三个客户端建立连接。执行下面5–8步的操作时,优先将电脑端的TCP Server建立起来!!摇杆OK键按下,创建三个TCP客户端,端口号分别是1024,1025和1026。按键K1按下,TCP客户端1发送4096字节数据给服务器。按键K2按下,TCP客户端2发送4096字节数据给服务器。按键K3按下,TCP客户端3发送4096字节数据给服务器。实验操作:
详见本章节13.8小节。
系统配置说明(Net_Config.c):
详见本章节13.3小节。
TCP配置说明(Net_Config_TCP.h):
详见本章节13.4小节。
以太网配置说明(Net_Config_ETH_0.h):
详见本章节13.5小节。
网络调试说明(Net_Debug.c):
详见本章节13.6小节。
FreeRTOS配置:
FreeRTOS配置向导详情如下:
Minimal stack size最小任务栈大小,主要是空闲任务,单位字(4个字节)。
当前设置的是512字节。
Total heap sizeFreeRTOS总的堆大小,单位字节。
当前设置的30960字节。
Kernel tick frequencyFreeRTOS的系统时钟节拍。
当前设置的是1KHz。
Timer task stack depth定时器任务栈大小,单位字(4字节)。
当前设置的2048字节。
Timer task priority定时器任务优先级。
当前设置的48。
Timer queue length定时器消息队列大小。
U time slicing使能时间片调度,这个选项非常重要,RL-TCPnet V7.X用于FreeRTOS版要用到。
FreeRTOS任务调试信息:
RL-TCPnet协议栈调试信息:
程序设计:
任务分配:
AppTaskUrIF任务 : 按键消息处理。
AppTaskLED任务 : LED闪烁。
AppTaskMsgPro任务 : TCPnet应用任务,TCP客户端1。
AppTaskClient1任务 : TCP客户端2。
AppTaskClient2任务 : TCP客户端3。
AppTaskEthCheck : 网线插拔状态检测。
AppTaskStart任务 : 启动任务,也是最高优先级任务,这里用作BSP驱动包处理。
netCore_Thread任务 : TCPnet内核任务。
netEth0_Thread任务 : TCPnet以太网接口任务。
osRtxTimerThread任务: 定时器任务,TCPnet时间基准。
系统栈大小分配:
FreeRTOS初始化
/*********************************************************************************************************** 函 数 名: main* 功能说明: 标准c程序入口。* 形 参: 无* 返 回 值: 无**********************************************************************************************************/int main (void) { /* HAL库,MPU,Cache,时钟等系统初始化 */ System_Init(); /* 内核开启前关闭HAL的时间基准 */ HAL_SuspendTick(); /* 内核初始化 */ osKernelInitialize(); /* 创建启动任务 */ ThreadIdStart = osThreadNew(AppTaskStart, NULL, &ThreadStart_Attr); /* 开启多任务 */ osKernelStart(); while(1);}
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*********************************************************************************************************** 函 数 名: System_Init* 功能说明: 系统初始化,主要是MPU,Cache和系统时钟配置* 形 参:无* 返 回 值: 无**********************************************************************************************************/void System_Init(void){ /* STM32F407 HAL 库初始化,此时系统用的还是F407自带的16MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIV优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到168MHz - 切换使用HSE。 - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。 - 默认不开启,如果要使能此选项,务必看V5开发板用户手册第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并开启 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart();#endif}/*********************************************************************************************************** 函 数 名: bsp_Init* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次* 形 参: 无* 返 回 值: 无**********************************************************************************************************/void bsp_Init(void){ bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */ bsp_InitTimer(); /* 初始化滴答定时器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitLed(); /* 初始化LED */ }
FreeRTOS任务创建:
/*********************************************************************************************************** 函 数 名: AppTaskCreate* 功能说明: 创建应用任务* 形 参: 无* 返 回 值: 无**********************************************************************************************************/static void AppTaskCreate (void){ ThreadIdTaskEthCheck = osThreadNew(AppTaskEthCheck, NULL, &ThreadEthCheck_Attr); ThreadIdTaskLED = osThreadNew(AppTaskLED, NULL, &ThreadLED_Attr); ThreadIdTaskUrIF = osThreadNew(AppTaskUrIF, NULL, &ThreadUrIF_Attr); }
几个FreeRTOS任务的实现:
/*********************************************************************************************************** 函 数 名: AppTaskUrIF* 功能说明: 按键消息处理 * 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal (数值越小优先级越低,这个跟uCOS相反)**********************************************************************************************************/void AppTaskUrIF(void *argument){ uint8_t ucKeyCode; while(1) { ucKeyCode = bsp_GetKey(); if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { /* K1键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit0 */ ca KEY_DOWN_K1: printf("K1键按下,直接发送事件标志给任务ThreadIdTaskMsgPro,bit0被设置\r\n"); osThreadFlagsSet(ThreadIdTaskMsgPro, KEY1_BIT0); break; /* K2键按下,直接发送事件标志给任务AppTaskClient1,设置bit0 */ ca KEY_DOWN_K2: printf("K2键按下,直接发送事件标志给任务AppTaskClient1,bit0被设置\r\n"); osThreadFlagsSet(ThreadIdTaskClient1, KEY1_BIT0); break; /* K3键按下,直接发送事件标志给任务AppTaskClient2,设置bit0 */ ca KEY_DOWN_K3: printf("K3键按下,直接发送事件标志给任务AppTaskClient2,bit0被设置\r\n"); osThreadFlagsSet(ThreadIdTaskClient2, KEY1_BIT0); break; /* 摇杆OK键按下,直接发送事件标志给三个TCP任务,设置bit3 */ ca JOY_DOWN_OK: printf("OK键按下,直接发送事件标志给三个TCP任务,bit3被设置\r\n"); osThreadFlagsSet(ThreadIdTaskMsgPro, KEY4_BIT3); osThreadFlagsSet(ThreadIdTaskClient1, KEY4_BIT3); osThreadFlagsSet(ThreadIdTaskClient2, KEY4_BIT3); break; /* 其他的键值不处理 */ default: break; } } osDelay(20); }}/*********************************************************************************************************** 函 数 名: AppTaskLED* 功能说明: LED闪烁。* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal1 **********************************************************************************************************/void AppTaskLED(void *argument){ const uint16_t usFrequency = 200; /* 延迟周期 */ uint32_t tick; /* 获取当前时间 */ tick = osKernelGetTickCount(); while(1) { bsp_LedToggle(2); /* 相对延迟 */ tick += usFrequency; osDelayUntil(tick); }}/*********************************************************************************************************** 函 数 名: AppTaskMsgPro* 功能说明: TCPnet应用任务* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal2 **********************************************************************************************************/void AppTaskMsgPro(void *argument){ while(1) { TCPnetTest(); } }/*********************************************************************************************************** 函 数 名: AppTaskClient1* 功能说明: TCPnet应用任务* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal2 **********************************************************************************************************/void AppTaskClient1(void *argument){ while(1) { TCPnetClient1(); } }/*********************************************************************************************************** 函 数 名: AppTaskClient2* 功能说明: TCPnet应用任务* 形 参:孤鸟by薄暮就茶 无* 返 回 值: 无* 优 先 级: osPriorityNormal2 **********************************************************************************************************/void AppTaskClient2(void *argument){ while(1) { TCPnetClient2(); } }/*********************************************************************************************************** 函 数 名: AppTaskEthCheck* 功能说明: 检查网线插拔状态。* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal3 **********************************************************************************************************/void AppTaskEthCheck(void *argument){ /* 初始化变量 */ ThreadIdTaskMsgPro = NULL; ThreadIdTaskC株组词组lient1 = NULL; ThreadIdTaskClient2 = NULL; g_ucEthLinkStatus = 0; /* 初始化网络 */ netInitialize(); while(1) { /* 网线插拔处理,方便移植,大家也可以根据需要发送任务事件标志做处理 */ switch (g_ucEthLinkStatus) { /* 插拔临时状态,无需处理 */ ca 0: ca 1: break; /* 网线插入,创应用任务 */ ca 2: if(ThreadIdTaskMsgPro == NULL) { printf_taskdbg("网线插入,创建应用任务\r\n"); ThreadIdTaskMsgPro = osThreadNew(AppTaskMsgPro, NULL, &ThreadMsgPro_Attr); ThreadIdTaskClient1 = osThreadNew(AppTaskClient1, NULL, &ThreadTCPClient1_Attr); ThreadIdTaskClient2 = osThreadNew(AppTaskClient2, NULL, &ThreadTCPClient2_Attr); } break; /* 网线拔掉,复位网络,删除应用任务 */ ca 3: printf_taskdbg("网线拔掉,复位网络,删除应用任务\r\n"); /* 释放所有网络资源,含TCPnet内核任务和ETH接口任务 */ netUninitialize(); printf_taskdbg("netUninitialize\r\n"); /* 删除TCPnet应用任务 */ osThreadTerminate(ThreadIdTaskMsgPro); osThreadTerminate(ThreadIdTaskClient1); osThreadTerminate(ThreadIdTaskClient2); ThreadIdTaskMsgPro = NULL; g_ucEthLinkStatus = 0; /* 重新初始化 */ netInitialize(); printf_taskdbg("netInitialize\r\n"); /* 其他的键值不处理 */ default: break; } osDelay(10); } }/*********************************************************************************************************** 函 数 名: AppTaskStart* 功能说明: 启动任务,这里用作BSP驱动包处理。* 形 参: 无* 返 回 值: 无* 优 先 级: osPriorityNormal4 **********************************************************************************************************/void AppTaskStart(void *argument){ const uint16_t usFrequency = 1; /* 延迟周期 */ uint32_t tick; /* 初始化外设 */ HAL_ResumeTick(); bsp_Init(); /* 创建任务 */ AppTaskCreate(); /* 获取当前时间 */ tick = osKernelGetTickCount(); while(1) { /* 需要周期性处理的程序,对应裸机工程调用的SysTick_ISR */ bsp_ProPer1ms(); /* 相对延迟 */ tick += usFrequency; osDelayUntil(tick); }}
RL-TCPnet功能测试
三个TCP客户端的创建是类似的,这里以TCP客户端1的创建为例进行说明,在文件app_tcpnet_lib.c里面实现。
/*********************************************************************************************************** 宏定义,远程服务器的IP和端口**********************************************************************************************************//* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */#define IP1 192#define IP2 168#define IP3 1#define IP4 2#define PORT_NUM 1001/* 这个是本地端口 */#define LocalPort_NUM 1024/*********************************************************************************************************** 变量**********************************************************************************************************/NET_ADDR4 addr = { NET_ADDR_IP4, PORT_NUM, IP1,IP2,IP3,IP4};int32_t tcp_sock;/* TCPnet API的返回值 */static const char * ReVal_Table[]= { "netOK: Operation succeeded", "netBusy: Process is busy", "netError: Unspecified error", "netInvalidParameter: Invalid parameter specified", "netWrongState: Wrong state error", "netDriverError: Driver error", "netServerError: Server error", "netAuthenticationFailed: Ur authentication failed", "netDnsResolverError: DNS host resolver failed", "netFileError: File not found or file r/w error", "netTimeout: Operation timeout",};/*********************************************************************************************************** 函 数 名: tcp_cb_client* 功能说明: TCP Socket的回调函数* 形 参: socket 句柄* event 事件类型* addr NET_ADDR类型变量,记录IP地址,端口号。* buf ptr指向的缓冲区记录着接收到的TCP数据。* len 记录接收到的数据个数。* 返 回 值: **********************************************************************************************************/uint32_t tcp_cb_client (int32_t socket, netTCP_Event event, const NET_ADDR *addr, const uint8_t *buf, uint32_t len) { switch (event) { /* 远程客户端连接消息 1、数组ptr存储远程设备的IP地址,par中存储端口号。 2、返回数值1允许连接,返回数值0禁止连接。 */ ca netTCP_EventConnect: return (1); /* Socket远程连接已经建立 */ ca netTCP_EventEstablished: printf_debug("Socket is connected to remote peer\r\n"); break; /* 连接断开 */ ca netTCP_EventClod: printf_debug("Connection has been clod\r\n"); break; /* 连接终止 */ ca netTCP_EventAborted: break; /* 发送的数据收到远程设备应答 */ ca netTCP_EventACK: break; /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */ ca netTCP_EventData: printf_debug("Data length = %d\r\n", len); printf ("%.*s\r\n",len, buf); break; } return (0);}/*********************************************************************************************************** 函 数 名: TCPnetTest* 功能说明: TCPnet应用* 形 参: 无* 返 回 值: 无**********************************************************************************************************/ void TCPnetTest(void){ int32_t iCount; uint8_t *ndbuf; uint32_t maxlen; netStatus res; const uint16_t usMaxBlockTime = 2; /* 延迟周期 */ uint32_t EvtFlag; tcp_sock = netTCP_GetSocket (tcp_cb_client); if (tcp_sock > 0) { /* 使能TCP_TYPE_KEEP_ALIVE,会一直保持连接 */ netTCP_SetOption (tcp_sock, netTCP_OptionKeepAlive, 1); } while (1) { EvtFlag = osThreadFlagsWait(0x0000000FU, osFlagsWaitAny, usMaxBlockTime); /* 按键消息的处理 */ switch (EvtFlag) { /* 接收到K1键按下,给远程TCP客户端发送4096字节数据 */ ca KEY1_BIT0: iCount = 4096; if(netTCP_GetState(tcp_sock) == netTCP_StateESTABLISHED) { do { if(netTCP_SendReady(tcp_sock) == true ) { maxlen = netTCP_GetMaxSegmentSize (tcp_sock); iCount -= maxlen; if(iCount < 0) { /* 这么计算没问题的 */ maxlen = iCount + maxlen; } ndbuf = netTCP_GetBuffer (maxlen); ndbuf[0] = '1'; ndbuf[1] = '2'; ndbuf[2] = '3'; ndbuf[3] = '4'; ndbuf[4] = '5'; ndbuf[5] = '6'; ndbuf[6] = '7'; ndbuf[7] = '8'; /* 必须使用申请的内存空间 */ netTCP_Send (tcp_sock, ndbuf, maxlen); } }while(iCount > 0); } break; /* 接收到摇杆OK键按下,连接远程服务器 */ 分钟用字母表示 ca KEY4_BIT3: if (tcp_sock > 0) { if(netTCP_GetState(tcp_sock) != netTCP_StateESTABLISHED) { res = netTCP_Connect (tcp_sock, (NET_ADDR *)&addr, LocalPort_NUM); printf_debug("%s\r\n", ReVal_Table[res]); } } break; /* 其他的键值不处理 */ default: break; } }}
本章节就为大家讲解这么多,希望大家多做测试,再多创建几个客户端进行测试。
本文地址:https://blog.csdn.net/Simon223/article/details/1089谢亭送别许浑98554
本文发布于:2023-04-08 23:22:21,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/d3c546ab0c783b5e90d32592c40b6293.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:【STM32F407】第13章 RL.doc
本文 PDF 下载地址:【STM32F407】第13章 RL.pdf
留言与评论(共有 0 条评论) |