【RL-TCPnet网络教程】第32章 RL-TCPnet之Telnet服务器

时间:2024-01-03 00:00:38

第32章      RL-TCPnet之Telnet服务器

本章节为大家讲解RL-TCPnet的Telnet应用,学习本章节前,务必要优先学习第31章的Telnet基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。

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

32.1  初学者重要提示

32.2  Telnet函数

32.3  Telnet配置说明(Net_Config.c)

32.4  Telnet调试说明(Net_Debug.c)

32.5  Telnet访问方法和板子的操作步骤

32.6  实验例程说明(裸机)

32.7  实验例程说明(RTX)

32.8  总结

32.1  初学者重要提示

  1. 学习本章节前,务必保证已经学习了第31章的基础知识。
  2. 对于初学者,务必要看本章节32.5小节的测试方法。

32.2  Telnet函数

使用如下9个函数可以实现RL-TCPnet的Telnet:

  • tnet_check_account
  • tnet_accept_host
  • tnet_cbfunc
  • tnet_ccmp
  • tnet_get_info
  • tnet_get_user_id
  • tnet_process_cmd
  • tnet_set_delay
  • tnet_msg_poll

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

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

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

  • tnet_cbfunc
  • tnet_process_cmd
  • tnet_ccmp
  • tnet_get_info

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

  1. Telnet的所有函数都不支持重入,也就是不支持多任务调用。

32.2.1    函数tnet_cbfunc

函数原型:

U16 tnet_cbfunc (

    U8  code,        /* 消息类型 */

    U8* buf,         /* 输出缓冲区地址 */

U16 buflen );    /* 输出缓冲区大小 */

函数描述:

函数tnet_cbfunc是Telnet服务器的回调函数,主要用于设置Telnet服务器给Telnet客户端发送连接和登录消息(为了更好理解此函数,实际操作一遍本章节配套的例子就明白了)。

  1. 第1个参数是消息类型,具体支持的消息类型如下:【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器
  2. 第2个参数是输出缓冲区地址,用于存储服务器返回给客户端的消息。
  3. 第3个参数是输出缓冲区大小,单位字节。
  4. 返回值,返回使用的输出缓冲区大小,单位字节。

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

  1. 输出缓冲区的大小是由TCP Socket的MSS最大报文段大小决定的,局域网中一般是1400字节左右,但是也可减小到500字节,甚至更小。
  2. 写到输出缓冲区的数据,不可以超过第三个参数buflen的大小,否则会造成内存指针链表的损坏,从而很容易造成系统崩溃。
  3. Telnet服务器不会自动扩展回车符,所以每个消息结束后务必自行加上回车和换行符。

使用举例:

/*----------------------------------------------------------------------------

 *      Telnet CallBack Functions

 *---------------------------------------------------------------------------*/

/*--------------------------- tnet_cbfunc -----------------------------------*/

U16 tnet_cbfunc (U8 code, U8 *buf, U16 buflen)

{

     /* This function is called by the Telnet Client to get formated system    */

     /* messages for different code values.                                    */

     /* Values for 'code':                                                     */

     /*    0 - initial header                                                  */

     /*    1 - prompt string                                                   */

     /*    2 - header for login only if authorization is enabled               */

     /*    3 - string 'Username' for login                                     */

     /*    4 - string 'Password' for login                                     */

     /*    5 - message 'Login incorrect'                                       */

     /*    6 - message 'Login timeout'                                         */

     /*    7 - Unsolicited messages from Server (ie. Basic Interpreter)        */

     U16 len = ;

     /* Make a reference to disable compiler warning. */

     buflen = buflen;

     switch (code)

     {

         case :

              /* Write initial header after login. */

              len = str_copy (buf, (U8 *)&tnet_header);

              break;

         case :

              /* Write a prompt string. */

              len = str_copy (buf, "\r\narmfly> ");

              break;

         case :

              /* Write Login header. */

              len = str_copy (buf, CLS "\r\nRL-TCPnet之Telnet服务器,"

                                          " 请登录...\r\n");

              break;

         case :

              /* Write 'username' prompt. */

              len = str_copy (buf, "\r\n用户名: ");

              break;

         case :

              /* Write 'Password' prompt. */

              len = str_copy (buf, "\r\n密  码: ");

              break;

         case :

              /* Write 'Login incorrect'.message. */

              len = str_copy (buf, "\r\n登录失败");

              break;

         case :

              /* Write 'Login Timeout' message. */

              len = str_copy (buf, "\r\n120秒无操作,退出登录\r\n");

              break;

     }

     return (len);

}

32.2.2   函数tnet_process_cmd

函数原型:

U16 tnet_process_cmd (

    U8*  cmd,       /* 指针变量,指向从Telnet客户端接收到的命令字符 */

    U8*  buf,       /* 输出缓冲区地址 */

    U16  buflen,    /* 输出缓冲区大小,单位字节 */

U32* pvar );    /* 指针变量,指向一个不会被改变的变量  */

函数描述:

函数tnet_process_cmd用于处理Telnet客户端请求的命令。Telnet服务器从Telnet客户端收到连续的回车(CR)和换行(LF)字符序列时,会调用tnet_process_cmd函数(通常由用户在telnet客户端终端上按Enter键)。

  1. 第1个参数指向从Telnet客户端接收到的命令字符。
  2. 第2个参数是输出缓冲区地址,用于函数tnet_process_cmd执行过程中存储要返回给Telnet客户端的消息。
  3. 第3个参数是输出缓冲区的大小,单位字节。
  4. 第4个参数指向不会被Telnet服务器更改的变量。用户可以将其作为重复计数器,或者简单地区分tnet_process_cmd函数的不同调用,亦或者任何其它应用均可。
  5. 返回值,返回写入到输出缓冲区的字节数。返回值是U16类型的,其中bit15和bit14还可以作为其它用途,而剩余的bit0-bit13表示的最大值是16383,足够表示TCP Socket的MSS最大报文段大小的1460字节。
    • bit14作为函数tnet_process_cmd是否重复调用的标志,如果此位设置为1,表示退出函数后,依然保持第1个参数cmd和第4个参数*pvar的数值,并再次调用函数tnet_process_cmd。如果此位设置为0,将不再重复调用。
    • bit15是断开连接标志,如果此位设置为1,Telnet服务器将断开客户端会话。如果此位是0,表示继续保证连接。

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

  1. 输出缓冲区的大小是由TCP Socket的MSS最大报文段大小决定的,局域网中一般是1400字节左右,但是也可减小到500字节,甚至更小。
  2. 写到输出缓冲区的数据,不可以超过第三个参数buflen的大小,否则会造成内存指针链表的损坏,从而很容易造成系统崩溃。
  3. Telnet服务器不会自动扩展回车符,所以每个消息结束后务必自行加上回车和换行符。
  4. 对于每个Telnet会话,*pvar(注意,这里是指的指针变量pvar所指向的存储单元)变量都是独立的,也就是说新创建一个会话,都会有一个独立的*pvar变量。另外,每个会话首次调用函数tnet_process_cmd之前都会将变量*pvar清零(注意,这里是指的指针变量pvar所指向的存储单元清零)。

使用举例:

/*--------------------------- tnet_process_cmd ------------------------------*/

U16 tnet_process_cmd (U8 *cmd, U8 *buf, U16 buflen, U32 *pvar)

{

     /* This is a Telnet Client callback function to make a formatted output   */

     /* for 'stdout'. It returns the number of bytes written to the out buffer.*/

     /* Hi-bit of return value (len is or-ed with 0x8000) is a disconnect flag.*/

     /* Bit 14 (len is or-ed with 0x4000) is a repeat flag for the Tnet client.*/

     /* If this bit is set to 1, the system will call the 'tnet_process_cmd()' */

     /* again with parameter 'pvar' pointing to a 4-byte buffer. This buffer   */

     /* can be used for storing different status variables for this function.  */

     /* It is set to 0 by Telnet server on first call and is not altered by    */

     /* Telnet server for repeated calls. This function should NEVER write     */

     /* more than 'buflen' bytes to the buffer.                                */

     /* Parameters:                                                            */

     /*   cmd    - telnet received command string                              */

     /*   buf    - Telnet transmit buffer                                      */

     /*   buflen - length of this buffer (500-1400 bytes - depends on MSS)     */

     /*   pvar   - pointer to local storage buffer used for repeated loops     */

     /*            This is a U32 variable - size is 4 bytes. Value is:         */

     /*            - on 1st call = 0                                           */

     /*            - 2nd call    = as set by this function on first call       */

     REMOTEM rm;

     U16 len = ;

     switch (MYBUF(pvar)->id)

     {

         case :

              /* First call to this function, the value of '*pvar' is 0 */

              break;

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len | 0x4000);

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len |= 0x4000);

     }

     /* Simple Command line parser */

     len = strlen ((const char *)cmd);

     if (tnet_ccmp (cmd, "LEDON") == __TRUE)

     {

          /* 'LED4' command received */

         len = str_copy (buf,"\r\n=====>LED4 Lights ON");

         bsp_LedOn();

         return (len);

     }

     if (tnet_ccmp (cmd, "LEDOFF") == __TRUE)

     {

         /* 'LED4' command received */

          len = str_copy (buf,"\r\n=====>LED4 Lights OFF");

         bsp_LedOff();

         return (len);

     }

     if (tnet_ccmp (cmd, "BYE") == __TRUE)

     {

          /* 'BYE' command, send message and disconnect */

         len = str_copy (buf, "\r\nDisconnect...\r\n");

         /* Hi bit of return value is a disconnect flag */

         return (len | 0x8000);

     }

     if (tnet_ccmp (cmd, "RINFO") == __TRUE)

     {

          /* Display Remote Machine IP and MAC address. */

         tnet_get_info (&rm);

         len  = sprintf ((char *)buf,"\r\n Remote IP : %d.%d.%d.%d",

         rm.IpAdr[],rm.IpAdr[],rm.IpAdr[],rm.IpAdr[]);

         len += sprintf ((char *)(buf+len),

         "\r\n Remote MAC: %02X-%02X-%02X-%02X-%02X-%02X",

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[],

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[]);

         return (len);

     }

     if (tnet_ccmp (cmd, "HELP") == __TRUE || tnet_ccmp (cmd, "?") == __TRUE)

     {

          /* 'HELP' command, display help text */

         len = str_copy (buf,(U8 *)tnet_help1);   

         len += str_copy (buf+len,(U8 *)tnet_help2);

         return (len);

     }

     /* Unknown command, display message */

     len = str_copy  (buf, "\r\n==> Unknown Command: ");

     len += str_copy (buf+len, cmd);

     return (len);

}

32.2.3   函数tnet_ccmp

函数原型:

BOOL tnet_ccmp (

    U8* buf,      /* 字符串地址 */

    U8* cmd );    /* 要比较的字符串地址 */

函数描述:

函数tnet_ccmp用于查询字符串中是否有要找的字符。

  1. 第1个参数是字符串地址。
  2. 第2个参数是要比较的字符串,即判断第1个参数的字符串里面是否有要查找的字符。
  3. 返回值,如果字符串中含有要查找的字符,返回__TRUE。如果没有,返回__FALSE。

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

  1. 此函数类似C标准库函数strcmp,区别是tnet_ccmp仅比较第一个字符串,即在第1个参数buf中,该字符串由NULL字符终止,或后跟空格字符。
  2. 字符串cmd中的所有字符必须大写,因为参数buf中的字符串也只有大写字母。这是因为在调用tnet_ccmp函数之前进行了内部转换。

使用举例:

/*--------------------------- tnet_process_cmd ------------------------------*/

U16 tnet_process_cmd (U8 *cmd, U8 *buf, U16 buflen, U32 *pvar)

{

     /* This is a Telnet Client callback function to make a formatted output   */

     /* for 'stdout'. It returns the number of bytes written to the out buffer.*/

     /* Hi-bit of return value (len is or-ed with 0x8000) is a disconnect flag.*/

     /* Bit 14 (len is or-ed with 0x4000) is a repeat flag for the Tnet client.*/

     /* If this bit is set to 1, the system will call the 'tnet_process_cmd()' */

     /* again with parameter 'pvar' pointing to a 4-byte buffer. This buffer   */

     /* can be used for storing different status variables for this function.  */

     /* It is set to 0 by Telnet server on first call and is not altered by    */

     /* Telnet server for repeated calls. This function should NEVER write     */

     /* more than 'buflen' bytes to the buffer.                                */

     /* Parameters:                                                            */

     /*   cmd    - telnet received command string                              */

     /*   buf    - Telnet transmit buffer                                      */

     /*   buflen - length of this buffer (500-1400 bytes - depends on MSS)     */

     /*   pvar   - pointer to local storage buffer used for repeated loops     */

     /*            This is a U32 variable - size is 4 bytes. Value is:         */

     /*            - on 1st call = 0                                           */

     /*            - 2nd call    = as set by this function on first call       */

     REMOTEM rm;

     U16 len = ;

     switch (MYBUF(pvar)->id)

     {

         case :

              /* First call to this function, the value of '*pvar' is 0 */

              break;

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len | 0x4000);

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len |= 0x4000);

     }

     /* Simple Command line parser */

     len = strlen ((const char *)cmd);

     if (tnet_ccmp (cmd, "LEDON") == __TRUE)

     {

          /* 'LED4' command received */

         len = str_copy (buf,"\r\n=====>LED4 Lights ON");

         bsp_LedOn();

         return (len);

     }

     if (tnet_ccmp (cmd, "LEDOFF") == __TRUE)

     {

         /* 'LED4' command received */

          len = str_copy (buf,"\r\n=====>LED4 Lights OFF");

         bsp_LedOff();

         return (len);

     }

     if (tnet_ccmp (cmd, "BYE") == __TRUE)

     {

          /* 'BYE' command, send message and disconnect */

         len = str_copy (buf, "\r\nDisconnect...\r\n");

         /* Hi bit of return value is a disconnect flag */

         return (len | 0x8000);

     }

     if (tnet_ccmp (cmd, "RINFO") == __TRUE)

     {

          /* Display Remote Machine IP and MAC address. */

         tnet_get_info (&rm);

         len  = sprintf ((char *)buf,"\r\n Remote IP : %d.%d.%d.%d",

         rm.IpAdr[],rm.IpAdr[],rm.IpAdr[],rm.IpAdr[]);

         len += sprintf ((char *)(buf+len),

         "\r\n Remote MAC: %02X-%02X-%02X-%02X-%02X-%02X",

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[],

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[]);

         return (len);

     }

     if (tnet_ccmp (cmd, "HELP") == __TRUE || tnet_ccmp (cmd, "?") == __TRUE)

     {

          /* 'HELP' command, display help text */

         len = str_copy (buf,(U8 *)tnet_help1);   

         len += str_copy (buf+len,(U8 *)tnet_help2);

         return (len);

     }

     /* Unknown command, display message */

     len = str_copy  (buf, "\r\n==> Unknown Command: ");

     len += str_copy (buf+len, cmd);

     return (len);

}

32.2.4   函数tnet_get_info

函数原型:

void tnet_get_info (

    REMOTEM* info );    /* 结构体变量地址 */

函数描述:

函数tnet_get_info用于返回远程客户端的IP地址和MAC地址。

  1. 第1个参数填写结构体变量地址,用于存储远程客户端的IP和MAC地址。

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

  1. 用户可以使用此函数设置远程客户端的操作权限,即通过此函数获取客户端地址,然后对这个客户端做操作限制。
  2. 对于SLIP或者PPP连接方式,MAC地址是00-00-00-00-00-00。

使用举例:

/*--------------------------- tnet_process_cmd ------------------------------*/

U16 tnet_process_cmd (U8 *cmd, U8 *buf, U16 buflen, U32 *pvar)

{

     /* This is a Telnet Client callback function to make a formatted output   */

     /* for 'stdout'. It returns the number of bytes written to the out buffer.*/

     /* Hi-bit of return value (len is or-ed with 0x8000) is a disconnect flag.*/

     /* Bit 14 (len is or-ed with 0x4000) is a repeat flag for the Tnet client.*/

     /* If this bit is set to 1, the system will call the 'tnet_process_cmd()' */

     /* again with parameter 'pvar' pointing to a 4-byte buffer. This buffer   */

     /* can be used for storing different status variables for this function.  */

     /* It is set to 0 by Telnet server on first call and is not altered by    */

     /* Telnet server for repeated calls. This function should NEVER write     */

     /* more than 'buflen' bytes to the buffer.                                */

     /* Parameters:                                                            */

     /*   cmd    - telnet received command string                              */

     /*   buf    - Telnet transmit buffer                                      */

     /*   buflen - length of this buffer (500-1400 bytes - depends on MSS)     */

     /*   pvar   - pointer to local storage buffer used for repeated loops     */

     /*            This is a U32 variable - size is 4 bytes. Value is:         */

     /*            - on 1st call = 0                                           */

     /*            - 2nd call    = as set by this function on first call       */

     REMOTEM rm;

     U16 len = ;

     switch (MYBUF(pvar)->id)

     {

         case :

              /* First call to this function, the value of '*pvar' is 0 */

              break;

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len | 0x4000);

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len |= 0x4000);

     }

     /* Simple Command line parser */

     len = strlen ((const char *)cmd);

     if (tnet_ccmp (cmd, "LEDON") == __TRUE)

     {

          /* 'LED4' command received */

         len = str_copy (buf,"\r\n=====>LED4 Lights ON");

         bsp_LedOn();

         return (len);

     }

     if (tnet_ccmp (cmd, "LEDOFF") == __TRUE)

     {

         /* 'LED4' command received */

          len = str_copy (buf,"\r\n=====>LED4 Lights OFF");

         bsp_LedOff();

         return (len);

     }

     if (tnet_ccmp (cmd, "BYE") == __TRUE)

     {

          /* 'BYE' command, send message and disconnect */

         len = str_copy (buf, "\r\nDisconnect...\r\n");

         /* Hi bit of return value is a disconnect flag */

         return (len | 0x8000);

     }

     if (tnet_ccmp (cmd, "RINFO") == __TRUE)

     {

          /* Display Remote Machine IP and MAC address. */

         tnet_get_info (&rm);

         len  = sprintf ((char *)buf,"\r\n Remote IP : %d.%d.%d.%d",

         rm.IpAdr[],rm.IpAdr[],rm.IpAdr[],rm.IpAdr[]);

         len += sprintf ((char *)(buf+len),

         "\r\n Remote MAC: %02X-%02X-%02X-%02X-%02X-%02X",

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[],

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[]);

         return (len);

     }

     if (tnet_ccmp (cmd, "HELP") == __TRUE || tnet_ccmp (cmd, "?") == __TRUE)

     {

          /* 'HELP' command, display help text */

         len = str_copy (buf,(U8 *)tnet_help1);   

         len += str_copy (buf+len,(U8 *)tnet_help2);

         return (len);

     }

     /* Unknown command, display message */

     len = str_copy  (buf, "\r\n==> Unknown Command: ");

     len += str_copy (buf+len, cmd);

     return (len);

}

32.3 Telnet配置说明(Net_Config.c)

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

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

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

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

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

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。

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

Ethernet Network Interface

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

(1)MAC Address

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

(2)IP Address

IP地址。

(3)Subnet mask

子网掩码。

(4)Default Gateway

默认网关。

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

Ethernet Network Interface

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

(1)NetBIOS Name Service

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

(2)Dynaminc Host Configuration

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

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

UDP Sockets

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

(1) Number of UDP Sockets

用于配置可创建的UDP Sockets数量,这里配置了5个。

范围1 – 20。

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

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接收窗口大小。

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

Telnet Server

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

(1)Number of Telnet Connections

范围1-10。

服务器可以同时连接的客户端数。

(2) Port Number

范围1-65535。

监听的端口号,Telnet的端口号一般就是23。

(3) Idle Connection Timeout in seconds

范围0-3600秒。

空闲连接溢出时间,如果连接后,这段时间内无操作,Telnet服务器将断开客户端的连接。

配置为数值0,将禁止超时断开连接,即一直保持连接。

(4) Disable Echo

禁止回显。如果此选项打上对勾的话,服务器将不会回显接收到的字符。即用户在客户端输入的字符,不会在客户端上面显示出来,比如输入用户名,就不会将其显示出来。

(5) Enable User Authentication

用户认证,如果此选项打上对勾的话,将使能用户认证。

Authentication Username 是用户名。

Authentication Password 是用户密码。

32.4 Telnet调试说明(Net_Debug.c)

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

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

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

Print Time Stamp

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

其它所有的选项

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

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

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

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

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

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

32.5 Telnet的访问方法和板子的操作步骤

我们这里使用电脑端自带的Telnet客户端功能访问开发板上面建立的Telnet服务器功能。

32.5.1 使能电脑端Telnet客户端

这里以WIN7 64bit为例进行说明:

  • 点击电脑左下角开始按钮,选择控制面板:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

  • 打开后,点击“程序”:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

  • 弹出的界面里面选择“打开或关闭Windows功能”。

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

  • 点击后,就会弹出Windows功能窗口:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

弹出后要稍等一会,界面会变成如下这个样子:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

将Telnet客户端前面单选框打上对勾,点击确定。这样就使能了电脑上的Telnet客户端。

32.5.2 登陆Telnet服务器并进行操作

登陆开发板的Telnet方法如下:

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

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

  • 弹出的命令窗口中,输入telnet armfly,当然,也可以将armfly换成板子实际的IP地址:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

  • 输入telnet armfly后,回车。

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

  • 用户名填admin,点击回车,密码填写123456:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

最后再点击回车,就进入到开发板上的Telnet服务器。

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

输入命令字符“?”或者“help”就可以打印出Telnet支持的命令,输入后点击回车,可以看到支持的命令如下:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

我们这里简单的测试下命令“lendon”和“rinfo”,输出命令后记得点击回车:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

另外注意,如果120秒内无操作,服务器将自动断开客户端的连接,同时弹出如下消息:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

还需要操作的话,重新登录即可。

32.6 实验例程说明(裸机)

32.6.1  STM32F407开发板实验

配套例子:

V5-1044_RL-TCPnet实验_Telnet应用(裸机)

实验目的:

  1. 学习RL-TCPnet的Telnet应用。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 程序中实现了一个Telnet服务器,用户名是admin,密码123456。

实验操作:

详见本章节32.5小节。

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

详见本章节32.3小节。

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

详见本章节32.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功能的测试,此文件主要实现网络主函数main_TcpNet的调用。

/*

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

*    函 数 名: tcpnet_poll

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

*    形    参: 无

*    返 回 值: 无

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

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer())

     {

         bsp_LedToggle();

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

         timer_tick ();

     }

     main_TcpNet ();

}

/*

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

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

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

*/

void TCPnetTest(void)

{ 

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

     init_TcpNet ();

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

    bsp_StartAutoTimer(, );

     while ()

     {

          /* TCP轮询 */

          tcpnet_poll();

     }

}

Telnet用户接口文件的实现

KEIL官网有提供Telnet的接口文件,名为Telnet_uif.c文件。我们就是在这个文件上修改。具体修改后的代码如下:

/* Net_Config.c */

extern struct tcp_cfg   tcp_config;

extern struct tnet_cfg  tnet_config;

#define tcp_NumSocks    tcp_config.NumSocks

#define tcp_socket      tcp_config.Scb

#define tnet_EnAuth     tnet_config.EnAuth

#define tnet_auth_passw tnet_config.Passw

/* ANSI ESC Sequences for terminal control. */

#define CLS     "\033[2J"

#define TBLUE   "\033[37;44m"

#define TNORM   "\033[0m"

/* My structure of a Telnet U32 storage variable. This variable is private */

/* for each Telnet Session and is not altered by Telnet Server. It is only */

/* set to zero when tnet_process_cmd() is called for the first time.       */

typedef struct

{

     U8 id;

     U8 nmax;

     U8 idx;

} MY_BUF;

#define MYBUF(p)        ((MY_BUF *)p)

/* Local variables */

static U8 const tnet_header[] =

{

     CLS "\r\n"

     "        " TBLUE

     "*=============================================================*\r\n" TNORM

     "        " TBLUE

     "*               RL-TCPnet之Telnet服务器实验                   *\r\n" TNORM

     "        " TBLUE

     "*=============================================================*\r\n" TNORM

};

static U8 const tnet_help1[] =

{

     "\r\n\r\n"

     "    当前支持的命令:\r\n"

     "    ----------------------------\r\n"

     "    ledon      - 打开LED4\r\n"

     "    ledoff     - 关闭LED4\r\n"

     "    rinfo      - 显示远程设备的IP地址和MAC\r\n"

};

static U8 const tnet_help2[] =

{

     "    help, ?     - 显示帮助\r\n"

     "    bye         - 断开连接\r\n\r\n"

     "    <ESC>,<^C>  - ESC按键,断开连接\r\n"

     "    <BS>        - 回车函数,删除左侧字符\r\n"

     "    <UP><DOWN>  - 按键UP和DOWM,浏览历史命令\r\n"

};

/*----------------------------------------------------------------------------

 *      Telnet CallBack Functions

 *---------------------------------------------------------------------------*/

/*--------------------------- tnet_cbfunc -----------------------------------*/

U16 tnet_cbfunc (U8 code, U8 *buf, U16 buflen)

{

     /* This function is called by the Telnet Client to get formated system    */

     /* messages for different code values.                                    */

     /* Values for 'code':                                                     */

     /*    0 - initial header                                                  */

     /*    1 - prompt string                                                   */

     /*    2 - header for login only if authorization is enabled               */

     /*    3 - string 'Username' for login                                     */

     /*    4 - string 'Password' for login                                     */

     /*    5 - message 'Login incorrect'                                       */

     /*    6 - message 'Login timeout'                                         */

     /*    7 - Unsolicited messages from Server (ie. Basic Interpreter)        */

     U16 len = ;

     /* Make a reference to disable compiler warning. */

     buflen = buflen;

     switch (code)

     {

         case :

              /* Write initial header after login. */

              len = str_copy (buf, (U8 *)&tnet_header);

              break;

         case :

              /* Write a prompt string. */

              len = str_copy (buf, "\r\narmfly> ");

              break;

         case :

              /* Write Login header. */

              len = str_copy (buf, CLS "\r\nRL-TCPnet之Telnet服务器,"

                                          " 请登录...\r\n");

              break;

         case :

              /* Write 'username' prompt. */

              len = str_copy (buf, "\r\n用户名: ");

              break;

         case :

              /* Write 'Password' prompt. */

              len = str_copy (buf, "\r\n密  码: ");

              break;

         case :

              /* Write 'Login incorrect'.message. */

              len = str_copy (buf, "\r\n登录失败");

              break;

         case :

              /* Write 'Login Timeout' message. */

              len = str_copy (buf, "\r\n120秒无操作,退出登录\r\n");

              break;

     }

     return (len);

}

/*--------------------------- tnet_process_cmd ------------------------------*/

U16 tnet_process_cmd (U8 *cmd, U8 *buf, U16 buflen, U32 *pvar)

{

     /* This is a Telnet Client callback function to make a formatted output   */

     /* for 'stdout'. It returns the number of bytes written to the out buffer.*/

     /* Hi-bit of return value (len is or-ed with 0x8000) is a disconnect flag.*/

     /* Bit 14 (len is or-ed with 0x4000) is a repeat flag for the Tnet client.*/

     /* If this bit is set to 1, the system will call the 'tnet_process_cmd()' */

     /* again with parameter 'pvar' pointing to a 4-byte buffer. This buffer   */

     /* can be used for storing different status variables for this function.  */

     /* It is set to 0 by Telnet server on first call and is not altered by    */

     /* Telnet server for repeated calls. This function should NEVER write     */

     /* more than 'buflen' bytes to the buffer.                                */

     /* Parameters:                                                            */

     /*   cmd    - telnet received command string                              */

     /*   buf    - Telnet transmit buffer                                      */

     /*   buflen - length of this buffer (500-1400 bytes - depends on MSS)     */

     /*   pvar   - pointer to local storage buffer used for repeated loops     */

     /*            This is a U32 variable - size is 4 bytes. Value is:         */

     /*            - on 1st call = 0                                           */

     /*            - 2nd call    = as set by this function on first call       */

     REMOTEM rm;

     U16 len = ;

     switch (MYBUF(pvar)->id)

     {

         case :

              /* First call to this function, the value of '*pvar' is 0 */

              break;

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len | 0x4000);

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len |= 0x4000);

     }

     /* Simple Command line parser */

     len = strlen ((const char *)cmd);

     if (tnet_ccmp (cmd, "LEDON") == __TRUE)

     {

          /* 'LED4' command received */

         len = str_copy (buf,"\r\n=====>LED4 Lights ON");

         bsp_LedOn();

         return (len);

     }

     if (tnet_ccmp (cmd, "LEDOFF") == __TRUE)

     {

         /* 'LED4' command received */

          len = str_copy (buf,"\r\n=====>LED4 Lights OFF");

         bsp_LedOff();

         return (len);

     }

     if (tnet_ccmp (cmd, "BYE") == __TRUE)

     {

          /* 'BYE' command, send message and disconnect */

         len = str_copy (buf, "\r\nDisconnect...\r\n");

         /* Hi bit of return value is a disconnect flag */

         return (len | 0x8000);

     }

     if (tnet_ccmp (cmd, "RINFO") == __TRUE)

     {

          /* Display Remote Machine IP and MAC address. */

         tnet_get_info (&rm);

         len  = sprintf ((char *)buf,"\r\n Remote IP : %d.%d.%d.%d",

         rm.IpAdr[],rm.IpAdr[],rm.IpAdr[],rm.IpAdr[]);

         len += sprintf ((char *)(buf+len),

         "\r\n Remote MAC: %02X-%02X-%02X-%02X-%02X-%02X",

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[],

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[]);

         return (len);

     }

     if (tnet_ccmp (cmd, "HELP") == __TRUE || tnet_ccmp (cmd, "?") == __TRUE)

     {

          /* 'HELP' command, display help text */

         len = str_copy (buf,(U8 *)tnet_help1);   

         len += str_copy (buf+len,(U8 *)tnet_help2);

         return (len);

     }

     /* Unknown command, display message */

     len = str_copy  (buf, "\r\n==> Unknown Command: ");

     len += str_copy (buf+len, cmd);

     return (len);

}

32.6.2 STM32F429开发板实验

配套例子:

V5-1044_RL-TCPnet实验_Telnet应用(裸机)

实验目的:

  1. 学习RL-TCPnet的Telnet应用。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 程序中实现了一个Telnet服务器,用户名是admin,密码123456。

实验操作:

详见本章节32.5小节。

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

详见本章节32.3小节。

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

详见本章节32.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功能的测试,此文件主要实现网络主函数main_TcpNet的调用。

/*

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

*    函 数 名: tcpnet_poll

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

*    形    参: 无

*    返 回 值: 无

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

*/

void tcpnet_poll(void)

{

     if(bsp_CheckTimer())

     {

         bsp_LedToggle();

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

         timer_tick ();

     }

     main_TcpNet ();

}

/*

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

*    函 数 名: TCPnetTest

*    功能说明: TCPnet应用

*    形    参: 无

*    返 回 值: 无

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

*/

void TCPnetTest(void)

{ 

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

     init_TcpNet ();

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

    bsp_StartAutoTimer(, );

     while ()

     {

          /* TCP轮询 */

          tcpnet_poll();

     }

}

Telnet用户接口文件的实现

KEIL官网有提供Telnet的接口文件,名为Telnet_uif.c文件。我们就是在这个文件上修改。具体修改后的代码如下:

/* Net_Config.c */

extern struct tcp_cfg   tcp_config;

extern struct tnet_cfg  tnet_config;

#define tcp_NumSocks    tcp_config.NumSocks

#define tcp_socket      tcp_config.Scb

#define tnet_EnAuth     tnet_config.EnAuth

#define tnet_auth_passw tnet_config.Passw

/* ANSI ESC Sequences for terminal control. */

#define CLS     "\033[2J"

#define TBLUE   "\033[37;44m"

#define TNORM   "\033[0m"

/* My structure of a Telnet U32 storage variable. This variable is private */

/* for each Telnet Session and is not altered by Telnet Server. It is only */

/* set to zero when tnet_process_cmd() is called for the first time.       */

typedef struct

{

     U8 id;

     U8 nmax;

     U8 idx;

} MY_BUF;

#define MYBUF(p)        ((MY_BUF *)p)

/* Local variables */

static U8 const tnet_header[] =

{

     CLS "\r\n"

     "        " TBLUE

     "*=============================================================*\r\n" TNORM

     "        " TBLUE

     "*               RL-TCPnet之Telnet服务器实验                   *\r\n" TNORM

     "        " TBLUE

     "*=============================================================*\r\n" TNORM

};

static U8 const tnet_help1[] =

{

     "\r\n\r\n"

     "    当前支持的命令:\r\n"

     "    ----------------------------\r\n"

     "    ledon      - 打开LED4\r\n"

     "    ledoff     - 关闭LED4\r\n"

     "    rinfo      - 显示远程设备的IP地址和MAC\r\n"

};

static U8 const tnet_help2[] =

{

     "    help, ?     - 显示帮助\r\n"

     "    bye         - 断开连接\r\n\r\n"

     "    <ESC>,<^C>  - ESC按键,断开连接\r\n"

     "    <BS>        - 回车函数,删除左侧字符\r\n"

     "    <UP><DOWN>  - 按键UP和DOWM,浏览历史命令\r\n"

};

/*----------------------------------------------------------------------------

 *      Telnet CallBack Functions

 *---------------------------------------------------------------------------*/

/*--------------------------- tnet_cbfunc -----------------------------------*/

U16 tnet_cbfunc (U8 code, U8 *buf, U16 buflen)

{

     /* This function is called by the Telnet Client to get formated system    */

     /* messages for different code values.                                    */

     /* Values for 'code':                                                     */

     /*    0 - initial header                                                  */

     /*    1 - prompt string                                                   */

     /*    2 - header for login only if authorization is enabled               */

     /*    3 - string 'Username' for login                                     */

     /*    4 - string 'Password' for login                                     */

     /*    5 - message 'Login incorrect'                                       */

     /*    6 - message 'Login timeout'                                         */

     /*    7 - Unsolicited messages from Server (ie. Basic Interpreter)        */

     U16 len = ;

     /* Make a reference to disable compiler warning. */

     buflen = buflen;

     switch (code)

     {

         case :

              /* Write initial header after login. */

              len = str_copy (buf, (U8 *)&tnet_header);

              break;

         case :

              /* Write a prompt string. */

              len = str_copy (buf, "\r\narmfly> ");

              break;

         case :

              /* Write Login header. */

              len = str_copy (buf, CLS "\r\nRL-TCPnet之Telnet服务器,"

                                          " 请登录...\r\n");

              break;

         case :

              /* Write 'username' prompt. */

              len = str_copy (buf, "\r\n用户名: ");

              break;

         case :

              /* Write 'Password' prompt. */

              len = str_copy (buf, "\r\n密  码: ");

              break;

         case :

              /* Write 'Login incorrect'.message. */

              len = str_copy (buf, "\r\n登录失败");

              break;

         case :

              /* Write 'Login Timeout' message. */

              len = str_copy (buf, "\r\n120秒无操作,退出登录\r\n");

              break;

     }

     return (len);

}

/*--------------------------- tnet_process_cmd ------------------------------*/

U16 tnet_process_cmd (U8 *cmd, U8 *buf, U16 buflen, U32 *pvar)

{

     /* This is a Telnet Client callback function to make a formatted output   */

     /* for 'stdout'. It returns the number of bytes written to the out buffer.*/

     /* Hi-bit of return value (len is or-ed with 0x8000) is a disconnect flag.*/

     /* Bit 14 (len is or-ed with 0x4000) is a repeat flag for the Tnet client.*/

     /* If this bit is set to 1, the system will call the 'tnet_process_cmd()' */

     /* again with parameter 'pvar' pointing to a 4-byte buffer. This buffer   */

     /* can be used for storing different status variables for this function.  */

     /* It is set to 0 by Telnet server on first call and is not altered by    */

     /* Telnet server for repeated calls. This function should NEVER write     */

     /* more than 'buflen' bytes to the buffer.                                */

     /* Parameters:                                                            */

     /*   cmd    - telnet received command string                              */

     /*   buf    - Telnet transmit buffer                                      */

     /*   buflen - length of this buffer (500-1400 bytes - depends on MSS)     */

     /*   pvar   - pointer to local storage buffer used for repeated loops     */

     /*            This is a U32 variable - size is 4 bytes. Value is:         */

     /*            - on 1st call = 0                                           */

     /*            - 2nd call    = as set by this function on first call       */

     REMOTEM rm;

     U16 len = ;

     switch (MYBUF(pvar)->id)

     {

         case :

              /* First call to this function, the value of '*pvar' is 0 */

              break;

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len | 0x4000);

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len |= 0x4000);

     }

     /* Simple Command line parser */

     len = strlen ((const char *)cmd);

     if (tnet_ccmp (cmd, "LEDON") == __TRUE)

     {

          /* 'LED4' command received */

         len = str_copy (buf,"\r\n=====>LED4 Lights ON");

         bsp_LedOn();

         return (len);

     }

     if (tnet_ccmp (cmd, "LEDOFF") == __TRUE)

     {

         /* 'LED4' command received */

          len = str_copy (buf,"\r\n=====>LED4 Lights OFF");

         bsp_LedOff();

         return (len);

     }

     if (tnet_ccmp (cmd, "BYE") == __TRUE)

     {

          /* 'BYE' command, send message and disconnect */

         len = str_copy (buf, "\r\nDisconnect...\r\n");

         /* Hi bit of return value is a disconnect flag */

         return (len | 0x8000);

     }

     if (tnet_ccmp (cmd, "RINFO") == __TRUE)

     {

          /* Display Remote Machine IP and MAC address. */

         tnet_get_info (&rm);

         len  = sprintf ((char *)buf,"\r\n Remote IP : %d.%d.%d.%d",

         rm.IpAdr[],rm.IpAdr[],rm.IpAdr[],rm.IpAdr[]);

         len += sprintf ((char *)(buf+len),

         "\r\n Remote MAC: %02X-%02X-%02X-%02X-%02X-%02X",

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[],

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[]);

         return (len);

     }

     if (tnet_ccmp (cmd, "HELP") == __TRUE || tnet_ccmp (cmd, "?") == __TRUE)

     {

          /* 'HELP' command, display help text */

         len = str_copy (buf,(U8 *)tnet_help1);   

         len += str_copy (buf+len,(U8 *)tnet_help2);

         return (len);

     }

     /* Unknown command, display message */

     len = str_copy  (buf, "\r\n==> Unknown Command: ");

     len += str_copy (buf+len, cmd);

     return (len);

}

32.7 实验例程说明(RTX)

32.7.1 STM32F407开发板实验

配套例子:

V5-1045_RL-TCPnet实验_Telnet应用(RTX)

实验目的:

  1. 学习RL-TCPnet的Telnet应用。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 程序中实现了一个Telnet服务器,用户名是admin,密码123456。

实验操作:

详见本章节32.5小节。

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

详见本章节32.3小节。

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

详见本章节32.4小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

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任务调试信息:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

程序设计:

任务栈大小分配:

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类型数据运算会出问题。

系统栈大小分配:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

RTX初始化:

/*

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

*    函 数 名: main

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

*    形    参: 无

*    返 回 值: 无

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

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 创建启动任务 */

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

                       ,                         /* 任务优先级 */

                       &AppTaskStartStk,          /* 任务栈 */

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

     while();

}

硬件外设初始化

硬件外设的初始化是在 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,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskUserIFStk,         /* 任务栈 */

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

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

                                        ,                       /* 任务优先级 */

                                        &AppTaskLEDStk,          /* 任务栈 */

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

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

                                           ,                         /* 任务优先级 */

                                           &AppTaskMsgProStk,         /* 任务栈 */

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

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

                                           ,                         /* 任务优先级 */

                                           &AppTaskTCPMainStk,         /* 任务栈 */

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

}

五个RTX任务的实现:

/*

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

*    函 数 名: AppTaskUserIF

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

*    形    参: 无

*    返 回 值: 无

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

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

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

    while()

    {

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下 */

                   case KEY_DOWN_K1:

                       printf("K1键按下 \r\n");       

                       break;  

                   /* K2键按下 */

                   case KEY_DOWN_K2:

                       printf("K2键按下 \r\n");

                       break;

                   /* K3键按下 */

                   case KEY_DOWN_K3:

                       printf("K3键按下 \r\n");

                       break;

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

                   default:                    

                       break;

              }

         }

         os_dly_wait();

     }

}

/*

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

*    函 数 名: AppTaskLED

*    功能说明: LED闪烁。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 2 

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

*/

__task void AppTaskLED(void)

{

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

     /* 设置延迟周期 */

     os_itv_set(usFrequency);

    while()

    {

         bsp_LedToggle();

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

         os_itv_wait();

    }

}

/*

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

*    函 数 名: AppTaskMsgPro

*    功能说明: 按键检测

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 3 

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

*/

__task void AppTaskMsgPro(void)

{

    while()

    {

         bsp_KeyScan();

         os_dly_wait();

    }

}

/*

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

*    函 数 名: AppTaskTCPMain

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

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

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

*/

__task void AppTaskTCPMain(void)

{

     while ()

     {

         TCPnetTest();

     }

}

/*

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

*    函 数 名: AppTaskStart

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

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

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

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

     /* 创建任务 */

     AppTaskCreate();

     os_itv_set ();

    while()

    {

         os_itv_wait ();

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

         timer_tick ();

         os_evt_set(0x0001, HandleTaskTCPMain);

    }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,此文件主要实现网络主函数main_TcpNet的调用。

#include "includes.h"

/*

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

*    函 数 名: TCPnetTest

*    功能说明: TCPent测试函数。

*    形    参: 无

*    返 回 值: 无

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

*/

void TCPnetTest(void)

{

     while ()

     {

         os_evt_wait_and(0x0001, 0xFFFF);

         while (main_TcpNet() == __TRUE);

     }

}

Telnet用户接口文件的实现

KEIL官网有提供Telnet的接口文件,名为Telnet_uif.c文件。我们就是在这个文件上修改。具体修改后的代码如下:

/* Net_Config.c */

extern struct tcp_cfg   tcp_config;

extern struct tnet_cfg  tnet_config;

#define tcp_NumSocks    tcp_config.NumSocks

#define tcp_socket      tcp_config.Scb

#define tnet_EnAuth     tnet_config.EnAuth

#define tnet_auth_passw tnet_config.Passw

/* ANSI ESC Sequences for terminal control. */

#define CLS     "\033[2J"

#define TBLUE   "\033[37;44m"

#define TNORM   "\033[0m"

/* My structure of a Telnet U32 storage variable. This variable is private */

/* for each Telnet Session and is not altered by Telnet Server. It is only */

/* set to zero when tnet_process_cmd() is called for the first time.       */

typedef struct

{

     U8 id;

     U8 nmax;

     U8 idx;

} MY_BUF;

#define MYBUF(p)        ((MY_BUF *)p)

/* Local variables */

static U8 const tnet_header[] =

{

     CLS "\r\n"

     "        " TBLUE

     "*=============================================================*\r\n" TNORM

     "        " TBLUE

     "*               RL-TCPnet之Telnet服务器实验                   *\r\n" TNORM

     "        " TBLUE

     "*=============================================================*\r\n" TNORM

};

static U8 const tnet_help1[] =

{

     "\r\n\r\n"

     "    当前支持的命令:\r\n"

     "    ----------------------------\r\n"

     "    ledon      - 打开LED4\r\n"

     "    ledoff     - 关闭LED4\r\n"

     "    rinfo      - 显示远程设备的IP地址和MAC\r\n"

};

static U8 const tnet_help2[] =

{

     "    help, ?     - 显示帮助\r\n"

     "    bye         - 断开连接\r\n\r\n"

     "    <ESC>,<^C>  - ESC按键,断开连接\r\n"

     "    <BS>        - 回车函数,删除左侧字符\r\n"

     "    <UP><DOWN>  - 按键UP和DOWM,浏览历史命令\r\n"

};

/*----------------------------------------------------------------------------

 *      Telnet CallBack Functions

 *---------------------------------------------------------------------------*/

/*--------------------------- tnet_cbfunc -----------------------------------*/

U16 tnet_cbfunc (U8 code, U8 *buf, U16 buflen)

{

     /* This function is called by the Telnet Client to get formated system    */

     /* messages for different code values.                                    */

     /* Values for 'code':                                                     */

     /*    0 - initial header                                                  */

     /*    1 - prompt string                                                   */

     /*    2 - header for login only if authorization is enabled               */

     /*    3 - string 'Username' for login                                     */

     /*    4 - string 'Password' for login                                     */

     /*    5 - message 'Login incorrect'                                       */

     /*    6 - message 'Login timeout'                                         */

     /*    7 - Unsolicited messages from Server (ie. Basic Interpreter)        */

     U16 len = ;

     /* Make a reference to disable compiler warning. */

     buflen = buflen;

     switch (code)

     {

         case :

              /* Write initial header after login. */

              len = str_copy (buf, (U8 *)&tnet_header);

              break;

         case :

              /* Write a prompt string. */

              len = str_copy (buf, "\r\narmfly> ");

              break;

         case :

              /* Write Login header. */

              len = str_copy (buf, CLS "\r\nRL-TCPnet之Telnet服务器,"

                                          " 请登录...\r\n");

              break;

         case :

              /* Write 'username' prompt. */

              len = str_copy (buf, "\r\n用户名: ");

              break;

         case :

              /* Write 'Password' prompt. */

              len = str_copy (buf, "\r\n密  码: ");

              break;

         case :

              /* Write 'Login incorrect'.message. */

              len = str_copy (buf, "\r\n登录失败");

              break;

         case :

              /* Write 'Login Timeout' message. */

              len = str_copy (buf, "\r\n120秒无操作,退出登录\r\n");

              break;

     }

     return (len);

}

/*--------------------------- tnet_process_cmd ------------------------------*/

U16 tnet_process_cmd (U8 *cmd, U8 *buf, U16 buflen, U32 *pvar)

{

     /* This is a Telnet Client callback function to make a formatted output   */

     /* for 'stdout'. It returns the number of bytes written to the out buffer.*/

     /* Hi-bit of return value (len is or-ed with 0x8000) is a disconnect flag.*/

     /* Bit 14 (len is or-ed with 0x4000) is a repeat flag for the Tnet client.*/

     /* If this bit is set to 1, the system will call the 'tnet_process_cmd()' */

     /* again with parameter 'pvar' pointing to a 4-byte buffer. This buffer   */

     /* can be used for storing different status variables for this function.  */

     /* It is set to 0 by Telnet server on first call and is not altered by    */

     /* Telnet server for repeated calls. This function should NEVER write     */

     /* more than 'buflen' bytes to the buffer.                                */

     /* Parameters:                                                            */

     /*   cmd    - telnet received command string                              */

     /*   buf    - Telnet transmit buffer                                      */

     /*   buflen - length of this buffer (500-1400 bytes - depends on MSS)     */

     /*   pvar   - pointer to local storage buffer used for repeated loops     */

     /*            This is a U32 variable - size is 4 bytes. Value is:         */

     /*            - on 1st call = 0                                           */

     /*            - 2nd call    = as set by this function on first call       */

     REMOTEM rm;

     U16 len = ;

     switch (MYBUF(pvar)->id)

     {

         case :

              /* First call to this function, the value of '*pvar' is 0 */

              break;

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len | 0x4000);

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len |= 0x4000);

     }

     /* Simple Command line parser */

     len = strlen ((const char *)cmd);

     if (tnet_ccmp (cmd, "LEDON") == __TRUE)

     {

          /* 'LED4' command received */

         len = str_copy (buf,"\r\n=====>LED4 Lights ON");

         bsp_LedOn();

         return (len);

     }

     if (tnet_ccmp (cmd, "LEDOFF") == __TRUE)

     {

         /* 'LED4' command received */

          len = str_copy (buf,"\r\n=====>LED4 Lights OFF");

         bsp_LedOff();

         return (len);

     }

     if (tnet_ccmp (cmd, "BYE") == __TRUE)

     {

          /* 'BYE' command, send message and disconnect */

         len = str_copy (buf, "\r\nDisconnect...\r\n");

         /* Hi bit of return value is a disconnect flag */

         return (len | 0x8000);

     }

     if (tnet_ccmp (cmd, "RINFO") == __TRUE)

     {

          /* Display Remote Machine IP and MAC address. */

         tnet_get_info (&rm);

         len  = sprintf ((char *)buf,"\r\n Remote IP : %d.%d.%d.%d",

         rm.IpAdr[],rm.IpAdr[],rm.IpAdr[],rm.IpAdr[]);

         len += sprintf ((char *)(buf+len),

         "\r\n Remote MAC: %02X-%02X-%02X-%02X-%02X-%02X",

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[],

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[]);

         return (len);

     }

     if (tnet_ccmp (cmd, "HELP") == __TRUE || tnet_ccmp (cmd, "?") == __TRUE)

     {

          /* 'HELP' command, display help text */

         len = str_copy (buf,(U8 *)tnet_help1);   

         len += str_copy (buf+len,(U8 *)tnet_help2);

         return (len);

     }

     /* Unknown command, display message */

     len = str_copy  (buf, "\r\n==> Unknown Command: ");

     len += str_copy (buf+len, cmd);

     return (len);

}

32.7.2 STM32F429开发板实验

配套例子:

V6-1045_RL-TCPnet实验_Telnet应用(RTX)

实验目的:

  1. 学习RL-TCPnet的Telnet应用。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. 程序中实现了一个Telnet服务器,用户名是admin,密码123456。

实验操作:

详见本章节32.5小节。

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

详见本章节32.3小节。

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

详见本章节32.4小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

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任务调试信息:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

程序设计:

任务栈大小分配:

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类型数据运算会出问题。

系统栈大小分配:

【RL-TCPnet网络教程】第32章    RL-TCPnet之Telnet服务器

RTX初始化:

/*

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

*    函 数 名: main

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

*    形    参: 无

*    返 回 值: 无

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

*/

int main (void)

{   

     /* 初始化外设 */

     bsp_Init();

     /* 创建启动任务 */

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

                       ,                         /* 任务优先级 */

                       &AppTaskStartStk,          /* 任务栈 */

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

     while();

}

硬件外设初始化

硬件外设的初始化是在 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,             /* 任务函数 */

                                           ,                         /* 任务优先级 */

                                           &AppTaskUserIFStk,         /* 任务栈 */

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

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

                                        ,                       /* 任务优先级 */

                                        &AppTaskLEDStk,          /* 任务栈 */

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

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

                                           ,                         /* 任务优先级 */

                                           &AppTaskMsgProStk,         /* 任务栈 */

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

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

                                           ,                         /* 任务优先级 */

                                           &AppTaskTCPMainStk,         /* 任务栈 */

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

}

五个RTX任务的实现:

/*

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

*    函 数 名: AppTaskUserIF

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

*    形    参: 无

*    返 回 值: 无

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

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

*/

__task void AppTaskUserIF(void)

{

     uint8_t ucKeyCode;

    while()

    {

         ucKeyCode = bsp_GetKey();

         if (ucKeyCode != KEY_NONE)

         {

              switch (ucKeyCode)

              {

                   /* K1键按下 */

                   case KEY_DOWN_K1:

                       printf("K1键按下 \r\n");       

                       break;  

                   /* K2键按下 */

                   case KEY_DOWN_K2:

                       printf("K2键按下 \r\n");

                       break;

                   /* K3键按下 */

                   case KEY_DOWN_K3:

                       printf("K3键按下 \r\n");

                       break;

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

                   default:                    

                       break;

              }

         }

         os_dly_wait();

     }

}

/*

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

*    函 数 名: AppTaskLED

*    功能说明: LED闪烁。

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 2 

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

*/

__task void AppTaskLED(void)

{

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

     /* 设置延迟周期 */

     os_itv_set(usFrequency);

    while()

    {

         bsp_LedToggle();

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

         os_itv_wait();

    }

}

/*

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

*    函 数 名: AppTaskMsgPro

*    功能说明: 按键检测

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 3 

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

*/

__task void AppTaskMsgPro(void)

{

    while()

    {

         bsp_KeyScan();

         os_dly_wait();

    }

}

/*

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

*    函 数 名: AppTaskTCPMain

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

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 4 

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

*/

__task void AppTaskTCPMain(void)

{

     while ()

     {

         TCPnetTest();

     }

}

/*

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

*    函 数 名: AppTaskStart

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

*    形    参: 无

*    返 回 值: 无

*   优 先 级: 5 

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

*/

__task void AppTaskStart(void)

{

     /* 初始化RL-TCPnet */

     init_TcpNet ();

     /* 创建任务 */

     AppTaskCreate();

     os_itv_set ();

    while()

    {

         os_itv_wait ();

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

         timer_tick ();

         os_evt_set(0x0001, HandleTaskTCPMain);

    }

}

RL-TCPnet功能测试

这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,此文件主要实现网络主函数main_TcpNet的调用。

#include "includes.h"

/*

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

*    函 数 名: TCPnetTest

*    功能说明: TCPent测试函数。

*    形    参: 无

*    返 回 值: 无

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

*/

void TCPnetTest(void)

{

     while ()

     {

         os_evt_wait_and(0x0001, 0xFFFF);

         while (main_TcpNet() == __TRUE);

     }

}

Telnet用户接口文件的实现

KEIL官网有提供Telnet的接口文件,名为Telnet_uif.c文件。我们就是在这个文件上修改。具体修改后的代码如下:

/* Net_Config.c */

extern struct tcp_cfg   tcp_config;

extern struct tnet_cfg  tnet_config;

#define tcp_NumSocks    tcp_config.NumSocks

#define tcp_socket      tcp_config.Scb

#define tnet_EnAuth     tnet_config.EnAuth

#define tnet_auth_passw tnet_config.Passw

/* ANSI ESC Sequences for terminal control. */

#define CLS     "\033[2J"

#define TBLUE   "\033[37;44m"

#define TNORM   "\033[0m"

/* My structure of a Telnet U32 storage variable. This variable is private */

/* for each Telnet Session and is not altered by Telnet Server. It is only */

/* set to zero when tnet_process_cmd() is called for the first time.       */

typedef struct

{

     U8 id;

     U8 nmax;

     U8 idx;

} MY_BUF;

#define MYBUF(p)        ((MY_BUF *)p)

/* Local variables */

static U8 const tnet_header[] =

{

     CLS "\r\n"

     "        " TBLUE

     "*=============================================================*\r\n" TNORM

     "        " TBLUE

     "*               RL-TCPnet之Telnet服务器实验                   *\r\n" TNORM

     "        " TBLUE

     "*=============================================================*\r\n" TNORM

};

static U8 const tnet_help1[] =

{

     "\r\n\r\n"

     "    当前支持的命令:\r\n"

     "    ----------------------------\r\n"

     "    ledon      - 打开LED4\r\n"

     "    ledoff     - 关闭LED4\r\n"

     "    rinfo      - 显示远程设备的IP地址和MAC\r\n"

};

static U8 const tnet_help2[] =

{

     "    help, ?     - 显示帮助\r\n"

     "    bye         - 断开连接\r\n\r\n"

     "    <ESC>,<^C>  - ESC按键,断开连接\r\n"

     "    <BS>        - 回车函数,删除左侧字符\r\n"

     "    <UP><DOWN>  - 按键UP和DOWM,浏览历史命令\r\n"

};

/*----------------------------------------------------------------------------

 *      Telnet CallBack Functions

 *---------------------------------------------------------------------------*/

/*--------------------------- tnet_cbfunc -----------------------------------*/

U16 tnet_cbfunc (U8 code, U8 *buf, U16 buflen)

{

     /* This function is called by the Telnet Client to get formated system    */

     /* messages for different code values.                                    */

     /* Values for 'code':                                                     */

     /*    0 - initial header                                                  */

     /*    1 - prompt string                                                   */

     /*    2 - header for login only if authorization is enabled               */

     /*    3 - string 'Username' for login                                     */

     /*    4 - string 'Password' for login                                     */

     /*    5 - message 'Login incorrect'                                       */

     /*    6 - message 'Login timeout'                                         */

     /*    7 - Unsolicited messages from Server (ie. Basic Interpreter)        */

     U16 len = ;

     /* Make a reference to disable compiler warning. */

     buflen = buflen;

     switch (code)

     {

         case :

              /* Write initial header after login. */

              len = str_copy (buf, (U8 *)&tnet_header);

              break;

         case :

              /* Write a prompt string. */

              len = str_copy (buf, "\r\narmfly> ");

              break;

         case :

              /* Write Login header. */

              len = str_copy (buf, CLS "\r\nRL-TCPnet之Telnet服务器,"

                                          " 请登录...\r\n");

              break;

         case :

              /* Write 'username' prompt. */

              len = str_copy (buf, "\r\n用户名: ");

              break;

         case :

              /* Write 'Password' prompt. */

              len = str_copy (buf, "\r\n密  码: ");

              break;

         case :

              /* Write 'Login incorrect'.message. */

              len = str_copy (buf, "\r\n登录失败");

              break;

         case :

              /* Write 'Login Timeout' message. */

              len = str_copy (buf, "\r\n120秒无操作,退出登录\r\n");

              break;

     }

     return (len);

}

/*--------------------------- tnet_process_cmd ------------------------------*/

U16 tnet_process_cmd (U8 *cmd, U8 *buf, U16 buflen, U32 *pvar)

{

     /* This is a Telnet Client callback function to make a formatted output   */

     /* for 'stdout'. It returns the number of bytes written to the out buffer.*/

     /* Hi-bit of return value (len is or-ed with 0x8000) is a disconnect flag.*/

     /* Bit 14 (len is or-ed with 0x4000) is a repeat flag for the Tnet client.*/

     /* If this bit is set to 1, the system will call the 'tnet_process_cmd()' */

     /* again with parameter 'pvar' pointing to a 4-byte buffer. This buffer   */

     /* can be used for storing different status variables for this function.  */

     /* It is set to 0 by Telnet server on first call and is not altered by    */

     /* Telnet server for repeated calls. This function should NEVER write     */

     /* more than 'buflen' bytes to the buffer.                                */

     /* Parameters:                                                            */

     /*   cmd    - telnet received command string                              */

     /*   buf    - Telnet transmit buffer                                      */

     /*   buflen - length of this buffer (500-1400 bytes - depends on MSS)     */

     /*   pvar   - pointer to local storage buffer used for repeated loops     */

     /*            This is a U32 variable - size is 4 bytes. Value is:         */

     /*            - on 1st call = 0                                           */

     /*            - 2nd call    = as set by this function on first call       */

     REMOTEM rm;

     U16 len = ;

     switch (MYBUF(pvar)->id)

     {

         case :

              /* First call to this function, the value of '*pvar' is 0 */

              break;

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len | 0x4000);

         case :

              /* Request a repeated call, bit 14 is a repeat flag. */

              return (len |= 0x4000);

     }

     /* Simple Command line parser */

     len = strlen ((const char *)cmd);

     if (tnet_ccmp (cmd, "LEDON") == __TRUE)

     {

          /* 'LED4' command received */

         len = str_copy (buf,"\r\n=====>LED4 Lights ON");

         bsp_LedOn();

         return (len);

     }

     if (tnet_ccmp (cmd, "LEDOFF") == __TRUE)

     {

         /* 'LED4' command received */

          len = str_copy (buf,"\r\n=====>LED4 Lights OFF");

         bsp_LedOff();

         return (len);

     }

     if (tnet_ccmp (cmd, "BYE") == __TRUE)

     {

          /* 'BYE' command, send message and disconnect */

         len = str_copy (buf, "\r\nDisconnect...\r\n");

         /* Hi bit of return value is a disconnect flag */

         return (len | 0x8000);

     }

     if (tnet_ccmp (cmd, "RINFO") == __TRUE)

     {

          /* Display Remote Machine IP and MAC address. */

         tnet_get_info (&rm);

         len  = sprintf ((char *)buf,"\r\n Remote IP : %d.%d.%d.%d",

         rm.IpAdr[],rm.IpAdr[],rm.IpAdr[],rm.IpAdr[]);

         len += sprintf ((char *)(buf+len),

         "\r\n Remote MAC: %02X-%02X-%02X-%02X-%02X-%02X",

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[],

         rm.HwAdr[],rm.HwAdr[],rm.HwAdr[]);

         return (len);

     }

     if (tnet_ccmp (cmd, "HELP") == __TRUE || tnet_ccmp (cmd, "?") == __TRUE)

     {

          /* 'HELP' command, display help text */

         len = str_copy (buf,(U8 *)tnet_help1);   

         len += str_copy (buf+len,(U8 *)tnet_help2);

         return (len);

     }

     /* Unknown command, display message */

     len = str_copy  (buf, "\r\n==> Unknown Command: ");

     len += str_copy (buf+len, cmd);

     return (len);

}

32.8 总结

本章节就为大家讲解这么多,其中Telnet的测试稍麻烦些,希望大家实际动手操作一遍,并将其熟练掌握。