【RL-TCPnet网络教程】第39章 RL-TCPnet之TFTP服务器

时间:2024-01-01 10:53:09

第39章     RL-TCPnet之TFTP服务器

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

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

39.1  初学者重要提示

39.2  TFTP函数

39.3  TFTP配置说明(Net_Config.c)

39.4  TFTP调试说明(Net_Debug.c)

39.5  TFTP客户端软件和板子的操作步骤

39.6  实验例程说明(裸机)

39.7  实验例程说明(RTX)

39.8  总结

39.1  初学者重要提示

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

39.2  TFTP函数

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

  • tftp_accept_host
  • tftp_fclose
  • tftp_fopen
  • tftp_fread
  • tftp_fwrite
  • tftpc_fclose
  • tftpc_fopen
  • tftpc_fread
  • tftpc_fwrite
  • tftpc_get
  • tftpc_put

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

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

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

  • tftp_accept_host
  • tftp_fclose
  • tftp_fopen
  • tftp_fread
  • tftp_fwrite
  • tftpc_fclose

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

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

39.2.1    函数tftp_fopen

函数原型:

void* tftp_fopen (

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

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

函数描述:

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

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

使用举例:

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

  /* 打开文件 */

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

}

39.2.2   函数tftp_fclose

函数原型:

void tftp_fclose (

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

函数描述:

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

  1. 第1个参数是要关闭的文件句柄地址。

使用举例:

void tftp_fclose (void *file) {

  /* 关闭文件,当TFTP会话将结束的时候会调用此函数 */

  fclose (file);

}

39.2.3   函数tftp_fread

函数原型:

U16 tftp_fread (

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

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

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

函数描述:

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

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

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

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

使用举例:

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

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

文件将被关闭*/

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

}

39.2.4   函数tftp_fwrite

函数原型:

U16 tftp_fwrite (

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

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

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

函数描述:

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

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

使用举例:

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

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

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

}

39.2.5   函数tftp_accept_host

函数原型:

BOOL tftp_accept_host (

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

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

函数描述:

函数tftp_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[] ==   &&

      rem_ip[] ==   &&

      rem_ip[] ==     &&

      rem_ip[] == ) {

    /* 接受此连接. */

    return (__TRUE);

  }

  /* 拒绝此连接 */

  return (__FALSE);

}

39.3 TFTP配置说明(Net_Config.c)

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

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

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

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

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

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网络教程】第39章	RL-TCPnet之TFTP服务器

Ethernet Network Interface

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

(1)  MAC Address

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

(2) IP Address

IP地址。

(3)  Subnet mask

子网掩码。

(4) Default Gateway

默认网关。

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

Ethernet Network Interface

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

(1)  NetBIOS Name Service

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

(2) Dynaminc Host Configuration

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

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

UDP Sockets

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

(1)  Number of UDP Sockets

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

范围1 – 20。

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

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网络教程】第39章	RL-TCPnet之TFTP服务器

TFTP Server

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

(1)  Number of TFTP Sessions

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

范围1-10。

(2)  Port Number

TFTP服务器的监听端口号。

范围1-65535。

(3) Enable Firewall Support

是否使能使用同一个端口号接收客户端的请求并回复。

打上对勾表示使能。

(4) Inactive Session Timeout in seconds

交互会话期间的溢出时间,如果会话期间,这段时间内无操作,TFTP服务器将断开客户端的连接。

范围5-120,单位秒。

(5)  Number of Retries

TFTP服务器放弃发送前,可以尝试的发送次数,超过这个次数还没有发送成功,将放弃发送。

范围1-10次。

39.4 TFTP调试说明(Net_Debug.c)

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

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

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

Print Time Stamp

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

其它所有的选项

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

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

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

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

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

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

39.5 TFTP客户端软件和板子的操作步骤

本章节配套的例子是用开发板做TFTP服务器,SD卡做为服务器的存储介质,所以务必准备好一个SD卡插到开发板上面。电脑端安装TFTP客户端进行访问,这里主要测试了文件上传和下载功能。

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

39.5.1 获取板子IP地址

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

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

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

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

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

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

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

获得IP地址是192.168.1.6。

39.5.2 TFTP客户端软件安装和设置

第1步:下载TFTP客户端软件。

TFTP软件推荐采用TFTPD32,客户端和服务器都支持,分32bit和64bit两个版本,大家根据自己电脑系统选择相应版本进行安装,另外推荐绿色版,无需安装,使用起来简单省事。下载地址:http://bbs.armfly.com/read.php?tid=32486

第2步:下载绿色版后,解压出来就可以使用,打开软件的效果如下(我的系统是WIN7 64bit,所以使用的是64位版本):

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

第3步:关闭不需要的功能,仅留下TFTP Client(不是必须的,仅剩下客户端功能,看着简洁些)。

首先点击settings:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

在弹出的窗口里面仅选择TFTP Client:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

设置后,点击OK按键,弹出如下窗口,继续点击OK:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

经过这么设置后,就仅剩下TFTP客户端功能了,为了使得设置的功能起作用,务必关闭软件,然后重新打开。

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

设置完毕后,就可以测试文件的上传和下载功能了。

39.5.3 通过TFTP客户端软件上传文件到开发板

第1步:准备一个测试文件:

为了方便查看下载文件的效果,找一个稍大些的文件,这里使用我们之前做的DSP教程,并将名字修改为111.pdf(已经将这个文件放在了本章节配套例子的Doc文件夹,修改名字是因为我们用的文件系统RL-FlashFS不支持中文名,仅支持ASCII字符)。大家将这个文件放到桌面即可:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

第2步:配置TFTP客户端:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

  1. 选择Tftp Client选项。
  2. 选择电脑端用于通信的网卡IP。
  3. 这里填写开发板的IP地址,因为已经使能了NetBIOS Name,所以这里直接填armfly即可,也可以填写前面39.5.1小节获取的IP地址192.168.1.6(如果填写IP地址的话,根据自己实际获取的填写)。
  4. 填写端口号,在例子的Net_Config.c文件中配置TFTP服务器的端口号是69,这里也填69即可。
  5. 添加放在桌面的111.pdf文件,点击右侧的小按钮,选择111.pdf文件。【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器
  6. 这里可以什么都不填,这样下载到开发板SD卡中的文件就叫111.pdf,如果填了文件名,那么下载到SD卡中的文件就叫这个新配置的文件名,我们这里是设置成文件名222.pdf(切不要是中文,因为不支持中文名)。
  7. 这里块大小的设置务必选择默认配置,实际测试发现,只有默认配置才比较稳定。
  8. 最后点击Put按钮就可以将111.pdf文件上传到开发板的SD卡中了,上传到SD卡中的文件名叫222.pdf。上传的过程中,会有一个上传进度:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

上传完毕后,会弹出如下的对话框:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

共传输了34202块,文件的大小是16.6MB,耗时18秒上传完毕,上传速度是16.6MB/18 = 922KB/S。另外,为了验证上传是否成功,大家可以查看SD卡中的222.pdf文件是否可以正常打开并浏览,如果可以,说明上传成功。

39.5.4 通过TFTP客户端软件来下载开发板中的文件

为了方便测试,我们这里直接将39.5.3小节中上传到开发板SD卡中的222.pdf文件下载到电脑端。并换个文件名,叫333.pdf(另外特别注意,这个Local File选项里面是一定要添加路径的,不可以空置)。

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

点击Get按钮就可以开始下载了,下载的过程中,下面截图中红色小方框的地方会记录已经传输的块大小:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

下载完毕后,弹出如下对话框:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

共传输了34202块,跟39.5.3小节中的上传的块数是一致的。只是这里耗时稍长,用了23秒,下载速度是16.6MB/23 = 721KB/S。另外,为了验证下载是否成功,需要大家查看电脑端的333.pdf文件是否可以正常浏览。

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

39.6 实验例程说明(RTX)

39.6.1 STM32F407开发板实验

配套例子:

V5-1057_RL-TCPnet实验_TFTP服务器(RTX)

实验目的:

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

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. TFTP服务器的存储器是采用的SD卡,所以测试本例子前务必准备好一个SD卡并插上。
  3. 文件系统是采用的RL-FlashFS,此文件系统的文件名仅支持ASCII字符,不支持中文,特别注意!!
  4. TFTP服务器的访问方法在本实例配套教程里面有详细讲解,测试本例子前必须要看。

实验操作:

详见本章节39.5小节。

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

详见本章节39.3小节。

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

详见本章节39.4小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

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网络教程】第39章	RL-TCPnet之TFTP服务器

程序设计:

任务栈大小分配:

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网络教程】第39章	RL-TCPnet之TFTP服务器

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

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

}

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_or(0x0001, 0xFFFF); 

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

         while (main_TcpNet() == __TRUE);

     }

}

TFTP用户接口文件的实现

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

#include <Net_Config.h>

#include <File_Config.h>

#include <stdio.h>

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

 * FTP Server File Access Functions

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

#include <stdio.h>

#include <Net_Config.h>

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

 * TFTP Server File Access Functions

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

/*--------------------------- tftp_fopen ------------------------------------*/

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

  /* Open filename fname for reading or writing. */

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

}

/*--------------------------- tftp_fclose -----------------------------------*/

void tftp_fclose (void *file) {

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

  fclose (file);

}

/*--------------------------- tftp_fread ------------------------------------*/

U16 tftp_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 < 'len'  */

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

}

/*--------------------------- tftp_fwrite -----------------------------------*/

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

  /* Write data to file. Return number of bytes actually written. */

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

}

/*--------------------------- tftp_accept_host ------------------------------*/

#if 0

BOOL tftp_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[] ==   &&

       rem_ip[] ==   &&

       rem_ip[] ==     &&

       rem_ip[] == ) {

      /* Accept a connection. */ 

      return (__TRUE);

   }

   /* Deny a connection. */

   return (__FALSE);

}

#endif

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

 * end of file

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

39.6.2 STM32F429开发板实验

配套例子:

V6-1057_RL-TCPnet实验_TFTP服务器(RTX)

实验目的:

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

实验内容:

  1. 强烈推荐将网线接到路由器或者交换机上面测试,因为已经使能了DHCP,可以自动获取IP地址。
  2. TFTP服务器的存储器是采用的SD卡,所以测试本例子前务必准备好一个SD卡并插上。
  3. 文件系统是采用的RL-FlashFS,此文件系统的文件名仅支持ASCII字符,不支持中文,特别注意!!
  4. TFTP服务器的访问方法在本实例配套教程里面有详细讲解,测试本例子前必须要看。

实验操作:

详见本章节39.5小节。

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

详见本章节39.3小节。

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

详见本章节39.4小节。

RTX配置:

RTX配置向导详情如下:

【RL-TCPnet网络教程】第39章	RL-TCPnet之TFTP服务器

  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网络教程】第39章	RL-TCPnet之TFTP服务器

程序设计:

任务栈大小分配:

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网络教程】第39章	RL-TCPnet之TFTP服务器

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

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

}

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_or(0x0001, 0xFFFF); 

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

         while (main_TcpNet() == __TRUE);

     }

}

TFTP用户接口文件的实现

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

#include <Net_Config.h>

#include <File_Config.h>

#include <stdio.h>

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

 * FTP Server File Access Functions

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

#include <stdio.h>

#include <Net_Config.h>

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

 * TFTP Server File Access Functions

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

/*--------------------------- tftp_fopen ------------------------------------*/

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

  /* Open filename fname for reading or writing. */

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

}

/*--------------------------- tftp_fclose -----------------------------------*/

void tftp_fclose (void *file) {

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

  fclose (file);

}

/*--------------------------- tftp_fread ------------------------------------*/

U16 tftp_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 < 'len'  */

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

}

/*--------------------------- tftp_fwrite -----------------------------------*/

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

  /* Write data to file. Return number of bytes actually written. */

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

}

/*--------------------------- tftp_accept_host ------------------------------*/

#if 0

BOOL tftp_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[] ==   &&

       rem_ip[] ==   &&

       rem_ip[] ==     &&

       rem_ip[] == ) {

      /* Accept a connection. */ 

      return (__TRUE);

   }

   /* Deny a connection. */

   return (__FALSE);

}

#endif

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

 * end of file

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

39.7 总结

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