标签:netbios 例子 ret ipconfig rmdir命令 调试 寄存器 字节 左右
本章节为大家讲解RL-TCPnet的FTP客户端应用,学习本章节前,务必要优先学习第35章的FTP基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。
本章教程含STM32F407开发板和STM32F429开发板。
37.1 初学者重要提示
37.2 FTP函数
37.3 FTP配置说明(Net_Config.c)
37.4 FTP调试说明(Net_Debug.c)
37.5 FTP访问方法和板子的操作步骤
37.6 实验例程说明(裸机)
37.7 实验例程说明(RTX)
37.8 总结
使用如下18个函数可以实现RL-TCPnet的FTP:
关于这18个函数的讲解及其使用方法可以看教程第 3 章 3.4 小节里面说的参考资料 rlarm.chm 文件:
这里我们重点的说以下6个函数,因为本章节配套的例子使用的是这6个函数:
关于这些函数注意以下三点:
函数原型:
void *ftpc_fopen ( U8* mode); /* 操作模式 */
函数描述:
函数ftpc_fopen用于打开本地文件(FTP客户端的文件)。此函数在MDK的安装目录中的FTPC_uif.c文件里面,属于底层接口函数,用户要在此函数里面添加具体的操作。
使用这个函数要注意以下问题:
使用举例:
void *ftpc_fopen (U8 *mode) { /* 打开文件,如果返回NULL,表示打开失败 */ return (fopen (LOCAL_FILE, (char *)mode)); }
函数原型:
void *ftpc_fclose ( FILE* file); /* 文件句柄地址 */
函数描述:
函数ftpc_fclose用于关闭文件。此函数在MDK的安装目录中的FTPC_uif.c文件里面,属于底层接口函数,用户要在此函数里面添加具体的操作。
使用这个函数要注意以下问题:
使用举例:
void ftpc_fclose (void *file) { /* 关闭文件 */ fclose (file); }
函数原型:
U16 ftpc_fread ( FILE* file, /* 文件句柄地址 */ U8* buf, /* 数据缓冲地址 */ U16 len ); /* 要读取的字节数 */
函数描述:
函数ftpc_fread用于从文件中读出len个字节数据。此函数在MDK的安装目录中的FTPC_uif.c文件里面,属于底层接口函数,用户要在此函数里面添加具体的操作。
使用这个函数要注意以下问题:
使用举例:
U16 ftpc_fread (void *file, U8 *buf, U16 len) { /* 读取len字节到buf中,返回值是实际读取的字节数,返回0的话,表示文件已经读取完毕,文件将被关闭 */ return (fread (buf, 1, len, file)); }
函数原型:
U16 ftpc_fwrite ( FILE* file, /* 文件句柄地址 */ U8* buf, /* 数据缓冲地址 */ U16 len ); /* 要写入的字节数 */
函数描述:
函数ftpc_fwrite用于往文件中写入len个字节数据。此函数在MDK的安装目录中的FTPC_uif.c文件里面,属于底层接口函数,用户要在此函数里面添加具体的操作。
使用这个函数要注意以下问题:
使用举例:
U16 ftpc_fwrite (void *file, U8 *buf, U16 len) { /* 将buf中的len字节写入到文件中,如果返回数值(实际写入的字节数)不等于len,数据传输将终止 */ return (fwrite (buf, 1, len, file)); }
函数原型:
U16 ftpc_cbfunc (
U8 code, /* 消息类型 */
U8* buf, /* 输出缓冲区地址 */
U16 buflen ); /* 输出缓冲区大小 */
函数描述:
函数ftpc_cbfunc是FTP客户端的回调函数,用于为FTP客户端会话提供额外参数,如登录FTP服务器的用户名和密码、本地和远程文件名等。FTP客户端多次调用此函数才可完成文件操作请求。
使用这个函数要注意以下问题:
使用举例:
#define FTPC_USERNAME "armfly" /* 远程FTP服务器的账号 */ #define FTPC_PASSWORD "123456" /* 远程FTP服务器的密码 */ #define FTPC_PATH "" /* 要访问的子文件夹,FTP电脑端设置后是没有路径的,填空 */ #define FTPC_FILENAME "server.pdf" /* 要访问的文件 */ #define FTPC_NEWNAME "renamed.pdf" /* 使用这里定义的文件字重命名宏定义FTPC_FILENAME的文件名 */ #define FTPC_DIRNAME "New_Folder" /* 创建名字为New_Folder的文件夹或者删除名字为New_Folder的文件夹 */ #define FTPC_LISTNAME "*" /* 浏览宏定义FTPC_PATH路径下所有文件 */ #define LOCAL_FILE "client.pdf" /* 开发板SD卡里面要上传的文件,或者从FTP服务器下载文件后, 文件会被设置成此名字 */ U16 ftpc_cbfunc (U8 code, U8 *buf, U16 buflen) { /* This function is called by the FTP client to get parameters. It returns*/ /* the number of bytes written to buffer ‘buf‘. This function should NEVER*/ /* write more than ‘buflen‘ bytes to this buffer. */ /* Parameters: */ /* code - function code with following values: */ /* 0 = Username for FTP authentication */ /* 1 = Password for FTP authentication */ /* 2 = Working directory path for all commands */ /* 3 = Filename for PUT, GET, APPEND, DELETE, RENAME command */ /* 4 - New filename for RENAME command */ /* 5 - Directory name for MKDIR, RMDIR command */ /* 6 - File filter/mask for LIST command (wildcards allowed) */ /* 7 = Received directory listing on LIST command */ /* buf - transmit/receive buffer */ /* buflen - length of this buffer */ /* on transmit, it specifies size of ‘buf‘ */ /* on receive, specifies the number of bytes received */ U32 i,len = 0; switch (code) { case 0: /* Enter Username for login. */ len = str_copy (buf, FTPC_USERNAME); break; case 1: /* Enter Password for login. */ len = str_copy (buf, FTPC_PASSWORD); break; case 2: /* Enter a working directory path. */ len = str_copy (buf, FTPC_PATH); break; case 3: /* Enter a filename for file operations. */ len = str_copy (buf, FTPC_FILENAME); break; case 4: /* Enter a new name for rename command. */ len = str_copy (buf, FTPC_NEWNAME); break; case 5: /* Enter a directory name to create or remove. */ len = str_copy (buf, FTPC_DIRNAME); break; case 6: /* Enter a file filter for list command. */ len = str_copy (buf, FTPC_LISTNAME); break; case 7: /* Process received directory listing in raw format. */ /* Function return value is don‘t care. */ for (i = 0; i < buflen; i++) { putchar (buf[i]); } break; } return ((U16)len); }
函数原型:
BOOL ftpc_connect ( U8* ipadr, /* FTP服务器的IP地址 */ U16 port, /* FTP服务器的端口号 */ U8 command, /* FTP命令 */ void (*cbfunc)(U8 event) ); /* 回调函数 */
函数描述:
函数ftpc_connect用于启动RL-TCPnet的FTP客户端登录FTP服务器,可以对FTP服务器进行文件管理,比如上传下载、删除、重命名等操作,FTP服务器的地址和端口号由此函数的前两个形参决定。如果第2个参数的端口号填0,系统将使用FTP服务器的标准端口号21进行连接。
使用这个函数要注意以下问题:
使用举例:
/* ********************************************************************************************************* * 变量 ********************************************************************************************************* */ /* ********************************************************************************************************* * 宏定义,远程FTP服务器的IP和端口 ********************************************************************************************************* */ /* 要访问的远程FTP服务器IP配置 */ #define IP1 192 #define IP2 168 #define IP3 1 #define IP4 4 #define PORT_NUM 21 /* FTP服务器,默认端口号是21,无需改动 */ /* ********************************************************************************************************* * 变量 ********************************************************************************************************* */ uint8_t ServerIP[4] = {IP1, IP2, IP3, IP4}; /* ********************************************************************************************************* * 函 数 名: ftpc_notify * 功能说明: 函数ftpc_connect的回调函数。 * 形 参: event 事件类型 * 返 回 值: 无 ********************************************************************************************************* */ static void ftpc_notify (U8 event) { switch (event) { /* 文件操作成功 */ case FTPC_EVT_SUCCESS: printf_debug ("Command successful\n"); break; /* 失败,因为FTP服务器响应时间溢出,因此FTP客户端终止操作 */ case FTPC_EVT_TIMEOUT: printf_debug ("Failed, timeout expired\n"); break; /* 失败,FTP客户端登陆FTP服务器失败 */ case FTPC_EVT_LOGINFAIL: printf ("Failed, username/password invalid\n"); break; /* 失败,禁止操作此文件 */ case FTPC_EVT_NOACCESS: printf_debug ("Failed, operation not allowed\n"); break; /* 失败,在FTP服务器上找不到请求的文件 */ case FTPC_EVT_NOTFOUND: printf_debug ("Failed, file or path not found\n"); break; /* 失败,在FTP服务器上找不到工作目录路径 */ case FTPC_EVT_NOPATH: printf ("Failed, working directory not found\n"); break; /* 失败,文件打开或者写入出错,即FTP客户端操作SD卡或者其它存储介质出错 */ case FTPC_EVT_ERRLOCAL: printf_debug ("Failed, local file open/write error\n"); break; /* 失败,未指定的协议错误,或者说在文件操作过程中遇到错误 */ case FTPC_EVT_ERROR: printf_debug ("Failed, unspecified protocol error\n"); break; } } /* ********************************************************************************************************* * 函 数 名: TCPnetTest * 功能说明: TCPent测试函数。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void TCPnetTest(void) { OS_RESULT xResult; while (1) { os_evt_wait_or(0x003F, 0xFFFF); xResult = os_evt_get (); switch (xResult) { /* 接收到K2键按下,发送FTPC_CMD_GET命令,表示从FTP服务器下载文件 */ case KEY2_BIT1: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_GET, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到K3键按下,发送FTPC_CMD_PUT命令,表示向FTP服务器上传文件 */ case KEY3_BIT2: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_PUT, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到摇杆上键按下,发送FTPC_CMD_LIST命令,表示列出当前目录下所有文件详细信息 */ case JOY_U_BIT3: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_LIST, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到摇杆左键按下,发送FTPC_CMD_MKDIR命令,表示创建一个文件夹 */ case JOY_L_BIT4: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_MKDIR, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到摇杆右键按下,发送FTPC_CMD_RMDIR命令,表示删除一个文件夹,文件夹为空的时候才可以删除 */ case JOY_R_BIT5: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_RMDIR, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 其他的键值不处理 */ default: break; } while (main_TcpNet() == __TRUE); } }
(本章节配套例子的配置与本小节的说明相同)
RL-TCPnet的配置工作是通过配置文件Net_Config.c实现。在MDK工程中打开文件Net_Config.c,可以看到下图所示的工程配置向导:
RL-TCPnet要配置的选项非常多,我们这里把几个主要的配置选项简单介绍下。
System Definitions
(1)Local Host Name
局域网域名。
这里起名为armfly,使用局域网域名限制为15个字符。
(2)Memory Pool size
参数范围1536-262144字节。
内存池大小配置,单位字节。另外注意一点,配置向导这里显示的单位是字节,如果看原始定义,MDK会做一个自动的4字节倍数转换,比如我们这里配置的是8192字节,那么原始定义是#define MEM_SIZE 2048,也就是8192/4 = 2048。
(3)Tick Timer interval
可取10,20,25,40,50,100,200,单位ms。
系统滴答时钟间隔,也就是网络协议栈的系统时间基准,默认情况下,取值100ms。
Ethernet Network Interface
以太网接口配置,勾选了此选项就可以配置了,如果没有使能DHCP的话,将使用这里配置的固定IP。
(1) MAC Address
局域网内可以随意配置,只要不跟局域网内其它设备的MAC地址冲突即可。
(2) IP Address
IP地址。
(3) Subnet mask
子网掩码。
(4) Default Gateway
默认网关。
Ethernet Network Interface
以太网接口配置,这个配置里面还有如下两项比较重要的配置需要说明。
(1) NetBIOS Name Service
NetBIOS局域网域名服务,这里打上对勾就使能了。这样我们就可以通过前面配置的Local Host Name局域网域名进行访问,而不需要通过IP地址访问了。
(2) Dynaminc Host Configuration
即DHCP,这里打上对勾就使能了。使能了DHCP后,RL-TCPnet就可以从外接的路由器上获得动态IP地址。
UDP Sockets
UDP Sockets配置,打上对勾就使能了此项功能
(1) Number of UDP Sockets
用于配置可创建的UDP Sockets数量,这里配置了5个。
范围1 – 20。
TCP Sockets
TCP Sockets配置,打上对勾就使能了此项功能
(1) Number of TCP Sockets
用于配置可创建的TCP Sockets数量。
(2) Number of Retries
范围0-20。
用于配置重试次数,TCP数据传输时,如果在设置的重试时间内得不到应答,算一次重试失败,这里就是配置的最大重试次数。
(3) Retry Timeout in seconds
范围1-10,单位秒。
重试时间。如果发送的数据在重试时间内得不到应答,将重新发送数据。
(4) Default Connect Timeout in seconds
范围1-600,单位秒。
用于配置默认的保持连接时间,即我们常说的Keep Alive时间,如果时间到了将断开连接。常用于HTTP Server,Telnet Server等。
(5) Maximum Segment Size
范围536-1460,单位字节。
MSS定义了TCP数据包能够传输的最大数据分段。
(6) Receive Window Size
范围536-65535,单位字节。
TCP接收窗口大小。
FTP Client
FTP 配置,打上对勾就使能了此项功能
(1) Response Timeout in seconds
FTP客户端等待FTP服务器响应时间,如果溢出,客户端将终止操作。
范围1-120,单位秒。
(2) Passive mode
被动模式,客户端发起到服务器的数据连接,详看第35章的35.3.5小节说明。
(重要说明,RL-TCPnet的调试是通过串口打印出来的)
RL-TCPnet的调试功能是通过配置文件Net_Debug.c实现。在MDK工程中打开文件Net_Debug.c,可以看到下图所示的工程配置向导:
Print Time Stamp
勾选了此选项的话,打印消息时,前面会附带时间信息。
其它所有的选项
默认情况下,所有的调试选项都关闭了,每个选项有三个调试级别可选择,这里我们以FTP Client Debug为例,点击下拉列表,可以看到里面有Off,Errors only和Full debug三个调试级别可供选择,每个调试选项里面都是这三个级别。
Off:表示关闭此选项的调试功能。
Errors only:表示仅在此选项出错时,将其错误打印出来。
Full debug:表示此选项的全功能调试。
具体测试,我们这里就不做了,大家可以按照第11章讲解的调试方法进行测试。
本章节的测试稍麻烦些,需要大家配置工程,并且在电脑端建立一个FTP服务器,而开发板是作为客户端,并且采用SD卡作为存储介质(测试前要准备好一个SD卡插到开发板上面),所以大家测试本章节配套的例子前,务必将这里的操作步骤全部看完才可以做测试!
另外有一点特别注意,我们使用的是RL-FlashFS文件系统,此文件系统的文件名仅支持ASCII字符,不支持中文,对于中文名的文件夹或者文件是无法操作的,因此,电脑端创建FTP服务器的时候,使用的文件夹和文件名也不要有中文。
获取电脑IP地址的方法很多,可以在网上邻居获取,也可以通过输入命令ipconfig获取:
获得电脑的IP地址是192.168.1.4。
根据刚获得的IP地址,需要大家配置程序中app_tcpnet_lib.c文件开头的宏定义:
/* ********************************************************************************************************* * 宏定义,远程FTP服务器的IP和端口 ********************************************************************************************************* */ /* 要访问的远程FTP服务器IP配置 */ #define IP1 192 #define IP2 168 #define IP3 1 #define IP4 4 #define PORT_NUM 21 /* FTP服务器,默认端口号是21,无需改动 */
FTP服务器的账号和文件管理参数已经在FTPC_uif.c文件开头的宏定义中配置好,大家做测试是无需修改的。建议当前的功能测试完毕后,修改相关参数,做一些新的测试,以此来熟练掌握这些配置所代表的含义:
#define FTPC_USERNAME "armfly" /* 远程FTP服务器的账号 */ #define FTPC_PASSWORD "123456" /* 远程FTP服务器的密码 */ #define FTPC_PATH "" /* 要访问的子文件夹,FTP电脑端设置后是没有路径的,填空 */ #define FTPC_FILENAME "server.pdf" /* 要访问的文件 */ #define FTPC_NEWNAME "renamed.pdf" /* 使用这里定义的文件字重命名宏定义FTPC_FILENAME的文件名 */ #define FTPC_DIRNAME "New_Folder" /* 创建名字为New_Folder的文件夹或者删除名字为New_Folder的文件夹 */ #define FTPC_LISTNAME "*" /* 浏览宏定义FTPC_PATH路径下所有文件 */ #define LOCAL_FILE "client.pdf" /* 开发板SD卡里面要上传的文件,或者从FTP服务器下载文件后, 文件会被设置成此名字 */
第1步:下载FTP服务器软件
FTP软件推荐采用FileZilla Server,下载地址:http://bbs.armfly.com/read.php?tid=32586 。建议下载帖子里面二楼的中文版。
第2步:下载后,安装此软件,安装完毕后,打开软件的效果如下
使用默认参数,不用做任何修改,点击确定,弹出如下界面:
第3步:设置一个可供外部访问的FTP服务器账户
点击编辑->用户:
弹出如下界面:
点击确定后,设置密码:
第4步:创建要分享的文件夹
简单的在电脑桌面上创建一个文件夹,起名为good(任何其它地方均可,但建议不要有中文,防止测试不成功)。
为了方便查看上传和下载文件的效果,找一个稍大些的文件放到此文件夹,这里将我们之前做的FreeRTOS教程放到这个新建的文件夹里面(已经将这个文件放在了本章节配套例子的Doc文件夹),起名为server.pdf,务必且只能设置成此名字,因为我们的程序中是配置成访问此文件。
仅放这一个文件即可。现在就可以将这个good文件夹路径添加到FTP服务器软件上。
添加完毕路径后,配置权限,将所有权限都使能:
至此,就可以进行开发板的功能测试了。
FTP服务器创建完毕后,开发板访问FTP服务器就比较简单了。
第1步:将本章节配套的例子下载到开发板后,务必将SD卡插上,因为从FTP服务器上传或者下载文件要用到。
第2步:务必优先测试下载功能(第3步还要将下载的这个文件上传),按下开发板的K2按键,会将FTP服务器分享的server.pdf文件下载到SD卡中,下载后的文件起名叫client.pdf,文件是一样的,只是换了个名字做区分。
下载成功后,串口调试助手打印出如下信息(波特率115200,数据位8,奇偶校验位无,停止位1):
第3步:测试上传功能,为方便起见,需要大家将电脑端分享的server.pdf文件删掉,然后按下开发板上的K3按键,实现将第2步下载的server.pdf文件重新上传到FTP服务器:
上传成功后,串口调试助手打印出如下信息:
第4步:测试完毕上传下载功能后,按下摇杆上键,可以将good文件夹中的所有文件及其详情列出来,下面是串口调试助手显示的信息:
上面打印出来的是FTP服务器端的server.pdf文件详情。
第5步:按下开发板上的摇杆左键,可以给good文件夹中创建一个新的文件夹New_Folder:
第6步:按下开发板上的摇杆右键,可以将新创建的New_Folder文件夹删除:
至此,开发板实现的几个功能已经都实现了,大家还可以测试下文件重命名、文件删除等功能,不过最主要的还是文件的上传和下载功能。大家测试的时候,别忘了实际打开文件看下,查看文件是否被损坏,以此来保证文件被成功传输了。
配套例子:
V5-1054_RL-TCPnet实验_FTP客户端(RTX)
实验目的:
实验内容:
实验操作:
详见本章节37.5小节。
配置向导文件设置(Net_Config.c):
详见本章节37.3小节。
调试文件设置(Net_Debug.c):
详见本章节37.4小节。
RTX配置:
RTX配置向导详情如下:
Task Configuration
(1)Number of concurrent running tasks
允许创建6个任务,实际创建了如下5个任务:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :按键检测。
AppTaskTCPMain任务:RL-TCPnet测试任务。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。
(2)Number of tasks with user-provided stack
创建的5个任务都是采用自定义堆栈方式。
(3)Run in privileged mode
设置任务运行在非特权级模式。
RTX任务调试信息:
程序设计:
任务栈大小分配:
static uint64_t AppTaskUserIFStk[1024/8]; /* 任务栈 */
static uint64_t AppTaskLEDStk[1024/8]; /* 任务栈 */
static uint64_t AppTaskMsgProStk[1024/8]; /* 任务栈 */
static uint64_t AppTaskTCPMainStk[4096/8]; /* 任务栈 */
static uint64_t AppTaskStartStk[1024/8]; /* 任务栈 */
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。
系统栈大小分配:
RTX初始化:
/* ********************************************************************************************************* * 函 数 名: main * 功能说明: 标准c程序入口。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ int main (void) { /* 初始化外设 */ bsp_Init(); /* 创建启动任务 */ os_sys_init_user (AppTaskStart, /* 任务函数 */ 5, /* 任务优先级 */ &AppTaskStartStk, /* 任务栈 */ sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */ while(1); }
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* 由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。 启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。 系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件 */ /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); bsp_InitDWT(); /* 初始化DWT */ bsp_InitUart(); /* 初始化串口 */ bsp_InitKey(); /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */ bsp_InitLed(); /* 初始LED指示灯端口 */ 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键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit1 */ case KEY_DOWN_K2: printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\r\n"); os_evt_set (KEY2_BIT1, HandleTaskTCPMain); break; /* K3键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit2 */ case KEY_DOWN_K3: printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\r\n"); os_evt_set (KEY3_BIT2, HandleTaskTCPMain); break; /* 摇杆上键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit3 */ case JOY_DOWN_U: printf("摇杆上键按下,直接发送事件标志给任务AppTaskTCPMain,bit3被设置\r\n"); os_evt_set (JOY_U_BIT3, HandleTaskTCPMain); break; /* 摇杆左键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit4 */ case JOY_DOWN_L: printf("摇杆左键按下,直接发送事件标志给任务AppTaskTCPMain,bit4被设置\r\n"); os_evt_set (JOY_L_BIT4, HandleTaskTCPMain); break; /* 摇杆右键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit5 */ case JOY_DOWN_R: printf("摇杆右键按下,直接发送事件标志给任务AppTaskTCPMain,bit5被设置\r\n"); os_evt_set (JOY_R_BIT5, HandleTaskTCPMain); break; /* 其他的键值不处理 */ default: break; } } os_dly_wait(20); } } /* ********************************************************************************************************* * 函 数 名: AppTaskLED * 功能说明: LED闪烁。 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 2 ********************************************************************************************************* */ __task void AppTaskLED(void) { const uint16_t usFrequency = 500; /* 延迟周期 */ /* 设置延迟周期 */ os_itv_set(usFrequency); while(1) { bsp_LedToggle(2); /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/ os_itv_wait(); } } /* ********************************************************************************************************* * 函 数 名: AppTaskMsgPro * 功能说明: 按键检测 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 3 ********************************************************************************************************* */ __task void AppTaskMsgPro(void) { while(1) { bsp_KeyScan(); os_dly_wait(10); } } /* ********************************************************************************************************* * 函 数 名: AppTaskTCPMain * 功能说明: RL-TCPnet测试任务 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 4 ********************************************************************************************************* */ __task void AppTaskTCPMain(void) { while (1) { TCPnetTest(); } } /* ********************************************************************************************************* * 函 数 名: AppTaskStart * 功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 5 ********************************************************************************************************* */ __task void AppTaskStart(void) { /* 初始化RL-TCPnet */ init_TcpNet (); /* 创建任务 */ AppTaskCreate(); os_itv_set (100); while(1) { os_itv_wait (); /* RL-TCPnet时间基准更新函数 */ timer_tick (); os_evt_set(0x0001, HandleTaskTCPMain); } }
RL-TCPnet功能测试
这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,此文件主要实现开发板发给FTP服务器的文件操作命令和网络主函数main_TcpNet的调用。
#include "includes.h" /* ********************************************************************************************************* * 用于本文件的调试 ********************************************************************************************************* */ #if 1 #define printf_debug printf #else #define printf_debug(...) #endif /* ********************************************************************************************************* * 宏定义,远程FTP服务器的IP和端口 ********************************************************************************************************* */ /* 要访问的远程FTP服务器IP配置 */ #define IP1 192 #define IP2 168 #define IP3 1 #define IP4 4 #define PORT_NUM 21 /* FTP服务器,默认端口号是21,无需改动 */ /* ********************************************************************************************************* * 变量 ********************************************************************************************************* */ uint8_t ServerIP[4] = {IP1, IP2, IP3, IP4}; /* ********************************************************************************************************* * 函 数 名: ftpc_notify * 功能说明: 函数ftpc_connect的回调函数。 * 形 参: event 事件类型 * 返 回 值: 无 ********************************************************************************************************* */ static void ftpc_notify (U8 event) { switch (event) { /* 文件操作成功 */ case FTPC_EVT_SUCCESS: printf_debug ("Command successful\n"); break; /* 失败,因为FTP服务器响应时间溢出,因此FTP客户端终止操作 */ case FTPC_EVT_TIMEOUT: printf_debug ("Failed, timeout expired\n"); break; /* 失败,FTP客户端登陆FTP服务器失败 */ case FTPC_EVT_LOGINFAIL: printf ("Failed, username/password invalid\n"); break; /* 失败,禁止操作此文件 */ case FTPC_EVT_NOACCESS: printf_debug ("Failed, operation not allowed\n"); break; /* 失败,在FTP服务器上找不到请求的文件 */ case FTPC_EVT_NOTFOUND: printf_debug ("Failed, file or path not found\n"); break; /* 失败,在FTP服务器上找不到工作目录路径 */ case FTPC_EVT_NOPATH: printf ("Failed, working directory not found\n"); break; /* 失败,文件打开或者写入出错,即FTP客户端操作SD卡或者其它存储介质出错 */ case FTPC_EVT_ERRLOCAL: printf_debug ("Failed, local file open/write error\n"); break; /* 失败,未指定的协议错误,或者说在文件操作过程中遇到错误 */ case FTPC_EVT_ERROR: printf_debug ("Failed, unspecified protocol error\n"); break; } } /* ********************************************************************************************************* * 函 数 名: TCPnetTest * 功能说明: TCPent测试函数。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void TCPnetTest(void) { OS_RESULT xResult; while (1) { os_evt_wait_or(0x003F, 0xFFFF); xResult = os_evt_get (); switch (xResult) { /* 接收到K2键按下,发送FTPC_CMD_GET命令,表示从FTP服务器下载文件 */ case KEY2_BIT1: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_GET, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到K3键按下,发送FTPC_CMD_PUT命令,表示向FTP服务器上传文件 */ case KEY3_BIT2: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_PUT, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到摇杆上键按下,发送FTPC_CMD_LIST命令,表示列出当前目录下所有文件详细信息 */ case JOY_U_BIT3: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_LIST, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到摇杆左键按下,发送FTPC_CMD_MKDIR命令,表示创建一个文件夹 */ case JOY_L_BIT4: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_MKDIR, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到摇杆右键按下,发送FTPC_CMD_RMDIR命令,表示删除一个文件夹,文件夹为空的时候才可以删除 */ case JOY_R_BIT5: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_RMDIR, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 其他的键值不处理 */ default: break; } while (main_TcpNet() == __TRUE); } }
FTP用户接口文件的实现
KEIL官网有提供FTP的接口文件,名为FTPC_uif.c文件。我们就是在这个文件上修改。具体修改后的代码如下:
#include <Net_Config.h> #include <stdio.h> #define FTPC_USERNAME "armfly" /* 远程FTP服务器的账号 */ #define FTPC_PASSWORD "123456" /* 远程FTP服务器的密码 */ #define FTPC_PATH "" /* 要访问的子文件夹,FTP电脑端设置后是没有路径的,填空 */ #define FTPC_FILENAME "server.pdf" /* 要访问的文件 */ #define FTPC_NEWNAME "renamed.pdf" /* 使用这里定义的文件字重命名宏定义FTPC_FILENAME的文件名 */ #define FTPC_DIRNAME "New_Folder" /* 创建名字为New_Folder的文件夹或者删除名字为New_Folder的文件夹 */ #define FTPC_LISTNAME "*" /* 浏览宏定义FTPC_PATH路径下所有文件 */ #define LOCAL_FILE "client.pdf" /* 开发板SD卡里面要上传的文件,或者从FTP服务器下载文件后, 文件会被设置成此名字 */ /*---------------------------------------------------------------------------- * FTP Client File Access and Data CallBack Functions *---------------------------------------------------------------------------*/ /*--------------------------- ftpc_fopen ------------------------------------*/ void *ftpc_fopen (U8 *mode) { /* Open local file for reading or writing. If the return value is NULL, */ /* processing of FTP Client commands PUT, APPEND or GET is cancelled. */ return (fopen (LOCAL_FILE, (char *)mode)); } /*--------------------------- ftpc_fclose -----------------------------------*/ void ftpc_fclose (void *file) { /* Close a local file. */ fclose (file); } /*--------------------------- ftpc_fread ------------------------------------*/ U16 ftpc_fread (void *file, U8 *buf, U16 len) { /* Read ‘len‘ bytes from file to buffer ‘buf‘. Return number of bytes */ /* copied. The file will be closed, when the return value is 0. */ /* For optimal performance the return value should be ‘len‘ */ return (fread (buf, 1, len, file)); } /*--------------------------- ftpc_fwrite -----------------------------------*/ U16 ftpc_fwrite (void *file, U8 *buf, U16 len) { /* Write ‘len‘ bytes from buffer ‘buf‘ to a file. Data transfer will be */ /* aborted, if the return value is not equal ‘len‘. */ return (fwrite (buf, 1, len, file)); } /*--------------------------- ftpc_cbfunc -----------------------------------*/ U16 ftpc_cbfunc (U8 code, U8 *buf, U16 buflen) { /* This function is called by the FTP client to get parameters. It returns*/ /* the number of bytes written to buffer ‘buf‘. This function should NEVER*/ /* write more than ‘buflen‘ bytes to this buffer. */ /* Parameters: */ /* code - function code with following values: */ /* 0 = Username for FTP authentication */ /* 1 = Password for FTP authentication */ /* 2 = Working directory path for all commands */ /* 3 = Filename for PUT, GET, APPEND, DELETE, RENAME command */ /* 4 - New filename for RENAME command */ /* 5 - Directory name for MKDIR, RMDIR command */ /* 6 - File filter/mask for LIST command (wildcards allowed) */ /* 7 = Received directory listing on LIST command */ /* buf - transmit/receive buffer */ /* buflen - length of this buffer */ /* on transmit, it specifies size of ‘buf‘ */ /* on receive, specifies the number of bytes received */ U32 i,len = 0; switch (code) { case 0: /* Enter Username for login. */ len = str_copy (buf, FTPC_USERNAME); break; case 1: /* Enter Password for login. */ len = str_copy (buf, FTPC_PASSWORD); break; case 2: /* Enter a working directory path. */ len = str_copy (buf, FTPC_PATH); break; case 3: /* Enter a filename for file operations. */ len = str_copy (buf, FTPC_FILENAME); break; case 4: /* Enter a new name for rename command. */ len = str_copy (buf, FTPC_NEWNAME); break; case 5: /* Enter a directory name to create or remove. */ len = str_copy (buf, FTPC_DIRNAME); break; case 6: /* Enter a file filter for list command. */ len = str_copy (buf, FTPC_LISTNAME); break; case 7: /* Process received directory listing in raw format. */ /* Function return value is don‘t care. */ for (i = 0; i < buflen; i++) { putchar (buf[i]); } break; } return ((U16)len); } /*---------------------------------------------------------------------------- * end of file *---------------------------------------------------------------------------*/
配套例子:
V6-1054_RL-TCPnet实验_FTP客户端(RTX)
实验目的:
实验内容:
实验操作:
详见本章节37.5小节。
配置向导文件设置(Net_Config.c):
详见本章节37.3小节。
调试文件设置(Net_Debug.c):
详见本章节37.4小节。
RTX配置:
RTX配置向导详情如下:
Task Configuration
(1)Number of concurrent running tasks
允许创建6个任务,实际创建了如下5个任务:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :按键检测。
AppTaskTCPMain任务:RL-TCPnet测试任务。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。
(2)Number of tasks with user-provided stack
创建的5个任务都是采用自定义堆栈方式。
(3)Run in privileged mode
设置任务运行在非特权级模式。
RTX任务调试信息:
程序设计:
任务栈大小分配:
static uint64_t AppTaskUserIFStk[1024/8]; /* 任务栈 */
static uint64_t AppTaskLEDStk[1024/8]; /* 任务栈 */
static uint64_t AppTaskMsgProStk[1024/8]; /* 任务栈 */
static uint64_t AppTaskTCPMainStk[4096/8]; /* 任务栈 */
static uint64_t AppTaskStartStk[1024/8]; /* 任务栈 */
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数、浮点运算和uint64_t类型数据运算会出问题。
系统栈大小分配:
RTX初始化:
/* ********************************************************************************************************* * 函 数 名: main * 功能说明: 标准c程序入口。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ int main (void) { /* 初始化外设 */ bsp_Init(); /* 创建启动任务 */ os_sys_init_user (AppTaskStart, /* 任务函数 */ 5, /* 任务优先级 */ &AppTaskStartStk, /* 任务栈 */ sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */ while(1); }
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/* ********************************************************************************************************* * 函 数 名: bsp_Init * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次 * 形 参:无 * 返 回 值: 无 ********************************************************************************************************* */ void bsp_Init(void) { /* 由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。 启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。 系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件 */ /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); SystemCoreClockUpdate(); /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */ bsp_InitDWT(); /* 初始化DWT */ bsp_InitUart(); /* 初始化串口 */ bsp_InitKey(); /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */ bsp_InitExtIO(); /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */ bsp_InitLed(); /* 初始LED指示灯端口 */ 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键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit1 */ case KEY_DOWN_K2: printf("K2键按下,直接发送事件标志给任务AppTaskTCPMain,bit1被设置\r\n"); os_evt_set (KEY2_BIT1, HandleTaskTCPMain); break; /* K3键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit2 */ case KEY_DOWN_K3: printf("K3键按下,直接发送事件标志给任务AppTaskTCPMain,bit2被设置\r\n"); os_evt_set (KEY3_BIT2, HandleTaskTCPMain); break; /* 摇杆上键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit3 */ case JOY_DOWN_U: printf("摇杆上键按下,直接发送事件标志给任务AppTaskTCPMain,bit3被设置\r\n"); os_evt_set (JOY_U_BIT3, HandleTaskTCPMain); break; /* 摇杆左键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit4 */ case JOY_DOWN_L: printf("摇杆左键按下,直接发送事件标志给任务AppTaskTCPMain,bit4被设置\r\n"); os_evt_set (JOY_L_BIT4, HandleTaskTCPMain); break; /* 摇杆右键按下,直接发送事件标志给任务AppTaskTCPMain,设置bit5 */ case JOY_DOWN_R: printf("摇杆右键按下,直接发送事件标志给任务AppTaskTCPMain,bit5被设置\r\n"); os_evt_set (JOY_R_BIT5, HandleTaskTCPMain); break; /* 其他的键值不处理 */ default: break; } } os_dly_wait(20); } } /* ********************************************************************************************************* * 函 数 名: AppTaskLED * 功能说明: LED闪烁。 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 2 ********************************************************************************************************* */ __task void AppTaskLED(void) { const uint16_t usFrequency = 500; /* 延迟周期 */ /* 设置延迟周期 */ os_itv_set(usFrequency); while(1) { bsp_LedToggle(2); /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/ os_itv_wait(); } } /* ********************************************************************************************************* * 函 数 名: AppTaskMsgPro * 功能说明: 按键检测 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 3 ********************************************************************************************************* */ __task void AppTaskMsgPro(void) { while(1) { bsp_KeyScan(); os_dly_wait(10); } } /* ********************************************************************************************************* * 函 数 名: AppTaskTCPMain * 功能说明: RL-TCPnet测试任务 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 4 ********************************************************************************************************* */ __task void AppTaskTCPMain(void) { while (1) { TCPnetTest(); } } /* ********************************************************************************************************* * 函 数 名: AppTaskStart * 功能说明: 启动任务,也是最高优先级任务,这里实现RL-TCPnet的时间基准更新。 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 5 ********************************************************************************************************* */ __task void AppTaskStart(void) { /* 初始化RL-TCPnet */ init_TcpNet (); /* 创建任务 */ AppTaskCreate(); os_itv_set (100); while(1) { os_itv_wait (); /* RL-TCPnet时间基准更新函数 */ timer_tick (); os_evt_set(0x0001, HandleTaskTCPMain); } }
RL-TCPnet功能测试
这里专门创建了一个app_tcpnet_lib.c文件用于RL-TCPnet功能的测试,此文件主要实现开发板发给FTP服务器的文件操作命令和网络主函数main_TcpNet的调用。
#include "includes.h" /* ********************************************************************************************************* * 用于本文件的调试 ********************************************************************************************************* */ #if 1 #define printf_debug printf #else #define printf_debug(...) #endif /* ********************************************************************************************************* * 宏定义,远程FTP服务器的IP和端口 ********************************************************************************************************* */ /* 要访问的远程FTP服务器IP配置 */ #define IP1 192 #define IP2 168 #define IP3 1 #define IP4 4 #define PORT_NUM 21 /* FTP服务器,默认端口号是21,无需改动 */ /* ********************************************************************************************************* * 变量 ********************************************************************************************************* */ uint8_t ServerIP[4] = {IP1, IP2, IP3, IP4}; /* ********************************************************************************************************* * 函 数 名: ftpc_notify * 功能说明: 函数ftpc_connect的回调函数。 * 形 参: event 事件类型 * 返 回 值: 无 ********************************************************************************************************* */ static void ftpc_notify (U8 event) { switch (event) { /* 文件操作成功 */ case FTPC_EVT_SUCCESS: printf_debug ("Command successful\n"); break; /* 失败,因为FTP服务器响应时间溢出,因此FTP客户端终止操作 */ case FTPC_EVT_TIMEOUT: printf_debug ("Failed, timeout expired\n"); break; /* 失败,FTP客户端登陆FTP服务器失败 */ case FTPC_EVT_LOGINFAIL: printf ("Failed, username/password invalid\n"); break; /* 失败,禁止操作此文件 */ case FTPC_EVT_NOACCESS: printf_debug ("Failed, operation not allowed\n"); break; /* 失败,在FTP服务器上找不到请求的文件 */ case FTPC_EVT_NOTFOUND: printf_debug ("Failed, file or path not found\n"); break; /* 失败,在FTP服务器上找不到工作目录路径 */ case FTPC_EVT_NOPATH: printf ("Failed, working directory not found\n"); break; /* 失败,文件打开或者写入出错,即FTP客户端操作SD卡或者其它存储介质出错 */ case FTPC_EVT_ERRLOCAL: printf_debug ("Failed, local file open/write error\n"); break; /* 失败,未指定的协议错误,或者说在文件操作过程中遇到错误 */ case FTPC_EVT_ERROR: printf_debug ("Failed, unspecified protocol error\n"); break; } } /* ********************************************************************************************************* * 函 数 名: TCPnetTest * 功能说明: TCPent测试函数。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void TCPnetTest(void) { OS_RESULT xResult; while (1) { os_evt_wait_or(0x003F, 0xFFFF); xResult = os_evt_get (); switch (xResult) { /* 接收到K2键按下,发送FTPC_CMD_GET命令,表示从FTP服务器下载文件 */ case KEY2_BIT1: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_GET, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到K3键按下,发送FTPC_CMD_PUT命令,表示向FTP服务器上传文件 */ case KEY3_BIT2: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_PUT, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到摇杆上键按下,发送FTPC_CMD_LIST命令,表示列出当前目录下所有文件详细信息 */ case JOY_U_BIT3: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_LIST, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到摇杆左键按下,发送FTPC_CMD_MKDIR命令,表示创建一个文件夹 */ case JOY_L_BIT4: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_MKDIR, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 接收到摇杆右键按下,发送FTPC_CMD_RMDIR命令,表示删除一个文件夹,文件夹为空的时候才可以删除 */ case JOY_R_BIT5: if (ftpc_connect (ServerIP, PORT_NUM, FTPC_CMD_RMDIR, ftpc_notify) == 0) { printf_debug("Connect failed, the client is busy.\n"); } else { printf_debug("FTP client started.\n"); } break; /* 其他的键值不处理 */ default: break; } while (main_TcpNet() == __TRUE); } }
FTP用户接口文件的实现
KEIL官网有提供FTP的接口文件,名为FTPC_uif.c文件。我们就是在这个文件上修改。具体修改后的代码如下:
#include <Net_Config.h> #include <stdio.h> #define FTPC_USERNAME "armfly" /* 远程FTP服务器的账号 */ #define FTPC_PASSWORD "123456" /* 远程FTP服务器的密码 */ #define FTPC_PATH "" /* 要访问的子文件夹,FTP电脑端设置后是没有路径的,填空 */ #define FTPC_FILENAME "server.pdf" /* 要访问的文件 */ #define FTPC_NEWNAME "renamed.pdf" /* 使用这里定义的文件字重命名宏定义FTPC_FILENAME的文件名 */ #define FTPC_DIRNAME "New_Folder" /* 创建名字为New_Folder的文件夹或者删除名字为New_Folder的文件夹 */ #define FTPC_LISTNAME "*" /* 浏览宏定义FTPC_PATH路径下所有文件 */ #define LOCAL_FILE "client.pdf" /* 开发板SD卡里面要上传的文件,或者从FTP服务器下载文件后, 文件会被设置成此名字 */ /*---------------------------------------------------------------------------- * FTP Client File Access and Data CallBack Functions *---------------------------------------------------------------------------*/ /*--------------------------- ftpc_fopen ------------------------------------*/ void *ftpc_fopen (U8 *mode) { /* Open local file for reading or writing. If the return value is NULL, */ /* processing of FTP Client commands PUT, APPEND or GET is cancelled. */ return (fopen (LOCAL_FILE, (char *)mode)); } /*--------------------------- ftpc_fclose -----------------------------------*/ void ftpc_fclose (void *file) { /* Close a local file. */ fclose (file); } /*--------------------------- ftpc_fread ------------------------------------*/ U16 ftpc_fread (void *file, U8 *buf, U16 len) { /* Read ‘len‘ bytes from file to buffer ‘buf‘. Return number of bytes */ /* copied. The file will be closed, when the return value is 0. */ /* For optimal performance the return value should be ‘len‘ */ return (fread (buf, 1, len, file)); } /*--------------------------- ftpc_fwrite -----------------------------------*/ U16 ftpc_fwrite (void *file, U8 *buf, U16 len) { /* Write ‘len‘ bytes from buffer ‘buf‘ to a file. Data transfer will be */ /* aborted, if the return value is not equal ‘len‘. */ return (fwrite (buf, 1, len, file)); } /*--------------------------- ftpc_cbfunc -----------------------------------*/ U16 ftpc_cbfunc (U8 code, U8 *buf, U16 buflen) { /* This function is called by the FTP client to get parameters. It returns*/ /* the number of bytes written to buffer ‘buf‘. This function should NEVER*/ /* write more than ‘buflen‘ bytes to this buffer. */ /* Parameters: */ /* code - function code with following values: */ /* 0 = Username for FTP authentication */ /* 1 = Password for FTP authentication */ /* 2 = Working directory path for all commands */ /* 3 = Filename for PUT, GET, APPEND, DELETE, RENAME command */ /* 4 - New filename for RENAME command */ /* 5 - Directory name for MKDIR, RMDIR command */ /* 6 - File filter/mask for LIST command (wildcards allowed) */ /* 7 = Received directory listing on LIST command */ /* buf - transmit/receive buffer */ /* buflen - length of this buffer */ /* on transmit, it specifies size of ‘buf‘ */ /* on receive, specifies the number of bytes received */ U32 i,len = 0; switch (code) { case 0: /* Enter Username for login. */ len = str_copy (buf, FTPC_USERNAME); break; case 1: /* Enter Password for login. */ len = str_copy (buf, FTPC_PASSWORD); break; case 2: /* Enter a working directory path. */ len = str_copy (buf, FTPC_PATH); break; case 3: /* Enter a filename for file operations. */ len = str_copy (buf, FTPC_FILENAME); break; case 4: /* Enter a new name for rename command. */ len = str_copy (buf, FTPC_NEWNAME); break; case 5: /* Enter a directory name to create or remove. */ len = str_copy (buf, FTPC_DIRNAME); break; case 6: /* Enter a file filter for list command. */ len = str_copy (buf, FTPC_LISTNAME); break; case 7: /* Process received directory listing in raw format. */ /* Function return value is don‘t care. */ for (i = 0; i < buflen; i++) { putchar (buf[i]); } break; } return ((U16)len); } /*---------------------------------------------------------------------------- * end of file *---------------------------------------------------------------------------*/
本章节就为大家讲解这么多,其中FTP的测试稍麻烦些,希望大家实际动手操作一遍,并将其熟练掌握。
【RL-TCPnet网络教程】第37章 RL-TCPnet之FTP客户端
标签:netbios 例子 ret ipconfig rmdir命令 调试 寄存器 字节 左右
原文地址:https://www.cnblogs.com/armfly/p/9609336.html