樊春春 4 ヶ月 前
コミット
f36e0d70ef

+ 9 - 2
CMakeLists.txt

@@ -39,9 +39,12 @@ file(GLOB_RECURSE C_SRCS
         ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/STM32_USB_OTG_Driver/src/usb_core.c
         ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/STM32_USB_OTG_Driver/src/usb_hcd.c
         ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/STM32_USB_OTG_Driver/src/usb_hcd_int.c
-        ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/FATFS/ff.c
-        ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/FATFS/option/cc936.c
+        ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/FATFS/src/ff.c
+        ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/FATFS/src/option/cc936.c
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/api/*.c
+        ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/apps/tftp/*.c
+        # ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/apps/httpd/fs.c
+        # ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/apps/httpd/fsdata.c
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/core/*.c
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/core/ipv4/*.c
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/core/snmp/*.c
@@ -95,6 +98,7 @@ set(INC_C_DIRS
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/uCOS_II/uCOS-II/Source
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/uCOS_II/uCOS-II/Ports/ARM-Cortex-M4/Generic/GNU
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/include
+        # ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/apps/httpd
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/include/lwip
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/include/netif
         ${CMAKE_CURRENT_SOURCE_DIR}/Third_Party/lwip-2.0.2/src/include/posix
@@ -121,9 +125,12 @@ set(INC_C_DIRS
         ${CMAKE_CURRENT_SOURCE_DIR}/User/bsp/uart
         ${CMAKE_CURRENT_SOURCE_DIR}/User/bsp/usbh
         ${CMAKE_CURRENT_SOURCE_DIR}/User/app
+        ${CMAKE_CURRENT_SOURCE_DIR}/User/app/avltree
+        ${CMAKE_CURRENT_SOURCE_DIR}/User/app/ftp
         ${CMAKE_CURRENT_SOURCE_DIR}/User/app/iec104
         ${CMAKE_CURRENT_SOURCE_DIR}/User/app/key
         ${CMAKE_CURRENT_SOURCE_DIR}/User/app/queue
+        ${CMAKE_CURRENT_SOURCE_DIR}/User/app/tftp
         ${CMAKE_CURRENT_SOURCE_DIR}/User/app/memory
         ${CMAKE_CURRENT_SOURCE_DIR}/User/app/modbus
         ${CMAKE_CURRENT_SOURCE_DIR}/User/app/net

+ 2 - 1
Project/conf/fatfs/bsp_fatfs.c

@@ -1,12 +1,13 @@
 #include "bsp_fatfs.h"
 #include "ext_sram.h"
-#include "interface.h"
 #include "project_var.h"
 #include "stm32f4xx.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+OS_EVENT *sd_mutex = NULL;
+
 FATFS FatFs[_VOLUMES]; /* FatFs文件系统对象  4144字节*/
 FIL   file_object;     /* 文件对象           4132字节*/
 

+ 19 - 1
Project/conf/fatfs/bsp_fatfs.h

@@ -2,7 +2,25 @@
 #define _FATFS_CONF_H
 
 #include "ff.h"
+#include "interface.h"
 #include "stm32f4xx.h"
+
+extern OS_EVENT *sd_mutex;
+
+struct FileDate // STM32 是小端 ,我觉得说应该找不到大端的cpu吧。。。
+{
+    uint16_t Day   : 5;
+    uint16_t Month : 4;
+    uint16_t Year  : 7;
+};
+struct FileTime // STM32 是小端 ,我觉得说应该找不到大端的cpu吧。。。
+{
+    uint16_t Sec  : 5;
+    uint16_t Min  : 6;
+    uint16_t Hour : 5;
+};
+
 extern FATFS FatFs[_VOLUMES];
-void fatfs_init(void); // 文件系统初始化
+extern FIL   file_object;      /* 文件对象           4132字节*/
+void         fatfs_init(void); // 文件系统初始化
 #endif

+ 1 - 1
Project/conf/lwip/lwipopts.h

@@ -109,7 +109,7 @@
 /* netconn options */
 #define LWIP_NETCONN 1 /* set to 1 to enable netconn API (require to use api_lib.c) */
 
-#define MEMP_NUM_NETCONN 4 /* the number of struct netconns */
+#define MEMP_NUM_NETCONN 10 /* the number of struct netconns */
 
 /* socket options */
 #define LWIP_SOCKET 1 /* set to 1 to enable socket API (require to use sockets.c) */

+ 27 - 6
Project/main.c

@@ -73,6 +73,7 @@ int main(void)
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
     OSInit();
     bsp_init();
+    ftp_server_init();
     iec10x_regist();
 
     /* init task */
@@ -150,15 +151,35 @@ void init_task(void *pvParameters)
                     (void *)0,
                     (INT16U)OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);
 
-    OSTaskCreateExt((void (*)(void *))modbus_tcp_task,
+    OSTaskCreateExt((void (*)(void *))ftp_server_cmd_task,
                     (void *)0,
-                    (OS_STK *)&tcp_task_stk[TCP_TASK_STK_SIZE - 1],
-                    (INT8U)TCP_PRIO,
-                    (INT16U)TCP_PRIO,
-                    (OS_STK *)&tcp_task_stk[0],
-                    (INT32U)TCP_TASK_STK_SIZE,
+                    (OS_STK *)&ftp_server_cmd_task_stk[FTP_CMD_TASK_STK_SIZE - 1],
+                    (INT8U)FTP_CMD_PRIO,
+                    (INT16U)FTP_CMD_PRIO,
+                    (OS_STK *)&ftp_server_cmd_task_stk[0],
+                    (INT32U)FTP_CMD_TASK_STK_SIZE,
                     (void *)0,
                     (INT16U)OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);
+
+    OSTaskCreateExt((void (*)(void *))ftp_server_data_task,
+                    (void *)0,
+                    (OS_STK *)&ftp_server_data_task_stk[FTP_DATA_TASK_STK_SIZE - 1],
+                    (INT8U)FTP_DATA_PRIO,
+                    (INT16U)FTP_DATA_PRIO,
+                    (OS_STK *)&ftp_server_data_task_stk[0],
+                    (INT32U)FTP_DATA_TASK_STK_SIZE,
+                    (void *)0,
+                    (INT16U)OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);
+
+    // OSTaskCreateExt((void (*)(void *))modbus_tcp_task,
+    //                 (void *)0,
+    //                 (OS_STK *)&tcp_task_stk[TCP_TASK_STK_SIZE - 1],
+    //                 (INT8U)TCP_PRIO,
+    //                 (INT16U)TCP_PRIO,
+    //                 (OS_STK *)&tcp_task_stk[0],
+    //                 (INT32U)TCP_TASK_STK_SIZE,
+    //                 (void *)0,
+    //                 (INT16U)OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);
 }
 
 void misc_task(void)

+ 1 - 0
Project/main.h

@@ -5,6 +5,7 @@
 #include "am2303.h"
 #include "bsp_fatfs.h"
 #include "fly_param.h"
+#include "ftp_server.h"
 #include "includes.h"
 #include "interface.h"
 #include "iwdg.h"

+ 2 - 0
User/Bsp/armfly_bsp.c

@@ -6,6 +6,8 @@ void bsp_init(void)
     net_mutex  = OSMutexCreate(15, &err);
     uart1_mbox = OSMboxCreate(NULL);
     uart3_mbox = OSMboxCreate(NULL);
+    ftp_mbox   = OSMboxCreate(NULL);
+    sd_mutex   = OSMutexCreate(SD_MUTEX_PRIO, &err);
 
     InitQueue(&CanQueueCan1);
     InitQueue(&CanQueueCan2);

+ 1 - 0
User/Bsp/armfly_bsp.h

@@ -17,6 +17,7 @@
 #include "queue.h"
 #include "spi.h"
 #include "stm32f4x7_phy.h"
+#include "tftp.h"
 #include "timer.h"
 #include "uart1.h"
 #include "uart3.h"

+ 592 - 0
User/app/avltree/avltree.c

@@ -0,0 +1,592 @@
+#include "avltree.h"
+#include <stdint.h>
+#include <stdio.h>
+
+// #define AVL_DEBUG
+
+#ifdef AVL_DEBUG
+#define avl_debug(...) printf(__VA_ARGS__)
+#else
+#define avl_debug(...)
+#endif
+
+static void __avl_rotate_right(struct avl_node *node, struct avl_root *root);
+static void __avl_rotate_left(struct avl_node *node, struct avl_root *root);
+
+static int __avl_balance_right(struct avl_node *node, struct avl_root *root);
+static int __avl_balance_left(struct avl_node *node, struct avl_root *root);
+
+static int __left_hand_insert_track_back(struct avl_node *node, struct avl_root *root);
+static int __right_hand_insert_track_back(struct avl_node *node, struct avl_root *root);
+
+#define __left_hand_delete_track_back(node, root)  __right_hand_insert_track_back(node, root)
+#define __right_hand_delete_track_back(node, root) __left_hand_insert_track_back(node, root)
+
+/**
+ * @brief    __avl_rotate_right
+ *           对 node 节点进行单右旋处理
+ * @param    node        二叉树节点
+ * @param    root        二叉树根
+ * @return
+ */
+static void __avl_rotate_right(struct avl_node *node, struct avl_root *root)
+{
+    // 对以*node为根的二叉树作右旋转处理,处理之后node指向新的树根结点
+    // 即旋转处理之前的左子树的根结点
+    struct avl_node *left   = node->avl_left;
+    struct avl_node *parent = avl_parent(node);
+
+    if ((node->avl_left = left->avl_right))
+        avl_set_parent(left->avl_right, node);
+
+    left->avl_right = node;
+
+    avl_set_parent(left, parent);
+
+    if (parent)
+    {
+        if (node == parent->avl_right)
+            parent->avl_right = left;
+        else
+            parent->avl_left = left;
+    }
+    else
+        root->avl_node = left;
+
+    avl_set_parent(node, left);
+}
+
+/**
+ * @brief    __avl_rotate_left
+ *           对 node 节点进行单左旋处理
+ * @param    node        二叉树节点
+ * @param    root        二叉树根
+ * @return
+ */
+static void __avl_rotate_left(struct avl_node *node, struct avl_root *root)
+{
+    struct avl_node *right  = node->avl_right;
+    struct avl_node *parent = avl_parent(node);
+
+    if ((node->avl_right = right->avl_left))
+        avl_set_parent(right->avl_left, node);
+
+    right->avl_left = node;
+
+    avl_set_parent(right, parent);
+
+    if (parent)
+    {
+        if (node == parent->avl_left)
+            parent->avl_left = right;
+        else
+            parent->avl_right = right;
+    }
+    else
+        root->avl_node = right;
+
+    avl_set_parent(node, right);
+}
+
+/**
+ * @brief    __avl_balance_left
+ *           当 node 节点左子树高于右子树, 对 node 节点进行左平衡处理并更新平衡因子
+ * @param    node        二叉树节点
+ * @param    root        二叉树根
+ * @return   对于原 node 所在位置,经过平衡处理使树的高度降低了返回 -1,否则返回0
+ */
+static int __avl_balance_left(struct avl_node *node, struct avl_root *root)
+{
+    int              retl = 0;
+    int              left_right_child_scale;
+    struct avl_node *left_child = node->avl_left;
+    struct avl_node *left_left_child;
+    struct avl_node *left_right_child;
+
+    if (left_child)
+    {
+        left_left_child  = left_child->avl_left;
+        left_right_child = left_child->avl_right;
+
+        switch (avl_scale(left_child))
+        {
+        case AVL_BALANCED: // 只有在删除的时候会出现这种情况,情况非常复杂,需小心处理
+            left_right_child_scale = avl_scale(left_right_child);
+            __avl_rotate_left(node->avl_left, root); // 对*node的左子树作左平衡处理
+            __avl_rotate_right(node, root);          // 对*node作右平衡处理
+            avl_set_tilt_left(left_child);
+            avl_set_tilt_left(left_right_child);
+
+            if (left_right_child_scale == AVL_BALANCED)
+                avl_set_balanced(node);
+            else if (left_right_child_scale == AVL_TILT_LEFT)
+                avl_set_tilt_right(node);
+            else
+            {
+                int left_left_child_scale = avl_scale(left_left_child);
+                avl_set_balanced(node);
+                retl = __avl_balance_left(left_child, root); // 这种情况需递归,并需要根据递归结果更新平衡因子
+                if ((retl < 0) || (left_left_child_scale != AVL_BALANCED))
+                    avl_set_balanced(left_right_child);
+            }
+            avl_debug("L_R\r\n");
+            break;
+
+        case AVL_TILT_LEFT:
+            __avl_rotate_right(node, root);
+            avl_set_balanced(node);
+            avl_set_balanced(left_child);
+            retl = -1; // 高度变低了
+            avl_debug("R\r\n");
+            break;
+
+        case AVL_TILT_RIGHT:
+            __avl_rotate_left(node->avl_left, root); // 对*node的左子树作左平衡处理
+            __avl_rotate_right(node, root);          // 对*node作右平衡处理
+
+            switch (avl_scale(left_right_child))
+            {
+            case AVL_BALANCED:
+                avl_set_balanced(node);
+                avl_set_balanced(left_child);
+                break;
+            case AVL_TILT_RIGHT:
+                avl_set_balanced(node);
+                avl_set_tilt_left(left_child);
+                break;
+            case AVL_TILT_LEFT:
+                avl_set_balanced(left_child);
+                avl_set_tilt_right(node);
+                break;
+            }
+
+            avl_set_balanced(left_right_child);
+            retl = -1; // 高度变低
+
+            avl_debug("L_R\r\n");
+            break;
+        }
+    }
+
+    return retl;
+}
+
+/**
+ * @brief    __avl_balance_right
+ *           当 node 节点右子树高于左子树, 对 node 节点进行左平衡处理并更新平衡因子
+ * @param    node        二叉树节点
+ * @param    root        二叉树根
+ * @return   对于原 node 所在树的位置,经过平衡处理使树的高度降低了返回 -1,否则返回0
+ */
+static int __avl_balance_right(struct avl_node *node, struct avl_root *root)
+{
+    int              ret = 0;
+    int              right_left_child_scale;
+    struct avl_node *right_child       = node->avl_right;
+    struct avl_node *right_left_child  = right_child->avl_left;
+    struct avl_node *right_right_child = right_child->avl_right;
+
+    if (right_child)
+    {
+        switch (avl_scale(right_child))
+        {
+        case AVL_BALANCED: // 删除的时候会出现这种情况,需要特别注意
+            right_left_child_scale = avl_scale(right_left_child);
+
+            __avl_rotate_right(node->avl_right, root);
+            __avl_rotate_left(node, root);
+            avl_set_tilt_right(right_left_child);
+            avl_set_tilt_right(right_child);
+
+            if (right_left_child_scale == AVL_BALANCED)
+                avl_set_balanced(node);
+            else if (right_left_child_scale == AVL_TILT_RIGHT)
+                avl_set_tilt_left(node);
+            else
+            {
+                int right_right_child_scale = avl_scale(right_right_child);
+                avl_set_balanced(node);
+                ret = __avl_balance_right(right_child, root); // 需递归一次
+                if ((ret < 0) || (right_right_child_scale != AVL_BALANCED))
+                    avl_set_balanced(right_left_child);
+            }
+            avl_debug("R_L\r\n");
+            break;
+
+        case AVL_TILT_RIGHT:
+            avl_debug("L\r\n");
+            __avl_rotate_left(node, root);
+            avl_set_balanced(node);
+            avl_set_balanced(right_child);
+            ret = -1; // 高度变低了//
+            break;
+
+        case AVL_TILT_LEFT:
+            __avl_rotate_right(node->avl_right, root);
+            __avl_rotate_left(node, root);
+
+            avl_debug("R_L\r\n");
+            switch (avl_scale(right_left_child)) // 旋转后要更新平衡因子
+            {
+            case AVL_TILT_LEFT:
+                avl_set_tilt_right(right_child);
+                avl_set_balanced(node);
+                break;
+            case AVL_BALANCED:
+                avl_set_balanced(node);
+                avl_set_balanced(right_child);
+                break;
+            case AVL_TILT_RIGHT:
+                avl_set_balanced(right_child);
+                avl_set_tilt_left(node);
+                break;
+            }
+
+            avl_set_balanced(right_left_child);
+            ret = -1; //
+            break;
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief    __left_hand_insert_track_back
+ *           在 node 节点的左子树进行了插入,更新平衡因子,如失衡则作平衡处理
+ * @param    node        二叉树节点
+ * @param    root        二叉树根
+ * @return   对于原 node 所在树的位置,经过平衡处理使树的高度降低了返回 -1,否则返回0
+ */
+static int __left_hand_insert_track_back(struct avl_node *node, struct avl_root *root)
+{
+    switch (avl_scale(node))
+    {
+    case AVL_BALANCED:
+        avl_set_tilt_left(node);
+        return 0;
+
+    case AVL_TILT_RIGHT:
+        avl_set_balanced(node);
+        return -1;
+
+    case AVL_TILT_LEFT:
+        return __avl_balance_left(node, root);
+    }
+
+    return 0;
+}
+
+/**
+ * @brief    __left_hand_insert_track_back
+ *           在 node 节点的右子树进行了插入,更新平衡因子,如失衡则作平衡处理
+ * @param    node        二叉树节点
+ * @param    root        二叉树根
+ * @return   对于原 node 所在树的位置,插入并平衡后高度降低了返回 -1,否则返回0
+ */
+static int __right_hand_insert_track_back(struct avl_node *node, struct avl_root *root)
+{
+    switch (avl_scale(node))
+    {
+    case AVL_BALANCED:
+        avl_set_tilt_right(node); // 父节点右倾
+        return 0;                 // 以 node 为根的树高度被改变,但未失衡
+
+    case AVL_TILT_LEFT:
+        avl_set_balanced(node); // 父节点平衡
+        return -1;              // 以 node 为根的树高度不改变
+
+    case AVL_TILT_RIGHT:                        // 以 node 为根的树已失衡,作平衡处理
+        return __avl_balance_right(node, root); //
+    }
+
+    return 0;
+}
+
+void avl_insert(
+    struct avl_root  *root,
+    struct avl_node  *insertnode,
+    struct avl_node  *parent,
+    struct avl_node **avl_link)
+{
+    int taller = 1;
+
+    struct avl_node *gparent = NULL;
+
+    uint8_t parent_gparent_path = 0;
+    uint8_t backtrack_path      = 0;
+
+    insertnode->avl_parent = (unsigned long)parent;
+    insertnode->avl_left = insertnode->avl_right = NULL;
+
+    *avl_link = insertnode;
+
+    if (root->avl_node == insertnode)
+        return;
+
+    if (AVL_BALANCED != avl_scale(parent))
+    {
+        avl_set_balanced(parent); // 父节点平衡
+        return;                   // 树没长高返回
+    }
+
+    backtrack_path = (insertnode == parent->avl_left) ? AVL_TILT_LEFT : AVL_TILT_RIGHT;
+
+    // 树长高了,需要回溯平衡
+    while (taller && parent)
+    {
+        // 回溯平衡过程会改变树的结构,先记录祖父节点和对应的回溯路径方向
+        if ((gparent = avl_parent(parent))) // 先赋值再判断
+            parent_gparent_path = (parent == gparent->avl_right) ? AVL_TILT_RIGHT : AVL_TILT_LEFT;
+
+        if (backtrack_path == AVL_TILT_RIGHT)
+            taller += __right_hand_insert_track_back(parent, root);
+        else
+            taller += __left_hand_insert_track_back(parent, root);
+
+        backtrack_path = parent_gparent_path; // 回溯
+        parent         = gparent;
+    }
+}
+
+#if 1
+void avl_delete(struct avl_root *root, struct avl_node *node)
+{
+    struct avl_node *child, *parent;
+    struct avl_node *gparent = NULL;
+    int              lower   = 1;
+
+    uint8_t parent_gparent_path = 0;
+    uint8_t backtrack_path      = 0;
+
+    if (!node->avl_left) // 如果被删节点不存在左子树
+    {
+        child = node->avl_right; // 把右子树接入父节点中
+
+        parent = avl_parent(node);
+
+        if (child)
+            avl_set_parent(child, parent);
+
+        if (parent)
+        {
+            if (parent->avl_left == node)
+            {
+                parent->avl_left = child;
+                backtrack_path   = AVL_TILT_LEFT;
+            }
+            else
+            {
+                parent->avl_right = child;
+                backtrack_path    = AVL_TILT_RIGHT;
+            }
+        }
+        else
+        {
+            root->avl_node = child;
+            if (child)
+                avl_set_balanced(child);
+            return;
+        }
+    }
+    else if (!node->avl_right) // 如果被删节点存在左子树,不存在右子树
+    {
+        child = node->avl_left;
+
+        parent = avl_parent(node);
+
+        avl_set_parent(child, parent);
+
+        if (parent)
+        {
+            if (parent->avl_left == node)
+            {
+                parent->avl_left = child;
+                backtrack_path   = AVL_TILT_LEFT;
+            }
+            else
+            {
+                parent->avl_right = child;
+                backtrack_path    = AVL_TILT_RIGHT;
+            }
+        }
+        else
+        {
+            root->avl_node = child;
+            if (child)
+                avl_set_balanced(child);
+            return;
+        }
+    }
+    else // 被删节点即存在左子树,也存在右子树
+    {
+        struct avl_node *old = node, *left;
+
+        node = node->avl_right;
+
+        while ((left = node->avl_left) != NULL)
+            node = left; // 找到右子树下最小的替代点
+
+        if (avl_parent(old)) // 旧点所在父节点
+        {
+            if (avl_parent(old)->avl_left == old)
+                avl_parent(old)->avl_left = node;
+            else
+                avl_parent(old)->avl_right = node;
+        }
+        else
+            root->avl_node = node;
+
+        child  = node->avl_right;  // 最小的替代点的右节点
+        parent = avl_parent(node); // 最小的替代点的父节点
+
+        if (parent == old) // 要删除的节点的右子树没有左子树,替代点(1)没有右节点,(2)存在一个右节点
+        {
+            backtrack_path = AVL_TILT_RIGHT;
+
+            parent           = node;
+            node->avl_parent = old->avl_parent;
+            node->avl_left   = old->avl_left;
+            avl_set_parent(old->avl_left, node);
+        }
+        else // 要删除的节点的右子树有左子树
+        {
+            backtrack_path = AVL_TILT_LEFT;
+
+            parent->avl_left = child; // 最小的替代点的右节点的父节点
+            node->avl_right  = old->avl_right;
+            avl_set_parent(old->avl_right, node);
+
+            node->avl_parent = old->avl_parent;
+            node->avl_left   = old->avl_left;
+            avl_set_parent(old->avl_left, node);
+
+            if (child)
+                avl_set_parent(child, parent); // 要删除的节点的右子树的左子树末尾点存在右节点
+        }
+    }
+
+    // 树低了,需要回溯平衡,直到回溯到根节点
+    while (lower && parent)
+    {
+        if ((gparent = avl_parent(parent))) //(parent && (gparent = avl_parent(parent)))//先赋值再判断
+            parent_gparent_path = (parent == gparent->avl_right) ? AVL_TILT_RIGHT : AVL_TILT_LEFT;
+
+        if (backtrack_path == AVL_TILT_RIGHT) // 经过回溯调整会改变树的结构,所以先记录 gparent 和回溯路径
+            lower = __right_hand_delete_track_back(parent, root);
+        else
+            lower = __left_hand_delete_track_back(parent, root);
+
+        backtrack_path = parent_gparent_path;
+        parent         = gparent;
+    }
+}
+
+#endif
+
+/*
+ * This function returns the first node (in sort oright_left_childer) of the tree.
+ */
+struct avl_node *avl_first(const struct avl_root *root)
+{
+    struct avl_node *n;
+
+    n = root->avl_node;
+    if (!n)
+        return NULL;
+    while (n->avl_left)
+        n = n->avl_left;
+    return n;
+}
+
+struct avl_node *avl_last(const struct avl_root *root)
+{
+    struct avl_node *n;
+
+    n = root->avl_node;
+    if (!n)
+        return NULL;
+    while (n->avl_right)
+        n = n->avl_right;
+    return n;
+}
+
+struct avl_node *avl_next(const struct avl_node *node)
+{
+    struct avl_node *parent;
+
+    if (avl_parent(node) == node)
+        return NULL;
+
+    /* If we have a right-hand child, go down and then left as far
+       as we can. */
+    if (node->avl_right)
+    {
+        node = node->avl_right;
+        while (node->avl_left)
+            node = node->avl_left;
+        return (struct avl_node *)node;
+    }
+
+    /* No right-hand children.  Everything down and left is
+       smaller than us, so any 'next' node must be in the general
+       direction of our parent. Go up the tree; any time the
+       ancestor is a right-hand child of its parent, keep going
+       up. First time it's a left-hand child of its parent, said
+       parent is our 'next' node. */
+    while ((parent = avl_parent(node)) && node == parent->avl_right)
+        node = parent;
+
+    return parent;
+}
+
+struct avl_node *avl_prev(const struct avl_node *node)
+{
+    struct avl_node *parent;
+
+    if (avl_parent(node) == node)
+        return NULL;
+
+    /* If we have a left-hand child, go down and then right as far
+       as we can. */
+    if (node->avl_left)
+    {
+        node = node->avl_left;
+        while (node->avl_right)
+            node = node->avl_right;
+        return (struct avl_node *)node;
+    }
+
+    /* No left-hand children. Go up till we find an ancestor which
+       is a right-hand child of its parent */
+    while ((parent = avl_parent(node)) && node == parent->avl_left)
+        node = parent;
+
+    return parent;
+}
+
+void avl_replace_node(struct avl_node *victim, struct avl_node *new,
+                      struct avl_root *root)
+{
+    struct avl_node *parent = avl_parent(victim);
+
+    /* Set the surrounding nodes to point to the replacement */
+    if (parent)
+    {
+        if (victim == parent->avl_left)
+            parent->avl_left = new;
+        else
+            parent->avl_right = new;
+    }
+    else
+    {
+        root->avl_node = new;
+    }
+    if (victim->avl_left)
+        avl_set_parent(victim->avl_left, new);
+    if (victim->avl_right)
+        avl_set_parent(victim->avl_right, new);
+
+    /* Copy the pointers/colour from the victim to the replacement */
+    *new = *victim;
+}

+ 91 - 0
User/app/avltree/avltree.h

@@ -0,0 +1,91 @@
+#ifndef __AVL_TREE_H__
+#define __AVL_TREE_H__
+
+//------------------------------------------------------------------
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER) // 获取"MEMBER成员"在"结构体TYPE"中的位置偏移
+#endif
+
+#ifndef container_of
+#if 1
+// 根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针
+#define container_of(ptr, type, member) ((type *)((char *)ptr - offsetof(type, member)))
+// 此宏定义原文为 GNU C 所写,如下,有些编译器只支持 ANSI C /C99 的,所以作以上修改
+#else
+#define container_of(ptr, type, member) ({          \
+		const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+		(type *)( (char *)__mptr - offsetof(type,member) ); })
+#endif
+#endif
+//------------------------------------------------------------------
+
+struct avl_node
+{
+    unsigned long    avl_parent;
+    struct avl_node *avl_left;
+    struct avl_node *avl_right;
+};
+
+struct avl_root
+{
+    struct avl_node *avl_node;
+};
+
+#define avl_parent(r) ((struct avl_node *)((r)->avl_parent & (~3)))
+
+//------------------------------------------------------------------
+// 获取 avl 节点的平衡因子,值如下
+#define avl_scale(r)   ((r)->avl_parent & 3)
+#define AVL_BALANCED   0 // 节点平衡
+#define AVL_TILT_RIGHT 1 // 节点右边比左边高,用 0b01 表示,右倾
+#define AVL_TILT_LEFT  2 // 节点左边比右边高,用 0b10 表示,左倾
+
+//------------------------------------------------------------------
+// 设置 avl 节点的平衡因子
+// 设置 avl 节点为平衡节点
+#define avl_set_balanced(r)        \
+    do                             \
+    {                              \
+        ((r)->avl_parent) &= (~3); \
+    } while (0)
+
+// 设置 avl 节点为右倾节点
+#define avl_set_tilt_right(r)                                        \
+    do                                                               \
+    {                                                                \
+        (r)->avl_parent = (((r)->avl_parent & ~3) | AVL_TILT_RIGHT); \
+    } while (0)
+
+// 设置 avl 节点为左倾节点
+#define avl_set_tilt_left(r)                                        \
+    do                                                              \
+    {                                                               \
+        (r)->avl_parent = (((r)->avl_parent & ~3) | AVL_TILT_LEFT); \
+    } while (0)
+
+//------------------------------------------------------------------
+
+#define avl_entry(ptr, type, member) container_of(ptr, type, member)
+
+// 设置 avl 节点的父节点为 p
+static inline void avl_set_parent(struct avl_node *avl, struct avl_node *p)
+{
+    avl->avl_parent = (avl->avl_parent & 3) | (unsigned long)p;
+}
+
+void avl_insert(
+    struct avl_root  *root,
+    struct avl_node  *insertnode,
+    struct avl_node  *insertparent,
+    struct avl_node **avl_link);
+
+void avl_delete(struct avl_root *root, struct avl_node *node);
+
+/* Find logical next and previous nodes in a tree */
+extern struct avl_node *avl_next(const struct avl_node *);
+extern struct avl_node *avl_prev(const struct avl_node *);
+extern struct avl_node *avl_first(const struct avl_root *);
+extern struct avl_node *avl_last(const struct avl_root *);
+void                    avl_replace_node(struct avl_node *victim, struct avl_node *new,
+                                         struct avl_root *root);
+#endif

+ 3 - 0
User/app/fly_param.c

@@ -10,6 +10,7 @@ OS_EVENT *can2_sem     = NULL;
 OS_EVENT *net_sem      = NULL;
 OS_EVENT *uart1_mbox   = NULL;
 OS_EVENT *uart3_mbox   = NULL;
+OS_EVENT *ftp_mbox     = NULL;
 
 SqQueue CanQueueCan1 __attribute__((section("EXT_SRAM")));
 SqQueue CanQueueCan2 __attribute__((section("EXT_SRAM")));
@@ -19,5 +20,7 @@ CPU_STK uart3_task_stk[UART3_TASK_STK_SIZE] __attribute__((section("EXT_SRAM")))
 CPU_STK init_task_stk[INIT_STK_SIZE];
 CPU_STK iec104_task_stk[IEC_TASK_STK_SIZE]; //__attribute__((section("EXT_SRAM")));
 CPU_STK iec_state_task_stk[IEC_STATE_TASK_STK_SIZE];
+CPU_STK ftp_server_cmd_task_stk[FTP_CMD_TASK_STK_SIZE];
+CPU_STK ftp_server_data_task_stk[FTP_DATA_TASK_STK_SIZE];
 CPU_STK tcp_task_stk[TCP_TASK_STK_SIZE];
 CPU_STK misc_task_stk[MISC_TASK_STK_SIZE]; //__attribute__((section("EXT_SRAM")));

+ 10 - 1
User/app/fly_param.h

@@ -6,18 +6,24 @@
 
 #define CAN_MSG_LEN 8
 
+#define SD_MUTEX_PRIO 20
+
 #define UART1_TASK_PRIO 23
 #define UART3_TASK_PRIO 24
 #define MISC_PRIO       28
 #define IEC_PRIO        30
-#define IEC_STATE_PRIO  32
 #define TCP_PRIO        31
+#define IEC_STATE_PRIO  32
+#define FTP_CMD_PRIO    33
+#define FTP_DATA_PRIO   34
 #define INIT_TASK_PRIO  35
 
 #define INIT_STK_SIZE           128
 #define MISC_TASK_STK_SIZE      512
 #define IEC_TASK_STK_SIZE       1024
 #define IEC_STATE_TASK_STK_SIZE 512
+#define FTP_CMD_TASK_STK_SIZE   512
+#define FTP_DATA_TASK_STK_SIZE  2048
 #define TCP_TASK_STK_SIZE       512
 #define LED1_STK_SIZE           512
 #define UART1_TASK_STK_SIZE     256
@@ -28,6 +34,7 @@ extern OS_EVENT *can1_sem;
 extern OS_EVENT *can2_sem;
 extern OS_EVENT *uart1_mbox;
 extern OS_EVENT *uart3_mbox;
+extern OS_EVENT *ftp_mbox;
 
 extern SqQueue CanQueueCan1;
 extern SqQueue CanQueueCan2;
@@ -38,6 +45,8 @@ extern CPU_STK uart3_task_stk[UART3_TASK_STK_SIZE];
 extern CPU_STK misc_task_stk[MISC_TASK_STK_SIZE];
 extern CPU_STK iec104_task_stk[IEC_TASK_STK_SIZE];
 extern CPU_STK iec_state_task_stk[IEC_STATE_TASK_STK_SIZE];
+extern CPU_STK ftp_server_cmd_task_stk[FTP_CMD_TASK_STK_SIZE];
+extern CPU_STK ftp_server_data_task_stk[FTP_DATA_TASK_STK_SIZE];
 extern CPU_STK tcp_task_stk[TCP_TASK_STK_SIZE];
 
 #endif

+ 535 - 0
User/app/ftp/ftpd.c

@@ -0,0 +1,535 @@
+#include "ftpd.h"
+#include "avltree.h"
+#include "bsp_fatfs.h"
+#include "ff.h"
+#include "fly_param.h"
+#include "ftp_server.h"
+#include "os_cpu.h"
+#include "sockets.h"
+#include "ucos_ii.h"
+#include <stdint.h>
+#include <stdio.h>
+
+static struct avl_root ftp_root = {.avl_node = NULL}; // 命令匹配的平衡二叉树树根
+
+/* Private variables ------------------------------------------------------------*/
+
+static const char  normal_format[]    = "-rw-rw-rw-   1 user     ftp  %11ld %s %02i %5i %s\r\n";
+static const char  this_year_format[] = "-rw-rw-rw-   1 user     ftp  %11ld %s %02i %02i:%02i %s\r\n";
+static const char *month_list[]       = { // 月份从 1 到 12 ,0 填充 NULL
+    NULL,
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dez"};
+
+// ftp 命令树构建
+#define FTP_REGISTER_COMMAND(CMD)        \
+    do                                   \
+    {                                    \
+        static ftp_cmd_t CmdBuf;         \
+        CmdBuf.index = FTP_STR2ID(#CMD); \
+        CmdBuf.func  = cmd_##CMD;        \
+        ftp_insert_command(&CmdBuf);     \
+    } while (0)
+
+// ftp 文件列表格式
+#define NORMAL_LIST(listbuf, filesize, month, day, year, filename) \
+    sprintf(listbuf, normal_format, (filesize), month_list[(month)], (day), (year), (filename))
+
+#define THIS_YEAR_LIST(listbuf, filesize, month, day, hour, min, filename) \
+    sprintf(listbuf, this_year_format, (filesize), month_list[(month)], (day), (hour), (min), (filename))
+
+// ftp 格式一般为 xxxx /dirx/diry/\r\n ,去掉 /\r\n 提取可用路径
+#define FTP_PATH_GET(path, pathend) \
+    do                              \
+    {                               \
+        while (*path == ' ')        \
+            ++path;                 \
+        if (*pathend == '\n')       \
+            *pathend-- = 0;         \
+        if (*pathend == '\r')       \
+            *pathend-- = 0;         \
+        if (*pathend == '/')        \
+            *pathend = 0;           \
+    } while (0)
+
+/**
+ * @brief    ftp_insert_command
+ *           命令树插入
+ * @param    pCmd        命令控制块
+ * @return   成功返回 0
+ */
+int ftp_insert_command(ftp_cmd_t *p_cmd)
+{
+    struct avl_node **tmp    = &ftp_root.avl_node;
+    struct avl_node  *parent = NULL;
+
+    while (*tmp)
+    {
+        ftp_cmd_t *this = container_of(*tmp, ftp_cmd_t, cmd_node);
+
+        parent = *tmp;
+        if (p_cmd->index < this->index)
+            tmp = &((*tmp)->avl_left);
+        else if (p_cmd->index > this->index)
+            tmp = &((*tmp)->avl_right);
+        else
+            return 1;
+    }
+
+    avl_insert(&ftp_root, &p_cmd->cmd_node, parent, tmp);
+    return 0;
+}
+
+/**
+ * @brief    ftp_search_command
+ *           命令树查找,根据 Index 号找到对应的控制块
+ * @param    index        命令号
+ * @return   成功 Index 号对应的控制块
+ */
+ftp_cmd_t *ftp_search_command(int i_ctrl_cmd)
+{
+    struct avl_node *node = ftp_root.avl_node;
+
+    while (node)
+    {
+        ftp_cmd_t *p_cmd = container_of(node, ftp_cmd_t, cmd_node);
+
+        if (i_ctrl_cmd < p_cmd->index)
+            node = node->avl_left;
+        else if (i_ctrl_cmd > p_cmd->index)
+            node = node->avl_right;
+        else
+            return p_cmd;
+    }
+
+    return NULL;
+}
+// 登录系统用户名
+static void cmd_user(ftp_mbox_t *msgbox)
+{
+    if (strcasecmp(msgbox->arg, "root") == 0)
+    {
+        send(msgbox->cmd_socket, msg331, sizeof(msg331) - 1, 0);
+    }
+    else
+    {
+        send(msgbox->cmd_socket, msg331, sizeof(msg331) - 1, 0);
+    }
+}
+
+// 登录系统密码
+static void cmd_pass(ftp_mbox_t *msgbox)
+{
+    // printf(msgbox->arg);
+    // if (strcasecmp(msgbox->arg, "ASASAS") == 0)
+    // {
+    send(msgbox->cmd_socket, msg230, sizeof(msg230) - 1, 0);
+    // }
+    // else
+    // {
+    //     send(msgbox->cmd_socket, msg503, sizeof(msg503) - 1, 0);
+    // }
+}
+
+// 操作系统类型
+static void cmd_syst(ftp_mbox_t *msgbox)
+{
+    static const char reply_msg[] = "215 UNIX Type: L8\r\n"; // 215 系统类型回复
+    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
+}
+
+// 显示当前工作目录
+static void cmd_pwd(ftp_mbox_t *msgbox)
+{
+#if 1
+    char reply_msg[128]; // 257 路径名建立
+    // sprintf(reply_msg, "257 \"%s/\"\r\n", msgbox->current_dir);
+    sprintf(reply_msg, "257 \"%s/\"\r\n", "LINUX");
+#else
+    static const char reply_msg[] = "257 \"/\"\r\n";
+#endif
+    send(msgbox->cmd_socket, reply_msg, strlen(reply_msg), 0);
+}
+
+/**
+ * @brief    cmd_noop
+ *           ftp 命令端口输入 noop
+ * @param    arg 命令所跟参数
+ * @return   NULL
+ */
+static void cmd_noop(ftp_mbox_t *msgbox)
+{
+    static const char reply_msg[] = "200 Operation successful\r\n";
+    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
+}
+
+// 路径名建立
+static void cmd_cwd(ftp_mbox_t *msgbox)
+{
+    static const char reply_msg[] = "250 Operation successful\r\n";
+
+    DIR   fsdir;
+    char *pcFilePath = msgbox->arg;
+    char *pcPathEnd  = msgbox->arg + msgbox->arglen - 1;
+
+    FTP_PATH_GET(pcFilePath, pcPathEnd);
+
+    if (FR_OK != f_opendir(&fsdir, pcFilePath))
+    {
+        goto cmddone;
+    }
+
+    f_closedir(&fsdir);
+
+    if (pcPathEnd != pcFilePath)
+        memcpy(msgbox->current_dir, pcFilePath, pcPathEnd - pcFilePath);
+
+    msgbox->current_dir[pcPathEnd - pcFilePath] = 0;
+
+cmddone:
+    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
+}
+
+// static void cmd_feat(ftp_mbox_t *msgbox)
+// {
+//     static const char reply_msg[] = "211-feature:\r\n";
+//     static const char msgtprt[]   = "EPRT\r\n";
+//     send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
+
+//     send(msgbox->cmd_socket, msgtprt, sizeof(msgtprt) - 1, 0);
+// }
+
+// 被动模式
+static void cmd_pasv(ftp_mbox_t *msgbox)
+{
+    static char reply_msg[64] = {0};
+
+    INT32S stor = strlen(reply_msg);
+
+    if (0 == stor) // 未初始化信息
+    {
+        sprintf(reply_msg, "227 pasv ok(%d,%d,%d,%d,%d,%d)\r\n",
+                192, 168, 1, 40, (FTP_DATA_PORT >> 8), FTP_DATA_PORT & 0x00ff);
+
+        stor = strlen(reply_msg);
+    }
+
+    send(msgbox->cmd_socket, reply_msg, stor, 0);
+}
+
+/**
+ * @brief    cmd_nlst
+ *           ftp 命令端口输入 nlst ,被动模式
+ *
+ * @param    arg 命令所跟参数
+ * @return   NULL
+ */
+static void cmd_nlst(ftp_mbox_t *msgbox)
+{
+    static const char reply_msg[] = "150 Directory listing\r\n"; // 150 打开连接
+
+    // 1.在控制端口对 list 命令进行回复
+    // 2.在数据端口发送 "total 0",这个貌似可以没有
+    // 3.在数据端口发送文件列表
+    // 4.关闭数据端口
+
+    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
+    msgbox->event = FTP_LIST; // 事件为列表事件
+
+    OSMboxPost(ftp_mbox, msgbox);
+}
+
+/**
+ * @brief    cmd_list
+ *           ftp 命令端口输入 list , 获取当前文件列表
+ * @param    arg 命令所跟参数
+ * @return   NULL
+ */
+static void cmd_list(ftp_mbox_t *msgbox)
+{
+    static const char reply_msg[] = "150 Directory listing\r\n"; // 150 打开连接
+
+    // 1.在控制端口对 list 命令进行回复
+    // 2.在数据端口发送 "total 0",这个貌似可以没有
+    // 3.在数据端口发送文件列表
+    // 4.关闭数据端口
+
+    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
+    msgbox->event = FTP_LIST; // 事件为列表事件
+    OSMboxPost(ftp_mbox, msgbox);
+}
+
+/**
+ * @brief    cmd_size
+ *           ftp 命令端口输入 size , 获取当前文件列表
+ * @param    arg 命令所跟参数
+ * @return   NULL
+ */
+static void cmd_size(ftp_mbox_t *msgbox)
+{
+    char     acFtpBuf[128];
+    uint32_t iFilesize;
+    char    *pcFilePath = msgbox->arg;
+    char    *pcPathEnd  = msgbox->arg + msgbox->arglen - 1;
+
+    FTP_PATH_GET(pcFilePath, pcPathEnd);
+
+    if (*pcFilePath != '/') // 相对路径补全为绝对路径
+    {
+        pcFilePath = acFtpBuf;
+    }
+
+    if (FR_OK != f_open(&file_object, pcFilePath, FA_READ))
+    {
+        goto sizedone;
+    }
+
+    iFilesize = f_size(&file_object);
+    f_close(&file_object);
+
+sizedone:
+    send(msgbox->cmd_socket, acFtpBuf, strlen(acFtpBuf), 0);
+}
+
+/**
+ * @brief    cmd_retr
+ *           ftp 命令端口输入 retr
+ * @param    arg 命令所跟参数
+ * @return   NULL
+ */
+static void cmd_retr(ftp_mbox_t *msgbox)
+{
+    static const char reply_msg[] = "108 Operation successful\r\n";
+
+    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
+
+    msgbox->event = FTP_SEND_FILE_DATA;
+
+    OSMboxPost(ftp_mbox, msgbox);
+}
+
+/**
+ * @brief    cmd_dele
+ *           ftp 命令端口输入 dele
+ * @param    arg 命令所跟参数
+ * @return   NULL
+ */
+static void cmd_dele(ftp_mbox_t *msgbox)
+{
+    FRESULT res;
+    char    databuf[128];
+    char   *pcFilePath = msgbox->arg;
+    char   *pcPathEnd  = msgbox->arg + msgbox->arglen - 1;
+    FTP_PATH_GET(pcFilePath, pcPathEnd);
+
+    if (*pcFilePath != '/') // 相对路径
+    {
+        sprintf(databuf, "%s/%s", msgbox->current_dir, pcFilePath);
+        pcFilePath = databuf;
+    }
+
+    res = f_unlink(pcFilePath);
+    if (FR_OK != res)
+        goto deleError;
+
+    send(msgbox->cmd_socket, msg250, sizeof(msg250) - 1, 0);
+    return;
+
+deleError:
+    send(msgbox->cmd_socket, msg450, sizeof(msg450) - 1, 0);
+
+    return;
+}
+
+/**
+ * @brief    cmd_stor
+ *           ftp 命令端口输入 stor
+ * @param    arg 命令所跟参数
+ * @return   NULL
+ */
+static void cmd_stor(ftp_mbox_t *msgbox)
+{
+    static const char reply_msgOK[] = "125 Waiting\r\n";
+
+    send(msgbox->cmd_socket, reply_msgOK, sizeof(reply_msgOK) - 1, 0);
+
+    msgbox->event = FTP_RECV_FILE;
+
+    OSMboxPost(ftp_mbox, msgbox);
+}
+
+static const char ftp_msg_451[] = "451 errors";
+static const char ftp_msg_226[] = "226 transfer complete\r\n";
+
+/* Start node to be scanned (***also used as work area***) */
+char *data_port_list_file(ftp_mbox_t *msgbox)
+{
+    char   *ctrl_msg = (char *)ftp_msg_226;
+    char    list_buf[128];
+    DIR     dir;
+    FILINFO fno;
+
+    if (FR_OK != f_opendir(&dir, msgbox->current_dir))
+    {
+        goto ScanDirDone;
+    }
+
+    for (;;)
+    {
+        struct FileDate *pStDate;
+        struct FileTime *pStTime;
+        FRESULT          res = f_readdir(&dir, &fno); /* Read a directory item */
+
+        if (res != FR_OK || fno.fname[0] == 0) /* Break on error or end of dir */
+            break;
+
+        if ((fno.fattrib & AM_DIR) && (fno.fattrib != AM_DIR)) // 不显示只读/系统/隐藏文件夹
+            continue;
+
+        pStDate = (struct FileDate *)(&fno.fdate);
+        pStTime = (struct FileTime *)(&fno.ftime);
+
+        if (fno.fdate == 0 || fno.ftime == 0) // 没有日期的文件
+            NORMAL_LIST(list_buf, fno.fsize, 1, 1, 1980, fno.fname);
+        else if (pStDate->Year + 1980 == 2018) // 同一年的文件
+            THIS_YEAR_LIST(list_buf, fno.fsize, pStDate->Month, pStDate->Day, pStTime->Hour, pStTime->Min, fno.fname);
+        else
+            NORMAL_LIST(list_buf, fno.fsize, pStDate->Month, pStDate->Day, pStDate->Year + 1980, fno.fname);
+
+        if (fno.fattrib & AM_DIR) /* It is a directory */
+            list_buf[0] = 'd';
+
+        send(g_ftp_data_socket, list_buf, strlen(list_buf), 0);
+    }
+
+    f_closedir(&dir); // 把路径关闭
+
+ScanDirDone:
+    return ctrl_msg;
+}
+
+// char *data_port_recv_file(ftp_mbox_t *msgbox)
+// {
+//     // static __align(4) char recv_buf[TCP_MSS]; // fatfs 写文件的时候,buf要地址对齐,否则容易出错
+
+//     char          *ctrl_msg = (char *)ftp_msg_451;
+//     FIL            RecvFile; /* File object */
+//     FRESULT        res;
+//     char          *pcFile    = msgbox->arg;
+//     char          *pcPathEnd = msgbox->arg + msgbox->arglen - 1;
+//     char           databuf[128];
+//     uint16_t       sRxlen;
+//     uint32_t       byteswritten;
+//     struct netbuf *data_netbuf;
+//     int            ret;
+
+//     // vFtp_GetLegalPath(pcFile, pcPathEnd);
+
+//     if (*pcFile != '/') // 相对路径
+//     {
+//         sprintf(databuf, "%s/%s", msgbox->current_dir, pcFile);
+//         pcFile = databuf;
+//     }
+
+//     res = f_open(&RecvFile, pcFile, FA_CREATE_ALWAYS | FA_WRITE);
+//     if (res != FR_OK)
+//     {
+//         Errors("cannot open/create \"%s\",error code = %d\r\n", pcFile, res);
+//         goto RecvEnd;
+//     }
+//     ret = recv(g_ftp_data_socket, data_netbuf, sizeof(data_netbuf) - 1, 0);
+
+//     //     while (ERR_OK == recv(g_ftp_data_socket, &data_netbuf)) // 阻塞直到收到数据
+//     //     {
+//     //         do
+//     //         {
+//     //             netbuf_data(data_netbuf, (void **)&pcFile, &sRxlen); // 提取数据指针
+
+//     // #if 1
+//     //             memcpy(recv_buf, pcFile, sRxlen); // 把数据拷出来,否则容易出错
+//     //             pcFile = recv_buf;
+//     // #endif
+
+//     //             res = f_write(&RecvFile, (void *)pcFile, sRxlen, &byteswritten);
+
+//     //             printk(".");
+//     //             if ((byteswritten == 0) || (res != FR_OK))
+//     //             {
+//     //                 f_close(&RecvFile);
+//     //                 Errors("write file error\r\n");
+//     //                 goto RecvEnd;
+//     //             }
+//     //         } while (netbuf_next(data_netbuf) >= 0);
+
+//     //         netbuf_delete(data_netbuf);
+//     //     }
+
+//     ctrl_msg = (char *)ftp_msg_226;
+//     f_close(&RecvFile);
+
+// RecvEnd:
+
+//     return ctrl_msg;
+// }
+
+// void ftp_data_deal(void)
+// {
+//     INT8U       err = 0;
+//     ftp_mbox_t *msgbox;
+
+//     char *ctrl_msg;
+//     while (g_ftp_data_socket != -1)
+//     {
+//         msgbox = (ftp_mbox_t *)OSMboxPend(ftp_mbox, 50, &err);
+//         printf((char *)msgbox->event);
+//         if ((err == OS_ERR_NONE) && (msgbox->arglen >= 2))
+//         {
+//             switch (msgbox->event) // 根据不同的操作命令进行操作
+//             {
+//             case FTP_LIST:
+//                 ctrl_msg = data_port_list_file(msgbox);
+//                 break;
+
+//                 // case FTP_SEND_FILE_DATA:
+//                 //     ctrl_msg = data_port_send_file(data_port_conn, msgbox);
+//                 //     break;
+
+//                 // case FTP_RECV_FILE:
+//                 //     ctrl_msg = data_port_recv_file(data_port_conn, msgbox);
+//                 //     break;
+
+//             default:;
+//             }
+//             send(msgbox->cmd_socket, ctrl_msg, strlen(ctrl_msg), 0); // 控制端口反馈
+//             lwip_close(g_ftp_data_socket);                           // 关闭链接
+//             g_ftp_data_socket = -1;                                  // 清空释放连接的内存
+//         }
+//         else
+//         {
+//             lwip_close(g_ftp_data_socket); // 关闭链接
+//             g_ftp_data_socket = -1;        // 清空释放连接的内存
+//         }
+//     }
+// }
+
+void ftp_server_init(void)
+{
+    ftp_cmd_func cmd_type = cmd_noop;
+
+    // 生成相关的命令二叉树
+    FTP_REGISTER_COMMAND(user);
+    FTP_REGISTER_COMMAND(pass);
+    FTP_REGISTER_COMMAND(syst);
+    FTP_REGISTER_COMMAND(pwd);
+    FTP_REGISTER_COMMAND(cwd);
+    // FTP_REGISTER_COMMAND(feat);
+    FTP_REGISTER_COMMAND(pasv);
+    FTP_REGISTER_COMMAND(nlst);
+    FTP_REGISTER_COMMAND(list);
+    FTP_REGISTER_COMMAND(noop);
+    FTP_REGISTER_COMMAND(type);
+    FTP_REGISTER_COMMAND(size);
+    FTP_REGISTER_COMMAND(retr);
+    FTP_REGISTER_COMMAND(dele);
+    FTP_REGISTER_COMMAND(stor);
+}

+ 49 - 0
User/app/ftp/ftpd.h

@@ -0,0 +1,49 @@
+#ifndef __FTPD_H
+#define __FTPD_H
+#include "avltree.h"
+#include "includes.h"
+#include "os_cpu.h"
+#include "tcpip.h"
+#include <sys/_intsup.h>
+
+#define FTP_STR2ID(str) ((*(int *)(str)) & 0xDFDFDFDF)
+
+#define msg200 "220 lwIP FTP Server ready.\r\n"
+#define msg230 "230 User logged in, proceed.\r\n"
+#define msg331 "331 User name okay, need password.\r\n"
+#define msg250 "250 Operation successful\r\n"
+#define msg450 "450 Operation error\r\n"
+#define msg501 "501 Syntax error in parameters or arguments."
+#define msg503 "503 Bad sequence of commands.\r\n"
+
+typedef struct
+{
+    int cmd_socket;
+
+    char *arg;
+    INT8U arglen;
+    char  event;
+
+#define FTP_LIST           1U
+#define FTP_SEND_FILE_DATA 2U
+#define FTP_RECV_FILE      3U
+
+    char current_dir[128];
+} ftp_mbox_t;
+
+typedef void (*ftp_cmd_func)(ftp_mbox_t *pmbox);
+
+typedef struct
+{
+    uint32_t        index;    // 命令标识码
+    ftp_cmd_func    func;     // 记录命令函数指针
+    struct avl_node cmd_node; // avl树节点
+} ftp_cmd_t;
+
+ftp_cmd_t *ftp_search_command(int i_ctrl_cmd);
+int        ftp_insert_command(ftp_cmd_t *p_cmd);
+
+char *data_port_list_file(ftp_mbox_t *msgbox);
+// char *data_port_recv_file(ftp_mbox_t *msgbox);
+
+#endif

+ 157 - 0
User/app/ftp/vfs.c

@@ -0,0 +1,157 @@
+#include "vfs.h"
+
+/* dirent that will be given to callers;
+ * note: both APIs assume that only one dirent ever exists
+ */
+vfs_dirent_t dir_ent;
+
+FIL guard_for_the_whole_fs;
+
+int vfs_read(void *buffer, int dummy, int len, vfs_file_t *file)
+{
+    unsigned int bytesread;
+    (void)dummy; /* suppress unused warning */
+    FRESULT r = f_read(file, buffer, len, &bytesread);
+    if (r != FR_OK)
+        return 0;
+    return bytesread;
+}
+
+vfs_dirent_t *vfs_readdir(vfs_dir_t *dir)
+{
+    FILINFO fi;
+#if _USE_LFN
+    fi.lfname = NULL;
+#endif
+    FRESULT r = f_readdir(dir, &fi);
+    if (r != FR_OK)
+        return NULL;
+    if (fi.fname[0] == 0)
+        return NULL;
+    memcpy(dir_ent.name, fi.fname, sizeof(fi.fname));
+    return &dir_ent;
+}
+
+int vfs_stat(vfs_t *vfs, const char *filename, vfs_stat_t *st)
+{
+    FILINFO f;
+#if _USE_LFN
+    f.lfname = NULL;
+#endif
+    (void)vfs; /* suppress unused warning */
+    if (FR_OK != f_stat(filename, &f))
+    {
+        return 1;
+    }
+    st->st_size  = f.fsize;
+    st->st_mode  = f.fattrib;
+    struct tm tm = {
+        .tm_sec  = 2 * (f.ftime & 0x1f),
+        .tm_min  = (f.ftime >> 5) & 0x3f,
+        .tm_hour = (f.ftime >> 11) & 0x1f,
+        .tm_mday = f.fdate & 0x1f,
+        .tm_mon  = (f.fdate >> 5) & 0xf,
+        .tm_year = 80 + ((f.fdate >> 9) & 0x7f),
+    };
+    st->st_mtime = mktime(&tm);
+    return 0;
+}
+
+void vfs_close(vfs_t *vfs)
+{
+    if (vfs != &guard_for_the_whole_fs)
+    {
+        /* Close a file */
+        f_close(vfs);
+        free(vfs);
+    }
+}
+
+int vfs_write(void *buffer, int dummy, int len, vfs_file_t *file)
+{
+    unsigned int byteswritten;
+    (void)dummy; /* suppress unused warning */
+
+    FRESULT r = f_write(file, buffer, len, &byteswritten);
+    if (r != FR_OK)
+        return 0;
+    return byteswritten;
+}
+
+vfs_t *vfs_openfs()
+{
+    return &guard_for_the_whole_fs;
+}
+
+vfs_file_t *vfs_open(vfs_t *vfs, const char *filename, const char *mode)
+{
+    vfs_file_t *f     = malloc(sizeof(vfs_file_t));
+    BYTE        flags = 0;
+    (void)vfs; /* suppress unused warning */
+
+    while (*mode != '\0')
+    {
+        if (*mode == 'r')
+            flags |= FA_READ;
+        if (*mode == 'w')
+            flags |= FA_WRITE | FA_CREATE_ALWAYS;
+        mode++;
+    }
+    FRESULT r = f_open(f, filename, flags);
+    if (FR_OK != r)
+    {
+        free(f);
+        return NULL;
+    }
+    return f;
+}
+
+char *vfs_getcwd(vfs_t *vfs, void *dummy1, int dummy2)
+{
+    char   *cwd = malloc(255);
+    FRESULT r   = f_getcwd(cwd, 255);
+    (void)dummy1; /* suppress unused warning */
+    (void)dummy2;
+    (void)vfs;
+
+    if (r != FR_OK)
+    {
+        free(cwd);
+        return NULL;
+    }
+    return cwd;
+}
+
+vfs_dir_t *vfs_opendir(vfs_t *vfs, const char *path)
+{
+    vfs_dir_t *dir = malloc(sizeof *dir);
+    FRESULT    r   = f_opendir(dir, path);
+    (void)vfs; /* suppress unused warning */
+    if (FR_OK != r)
+    {
+        free(dir);
+        return NULL;
+    }
+    return dir;
+}
+
+void vfs_closedir(vfs_dir_t *dir)
+{
+    if (dir)
+    {
+        f_closedir(dir);
+        free(dir);
+    }
+}
+
+struct tm dummy = {
+    .tm_year = 70,
+    .tm_mon  = 0,
+    .tm_mday = 1,
+    .tm_hour = 0,
+    .tm_min  = 0};
+struct tm *gmtime(const time_t *c_t)
+{
+    (void)c_t;
+    return &dummy;
+}

+ 59 - 0
User/app/ftp/vfs.h

@@ -0,0 +1,59 @@
+#ifndef INCLUDE_VFS_H
+#define INCLUDE_VFS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "ff.h"
+#include "time.h"
+// #include <stddef.h>
+// #include <stdlib.h>
+#include <string.h>
+
+#define vfs_load_plugin(x)
+#define bcopy(src, dest, len) memmove(dest, src, len)
+    typedef DIR vfs_dir_t;
+    typedef FIL vfs_file_t;
+    typedef struct
+    {
+        long   st_size;
+        char   st_mode;
+        time_t st_mtime;
+    } vfs_stat_t;
+    typedef struct
+    {
+        char name[13];
+    } vfs_dirent_t;
+    typedef FIL vfs_t;
+
+#define time(x)
+#define vfs_eof                    f_eof
+#define VFS_ISDIR(st_mode)         ((st_mode)&AM_DIR)
+#define VFS_ISREG(st_mode)         !((st_mode)&AM_DIR)
+#define vfs_rename(vfs, from, to)  f_rename(from, to)
+#define VFS_IRWXU                  0
+#define VFS_IRWXG                  0
+#define VFS_IRWXO                  0
+#define vfs_mkdir(vfs, name, mode) f_mkdir(name)
+#define vfs_rmdir(vfs, name)       f_unlink(name)
+#define vfs_remove(vfs, name)      f_unlink(name)
+#define vfs_chdir(vfs, dir)        f_chdir(dir)
+    char         *vfs_getcwd(vfs_t *vfs, void *, int dummy);
+    int           vfs_read(void *buffer, int dummy, int len, vfs_file_t *file);
+    int           vfs_write(void *buffer, int dummy, int len, vfs_file_t *file);
+    vfs_dirent_t *vfs_readdir(vfs_dir_t *dir);
+    vfs_file_t   *vfs_open(vfs_t *vfs, const char *filename, const char *mode);
+    vfs_t        *vfs_openfs();
+    void          vfs_close(vfs_t *vfs);
+    int           vfs_stat(vfs_t *vfs, const char *filename, vfs_stat_t *st);
+    void          vfs_closedir(vfs_dir_t *dir);
+    vfs_dir_t    *vfs_opendir(vfs_t *vfs, const char *path);
+    // struct tm    *gmtime(const time_t *c_t);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* INCLUDE_VFS_H */

+ 190 - 0
User/app/net/ftp_server.c

@@ -0,0 +1,190 @@
+#include "ftp_server.h"
+#include "fly_param.h"
+#include "ftpd.h"
+#include "os_cpu.h"
+#include "sockets.h"
+#include <stdio.h>
+
+INT32S g_ftp_cmd_socket  = -1; /* 创建与ftp交互的socket */
+INT32S g_ftp_data_socket = -1; /* 创建与ftp交互的socket */
+
+void ftp_server_cmd_task(void)
+{
+
+    /* socket 创建 */
+    INT8U                     buf[500];
+    INT32S                    ret = 0;
+    static INT32S             ftp_cmd_sockfd;
+    static struct sockaddr_in svr_addr = {0};
+    struct sockaddr_in        clt_addr;
+    INT32U                    non_block = 1;
+    INT32U                    len;
+    INT32U                    i_ctrl_cmd;
+    static const char         ftp_reply_unkown[] = "500 Unknown command\r\n";
+    ftp_cmd_t                *cmd_match;
+    ftp_mbox_t                msgbox;
+
+    msgbox.current_dir[0]    = 0; // 路径为空,即根目录
+    svr_addr.sin_family      = AF_INET;
+    svr_addr.sin_port        = htons(FTP_CMD_PORT);
+    svr_addr.sin_addr.s_addr = htons(INADDR_ANY);
+
+    while (1)
+    {
+        ftp_cmd_sockfd = socket(AF_INET, SOCK_STREAM, 0);
+        if (ftp_cmd_sockfd < 0)
+        {
+            continue;
+        }
+
+        ret = bind(ftp_cmd_sockfd, (struct sockaddr *)&svr_addr, sizeof(svr_addr));
+        if (ret < 0)
+        {
+            lwip_close(ftp_cmd_sockfd);
+            ftp_cmd_sockfd = -1;
+            continue;
+        }
+
+        ret = listen(ftp_cmd_sockfd, 1);
+        if (ret < 0)
+        {
+            lwip_close(ftp_cmd_sockfd);
+            continue;
+        }
+
+        g_ftp_cmd_socket = accept(ftp_cmd_sockfd, NULL, NULL);
+        while (-1 != g_ftp_cmd_socket)
+        {
+            msgbox.cmd_socket = g_ftp_cmd_socket; // 当前 ftp 控制端口连接句柄
+
+            send(g_ftp_cmd_socket, msg200, sizeof(msg200) - 1, 0); // 控制端口反馈
+            while (-1 != g_ftp_cmd_socket)
+            {
+                ret = recv(g_ftp_cmd_socket, buf, sizeof(buf) - 1, 0);
+                if (ret <= 0)
+                {
+                    lwip_close(g_ftp_cmd_socket);
+                    g_ftp_cmd_socket = -1;
+                    break;
+                }
+                i_ctrl_cmd = FTP_STR2ID(buf);
+
+                if (buf[3] < 'A' || buf[3] > 'z') // 有些命令只有三个字节,需要判断
+                {
+                    i_ctrl_cmd &= 0x00ffffff;
+                    msgbox.arg    = buf + 4;
+                    msgbox.arglen = sizeof(buf) - 1 - 4;
+                }
+                else
+                {
+                    msgbox.arg    = buf + 5;
+                    msgbox.arglen = sizeof(buf) - 1 - 5;
+                }
+
+                cmd_match = ftp_search_command(i_ctrl_cmd); // 匹配命令号
+
+                if (NULL == cmd_match)
+                    send(msgbox.cmd_socket, ftp_reply_unkown, sizeof(ftp_reply_unkown) - 1, 0);
+                else
+                    cmd_match->func(&msgbox);
+            }
+            lwip_close(g_ftp_cmd_socket);
+            g_ftp_cmd_socket = -1;
+            break;
+        }
+
+        lwip_close(ftp_cmd_sockfd);
+        ftp_cmd_sockfd = -1;
+    }
+}
+
+void ftp_server_data_task(void)
+{
+    /* socket 创建 */
+    INT8U                     buf[500];
+    INT32S                    ret = 0;
+    static INT32S             ftp_data_sockfd;
+    static struct sockaddr_in svr_addr = {0};
+    struct sockaddr_in        clt_addr;
+    INT32U                    len;
+    ftp_mbox_t               *msgbox;
+    char                     *ctrl_msg;
+    INT8U                     err = 0;
+
+    svr_addr.sin_family      = AF_INET;
+    svr_addr.sin_port        = htons(FTP_DATA_PORT);
+    svr_addr.sin_addr.s_addr = htons(INADDR_ANY);
+
+    while (1)
+    {
+        ftp_data_sockfd = socket(AF_INET, SOCK_STREAM, 0);
+        if (ftp_data_sockfd < 0)
+        {
+            continue;
+        }
+
+        ret = bind(ftp_data_sockfd, (struct sockaddr *)&svr_addr, sizeof(svr_addr));
+
+        if (ret < 0)
+        {
+            lwip_close(ftp_data_sockfd);
+            ftp_data_sockfd = -1;
+            continue;
+        }
+
+        ret = listen(ftp_data_sockfd, 1);
+
+        if (ret < 0)
+        {
+            lwip_close(ftp_data_sockfd);
+            continue;
+        }
+        // len               = sizeof(clt_addr);
+        // g_ftp_data_socket = accept(ftp_data_sockfd, (struct sockaddr *)&clt_addr, len);
+        g_ftp_data_socket = accept(ftp_data_sockfd, NULL, NULL);
+        // printf("1111111\r\n");
+        while (-1 != g_ftp_data_socket)
+        {
+            msgbox = (ftp_mbox_t *)OSMboxPend(ftp_mbox, 50, &err);
+            // ret = recv(g_ftp_data_socket, buf, sizeof(buf) - 1, 0);
+            // if (ret <= 0)
+            // {
+            //     lwip_close(g_ftp_data_socket);
+            //     g_ftp_data_socket = -1;
+            //     break;
+            // }
+            // if (msgbox->arglen >= 2)
+            // {
+            if (err == 0)
+            {
+                switch (msgbox->event) // 根据不同的操作命令进行操作
+                {
+                case FTP_LIST:
+                    ctrl_msg = data_port_list_file(msgbox);
+                    break;
+
+                    // case FTP_SEND_FILE_DATA:
+                    //     ctrl_msg = data_port_send_file(data_port_conn, msgbox);
+                    //     break;
+
+                    // case FTP_RECV_FILE:
+                    //     ctrl_msg = data_port_recv_file(msgbox);
+                    //     break;
+
+                default:;
+                }
+                send(msgbox->cmd_socket, ctrl_msg, strlen(ctrl_msg), 0); // 控制端口反馈
+                // }
+            }
+            else
+            {
+                lwip_close(g_ftp_data_socket);
+                g_ftp_data_socket = -1;
+                break;
+            }
+        }
+
+        lwip_close(ftp_data_sockfd);
+        ftp_data_sockfd = -1;
+    }
+}

+ 17 - 0
User/app/net/ftp_server.h

@@ -0,0 +1,17 @@
+#ifndef __FTP_SERVER_H
+#define __FTP_SERVER_H
+#include "includes.h"
+#include "lwip/sockets.h"
+
+extern INT32S g_ftp_cmd_socket;
+extern INT32S g_ftp_data_socket;
+
+#define FTP_CMD_PORT  21
+#define FTP_DATA_PORT 40732
+
+void ftp_server_cmd_task(void);
+void ftp_server_data_task(void);
+
+void ftp_server_init(void);
+
+#endif

+ 126 - 0
User/app/tftp/tftp.c

@@ -0,0 +1,126 @@
+#include "tftp.h"
+
+struct tftp_context bms_tftp;
+TFTP_Handler        tftp_handler;
+FIL                 tftp_object;
+
+void *bms_tftp_open(const char *fname, const char *mode, INT8U write)
+{
+    INT8U res = FR_OK, err = 0;         //, len = 0;
+    INT8U name[MAX_FILENAME_LEN] = {0}; //, file_head[]="0:";
+
+    //	len = snprintf((char *)name, MAX_FILENAME_LEN, "0:");
+    strcpy((char *)name, "0:");
+    strcat((char *)name, fname);
+
+    OSMutexPend(sd_mutex, 0, &err);
+
+    if (write)
+    {
+        DeleteFile(name);
+        res = f_open(&tftp_object, (const char *)name, FA_OPEN_ALWAYS | FA_WRITE);
+    }
+    else
+    {
+        res = f_open(&tftp_object, (const char *)name, FA_OPEN_EXISTING | FA_READ);
+    }
+
+    if (res == FR_OK)
+    {
+        tftp_handler.write   = write;
+        tftp_handler.type    = 0;
+        tftp_handler.Open_OK = TRUE;
+
+        return &tftp_handler;
+    }
+    else
+    {
+        OSMutexPost(sd_mutex);
+        return 0;
+    }
+}
+
+void bms_tftp_close(void *handle)
+{
+    // INT8U res = 0;
+
+    if (((TFTP_Handler *)handle)->Open_OK == TRUE)
+    {
+        f_close(&tftp_object);
+        tftp_handler.Open_OK = FALSE;
+        OSMutexPost(sd_mutex);
+    }
+}
+
+// 入参中bytes固定为512
+int bms_tftp_read(void *handle, void *buf, int bytes)
+{
+    INT8U  res = 0;
+    INT32U bys = 0;
+
+    if (((TFTP_Handler *)handle)->Open_OK != TRUE)
+    {
+        //		OSMutexPost(sd_mutex);
+        return -1;
+    }
+
+    res = f_read(&tftp_object, (INT8U *)buf, bytes, &bys);
+    if (res == FR_OK)
+    {
+        return bys;
+    }
+    else
+    {
+        //		OSMutexPost(sd_mutex);
+        return -1;
+    }
+}
+
+int bms_tftp_write(void *handle, struct pbuf *p)
+{
+    INT8U        res = 0;
+    INT32U       bys = 0;
+    struct pbuf *q   = p;
+
+    if (((TFTP_Handler *)handle)->Open_OK != TRUE)
+    {
+        //		OSMutexPost(sd_mutex);
+        return -1;
+    }
+
+    while (q != NULL)
+    {
+        res = f_write(&tftp_object, q->payload, q->len, &bys);
+        if (res == FR_OK)
+        {
+            q = q->next;
+        }
+        else
+        {
+            break;
+        }
+    }
+    //	res = f_write(&tftp_object, p->payload, p->len, &bys);
+
+    if (res == FR_OK)
+    {
+        return 0;
+    }
+    else
+    {
+        //		OSMutexPost(sd_mutex);
+        return -1;
+    }
+}
+
+void tftp_context_init(void)
+{
+    tftp_handler.Open_OK = FALSE;
+
+    bms_tftp.close = bms_tftp_close;
+    bms_tftp.open  = bms_tftp_open;
+    bms_tftp.read  = bms_tftp_read;
+    bms_tftp.write = bms_tftp_write;
+
+    tftp_init(&bms_tftp);
+}

+ 21 - 0
User/app/tftp/tftp.h

@@ -0,0 +1,21 @@
+#ifndef __TFTP_H
+#define __TFTP_H
+#include "bsp_fatfs.h"
+#include "ff.h"
+#include "includes.h"
+#include "lwip/apps/tftp_server.h"
+#include "string.h"
+
+#define MAX_FILENAME_LEN 64
+
+typedef struct
+{
+    INT8U Open_OK;
+    INT8U type;
+    INT8U write;
+    char  name[64];
+} TFTP_Handler;
+
+void tftp_context_init(void);
+
+#endif