前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【RL-TCPnet网络教程】第14章 RL-TCPnet之TCP客户端

【RL-TCPnet网络教程】第14章 RL-TCPnet之TCP客户端

作者头像
Simon223
发布2018-09-04 09:48:02
3.2K0
发布2018-09-04 09:48:02
举报

第14章      RL-TCPnet之TCP客户端

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

本章教程含STM32F407开发板和STM32F429开发板。

14.1  初学者重要提示

1、学习本章节前,务必保证已经学习了第12章的基础知识。

2、相比上一个章节的TCP服务器,TCP客户端的测试要稍麻烦些,例子中默认访问的TCP服务器端IP地址是192.168.1.2,端口号1001。大家测试时要根据自己电脑的实际IP地址设置app_tcpnet_lib.c文件中远程IP和端口。具体测试方法详看本章节的14.6小节。

3、本章要掌握的函数稍多,可以先学会基本的使用,然后再深入了解这些函数使用时的注意事项,争取能够熟练使用。

14.2  TCP客户端API函数

使用如下12个函数可以实现RL-TCPnet的TCP通信:

(1)tcp_get_socket (2)tcp_connect (3)tcp_listen (4)tcp_close (5)tcp_abort (6)tcp_release_socket (7)tcp_get_buf (8)tcp_max_dsize (9)tcp_send (10)tcp_get_state (11)tcp_check_send (12)tcp_reset_window

关于这12个函数的讲解及其使用方法可以看教程第 3 章 3.4 小节里面说的参考资料 rlarm.chm 文件:

这里我们重点的说以下 7个函数,因为本章节配套的例子使用的是这 7 个函数:

(1)tcp_get_socket (2)tcp_connect (3)tcp_check_send (4)tcp_max_dsize (5)tcp_get_buf (6)tcp_send (7)tcp_get_state

关于这些函数注意以下两点:

1、这些函数都不支持重入,也就是不支持多任务调用。

2、TCP接口函数通过TCP Socket做数据传输,主要用于数据安全作为首选的场合。TCP Socket发送完毕数据后会等待应答,任何数据包失败都会重传。

14.2.1  函数tcp_get_socket

函数原型:

U8 tcp_get_socket (

    U8   type,          /* TCP Socket类型          */

    U8   tos,           /* TCP服务类型             */

    U16  tout,          /* 断开连接前的空闲溢出时间 */

    U16 (*listener)(    /* 回调函数                */

        U8  socket,     /* Socket句柄              */

        U8  event,      /* TCP事件                 */

        U8* ptr,        /* 记录接收到的数据或者远程机器的IP地址    */

        U16 par ));     /* 记录接收到的数据长度或者远程机器的端口号 */                     

函数描述:

函数tcp_get_socket用于获取一个TCP Socket。

(1)第1个参数是TCP Socket的类型。

(2)第2个参数用于指定服务类型,默认取零即可。

(3)第3个参数用于设置空闲溢出时间,单位秒。Keep alive定时器用于监控TCP连接,如果连接的空闲时间(也就是长时间没有数据通信)超出了,那么会断开连接。如果设置了TCP_TYPE_KEEP_ALIVE属性,会通过发送keep alive数据包来保持连接。

(4)第4个参数是回调函数,用于事件监听。

        a. 回调函数第1个参数,TCP Socket的句柄,也就是函数tcp_get_socket的返回值。

        b. 回调函数第2个参数,事件类型。

        c. 回调函数第3个参数,事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址。

        d. 回调函数第4个参数,记录接收到的数据个数,其余事件记录端口号。

(5)返回值,如果获取成功,返回TCP Socket句柄,如果获取失败,返回0。

使用这个函数要注意以下问题:

  1. 调用TCP Socket任何其它函数前,务必要调用此函数tcp_get_socket。
  2. 使用函数tcp_get_socket,第四个参数的监听回调函数务必要设置。
  3. 如果需要长时间连接,需要设置属性TCP_TYPE_KEEP_ALIVE。

使用举例:

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     uint8_t res;

    

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

 

     while (1)

     {

         /* 省略 */

     }

}

14.2.2 函数tcp_connect

函数原型:

BOOL tcp_connect (

    U8  socket,       /* Socket 句柄 */

    U8* remip,        /* 远程IP地址  */

    U16 remport,      /* 远程端口号  */

    U16 locport );    /* 本地端口号 */                 

函数描述:

函数tcp_connect用于连接远程TCP服务器。

(1)第1个参数是要设置监听的TCP Socket句柄。

(2)第2个参数是远程服务器IP。

(3)第3个参数是远程服务器端口号。

(4)第4个参数是本地端口号。如果此参数设置为0,RL-TCPnet自动为其分配未使用的TCP端口号。

(5)返回值,成功开启连接返回__TRUE(注意,并不是已经建立了连接),否则返回__FALSE。

使用这个函数要注意以下问题:

  1. 只有创建的TCP客户端才可以使用此函数,也就是调用函数tcp_get_socket的第一个形参必须得是TCP_TYPE_CLIENT 或者 TCP_TYPE_CLIENT_SERVER。

使用举例:

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     uint8_t res;

    

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

 

     while (1)

     {

         /* 省略 */

     }

}

14.2.3 函数tcp_check_send

函数原型:

BOOL tcp_check_send (

           U8 socket );    /* TCP socket 句柄 */               

函数描述:

函数tcp_check_send用于检测是否可以发送数据。此函数通过检测TCP连接是否建立以及上次发送的数据是否接收到远程机器的应答来判断是否可以发送数据。

(1)第1个参数是TCP Socket句柄。

(2)返回值,可以发送数据,返回__TRUE;不可以发送数据,返回__FALSE。

使用举例:

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

          /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2;·

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

    

     while (1)

     {

          /* RL-TCPnet处理函数 */

         main_TcpNet();

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

        

          /* 按键消息的处理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

14.2.4 函数tcp_max_dsize

函数原型:

U16 tcp_max_dsize (

    U8 socket );    /* TCP socket 句柄 */                 

函数描述:

函数tcp_max_dsize用于获得当前可以发送的最大报文长度(MSS,Maximum Segment Size)。在配置向导中,默认配置的MSS是1460字节,然而在实际建立连接后,此值会被动态调整,但一定是小于等于1460字节的。

(1)第1个参数是TCP Socket句柄。

(2)返回值,返回本次可以发送的最大报文长度,单位字节。

使用举例:

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

          /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2;·

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

    

     while (1)

     {

          /* RL-TCPnet处理函数 */

         main_TcpNet();

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

        

          /* 按键消息的处理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

14.2.5 函数tcp_get_buf

函数原型:

U8* tcp_get_buf (

U16 size );    /* 申请的缓冲区大小 */

函数描述:

函数tcp_get_buf用于获取TCP发送缓冲区,用户将要发送的数据存到这个缓冲区中,然后通过函数tcp_send发送。发送完毕后要等待远程主机的应答,收到应答后,会在函数tcp_send中释放申请的发送缓冲区。

(1)第1个参数是要申请的缓冲区大小。

(2)返回值,返回获取的缓冲区地址。

使用这个函数要注意以下问题:

  1. 每次发送都需要调用此函数获取发送缓冲区地址。
  2. 申请的发送缓冲区大小不可超过最大报文长度(MSS,Maximum Segment Size),即1460字节。
  3. 操作缓冲区的时候,切不可超过申请的缓冲区大小。

使用举例:

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

          /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

          /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2;·

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

    

     while (1)

     {

          /* RL-TCPnet处理函数 */

         main_TcpNet();

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

        

          /* 按键消息的处理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

14.2.6 函数tcp_send

函数原型:

BOOL tcp_send (

    U8  socket,    /* TCP socket 句柄 */

    U8* buf,       /* 数据缓冲地址 */

    U16 dlen );    /* 要发送的数据个数,单位字节 */

函数描述:

函数tcp_send用于数据包发送。

(1)第1个参数是TCP Socket句柄。

(2)第2个参数是函数tcp_get_buf获取的缓冲区地址。

(3)第3个参数是发送数据个数,单位字节。

(4)返回值,发送成功返回__TRUE,发送失败返回__FALSE。

使用这个函数要注意以下问题:

  1. 不管函数tcp_send发送成功还是失败,都会释放通过函数tcp_get_buf获取的缓冲区。
  2. 以下两种情况不可使用函数tcp_send发送数据包: (1)    TCP连接还未建立。 (2)    发送给远程机器的数据包还未收到应答。
  3. 调用函数tcp_send前务必要调用函数tcp_get_buf获得缓冲区。
  4. 申请的发送缓冲区大小不可超过最大报文长度(MSS,Maximum Segment Size),即1460字节。
  5. 操作缓冲区的时候,切不可超过申请的缓冲区大小。

使用举例:

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

          /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2;·

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

    

     while (1)

     {

          /* RL-TCPnet处理函数 */

         main_TcpNet();

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

        

          /* 按键消息的处理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

14.2.7 函数tcp_get_state

函数原型:

U8 tcp_get_state (

    U8 socket );    /* TCP socket 句柄 */

函数描述:

函数tcp_get_state用于获取TCP Socket的当前状态。用户应用程序可以通过此函数监控TCP Socket的连接、断开等状态。最有用的状态值是TCP_STATE_CLOSED,TCP_STATE_LISTEN和TCP_STATE_CONNECT。

(1)第1个参数是TCP Socket句柄。

(2)返回值,返回以下几种状态值:

使用举例:

/*

*********************************************************************************************************

*    函 数 名: TCP_StatusCheck

*    功能说明: 检测TCP的连接状态,主要用于网线插拔的判断

*    形    参: 无

*    返 回 值: __TRUE  连接

*             __FALSE 断开

*********************************************************************************************************

*/

uint8_t TCP_StatusCheck(void)

{

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              {

                   /* 这里默认不开启调试,否则未跟电脑端服务器连接前会一直发 */

                   #if 0

                     uint8_t res;

                     res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                     printf_debug("tcp connect res = %d\r\n", res);

                #else

                     tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                   #endif

              }

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

14.3 TCP配置说明(Net_Config.c)

(本章节配套例子的配置与本小节的说明相同)

RL-TCPnet的配置工作是通过配置文件Net_Config.c实现。在MDK工程中打开文件Net_Config.c,可以看到下图所示的工程配置向导:

RL-TCPnet要配置的选项非常多,我们这里把几个主要的配置选项简单介绍下。

System Definitions

(1)Local Host Name

局域网域名。

这里起名为armfly,使用局域网域名限制为15个字符。

(2)Memory Pool size

参数范围1536-262144字节。

内存池大小配置,单位字节。另外注意一点,配置向导这里显示的单位是字节,如果看原始定义,MDK会做一个自动的4字节倍数转换,比如我们这里配置的是8192字节,那么原始定义是#define MEM_SIZE  2048,也就是8192/4 = 2048。

(3)Tick Timer interval

可取10,20,25,40,50,100,200,单位ms。

系统滴答时钟间隔,也就是网络协议栈的系统时间基准,默认情况下,取值100ms。

Ethernet Network Interface

以太网接口配置,勾选了此选项就可以配置了,如果没有使能DHCP的话,将使用这里配置的固定IP

(1)MAC Address

局域网内可以随意配置,只要不跟局域网内其它设备的MAC地址冲突即可。

(2)IP Address

IP地址。

(3)Subnet mask

子网掩码。

(4)Default Gateway

默认网关。

Ethernet Network Interface

以太网接口配置,这个配置里面还有如下两项比较重要的配置需要说明。

(1)NetBIOS Name Service

NetBIOS局域网域名服务,这里打上对勾就使能了。这样我们就可以通过前面配置的Local Host Name局域网域名进行访问,而不需要通过IP地址访问了。

(2)Dynaminc Host Configuration

即DHCP,这里打上对勾就使能了。使能了DHCP后,RL-TCPnet就可以从外接的路由器上获得动态IP地址。

UDP Sockets

UDP Sockets配置,打上对勾就使能了此项功能

(1)Number of UDP Sockets

用于配置可创建的UDP Sockets数量。

范围1 – 20。

TCP Sockets

TCP Sockets配置,打上对勾就使能了此项功能

(1) Number of TCP Sockets

用于配置可创建的TCP Sockets数量。

(2)Number of Retries

范围0-20。

用于配置重试次数,TCP数据传输时,如果在设置的重试时间内得不到应答,算一次重试失败,这里就是配置的最大重试次数。

(3)Retry Timeout in seconds

范围1-10,单位秒。

重试时间。如果发送的数据在时间内得不到应答,将重新发送数据。

(4)Default Connect Timeout in seconds

范围1-600,单位秒。

用于配置默认的保持连接时间,即我们常说的Keep Alive时间,如果时间到了将断开连接。常用于HTTP Server,Telnet Server等。

(5)Maximum Segment Size

范围536-1460,单位字节。

MSS定义了TCP数据包能够传输的最大数据分段。

(6)Receive Window Size

范围536-65535,单位字节。

TCP接收窗口大小。

14.4 TCP调试说明(Net_Debug.c)

(重要说明,RL-TCPnet的调试是通过串口打印出来的)

RL-TCPnet的调试功能是通过配置文件Net_Debug.c实现。在MDK工程中打开文件Net_Debug.c,可以看到下图所示的工程配置向导:

Print Time Stamp

勾选了此选项的话,打印消息时,前面会附带时间信息。

其它所有的选项

默认情况下,所有的调试选项都关闭了,每个选项有三个调试级别可选择,这里我们以Memory Management Debug为例,点击下拉列表,可以看到里面有Off,Errors only和Full debug三个调试级别可供选择,每个调试选项里面都是这三个级别。

Off:表示关闭此选项的调试功能。

Errors only:表示仅在此选项出错时,将其错误打印出来。

Full debug:表示此选项的全功能调试。

具体测试,我们这里就不做了,大家可以按照第11章讲解的调试方法进行测试。

14.5 TCP客户端的实现方法

有了本章节14.3小节的配置后,剩下的问题就是TCP客户端的创建和TCP客户端数据收发的实现。

14.5.1 创建TCP客户端

TCP客户端的创建比较简单,调用函数tcp_get_socket即可,此函数的使用和注意事项在本章的14.2.1小节有讲解:

/*

*********************************************************************************************************

*                                        宏定义

*********************************************************************************************************

*/

#define PORT_NUM       1001    /* TCP服务器监听端口号 */

 

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

          /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     uint8_t res;

    

    

     /* 初始化网络协议栈 */

     init_TcpNet ();

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

 

    /* 省略 */

 

}

14.5.2 TCP数据发送

TCP Socket的数据发送一定要注意各个函数调用顺序和使用方法,非常重要!否则,数据发送很容易失败。数据发送所用到函数的使用方法和注意事项在本章节的14.2小节有讲解。下面的代码中对数据发送专门做了处理,支持任意字节大小的数据发送,仅需修改计数变量iCount的初始值即可,初始值是多少,就是发送多少字节。下面的代码是裸机方式的,测试发送8字节,1024字节和5MB:

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer(0))

     {

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();  //--------------(1)

     }

    

     main_TcpNet ();     //--------------(2)

 

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     uint8_t ucKeyCode;

    

    

     /* 初始化网络协议栈 */

     init_TcpNet ();

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

 

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(0, 100);

    

     while (1)

     {

          /* TCP轮询 */

         tcpnet_poll();

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按键消息的处理 */

         ucKeyCode = bsp_GetKey();

         if ((ucKeyCode != KEY_NONE)&&(tcp_status == __TRUE))

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY_DOWN_K1:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;  //--------------(3)

                       do           //--------------(4)

                       {

                            tcpnet_poll(); 

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* K2键按下,给远程TCP服务器发送1024字节的数据 */

                   case KEY_DOWN_K2:     

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024; //--------------(5)

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* K3键按下,给远程TCP服务器发送5MB数据 */

                   case KEY_DOWN_K3:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024; //--------------(6)

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}
  1. 函数timer_tick用于实现网络时间基准,必须要周期性调用,周期大小是由配置向导文件中参数Tick Timer interval决定的。默认情况下,我们都取100ms。
  2. 函数main_TcpNet必须要一直调用着,协议栈的执行,主要靠它。
  3. 通过变量iCount设置要发送的字节数,这里是发送8字节数据。
  4. do while语句中的流程很重要:     (1)    函数tcp_poll一定要实时调用着。     (2)    发送前务必要调用函数tcp_check_send查看发送是否就绪。     (3)    函数tcp_max_dsize,tcp_get_buf和tcp_send务必要依次调用,一个都不能少。
  5. 通过变量iCount设置要发送的字节数,这里是发送1024字节数据。
  6. 通过变量iCount设置要发送的字节数,这里是发送5MB数据。

说完了裸机方式,下面说说RTOS方式的数据发送,这里我们以RTX操作系统为例进行说明(其它的uCOS-III和FreeRTOS的思路是一样的)。RTX操作系统与裸机方式的主要不同是为RL-TCPnet专门配套了两个任务,一个是RL-TCPnet主任务,另一个是网络系统时间基准更新任务。

网络系统时间更新任务:

/*

*********************************************************************************************************

*    函 数 名: AppTaskStart

*    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

*********************************************************************************************************

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

    

     /* 创建任务 */

     AppTaskCreate();

    

     os_itv_set (100);

    

    while(1)

    {

         os_itv_wait ();

        

         /* RL-TCPnet时间基准更新函数 */

         timer_tick ();

    }

}

特别注意,这里的网络时间基准函数timer_tick,必须要周期性调用,周期大小是由配置向导文件中参数Tick Timer interval决定的。默认情况下,我们都取100ms,所以这里的延迟一定要匹配。

RL-TCPnet主任务,TCP数据收发在这个任务里面实现:

/*

*********************************************************************************************************

*    函 数 名: AppTaskTCPMain

*    功能说明: RL-TCPnet测试任务

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

*********************************************************************************************************

*/

__task void AppTaskTCPMain(void)

{

     while (1)

     {

         TCPnetTest();

     }

}

函数TCPnetTest的具体实现如下:

/*

*********************************************************************************************************

*                                      宏定义

*********************************************************************************************************

*/

#define PORT_NUM       1001    /* TCP服务器监听端口号 */

 

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2; /* 延迟周期 */

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

    

     while (1)

     {

         /* RL-TCPnet处理函数 */

         main_TcpNet();  //--------------(1)

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按键消息的处理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE)) //------(2)

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8; //--------------(3)

                       do

                       {

                            main_TcpNet();//--------------(4)

                            if (tcp_check_send (socket_tcp) == __TRUE)  

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* 接收到K2键按下,给远程TCP服务器发送1024字节的数据 */

                   case KEY2_BIT1:       

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024; //--------------(5)

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* 接收到K3键按下,给远程TCP服务器发送5MB数据 */

                   case KEY3_BIT2:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024; //--------------(6)

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}
  1. 函数main_TcpNet必须要一直调用着,协议栈的执行,主要靠它。
  2. 这里使用了事件标志组,溢出时间设置为2毫秒。这样一方面保证了函数main_TcpNet的周期性执行,另一方面用来等待按键发送事件标志消息。
  3. 通过变量iCount设置要发送的字节数,这里是发送8字节数据。
  4. do while语句中的流程很重要:     (1)    函数main_TcpNet一定要实时调用着。     (2)    发送前务必要调用函数tcp_check_send查看发送是否就绪。     (3)    函数tcp_max_dsize,tcp_get_buf和tcp_send务必要依次调用,一个都不能少。
  5. 通过变量iCount设置要发送的字节数,这里是发送1024字节数据。
  6. 通过变量iCount设置要发送的字节数,这里是发送5MB数据。

14.5.3 TCP数据接收

TCP数据接收主要是通过函数tcp_get_socket的回调函数实现(裸机,RTX,uCOS-III和FreeRTOS是一样的):

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

          case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA: //--------------(1)

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}
  1. TCP服务器的数据接收主要是通过回调函数的TCP_EVT_DATA消息实现,进入消息后,指针变量ptr是接收数据缓冲区首地址,变量par记录接收到的数据长度,单位字节。

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

我们这里使用下面这款调试助手,任何其它网络调试助手均可,不限制:

http://bbs.armfly.com/read.php?tid=1568

14.6.1 获取板子IP地址

(说明,对于TCP客户端实验,这一步已经不需要了,不过大家还可以进行测试)

首先,强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址,而且在前面的配置中使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址。测试方法如下:

(1)WIN+R组合键打开“运行”窗口,输入cmd。

(2)弹出的命令窗口中,输入ping armfly。

(3)输入ping armfly后,回车。

获得IP地址是192.168.1.6。也许有读者会问,这里的响应时间为什么不是小于1ms的?这是因为我们还没有让板子跟电脑端的服务器建立连接,客户端一直在发SYN包尝试建立连接,所以占用了不少时间,从而导致响应时间不是小于1ms的,建立了连接后就好了。

14.6.2 获取电脑的IP地址

获取电脑IP地址的方法很多,可以在网上邻居获取,也可以通过输入命令ipconfig获取,方法跟上面14.6.1小节中的方式一样。

(1)WIN+R组合键打开“运行”窗口,输入cmd。

(2)弹出的命令窗口中,输入ipconfig。

(3)输入ipconfig后,回车。

获得电脑的IP地址是192.168.1.2.

14.6.3 在程序中配置要访问的远程IP地址和端口

根据前面14.6.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

14.6.4 网络调试助手创建TCP服务器

1、打开调试助手,点击左上角创建服务器:

2、弹出如下界面,指定IP设置为192.168.1.2,一定要跟14.6.2小节中获得的电脑IP地址一致,端口号1001,最后点击确定:

3、点击确定后的界面效果如下:

4、然后点击启动服务器:

如果开发板下载了TCP客户端的程序,并且开发板已经上电,可以看到客户端连接已经加入:

跟我们在程序中设置的端口号,即app_tcpnet_lib.c文件开头的宏定义:

#define LocalPort_NUM    1024是一致的。IP地址也跟14.6.1小节中获取的IP地址也是一致的。

连接上后,串口软件也会打印出如下信息(波特率115200,数据位8,奇偶校验位无,停止位1):

14.6.5 TCP客户端发送数据

板子和网络调试助手建立连接后就可以互相收发数据了。对于发送数据,程序中创建了三种大小的数据发送测试。

(1)K1按键按下,发送了8个字符,从1到8。

(2)K2按键按下,发送1024字节,每次发送数据包的前8个字节设置了字符a到字符h,后面都未做设置。

(3)K3按键按下,发送5*1024*1024 = 5242880字节,即5MB。每次发送数据包的前8个字节设置了字符a到字符h,后面都未做设置。

14.6.6 TCP客户端接收数据

TCP服务器接收数据的测试也比较方便,我们这里通过网络调试助手给板子发送0到9,共10个字符:

点击发送后,可以看到串口软件打印出接收到的10个字符:

字符0对应的ASCII值就是48,其它字符数值依次增加。测试也是没问题的。

14.7 实验例程说明(裸机)

14.7.1 STM32F407开发板实验

配套例子:

V5-1012_RL-TCPnet实验_TCP客户端(裸机)

实验目的:

  1. 学习RL-TCPnet的TCP客户端创建和数据收发。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c文件开头,测试的时候板子要连接这个IP和端口:     #define IP1            192     #define IP2            168     #define IP3            1     #define IP4            2                     #define PORT_NUM    1001
  3. 创建了一个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,端口号1024。
  4. 用户可以在电脑端用网络调试软件创建TCP Server,让客户端去连接。
  5. 按键K1按下,发送8字节的数据给TCP Server。
  6. 按键K2按下,发送1024字节的数据给TCP Server。
  7. 按键K3按下,发送5MB字节的数据给TCP Server。

实验操作:

详见本章节14.6小节。

配置向导文件设置(Net_Config.c):

详见本章节14.3小节。

调试文件设置(Net_Debug.c):

详见本章节14.4小节。

程序设计:

主函数初始化

在main.c文件实现:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

 

     /* 进入RL-TCPnet测试函数 */

     TCPnetTest();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

 

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

 

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitLed();     /* 初始LED指示灯端口 */

     bsp_InitTimer();   /* 初始化滴答定时器 */

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,主要功能是创建了一个TCP Client。

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCP_StatusCheck

*    功能说明: 检测TCP的连接状态,主要用于网线插拔的判断

*    形    参: 无

*    返 回 值: __TRUE  连接

*             __FALSE 断开

*********************************************************************************************************

*/

uint8_t TCP_StatusCheck(void)

{

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              {

                   /* 这里默认不开启调试,否则未跟电脑端服务器连接前会一直发 */

                   #if 1

                     uint8_t res;

                     res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                     printf_debug("tcp connect res = %d\r\n", res);

                #else

                     tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                   #endif

              }

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer(0))

     {

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();

     }

    

     main_TcpNet ();

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     uint8_t ucKeyCode;

    

    

     /* 初始化网络协议栈 */

     init_TcpNet ();

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

 

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(0, 100);

    

     while (1)

     {

         /* TCP轮询 */

         tcpnet_poll();

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

 

         /* 按键消息的处理 */

         ucKeyCode = bsp_GetKey();

         if ((ucKeyCode != KEY_NONE)&&(tcp_status == __TRUE))

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY_DOWN_K1:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* K2键按下,给远程TCP服务器发送1024字节的数据 */

                   case KEY_DOWN_K2:     

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* K3键按下,给远程TCP服务器发送5MB数据 */

                   case KEY_DOWN_K3:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

14.7.2 STM32F429开发板实验

配套例子:

V6-1012_RL-TCPnet实验_TCP客户端(裸机)

实验目的:

  1. 学习RL-TCPnet的TCP客户端创建和数据收发。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c文件开头,测试的时候板子要连接这个IP和端口:     #define IP1            192     #define IP2            168     #define IP3            1     #define IP4            2                     #define PORT_NUM    1001
  3. 创建了一个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,端口号1024。
  4. 用户可以在电脑端用网络调试软件创建TCP Server,让客户端去连接。
  5. 按键K1按下,发送8字节的数据给TCP Server。
  6. 按键K2按下,发送1024字节的数据给TCP Server。
  7. 按键K3按下,发送5MB字节的数据给TCP Server。

实验操作:

详见本章节14.6小节。

配置向导文件设置(Net_Config.c):

详见本章节14.3小节。

调试文件设置(Net_Debug.c):

详见本章节14.4小节。

程序设计:

主函数初始化

在main.c文件实现:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

 

     /* 进入RL-TCPnet测试函数 */

     TCPnetTest();

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

 

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

 

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitLed();     /* 初始LED指示灯端口 */

     bsp_InitTimer();   /* 初始化滴答定时器 */

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,主要功能是创建了一个TCP Client。

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCP_StatusCheck

*    功能说明: 检测TCP的连接状态,主要用于网线插拔的判断

*    形    参: 无

*    返 回 值: __TRUE  连接

*             __FALSE 断开

*********************************************************************************************************

*/

uint8_t TCP_StatusCheck(void)

{

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              {

                   /* 这里默认不开启调试,否则未跟电脑端服务器连接前会一直发 */

                   #if 1

                     uint8_t res;

                     res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                     printf_debug("tcp connect res = %d\r\n", res);

                #else

                     tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                   #endif

              }

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

*********************************************************************************************************

*    函 数 名: tcpnet_poll

*    功能说明: 使用TCPnet必须要一直调用的函数

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer(0))

     {

         /* 此函数坚决不可以放在中断里面跑 */

         timer_tick ();

     }

    

     main_TcpNet ();

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     uint8_t ucKeyCode;

    

    

     /* 初始化网络协议栈 */

     init_TcpNet ();

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

 

     /* 创建一个周期是100ms的软定时器 */

    bsp_StartAutoTimer(0, 100);

    

     while (1)

     {

         /* TCP轮询 */

         tcpnet_poll();

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

 

         /* 按键消息的处理 */

         ucKeyCode = bsp_GetKey();

         if ((ucKeyCode != KEY_NONE)&&(tcp_status == __TRUE))

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY_DOWN_K1:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* K2键按下,给远程TCP服务器发送1024字节的数据 */

                   case KEY_DOWN_K2:     

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* K3键按下,给远程TCP服务器发送5MB数据 */

                   case KEY_DOWN_K3:            

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024;

                       do

                       {

                            tcpnet_poll();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

14.8 实验例程说明(RTX)

14.8.1 STM32F407开发板实验

配套例子:

V5-1013_RL-TCPnet实验_TCP客户端(RTX)

实验目的:

  1. 学习RL-TCPnet的TCP客户端创建和数据收发。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c文件开头,测试的时候板子要连接这个IP和端口:     #define IP1            192     #define IP2            168     #define IP3            1     #define IP4            2                     #define PORT_NUM    1001
  3. 创建了一个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得板子的IP地址,端口号1024。
  4. 用户可以在电脑端用网络调试软件创建TCP Server,让客户端去连接。
  5. 按键K1按下,发送8字节的数据给TCP Server。
  6. 按键K2按下,发送1024字节的数据给TCP Server。
  7. 按键K3按下,发送5MB字节的数据给TCP Server。

实验操作:

详见本章节14.6小节。

配置向导文件设置(Net_Config.c):

详见本章节14.3小节。

调试文件设置(Net_Debug.c):

详见本章节14.4小节。

RTX配置:

RTX配置向导详情如下:

Task Configuration

(1)Number of concurrent running tasks

允许创建6个任务,实际创建了如下5个任务:

    AppTaskUserIF任务   :按键消息处理。

    AppTaskLED任务     :LED闪烁。

    AppTaskMsgPro任务 :按键检测。

    AppTaskTCPMain任务:RL-TCPnet测试任务。

    AppTaskStart任务  :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

(2)Number of tasks with user-provided stack

创建的5个任务都是采用自定义堆栈方式。

(3)Run in privileged mode

设置任务运行在非特权级模式。

RTX任务调试信息:

程序设计:

任务栈大小分配:

static uint64_t AppTaskUserIFStk[1024/8];   /* 任务栈 */

static uint64_t AppTaskLEDStk[1024/8];      /* 任务栈 */

static uint64_t AppTaskMsgProStk[1024/8];  /* 任务栈 */

static uint64_t AppTaskTCPMainStk[2048/8]; /* 任务栈 */

static uint64_t AppTaskStartStk[1024/8];     /* 任务栈 */

将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。

系统栈大小分配:

RTX初始化:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

    

     /* 创建启动任务 */

     os_sys_init_user (AppTaskStart,              /* 任务函数 */

                       5,                         /* 任务优先级 */

                       &AppTaskStartStk,          /* 任务栈 */

                       sizeof(AppTaskStartStk));  /* 任务栈大小,单位字节数 */

     while(1);

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

 

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

 

     bsp_InitDWT();     /* 初始化DWT */

     bsp_InitUart();    /* 初始化串口 */

     bsp_InitKey();    /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

     bsp_InitLed();    /* 初始LED指示灯端口 */

}

RTX任务创建:

/*

*********************************************************************************************************

*    函 数 名: AppTaskCreate

*    功能说明: 创建应用任务

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */

                                           1,                         /* 任务优先级 */

                                           &AppTaskUserIFStk,         /* 任务栈 */

                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */

                                        2,                       /* 任务优先级 */

                                        &AppTaskLEDStk,          /* 任务栈 */

                                        sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */

                                           3,                         /* 任务优先级 */

                                           &AppTaskMsgProStk,         /* 任务栈 */

                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */

    

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任务函数 */

                                           4,                         /* 任务优先级 */

                                           &AppTaskTCPMainStk,         /* 任务栈 */

                                           sizeof(AppTaskTCPMainStk)); /* 任务栈大小,单位字节数 */

}

五个RTX任务的实现:

/*

*********************************************************************************************************

*    函 数 名: AppTaskUserIF

*    功能说明: 按键消息处理     

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)

*********************************************************************************************************

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit0 */

                   case KEY_DOWN_K1:

                       printf("K1键按下,直接发送事件标志给任务AppTaskTCPMain,bit0被设置\r\n");

                       os_evt_set (KEY1_BIT0, HandleTaskTCPMain);                      

                       break;  

 

                   /* K2键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit1 */

                   case KEY_DOWN_K2:

                       printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\r\n");

                       os_evt_set (KEY2_BIT1, HandleTaskTCPMain);

                       break;

                  

                   /* K3键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit2 */

                   case KEY_DOWN_K3:

                       printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\r\n");

                       os_evt_set (KEY3_BIT2, HandleTaskTCPMain);

                       break;

 

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

/*

*********************************************************************************************************

*    函 数 名: AppTaskLED

*    功能说明: LED闪烁。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 2 

*********************************************************************************************************

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = 500; /* 延迟周期 */

    

     /* 设置延迟周期 */

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(2);

 

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/

         os_itv_wait();

    }

}

 

/*

*********************************************************************************************************

*    函 数 名: AppTaskMsgPro

*    功能说明: 按键检测

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 3 

*********************************************************************************************************

*/

__task void AppTaskMsgPro(void)

{

    while(1)

    {

         bsp_KeyScan();

         os_dly_wait(10);

    }

}

 

/*

*********************************************************************************************************

*    函 数 名: AppTaskTCPMain

*    功能说明: RL-TCPnet测试任务

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

*********************************************************************************************************

*/

__task void AppTaskTCPMain(void)

{

     while (1)

     {

         TCPnetTest();

     }

}

 

/*

*********************************************************************************************************

*    函 数 名: AppTaskStart

*    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

*********************************************************************************************************

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

    

     /* 创建任务 */

     AppTaskCreate();

    

     os_itv_set (100);

    

    while(1)

    {

         os_itv_wait ();

        

         /* RL-TCPnet时间基准更新函数 */

         timer_tick ();

    }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,主要功能是创建了一个TCP Client。

#include "includes.h" 

 

 

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

          /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCP_StatusCheck

*    功能说明: 检测TCP的连接状态,主要用于网线插拔的判断

*    形    参: 无

*    返 回 值: __TRUE  连接

*             __FALSE 断开

*********************************************************************************************************

*/

uint8_t TCP_StatusCheck(void)

{

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              {

                   /* 这里默认不开启调试,否则未跟电脑端服务器连接前会一直发 */

                   #if 0

                     uint8_t res;

                     res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                     printf_debug("tcp connect res = %d\r\n", res);

                #else

                     tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                   #endif

              }

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2; /* 延迟周期 */

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

    

     while (1)

     {

         /* RL-TCPnet处理函数 */

          main_TcpNet();

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按键消息的处理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                 sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* 接收到K2键按下,给远程TCP服务器发送1024字节的数据 */

                   case KEY2_BIT1:       

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                               

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* 接收到K3键按下,给远程TCP服务器发送5MB数据 */

                   case KEY3_BIT2:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

14.8.2 STM32F429开发板实验

配套例子:

V6-1013_RL-TCPnet实验_TCP客户端(RTX)

实验目的:

  1. 学习RL-TCPnet的TCP客户端创建和数据收发。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 客户端的例子相比服务器的例子稍麻烦些,因为客户端的例子需要用户知道电脑端IP和端口号。并根据实际情况设置IP和端口号的宏定义,这个配置在文件app_tcpnet_lib.c文件开头,测试的时候板子要连接这个IP和端口:     #define IP1            192     #define IP2            168     #define IP3            1     #define IP4            2                     #define PORT_NUM    1001
  3. 创建了一个TCP Client,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获得获取板子的IP地址,端口号1024。
  4. 用户可以在电脑端用网络调试软件创建TCP Server,让客户端去连接。
  5. 按键K1按下,发送8字节的数据给TCP Server。
  6. 按键K2按下,发送1024字节的数据给TCP Server。
  7. 按键K3按下,发送5MB字节的数据给TCP Server。

实验操作:

详见本章节14.6小节。

配置向导文件设置(Net_Config.c):

详见本章节14.3小节。

调试文件设置(Net_Debug.c):

详见本章节14.4小节。  

RTX配置:

RTX配置向导详情如下:

Task Configuration

(1)Number of concurrent running tasks

允许创建6个任务,实际创建了如下5个任务:

    AppTaskUserIF任务   :按键消息处理。

    AppTaskLED任务     :LED闪烁。

    AppTaskMsgPro任务 :按键检测。

    AppTaskTCPMain任务:RL-TCPnet测试任务。

    AppTaskStart任务  :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

(2)Number of tasks with user-provided stack

创建的5个任务都是采用自定义堆栈方式。

(3)Run in privileged mode

设置任务运行在非特权级模式。

RTX任务调试信息:

程序设计:

任务栈大小分配:

static uint64_t AppTaskUserIFStk[1024/8];   /* 任务栈 */

static uint64_t AppTaskLEDStk[1024/8];      /* 任务栈 */

static uint64_t AppTaskMsgProStk[1024/8];  /* 任务栈 */

static uint64_t AppTaskTCPMainStk[2048/8]; /* 任务栈 */

static uint64_t AppTaskStartStk[1024/8];     /* 任务栈 */

将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。

系统栈大小分配:

RTX初始化:

/*

*********************************************************************************************************

*    函 数 名: main

*    功能说明: 标准c程序入口。

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

    

     /* 创建启动任务 */

     os_sys_init_user (AppTaskStart,              /* 任务函数 */

                       5,                         /* 任务优先级 */

                       &AppTaskStartStk,          /* 任务栈 */

                       sizeof(AppTaskStartStk));  /* 任务栈大小,单位字节数 */

     while(1);

}

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*

*********************************************************************************************************

*    函 数 名: bsp_Init

*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

*    形    参:无

*    返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

     /*

         由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。

         启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

 

         系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件

     */

     /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/

     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

 

     SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */

 

     bsp_InitDWT();      /* 初始化DWT */

     bsp_InitUart();     /* 初始化串口 */

     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */

 

     bsp_InitExtIO();    /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */

     bsp_InitLed();      /* 初始LED指示灯端口 */

}

RTX任务创建:

/*

*********************************************************************************************************

*    函 数 名: AppTaskCreate

*    功能说明: 创建应用任务

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

static void AppTaskCreate (void)

{

     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */

                                           1,                         /* 任务优先级 */

                                           &AppTaskUserIFStk,         /* 任务栈 */

                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */

    

     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */

                                        2,                       /* 任务优先级 */

                                        &AppTaskLEDStk,          /* 任务栈 */

                                        sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */

    

     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */

                                           3,                         /* 任务优先级 */

                                           &AppTaskMsgProStk,         /* 任务栈 */

                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */

    

    HandleTaskTCPMain = os_tsk_create_user(AppTaskTCPMain,             /* 任务函数 */

                                           4,                         /* 任务优先级 */

                                           &AppTaskTCPMainStk,         /* 任务栈 */

                                           sizeof(AppTaskTCPMainStk)); /* 任务栈大小,单位字节数 */

}

五个RTX任务的实现:

/*

*********************************************************************************************************

*    函 数 名: AppTaskUserIF

*    功能说明: 按键消息处理     

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)

*********************************************************************************************************

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

 

    while(1)

    {

         ucKeyCode = bsp_GetKey();

        

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit0 */

                   case KEY_DOWN_K1:

                       printf("K1键按下,直接发送事件标志给任务AppTaskTCPMain,bit0被设置\r\n");

                       os_evt_set (KEY1_BIT0, HandleTaskTCPMain);                      

                       break;  

 

                   /* K2键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit1 */

                   case KEY_DOWN_K2:

                       printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\r\n");

                       os_evt_set (KEY2_BIT1, HandleTaskTCPMain);

                       break;

                  

                   /* K3键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit2 */

                   case KEY_DOWN_K3:

                       printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\r\n");

                       os_evt_set (KEY3_BIT2, HandleTaskTCPMain);

                       break;

 

                   /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

        

         os_dly_wait(20);

     }

}

 

/*

*********************************************************************************************************

*    函 数 名: AppTaskLED

*    功能说明: LED闪烁。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 2 

*********************************************************************************************************

*/

__task void AppTaskLED(void)

{

     const uint16_t usFrequency = 500; /* 延迟周期 */

    

     /* 设置延迟周期 */

     os_itv_set(usFrequency);

    

    while(1)

    {

         bsp_LedToggle(2);

 

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/

         os_itv_wait();

    }

}

 

/*

*********************************************************************************************************

*    函 数 名: AppTaskMsgPro

*    功能说明: 按键检测

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 3 

*********************************************************************************************************

*/

__task void AppTaskMsgPro(void)

{

    while(1)

    {

         bsp_KeyScan();

         os_dly_wait(10);

    }

}

 

/*

*********************************************************************************************************

*    函 数 名: AppTaskTCPMain

*    功能说明: RL-TCPnet测试任务

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

*********************************************************************************************************

*/

__task void AppTaskTCPMain(void)

{

     while (1)

     {

         TCPnetTest();

     }

}

 

/*

*********************************************************************************************************

*    函 数 名: AppTaskStart

*    功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

*********************************************************************************************************

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

    

     /* 创建任务 */

     AppTaskCreate();

    

     os_itv_set (100);

    

    while(1)

    {

         os_itv_wait ();

        

         /* RL-TCPnet时间基准更新函数 */

          timer_tick ();

    }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,主要功能是创建了一个TCP Client。

#include "includes.h" 

 

 

/*

*********************************************************************************************************

*                                      用于本文件的调试

*********************************************************************************************************

*/

#if 1

     #define printf_debug printf

#else

     #define printf_debug(...)

#endif

 

 

/*

*********************************************************************************************************

*                              宏定义,远程服务器的IP和端口

*********************************************************************************************************

*/

/* 要访问的远程服务器IP和端口配置,也就是电脑端调试助手设置的IP和端口号 */

#define IP1            192

#define IP2            168

#define IP3            1

#define IP4            2

 

#define PORT_NUM         1001

 

/* 这个是本地端口 */

#define LocalPort_NUM    1024

 

/*

*********************************************************************************************************

*                                         变量

*********************************************************************************************************

*/

uint8_t socket_tcp;

uint8_t Rem_IP[4] = {IP1,IP2,IP3,IP4};

 

 

/*

*********************************************************************************************************

*    函 数 名: tcp_callback

*    功能说明: TCP Socket的回调函数

*    形    参: soc  TCP Socket类型

*             evt  事件类型

*             ptr  事件类型是TCP_EVT_DATA,ptr指向的缓冲区记录着接收到的TCP数据,其余事件记录IP地址

*             par  事件类型是TCP_EVT_DATA,记录接收到的数据个数,其余事件记录端口号

*    返 回 值:

*********************************************************************************************************

*/

U16 tcp_callback (U8 soc, U8 evt, U8 *ptr, U16 par)

{

     char buf[50];

     uint16_t i;

    

     /* 确保是socket_tcp的回调 */

     if (soc != socket_tcp)

     {

         return (0);

     }

 

     switch (evt)

     {

         /*

              远程客户端连接消息

             1、数组ptr存储远程设备的IP地址,par中存储端口号。

             2、返回数值1允许连接,返回数值0禁止连接。

         */

         case TCP_EVT_CONREQ:

              sprintf(buf, "远程客户端请求连接IP: %d.%d.%d.%d", ptr[0], ptr[1], ptr[2], ptr[3]);

              printf_debug("IP:%s  port:%d\r\n", buf, par);

              return (1);

        

         /* 连接终止 */

         case TCP_EVT_ABORT:

              break;

        

         /* Socket远程连接已经建立 */

         case TCP_EVT_CONNECT:

              printf_debug("Socket is connected to remote peer\r\n");

              break;

        

         /* 连接断开 */

         case TCP_EVT_CLOSE:

              printf_debug("Connection has been closed\r\n");

              break;

        

         /* 发送的数据收到远程设备应答 */

         case TCP_EVT_ACK:

              break;

        

         /* 接收到TCP数据帧,ptr指向数据地址,par记录数据长度,单位字节 */

         case TCP_EVT_DATA:

              printf_debug("Data length = %d\r\n", par);

              for(i = 0; i < par; i++)

              {

                   printf_debug("ptr[%d] = %d\r\n", i, ptr[i]);

              }

              break;

     }

    

     return (0);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCP_StatusCheck

*    功能说明: 检测TCP的连接状态,主要用于网线插拔的判断

*    形    参: 无

*    返 回 值: __TRUE  连接

*             __FALSE 断开

*********************************************************************************************************

*/

uint8_t TCP_StatusCheck(void)

{

     switch (tcp_get_state(socket_tcp))

     {

         case TCP_STATE_FREE:

         case TCP_STATE_CLOSED:

              {

                   /* 这里默认不开启调试,否则未跟电脑端服务器连接前会一直发 */

                   #if 0

                     uint8_t res;

                     res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                     printf_debug("tcp connect res = %d\r\n", res);

                #else

                     tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

                   #endif

              }

              break;

        

         case TCP_STATE_LISTEN:

              break;

        

         case TCP_STATE_CONNECT:

              return (__TRUE);

             

         default: 

              break;

     }

    

     return (__FALSE);

}

 

/*

*********************************************************************************************************

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

*********************************************************************************************************

*/

void TCPnetTest(void)

{ 

     int32_t iCount;

     uint8_t *sendbuf;

     uint8_t tcp_status;

     uint16_t maxlen;

     uint8_t res;

     OS_RESULT xResult;

     const uint16_t usMaxBlockTime = 2; /* 延迟周期 */

 

     /*

        创建TCP Socket并连接,客户端连接服务器后,10秒内无数据通信将断开连接。

        但是由于这里使能了TCP_TYPE_KEEP_ALIVE,会一直保持连接,不受10秒的时间限制。

     */

    socket_tcp = tcp_get_socket (TCP_TYPE_CLIENT | TCP_TYPE_KEEP_ALIVE, 0, 10, tcp_callback);

     if(socket_tcp != 0)

     {

         res = tcp_connect (socket_tcp, Rem_IP, PORT_NUM, LocalPort_NUM);

         printf("TCP Socket创建成功res = %d\r\n", res);

     }

    

     while (1)

     {

         /* RL-TCPnet处理函数 */

         main_TcpNet();

        

         /* 用于网线插拔的处理 */

         tcp_status = TCP_StatusCheck();

        

         /* 按键消息的处理 */

         if((os_evt_wait_or(0xFFFF, usMaxBlockTime) == OS_R_EVT)&&(tcp_status == __TRUE))

         {

              xResult = os_evt_get ();

              switch (xResult)

              {

                   /* 接收到K1键按下,给远程TCP服务器发送8字节数据 */

                   case KEY1_BIT0:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 8;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = '1';

                                 sendbuf[1] = '2';

                                 sendbuf[2] = '3';

                                 sendbuf[3] = '4';

                                sendbuf[4] = '5';

                                 sendbuf[5] = '6';

                                 sendbuf[6] = '7';

                                 sendbuf[7] = '8';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

 

                   /* 接收到K2键按下,给远程TCP服务器发送1024字节的数据 */

                   case KEY2_BIT1:       

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                     /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);                      

                       break;

                      

                   /* 接收到K3键按下,给远程TCP服务器发送5MB数据 */

                   case KEY3_BIT2:              

                       printf_debug("tcp_get_state(socket_tcp) = %d\r\n", tcp_get_state(socket_tcp));

                       iCount = 5*1024*1024;

                       do

                       {

                            main_TcpNet();

                            if (tcp_check_send (socket_tcp) == __TRUE)

                            {

                                 maxlen = tcp_max_dsize (socket_tcp);

                                 iCount -= maxlen;

                                

                                 if(iCount < 0)

                                 {

                                      /* 这么计算没问题的 */

                                     maxlen = iCount + maxlen;

                                 }

                                

                                 /* 这里仅初始化了每次所发送数据包的前8个字节 */

                                 sendbuf = tcp_get_buf(maxlen);

                                 sendbuf[0] = 'a';

                                 sendbuf[1] = 'b';

                                 sendbuf[2] = 'c';

                                 sendbuf[3] = 'd';

                                 sendbuf[4] = 'e';

                                 sendbuf[5] = 'f';

                                 sendbuf[6] = 'g';

                                 sendbuf[7] = 'h';

                                

                                 /* 测试发现只能使用获取的内存 */

                                 tcp_send (socket_tcp, sendbuf, maxlen);

                            }

                           

                       }while(iCount > 0);

                       break;

                  

                    /* 其他的键值不处理 */

                   default:                    

                       break;

              }

         }

     }

}

14.9 总结

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-07-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第14章      RL-TCPnet之TCP客户端
    • 14.1  初学者重要提示
      • 14.2  TCP客户端API函数
        • 14.2.1  函数tcp_get_socket
        • 14.2.2 函数tcp_connect
        • 14.2.3 函数tcp_check_send
        • 14.2.4 函数tcp_max_dsize
        • 14.2.5 函数tcp_get_buf
        • 14.2.6 函数tcp_send
        • 14.2.7 函数tcp_get_state
      • 14.3 TCP配置说明(Net_Config.c)
        • 14.4 TCP调试说明(Net_Debug.c)
          • 14.5 TCP客户端的实现方法
            • 14.5.1 创建TCP客户端
            • 14.5.2 TCP数据发送
            • 14.5.3 TCP数据接收
          • 14.6 网络调试助手和板子的调试操作步骤
            • 14.6.1 获取板子IP地址
            • 14.6.2 获取电脑的IP地址
            • 14.6.3 在程序中配置要访问的远程IP地址和端口
            • 14.6.4 网络调试助手创建TCP服务器
            • 14.6.5 TCP客户端发送数据
            • 14.6.6 TCP客户端接收数据
          • 14.7 实验例程说明(裸机)
            • 14.7.1 STM32F407开发板实验
            • 14.7.2 STM32F429开发板实验
          • 14.8 实验例程说明(RTX)
            • 14.8.1 STM32F407开发板实验
            • 14.8.2 STM32F429开发板实验
          • 14.9 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档