【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器

第36章      RL-TCPnet之FTP服务器

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

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

36.1  初学者重要提示

36.2  FTP函数

36.3  FTP配置说明(Net_Config.c)

36.4  FTP调试说明(Net_Debug.c)

36.5  FTP访问方法和板子的操作步骤

36.6  实验例程说明(裸机)

36.7  实验例程说明(RTX)

36.8  总结

 

 

36.1  初学者重要提示

  1. 学习本章节前,务必保证已经学习了第35章的基础知识。
  2.  本章配套的例子是将开发板作为FTP服务器,使用开发板上面的SD卡作为服务器的存储介质。所以测试本章节的例子,务必要准备一个SD卡。
  3. 由于配套例子的文件系统是采用的RL-FlashFS,此文件系统的文件名仅支持ASCII字符,不支持中文,特别注意!
  4. 具体FTP服务器的访问方法在本章的36.5小节有详细说明。

36.2  FTP函数

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

  •   ftp_accept_host
  •   ftp_check_account
  •   ftp_fclose
  •   ftp_evt_notify
  •   ftp_fdelete
  •   ftp_ffind
  •   ftp_file_access
  •   ftp_fopen
  •   ftp_fread
  •   ftp_frename Server
  •   ftp_fwrite Server
  •   ftp_get_user_id
  •   ftpc_cbfunc
  •   ftpc_connect
  •   ftpc_fclose
  •   ftpc_fopen
  •   ftpc_fread
  •   ftpc_fwrite

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

 【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第1张图片

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

  •   ftp_fopen
  •   ftp_fclose
  •   ftp_fread
  •   ftp_fwrite
  •   ftp_fdelete
  •   ftp_frename
  •   ftp_ffind
  •   ftp_accept_host
  •   ftp_evt_notify

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

  1.   FTP的所有函数都不支持重入,也就是不支持多任务调用。
  2.   以ftp_开头的函数是用于FTP服务器的。
  3.  以ftpc_开头的函数是用于FTP客户端的。

36.2.1    函数ftp_fopen

函数原型:

void *ftp_fopen (

    U8* fname,    /* 文件名地址 */

    U8* mode);    /* 操作模式 */

 

函数描述:

函数ftp_fopen用于打开文件。此函数在MDK的安装目录中的FTP_uif.c文件里面,属于底层接口函数,用户要在此函数里面添加具体的操作。

  1. 第1个参数是文件名地址。
  2. 第2个参数是操作模式,可以是读操作或者写操作,具体支持的形参类型如下:【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第2张图片
  3. 返回值,打开文件成功的话,返回指向此文件的指针变量,否则返回NULL。

使用举例:

void *ftp_fopen (U8 *fname, U8 *mode) {

  /* 打开文件 */

  return (fopen ((const char *)fname, (const char *)mode));

}

 

36.2.2   函数ftp_fclose

函数原型:

void *ftp_fclose (

FILE* file);  /* 文件句柄地址 */

 

函数描述:

函数ftp_fclose用于关闭文件。此函数在MDK的安装目录中的FTP_uif.c文件里面,属于底层接口函数,用户要在此函数里面添加具体的操作。

  1. 第1个参数是要关闭的文件句柄地址。
  2. 返回值,实际上此函数无需返回任何数值,写成下面使用举例中的形式即可。

使用举例:

void ftp_fclose (void *file) {

  /* 关闭文件 */

  fclose (file);

}

 

36.2.3   函数ftp_fread

函数原型:

U16 ftp_fread (

    FILE* file,     /* 文件句柄地址 */

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

    U16   len );    /* 要读取的字节数 */

 

函数描述:

函数ftp_fread用于从文件中读出len个字节数据。此函数在MDK的安装目录中的FTP_uif.c文件里面,属于底层接口函数,用户要在此函数里面添加具体的操作。

  1.   第1个参数是要读取数据的文件句柄地址。
  2.   第2个参数是数据缓冲地址,用于存储读取出来的数据。
  3.  第3个参数是要读取出来的数据大小,单位字节。
  4.   返回值,返回从文件中实际读出的字节数。

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

  1. 设置读取函数时,必须设置指定大小的字节数。如果实际读出的字节数小于len,将停止读取并关闭文件,这种情况一般都是文件已经读取完毕。

使用举例:

U16 ftp_fread (void *file, U8 *buf, U16 len) {

 /* 读取len字节到buf中,当此函数的返回值,即实际读取的字节数小于len的时候,说明文件已经读取完毕,

文件将被关闭*/

  return (fread (buf, 1, len, file));

}

 

36.2.4   函数ftp_fwrite

函数原型:

U16 ftp_fwrite (

    FILE* file,     /* 文件句柄地址 */

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

U16   len );    /* 要写入的字节数 */

 

函数描述:

函数ftp_fwrite用于往文件中写入len个字节数据。此函数在MDK的安装目录中的FTP_uif.c文件里面,属于底层接口函数,用户要在此函数里面添加具体的操作。

  1.   第1个参数是要写入数据的文件句柄地址。
  2.   第2个参数是数据缓冲地址,存储要写入的数据。
  3.   第3个参数是要写入的数据大小,单位字节。
  4.   返回值,返回实际写入文件的字节数。

使用举例:

U16 ftp_fwrite (FILE *file, U8 *buf, U16 len) {

  /* 将buf中的len字节写入到文件中 */

  return (fwrite (buf, 1, len, file));

}

 

36.2.5   函数ftp_fdelete

函数原型:

BOOL ftp_fdelete (

    U8* fname );   /* 文件名 */

 

函数描述:

函数ftp_fdelete用于删除文件或文件夹。此函数在MDK的安装目录中的FTP_uif.c文件里面,属于底层接口函数,用户要在此函数里面添加具体的操作。

  1. 第1个参数要删除的文件或者文件夹名字。
  2. 返回值,返回__TRUE表示删除成功,返回__FALSE表示删除失败。

使用举例:

BOOL ftp_fdelete (U8 *fname) {

  /* 删除文件,返回__TRUE表示删除成功 */

  if (fdelete((char *)fname) == 0) {

    return (__TRUE);

  }

  /* 返回__FALSE表示删除失败 */

  return (__FALSE);

}

 

36.2.6   函数ftp_frename

函数原型:

BOOL ftp_frename (

    U8* fname,      /* 老文件名 */

U8* newn );     /* 新文件名 */

 

函数描述:

函数ftp_frename用于文件或者文件夹的重命名。要操作的文件或者文件夹名字fname必须存在,而新命名的名字newn必须是当前目录不存在的。

  1. 第1个参数是要替换的文件或者文件夹名字,即老的文件名。
  2. 第2个参数是新命名文件或者文件夹名字,即新的文件名。
  3. 返回值,返回__TRUE表示重命名成功,返回__FALSE表示重命名失败。

使用举例:

BOOL ftp_frename (U8 *fname, U8 *newn) {

  /* 文件重命名,返回__TRUE表示重命名成功 */

  if (frename((char *)fname, (char *)newn) == 0) {

    return (__TRUE);

  }

  /* 返回__FALSE表示重命名失败 */

  return (__FALSE);

}

 

36.2.7   函数ftp_ffind

函数原型:

U16 ftp_ffind (

    U8  code,      /* 请求类型 */

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

    U8* mask,      /* 检索的字符 */

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

 

函数描述:

函数ftp_ffind用于在文件系统目录中搜索与参数mask匹配的文件。匹配的文件信息会被存储到数据缓冲区buf中。输出数据必须以FTP文件夹列表格式进行存储。

  1.  第1个参数是请求类型:【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第3张图片
  2.  第2个参数是输出缓冲区地址,用于存储文件信息和数据。
  3.   第3个参数用于文件检索的字符,类似于我们在电脑端输入关键字符进行文件或者文件夹的检索。
  4.  第4个参数是输出缓冲区的大小,单位字节。
  5. 返回值,返回写入到输出缓冲区的字节数。

使用举例:

U16 ftp_ffind (U8 code, U8 *buf, U8 *mask, U16 len) {

  /* Find file names and other file information. */

  static FINFO info;

  U32 rlen,v;

  U8 *tp;

 

  if (code < 4) {

    /* First call to ffind, initialize the info. */

    info.fileID = 0;

  }

 

  rlen = 0;

next:

  if (ffind ((char *)mask, &info) == 0) {

    /* File found, print file information. */

    if (info.name[0] == '.') {

      if ((info.name[1] == 0) || (info.name[1] == '.' && info.name[2]) == 0) {

        /* Ignore the '.' and '..' folders. */

        goto next;

      }

    }

    switch (code) {

      case 0:

        /* Return file size as decimal number. */

        rlen = sprintf ((char *)buf,"%d\r\n", info.size);

        break;

 

      case 1:

        /* Return last-modified time in format "YYYYMMDDhhmmss". */

        rlen  = sprintf ((char *)buf,"%04d%02d%02d",

                         info.time.year, info.time.mon, info.time.day);

        rlen += sprintf ((char *)&buf[rlen],"%02d%02d%02d\r\n",

                         info.time.hr, info.time.min, info.time.sec);

        break;

 

      case 2:

      case 4:

        /* List file names only. */

        rlen = sprintf ((char *)buf,"%s\r\n", info.name);

        break;

 

      case 3:

      case 5:

        /* List directory in extended format. */

        rlen  = sprintf ((char *)buf,"%02d-%02d-%02d",

                         info.time.mon, info.time.day, info.time.year%100);

        /* Convert time to "AM/PM" format. */

        v = info.time.hr % 12;

        if (v == 0) v = 12;

        if (info.time.hr < 12) tp = "AM";

        else                   tp = "PM";

        rlen += sprintf ((char *)&buf[rlen],"  %02d:%02d%s",v,info.time.min,tp);

        if (info.attrib & ATTR_DIRECTORY) {

          rlen += sprintf ((char *)&buf[rlen],"%-21s","       ");

        }

        else {

          rlen += sprintf ((char *)&buf[rlen],"%21d", info.size);

        }

        rlen += sprintf ((char *)&buf[rlen]," %s\r\n", info.name);

        break;

    }

  }

  return (rlen);

}

 

36.2.8   函数ftp_accept_host

函数原型:

BOOL ftp_accept_host (

    U8* rem_ip,      /* 远程设备IP地址 */

U16 rem_port );  /* 远程设备端口号 */

 

函数描述:

函数ftp_accept_host用于设置是否接受远程连接,用户可以通过此函数选择允许哪些设备可以连接,哪些不可以连接。

  1. 第1个参数是远程设备的IP地址。
  2. 第2个参数是远程设备的端口号。
  3. 返回值,返回__TRUE表示允许此远程连接,返回__FALSE表示不允许此远程连接。

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

  1. 此函数是可选的,如果大家在工程中没有写这个函数,RL-TCPnet库会调用默认的函数,允许所有的连接请求,如果在工程中写了此函数,会执行新写的这个函数。

使用举例:

BOOL ftp_accept_host (U8 *rem_ip, U16 rem_port) {

 

  if (rem_ip[0] == 192  &&

      rem_ip[1] == 168  &&

      rem_ip[2] == 1    &&

      rem_ip[3] == 1) {

    /* 接受此连接. */

    return (__TRUE);

  }

  /* 拒绝此连接 */

  return (__FALSE);

}

 

36.2.9   函数ftp_evt_notify

函数原型:

void ftp_evt_notify (U8 evt); /* 事件类型 */

 

函数描述:

函数ftp_evt_notify用于通知用户应用程序:FTP服务器中的事件。通过这些事件消息可以做很多有用的事情,比如服务器的固件升级,接收到消息FTP_EVT_LOGOUT后,表示服务器已经接收到固件,可以更新固件了。

  1. 第1个参数是FTP服务器的事件类型。

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

  1. 此函数是可选的,一般正常的FTP服务器操作无需此函数。

使用举例:

void ftp_evt_notify (U8 evt) {

  /* Notify the user application about events in FTP server.*/

 

  switch (evt) {

    case FTP_EVT_LOGIN:

      /* User logged in, FTP session is busy. */

      break;

 

    case FTP_EVT_LOGOUT;

      /* User logged out, session is idle. */

      break;

 

    case FTP_EVT_LOGFAIL:

      /* User login failed (invalid credentials). */

      break;

 

    case FTP_EVT_DOWNLOAD:

      /* File download ended. */

      break;

 

    case FTP_EVT_UPLOAD:

      /* File upload ended. */

      break;

 

    case FTP_EVT_DELETE:

      /* File deleted. */

      break;

 

    case FTP_EVT_RENAME:

      /* File or directory renamed. */

      break;

 

    case FTP_EVT_MKDIR:

      /* Directory created. */

      break;

 

    case FTP_EVT_RMDIR:

      /* Directory removed. */

      break;

 

    case FTP_EVT_ERRLOCAL:

      /* Local file operation error. */

      break;

 

    case FTP_EVT_DENIED:

      /* Requested file operation denied. */

      break;

 

    case FTP_EVT_ERROR:

      /* Generic file operation or protocol error. */

      break;

  }

 

36.3 FTP配置说明(Net_Config.c)

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

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

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第4张图片 

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。

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第5张图片 

 Ethernet Network Interface

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

(1)  MAC Address

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

(2)IP Address

IP地址。

(3)Subnet mask

子网掩码。

(4) Default Gateway

默认网关。

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第6张图片 

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数量,这里配置了5个。

范围1 – 20。

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第7张图片 

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网络教程】第36章 RL-TCPnet之FTP服务器_第8张图片 

FTP Server

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

(1)  Number of FTP Sessions

同时可以连接的会话个数,即可以连接的FTP客户端个数。

范围1-10。

(2)Port Number

FTP服务器的监听端口号。

范围1-65535。

(3) Welcome Message

登录FTP服务器后的欢迎消息,如果用户没有配置此选项,将使用默认的欢迎消息,如果配置了,将使用用户配置的。

(4) Idle Session Timeout in seconds

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

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

范围0-3600,单位秒。

(5) Enable User Authentication

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

Authentication Username 是用户名。

Authentication Password 是用户密码。

 

36.4 FTP调试说明(Net_Debug.c)

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

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

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第9张图片 

Print Time Stamp

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

其它所有的选项

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

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第10张图片 

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

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

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

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

 

36.5 FTP服务器的访问方法和板子的操作步骤

我们这里介绍三种访问FTP服务器的方法,对于本实验实现的FTP服务器,支持的操作如下:

  • 支持文件的上传和下载。
  •  支持文件和文件夹的删除。
  • 支持文件和文件夹的重命名。
  • 支持新建文件和文件夹。

另外,本章节配套的例子是用开发板做FTP服务器,SD卡做为服务器的存储介质,所以务必准备好一个SD卡插到开发板上面。

最后,特别注意一点,我们使用的是RL-FlashFS文件系统,此文件系统的文件名仅支持ASCII字符,不支持中文,对于中文名的文件夹或者文件是无法操作的。

 

36.5.1 使用FTP客户端软件访问

  • 第1步:下载FTP客户端软件

FTP软件推荐采用FileZilla Client,下载地址:http://bbs.armfly.com/read.php?tid=32486 。

  • 第2步:下载后,安装此软件,安装完毕后,打开软件的效果如下

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第11张图片 

  • 第3步:输入地址,账号和端口号

主机:armfly (备注,因为使能了NetBIOS Name,这里可以直接写armfly或者实际的IP地址)。

用户名:admin

密码:123456

端口:21

 

如果开发板已经上电,就可以点击快速连接进行访问了。

  • 第4步:点击“快速连接”按钮就可以访问了

访问后效果如下:

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第12张图片 

访问FTP服务器成功后,这个客户端软件操作也比较简单,大家摸索下就熟练了,文件的上传下载、文件和文件夹的创建、删除、重命名等都支持。这里主要说上传和下载文件的方法。

  • 第5步:文件的上传方法

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第13张图片 

点击了上传后,状态栏里面会有上传速度和上传进度:

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第14张图片 

  •  第6步:文件的下载方法

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第15张图片 

点击了下载后,状态栏里面会有下载速度和下载进度:

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第16张图片 

36.5.2 “网络”地址栏输入地址访问

这种方法访问也非常方便,大家打开电脑端的“网络”或者“计算机”图标,早期的XP系统是叫“我的电脑”和“网上邻居”。

 

  • 第1步:在地址栏输入ftp://armfly 【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第17张图片
  • 第2步:输入地址后,点击键盘回车键,弹出如下窗口 【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第18张图片
  • 第3步:点击登录按钮后,就可以看到FTP服务器中SD卡的内容了 【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第19张图片

剩下的操作就很简单了,电脑端怎么操作文件的,这里就怎么操作(特别注意,不支持中文文件名,仅支持ASCII字符的文件名)。大家可以实际测试下文件的复制粘贴、文件和文件夹的创建、删除、重命名等功能。

另外这种访问方式,有一种情况要注意,如果FTP服务器中有某个文件了,重复上传会弹出一个窗口,提示正在计算上传文件所需时间,这个窗口不用管它,直接点击“是”或者“全是”按钮即可。

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第20张图片 

36.5.3 浏览器访问

浏览器访问FTP服务器也比较简单,这里以谷歌浏览器为例,按照下图所标注的四步即可登录:

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第21张图片 

登录成功后的界面如下:

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第22张图片 

不过这种方式仅支持文件的浏览,其它的任何操作均不支持。

 

36.6 实验例程说明(RTX)

36.6.1 STM32F407开发板实验

配套例子:

V5-1051_RL-TCPnet实验_FTP服务器(RTX)

实验目的:

  1. 学习RL-TCPnet的FTP服务器实现。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. FTP服务器的存储器是采用的SD卡,所以测试本例子前务必准备好一个SD卡并插上。
  3. 文件系统是采用的RL-FlashFS,此文件系统的文件名仅支持ASCII字符,不支持中文,特别注意!
  4. FTP服务器的访问方法在本实例配套教程里面有详细讲解。可以使用FTP客户端软件访问,也可以在“我的电脑”地址栏输入ftp://armfly进行访问。
  5. FTP服务器的用户名admin,密码123456。

实验操作:

详见本章节36.5小节。

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

详见本章节36.3小节。

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

详见本章节36.4小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第23张图片 

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网络教程】第36章 RL-TCPnet之FTP服务器_第24张图片 

程序设计:

任务栈大小分配:

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

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

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

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

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

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

系统栈大小分配:

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第25张图片 

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指示灯端口 */

 

     MountSD();        /* 挂载SD卡 */

}

 

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键按下 */

                   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(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 ();

         os_evt_set(0x0001, HandleTaskTCPMain);

    }

}

 

RL-TCPnet功能测试

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

#include "includes.h"

 

 

 

/*

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

*    函 数 名: TCPnetTest

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

*    形    参: 无

*    返 回 值: 无

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

*/

void TCPnetTest(void)

{

    

     while (1)

     {

         os_evt_wait_or(0x0001, 0xFFFF); 

        

         /* RL-TCPnet主处理函数 */       

         while (main_TcpNet() == __TRUE);

     }

}

 

FTP用户接口文件的实现

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

#include 

#include 

#include 

 

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

 * FTP Server File Access Functions

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

 

/*--------------------------- ftp_fopen -------------------------------------*/

 

void *ftp_fopen (U8 *fname, U8 *mode) {

  /* Open file 'fname' for reading or writing. Return file handle. */

  return (fopen ((const char *)fname, (const char *)mode));

}

 

 

/*--------------------------- ftp_fclose ------------------------------------*/

 

void ftp_fclose (void *file) {

  /* Close the file opened for reading or writing. */

  fclose (file);

}

 

 

/*--------------------------- ftp_fread -------------------------------------*/

 

U16 ftp_fread (void *file, U8 *buf, U16 len) {

  /* Read 'len' bytes from file to buffer 'buf'. The file will be closed, */

  /* when the number of bytes read is less than 'len'. */

  return (fread (buf, 1, len, file));

}

 

 

/*--------------------------- ftp_fwrite ------------------------------------*/

 

U16 ftp_fwrite (void *file, U8 *buf, U16 len) {

  /* Write 'len' bytes from buffer 'buf' to a file. */

  return (fwrite (buf, 1, len, file));

}

 

 

/*--------------------------- ftp_fdelete -----------------------------------*/

 

BOOL ftp_fdelete (U8 *fname) {

  /* Delete a file, return __TRUE on success. */

  if (fdelete((char *)fname) == 0) {

    return (__TRUE);

  }

  return (__FALSE);

}

 

 

/*--------------------------- ftp_frename -----------------------------------*/

 

BOOL ftp_frename (U8 *fname, U8 *newn) {

  /* Rename a file, return __TRUE on success. */

  if (frename((char *)fname, (char *)newn) == 0) {

    return (__TRUE);

  }

  return (__FALSE);

}

 

 

/*--------------------------- ftp_ffind -------------------------------------*/

 

U16 ftp_ffind (U8 code, U8 *buf, U8 *mask, U16 buflen) {

  /* This function is called by the FTP server to find file names and other */

  /* file information. The output data is stored in ascii format to output  */

  /* buffer 'buf' Parameter 'code' specifies requested file information.    */

  /* Values for 'code':                                                     */

  /*    0 - read file size                                                  */

  /*    1 - read last-modified time of a file                               */

  /*    2 - list file names only (first call)                               */

  /*    3 - list file directory in extended format (first call)             */

  /*    4 - list file names only (repeated call)                            */

  /*    5 - list file directory in extended format (repeated call)          */

  static FINFO info;

  U32 rlen,v;

  U8 *tp;

 

  if (code < 4) {

    /* First call to ffind, initialize the info. */

    info.fileID = 0;

  }

 

  rlen = 0;

next:

  if (ffind ((char *)mask, &info) == 0) {

    /* File found, print file information. */

    if (info.name[0] == '.') {

      if ((info.name[1] == 0) || (info.name[1] == '.' && info.name[2] == 0)) {

        /* Ignore the '.' and '..' folders. */

        goto next;

      }

    }

    switch (code) {

      case 0:

        /* Return file size as decimal number. */

        rlen = sprintf ((char *)buf,"%d\r\n", info.size);

        break;

 

      case 1:

        /* Return last-modified time in format "YYYYMMDDhhmmss". */

        rlen  = sprintf ((char *)buf,"%04d%02d%02d",

                         info.time.year, info.time.mon, info.time.day);

        rlen += sprintf ((char *)&buf[rlen],"%02d%02d%02d\r\n",

                         info.time.hr, info.time.min, info.time.sec);

        break;

 

      case 2:

      case 4:

        /* List file names only. */

        rlen = sprintf ((char *)buf,"%s\r\n", info.name);

        break;

 

      case 3:

      case 5:

        /* List directory in extended format. */

        rlen  = sprintf ((char *)buf,"%02d-%02d-%02d",

                         info.time.mon, info.time.day, info.time.year%100);

        /* Convert time to "AM/PM" format. */

        v = info.time.hr % 12;

        if (v == 0) v = 12;

        if (info.time.hr < 12) tp = "AM";

        else                   tp = "PM";

        rlen += sprintf ((char *)&buf[rlen],"  %02d:%02d%s",v,info.time.min,tp);

        if (info.attrib & ATTR_DIRECTORY) {

          rlen += sprintf ((char *)&buf[rlen],"%-21s","       ");

        }

        else {

          rlen += sprintf ((char *)&buf[rlen],"%21d", info.size);

        }

        rlen += sprintf ((char *)&buf[rlen]," %s\r\n", info.name);

        break;

    }

  }

  return (rlen);

}

 

 

/*--------------------------- ftp_accept_host -------------------------------*/

#if 0

BOOL ftp_accept_host (U8 *rem_ip, U16 rem_port) {

  /* This function checks if a connection from remote host is accepted or  */

  /* not. If this function is missing, all remote hosts are accepted.      */

 

  if (rem_ip[0] == 192  &&

      rem_ip[1] == 168  &&

      rem_ip[2] == 1    &&

      rem_ip[3] == 1) {

    /* Accept a connection. */

    return (__TRUE);

  }

  /* Deny a connection. */

  return (__FALSE);

}

#endif

 

 

/*--------------------------- ftp_evt_notify --------------------------------*/

#if 0

void ftp_evt_notify (U8 evt) {

  /* This function notifies the user application about events in FTP server.*/

 

  switch (evt) {

    case FTP_EVT_LOGIN:

      /* User logged in, FTP session is busy. */

      break;

 

    case FTP_EVT_LOGOUT;

      /* User logged out, session is idle. */

      break;

 

    case FTP_EVT_LOGFAIL:

      /* User login failed (invalid credentials). */

      break;

 

    case FTP_EVT_DOWNLOAD:

      /* File download ended. */

      break;

 

    case FTP_EVT_UPLOAD:

      /* File upload ended. */

      break;

 

    case FTP_EVT_DELETE:

      /* File deleted. */

      break;

 

    case FTP_EVT_RENAME:

      /* File or directory renamed. */

      break;

 

    case FTP_EVT_MKDIR:

      /* Directory created. */

      break;

 

    case FTP_EVT_RMDIR:

      /* Directory removed. */

      break;

 

    case FTP_EVT_ERRLOCAL:

      /* Local file operation error. */

      break;

 

    case FTP_EVT_DENIED:

      /* Requested file operation denied. */

      break;

 

    case FTP_EVT_ERROR:

      /* Generic file operation or protocol error. */

      break;

  }

}

#endif

 

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

 * end of file

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

 

36.6.2 STM32F429开发板实验

配套例子:

V6-1051_RL-TCPnet实验_FTP服务器(RTX)

实验目的:

  1. 学习RL-TCPnet的FTP服务器实现。

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. FTP服务器的存储器是采用的SD卡,所以测试本例子前务必准备好一个SD卡并插上。
  3. 文件系统是采用的RL-FlashFS,此文件系统的文件名仅支持ASCII字符,不支持中文,特别注意!
  4. FTP服务器的访问方法在本实例配套教程里面有详细讲解。可以使用FTP客户端软件访问,也可以在“我的电脑”地址栏输入ftp://armfly进行访问。
  5. FTP服务器的用户名admin,密码123456。

实验操作:

详见本章节36.5小节。

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

详见本章节36.3小节。

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

详见本章节36.4小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第26张图片 

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网络教程】第36章 RL-TCPnet之FTP服务器_第27张图片 

程序设计:

  任务栈大小分配:

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

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

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

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

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

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

  系统栈大小分配:

【RL-TCPnet网络教程】第36章 RL-TCPnet之FTP服务器_第28张图片 

  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指示灯端口 */

 

     MountSD();          /* 挂载SD卡 */

}

 

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键按下 */

                   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(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 ();

         os_evt_set(0x0001, HandleTaskTCPMain);

    }

}

 

RL-TCPnet功能测试

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

#include "includes.h"

 

 

 

/*

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

*    函 数 名: TCPnetTest

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

*    形    参: 无

*    返 回 值: 无

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

*/

void TCPnetTest(void)

{

    

     while (1)

     {

         os_evt_wait_or(0x0001, 0xFFFF); 

        

         /* RL-TCPnet主处理函数 */       

         while (main_TcpNet() == __TRUE);

     }

}

 

FTP用户接口文件的实现

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

#include 

#include 

#include 

 

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

 * FTP Server File Access Functions

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

 

/*--------------------------- ftp_fopen -------------------------------------*/

 

void *ftp_fopen (U8 *fname, U8 *mode) {

  /* Open file 'fname' for reading or writing. Return file handle. */

  return (fopen ((const char *)fname, (const char *)mode));

}

 

 

/*--------------------------- ftp_fclose ------------------------------------*/

 

void ftp_fclose (void *file) {

  /* Close the file opened for reading or writing. */

  fclose (file);

}

 

 

/*--------------------------- ftp_fread -------------------------------------*/

 

U16 ftp_fread (void *file, U8 *buf, U16 len) {

  /* Read 'len' bytes from file to buffer 'buf'. The file will be closed, */

  /* when the number of bytes read is less than 'len'. */

  return (fread (buf, 1, len, file));

}

 

 

/*--------------------------- ftp_fwrite ------------------------------------*/

 

U16 ftp_fwrite (void *file, U8 *buf, U16 len) {

  /* Write 'len' bytes from buffer 'buf' to a file. */

  return (fwrite (buf, 1, len, file));

}

 

 

/*--------------------------- ftp_fdelete -----------------------------------*/

 

BOOL ftp_fdelete (U8 *fname) {

  /* Delete a file, return __TRUE on success. */

  if (fdelete((char *)fname) == 0) {

    return (__TRUE);

  }

  return (__FALSE);

}

 

 

/*--------------------------- ftp_frename -----------------------------------*/

 

BOOL ftp_frename (U8 *fname, U8 *newn) {

  /* Rename a file, return __TRUE on success. */

  if (frename((char *)fname, (char *)newn) == 0) {

    return (__TRUE);

  }

  return (__FALSE);

}

 

 

/*--------------------------- ftp_ffind -------------------------------------*/

 

U16 ftp_ffind (U8 code, U8 *buf, U8 *mask, U16 buflen) {

  /* This function is called by the FTP server to find file names and other */

  /* file information. The output data is stored in ascii format to output  */

  /* buffer 'buf' Parameter 'code' specifies requested file information.    */

  /* Values for 'code':                                                     */

  /*    0 - read file size                                                  */

  /*    1 - read last-modified time of a file                               */

  /*    2 - list file names only (first call)                               */

  /*    3 - list file directory in extended format (first call)             */

  /*    4 - list file names only (repeated call)                            */

  /*    5 - list file directory in extended format (repeated call)          */

  static FINFO info;

  U32 rlen,v;

  U8 *tp;

 

  if (code < 4) {

    /* First call to ffind, initialize the info. */

    info.fileID = 0;

  }

 

  rlen = 0;

next:

  if (ffind ((char *)mask, &info) == 0) {

    /* File found, print file information. */

    if (info.name[0] == '.') {

      if ((info.name[1] == 0) || (info.name[1] == '.' && info.name[2] == 0)) {

        /* Ignore the '.' and '..' folders. */

        goto next;

      }

    }

    switch (code) {

      case 0:

        /* Return file size as decimal number. */

        rlen = sprintf ((char *)buf,"%d\r\n", info.size);

        break;

 

      case 1:

        /* Return last-modified time in format "YYYYMMDDhhmmss". */

        rlen  = sprintf ((char *)buf,"%04d%02d%02d",

                         info.time.year, info.time.mon, info.time.day);

        rlen += sprintf ((char *)&buf[rlen],"%02d%02d%02d\r\n",

                         info.time.hr, info.time.min, info.time.sec);

        break;

 

      case 2:

      case 4:

        /* List file names only. */

        rlen = sprintf ((char *)buf,"%s\r\n", info.name);

        break;

 

      case 3:

      case 5:

        /* List directory in extended format. */

        rlen  = sprintf ((char *)buf,"%02d-%02d-%02d",

                         info.time.mon, info.time.day, info.time.year%100);

        /* Convert time to "AM/PM" format. */

        v = info.time.hr % 12;

        if (v == 0) v = 12;

        if (info.time.hr < 12) tp = "AM";

        else                   tp = "PM";

        rlen += sprintf ((char *)&buf[rlen],"  %02d:%02d%s",v,info.time.min,tp);

        if (info.attrib & ATTR_DIRECTORY) {

          rlen += sprintf ((char *)&buf[rlen],"%-21s","       ");

        }

        else {

          rlen += sprintf ((char *)&buf[rlen],"%21d", info.size);

        }

        rlen += sprintf ((char *)&buf[rlen]," %s\r\n", info.name);

        break;

    }

  }

  return (rlen);

}

 

 

/*--------------------------- ftp_accept_host -------------------------------*/

#if 0

BOOL ftp_accept_host (U8 *rem_ip, U16 rem_port) {

  /* This function checks if a connection from remote host is accepted or  */

  /* not. If this function is missing, all remote hosts are accepted.      */

 

  if (rem_ip[0] == 192  &&

      rem_ip[1] == 168  &&

      rem_ip[2] == 1    &&

      rem_ip[3] == 1) {

    /* Accept a connection. */

    return (__TRUE);

  }

  /* Deny a connection. */

  return (__FALSE);

}

#endif

 

 

/*--------------------------- ftp_evt_notify --------------------------------*/

#if 0

void ftp_evt_notify (U8 evt) {

  /* This function notifies the user application about events in FTP server.*/

 

  switch (evt) {

    case FTP_EVT_LOGIN:

      /* User logged in, FTP session is busy. */

      break;

 

    case FTP_EVT_LOGOUT;

      /* User logged out, session is idle. */

      break;

 

    case FTP_EVT_LOGFAIL:

      /* User login failed (invalid credentials). */

      break;

 

    case FTP_EVT_DOWNLOAD:

      /* File download ended. */

      break;

 

    case FTP_EVT_UPLOAD:

      /* File upload ended. */

      break;

 

    case FTP_EVT_DELETE:

      /* File deleted. */

      break;

 

    case FTP_EVT_RENAME:

      /* File or directory renamed. */

      break;

 

    case FTP_EVT_MKDIR:

      /* Directory created. */

      break;

 

    case FTP_EVT_RMDIR:

      /* Directory removed. */

      break;

 

    case FTP_EVT_ERRLOCAL:

      /* Local file operation error. */

      break;

 

    case FTP_EVT_DENIED:

      /* Requested file operation denied. */

      break;

 

    case FTP_EVT_ERROR:

      /* Generic file operation or protocol error. */

      break;

  }

}

#endif

 

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

 * end of file

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

 

36.7 总结

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

 

转载于:https://www.cnblogs.com/armfly/p/9585389.html

你可能感兴趣的:(嵌入式,php,c/c++)