零基础 Ubuntu 20.04.01 下搭建51单片机开发环境[开源编译器SDCC]

news2024/12/28 5:51:20

原创首发于CSDN,转载请注明出处,谢谢!


文章目录

    • 为何会在Linux下开发单片机
    • 个人系统环境与所用开发板
    • 安装开源编译器 sdcc
    • STC MCU ISP 闪存工具 stcgal 的安装
    • 单片机代码的编译与测试
      • |编写主代码 main.c
      • |使用 sdcc 编译
      • |闪存烧录 stcgal
    • 单片机效果展示
    • 拓展1⃣️:使用SDCC进行单片机程序编写的不同点
    • 拓展2⃣️:附头文件 8051.h 文本
    • 参考资料
    • 文章更新时间记录


为何会在Linux下开发单片机

个人初步学习51单片机的时,所能接触到的教程基本都是在Windows环境下使用Keil开发。诚然,Keil确实是一款强大的开发软件,但博主个人受限于以下因素:

  • 个人使用的是苹果电脑 MacBook Air;
  • 所装的VM虚拟机里已经安装了 Ubuntu 20.04.01。

加之其他琐碎的原因,博主终是走上了适配在Linux环境下的开源编译器SDCC来开发单片机这条路【不止是现在的C51单片机,还有以后的STM32,统一在Linux环境下进行开发 】。其实两者在抛开系统环境、编译软件的差异,大体上都是一样的:①【SDCC、Keil】编译生成hex文件;②【stcgal、stc-isp】启动烧录软件烧录程序。

在Ubuntu系统下搭建51单片机开发环境全过程里,出乎预料的反而是察觉了个人对于Linux系统的 文件架构软硬链接软链接的“漂移”环境变量 等几个方面的认识不足,惹出了几个无厘头的“bug”。 故才有了“参考资料”一节内那几篇环境变量、系统变量的博文,对于这几个主题的研究,以后也会出几篇相关内容的博文。


个人系统环境与所用开发板

  • 电脑:MacBook Air
  • 虚拟机:VMWare Fusion 12
  • Linux系统:Ubuntu 20.04.01
  • 开发板:普中HC6800V2.0 (开发板实物请跳转到“单片机效果展示”一节。)

安装开源编译器 sdcc

终端输入指令 sudo -i 进入根目录 /,再次输入指令 sudo apt-get install sdcc 。安装完毕,全程无报错。终端输入指令 sdcc -v 验证编译器是否安装成功:
sdcc -v

∗ \ast 小贴士:SDCC的全称是Small Device C Compiler,即“小型设备C语言编译器”。根据官网(http://sdcc.sourceforge.net/)的说法,SDCC是一个可重定向目标的、优化的标准C编译器套件(支持ANSI C89、ISO C99和ISO C11),支持基于英特尔MCS51(8031、8032、8051、8052等)、Maxim(原Dallas)的DS80C390系列、Freescale(原摩托罗拉)的HC08系列(hc08、s08)、Zilog的Z80系列(z80、z180、gbz80、Rabbit 2000/3000、Rabbit 3000A、TLCS-90)、Padauk(pdk14、pdk15)和意法半导体的STM8。

图片里打框的MCS51就是我们常说的51单片机,在根目录里进入路径 usr/share/sdcc/include/mcs51 输入 ls,会显示出一堆不同型号的51单片机芯片的适配头文件。至此,编译器SDCC的安装大功告成。

mcs51


STC MCU ISP 闪存工具 stcgal 的安装

在根目录 / 下输入指令 pip3 install stcgal(系统没有安装 pip3 就自己下载安装),安装的同时会下载两个软件 tqdm-4.64.1pyserial-3.5 。前者显示文件 main.hex 的烧录进度条,后者则是串口通信软件。(记住了,不管安装什么软件,在安装之前必须要好好查一查其具体的功能是什么。

tqdm && pyserial


单片机代码的编译与测试

|编写主代码 main.c

#include <mcs51/8051.h>

void Delay(unsigned int ms);

void main(){
        while(1){
        		//自行对照开发板引脚图。
                P1_1 = 0;   
                Delay(500);
                P1_1 = 1;
                //led灯以一秒为一个周期闪烁。
                Delay(500); 
        }
}

void Delay(unsigned int ms){
        unsigned int a, b;
        for(a = ms; a > 0; a--){
                for(b = 100; b > 0; b--);
        }
}

|使用 sdcc 编译

在终端前后输入以下两条指令,经过sdcc的编译,测试文件夹内会多出一堆的文件,我们需要的主文件是 main.hex

	sdcc main.c 
	//sdcc默认生成的文件后缀不是hex而是ihx,需要使用packihx转换。
	packihx main.ihx > main.hex && ls

搜索相关资料的时候发现了第三条指令:makebin (makebin main.ihx > main.bin)。原来与 ∗ \ast .hex 格式文件相比, ∗ \ast .bin 格式有文件三个方面的差异(请自行验证):

  • HEX文件是包括地址信息的,而BIN文件格式只包括了数据本身。 在烧写/下载HEX文件时一般不需要用户指定地址,因为HEX文件内部信息已经包括了地址,而烧写BIN文件时,用户必须指定地址信息;
  • BIN文件。对二进制文件而言,没有格式,文件只是纯粹的二进制数据。
  • HEX文件使用ASCII来表示二进制的数值。 如一般的8-BIT的二进制数值0x3F,用ASCII来表示就需要表示字符‘3’和字符‘F’,每个字符需要一个BYTE,所以 HEX文件需要大于BIN文件2倍的内存空间

|闪存烧录 stcgal

用USB线连接电脑将与单片机(请自行安装与系统版本对应的CH341驱动,无头绪的读者请参考下列第二篇博文《搭建Ubuntu的51单片机开发环境(学习记录)》里对应的驱动安装一节),输入指令 ls /dev/tty ∗ \ast 确认USB接入后在终端输入指令

stcgal -P stc89 -p /dev/ttyUSB0 main.hex

或者直接输入

stcgal main.hex 

笔者在烧录程序时直接用第二条,在上文里代码头文件已经选择了51单片机类型为 8051 #include <mcs51/8051.h>,个人认为 stcgal 运行时默认 port 为 /dev/ttyUSB0,故使用时直接运行,无需手动指定端口和波特率。

stcgal main.hex


单片机效果展示

单片机的LED灯模块,右边数第一个LED灯以一秒为一个周期闪烁。

No shining
shining


拓展1⃣️:使用SDCC进行单片机程序编写的不同点

SDCC支持的C语言与Keil的差异:

  • 包含自定义的头文件时必须使用双引号。 某个项目中有个名为tm1638.h 的文件,在Keil写成 #include <tm1638.h> ,但在SDCC中必须写成 #include “tm1638.h” ;
  • sdcc编译51单片机代码文件的头文件(引脚定义文件)名字不一样。 Keil中为 #include <reg52.h>,而在SDCC中需要写成 #include <mcs51/8051.h>;
  • Keil中的特殊类型 sbit 和 sfr 在SDCC中为 __sbit和 __sfr 。 如Keil中的代码 sfr P0 = 0x80、sbit P0_1 = P0 ^ 1。在SDCC中就要写成:__sfr __at (0x80) P0、__sbit __at (0x81) P0_1 。但在8051.h 中已经定义了常用的端口,需要使用哪个端口时,直接使用P0、P1、P2_1之类的宏即可;
  • Keil中的code关键字(用于将数据放入代码段)在SDCC中应该写成__code。 在Keil中的代码unsigned char code sevenseg_hex[] = { … },在SDCC中应该这样写:__code unsigned char sevenseg_hex[] = { … };
  • Keil中的 interrupt 关键字在SDCC中应该写成 __interrupt。 定义中断处理函数的代码在SDCC中应该写成:void timer0() __interrupt 1 { … } 。

< 请读者自行验证以上,同时欢迎在评论区补充不同的差异点。>

拓展2⃣️:附头文件 8051.h 文本

/*-------------------------------------------------------------------------
   8051.h: Register Declarations for the Intel 8051 Processor

   Copyright (C) 2000, Bela Torok / bela.torok@kssg.ch

   This library is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License 
   along with this library; see the file COPYING. If not, write to the
   Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA.

   As a special exception, if you link this library with other files,
   some of which are compiled with SDCC, to produce an executable,
   this library does not by itself cause the resulting executable to
   be covered by the GNU General Public License. This exception does
   not however invalidate any other reasons why the executable file
   might be covered by the GNU General Public License.
-------------------------------------------------------------------------*/

#ifndef REG8051_H
#define REG8051_H

/*  BYTE Register  */
__sfr __at (0x80) P0   ;
__sfr __at (0x81) SP   ;
__sfr __at (0x82) DPL  ;
__sfr __at (0x83) DPH  ;
__sfr __at (0x87) PCON ;
__sfr __at (0x88) TCON ;
__sfr __at (0x89) TMOD ;
__sfr __at (0x8A) TL0  ;
__sfr __at (0x8B) TL1  ;
__sfr __at (0x8C) TH0  ;
__sfr __at (0x8D) TH1  ;
__sfr __at (0x90) P1   ;
__sfr __at (0x98) SCON ;
__sfr __at (0x99) SBUF ;
__sfr __at (0xA0) P2   ;
__sfr __at (0xA8) IE   ;
__sfr __at (0xB0) P3   ;
__sfr __at (0xB8) IP   ;
__sfr __at (0xD0) PSW  ;
__sfr __at (0xE0) ACC  ;
__sfr __at (0xF0) B    ;


/*  BIT Register  */
/* P0 */
__sbit __at (0x80) P0_0 ;
__sbit __at (0x81) P0_1 ;
__sbit __at (0x82) P0_2 ;
__sbit __at (0x83) P0_3 ;
__sbit __at (0x84) P0_4 ;
__sbit __at (0x85) P0_5 ;
__sbit __at (0x86) P0_6 ;
__sbit __at (0x87) P0_7 ;

/*  TCON  */
__sbit __at (0x88) IT0  ;
__sbit __at (0x89) IE0  ;
__sbit __at (0x8A) IT1  ;
__sbit __at (0x8B) IE1  ;
__sbit __at (0x8C) TR0  ;
__sbit __at (0x8D) TF0  ;
__sbit __at (0x8E) TR1  ;
__sbit __at (0x8F) TF1  ;

/* P1 */
__sbit __at (0x90) P1_0 ;
__sbit __at (0x91) P1_1 ;
__sbit __at (0x92) P1_2 ;
__sbit __at (0x93) P1_3 ;
__sbit __at (0x94) P1_4 ;
__sbit __at (0x95) P1_5 ;
__sbit __at (0x96) P1_6 ;
__sbit __at (0x97) P1_7 ;

/*  SCON  */
__sbit __at (0x98) RI   ;
__sbit __at (0x99) TI   ;
__sbit __at (0x9A) RB8  ;
__sbit __at (0x9B) TB8  ;
__sbit __at (0x9C) REN  ;
__sbit __at (0x9D) SM2  ;
__sbit __at (0x9E) SM1  ;
__sbit __at (0x9F) SM0  ;

/* P2 */
__sbit __at (0xA0) P2_0 ;
__sbit __at (0xA1) P2_1 ;
__sbit __at (0xA2) P2_2 ;
__sbit __at (0xA3) P2_3 ;
__sbit __at (0xA4) P2_4 ;
__sbit __at (0xA5) P2_5 ;
__sbit __at (0xA6) P2_6 ;
__sbit __at (0xA7) P2_7 ;

/*  IE   */
__sbit __at (0xA8) EX0  ;
__sbit __at (0xA9) ET0  ;
__sbit __at (0xAA) EX1  ;
__sbit __at (0xAB) ET1  ;
__sbit __at (0xAC) ES   ;
__sbit __at (0xAF) EA   ;

/*  P3  */
__sbit __at (0xB0) P3_0 ;
__sbit __at (0xB1) P3_1 ;
__sbit __at (0xB2) P3_2 ;
__sbit __at (0xB3) P3_3 ;
__sbit __at (0xB4) P3_4 ;
__sbit __at (0xB5) P3_5 ;
__sbit __at (0xB6) P3_6 ;
__sbit __at (0xB7) P3_7 ;

__sbit __at (0xB0) RXD  ;
__sbit __at (0xB1) TXD  ;
__sbit __at (0xB2) INT0 ;
__sbit __at (0xB3) INT1 ;
__sbit __at (0xB4) T0   ;
__sbit __at (0xB5) T1   ;
__sbit __at (0xB6) WR   ;
__sbit __at (0xB7) RD   ;

/*  IP   */
__sbit __at (0xB8) PX0  ;
__sbit __at (0xB9) PT0  ;
__sbit __at (0xBA) PX1  ;
__sbit __at (0xBB) PT1  ;
__sbit __at (0xBC) PS   ;

/*  PSW   */
__sbit __at (0xD0) P    ;
__sbit __at (0xD1) F1   ;
__sbit __at (0xD2) OV   ;
__sbit __at (0xD3) RS0  ;
__sbit __at (0xD4) RS1  ;
__sbit __at (0xD5) F0   ;
__sbit __at (0xD6) AC   ;
__sbit __at (0xD7) CY   ;

/* BIT definitions for bits that are not directly accessible */
/* PCON bits */
#define IDL             0x01
#define PD              0x02
#define GF0             0x04
#define GF1             0x08
#define SMOD            0x80

/* TMOD bits */
#define T0_M0           0x01
#define T0_M1           0x02
#define T0_CT           0x04
#define T0_GATE         0x08
#define T1_M0           0x10
#define T1_M1           0x20
#define T1_CT           0x40
#define T1_GATE         0x80

#define T0_MASK         0x0F
#define T1_MASK         0xF0

/* Interrupt numbers: address = (number * 8) + 3 */
#define IE0_VECTOR      0       /* 0x03 external interrupt 0 */
#define TF0_VECTOR      1       /* 0x0b timer 0 */
#define IE1_VECTOR      2       /* 0x13 external interrupt 1 */
#define TF1_VECTOR      3       /* 0x1b timer 1 */
#define SI0_VECTOR      4       /* 0x23 serial port 0 */

#endif

参考资料

  • 《Mac版下实现51单片机进行开发的环境搭建》,CSDN博主:熺子,时间:2022年3月29日;
  • 《搭建Ubuntu的51单片机开发环境(学习记录)》,CSDN博主:横着望的猫,时间:2021年12月20日;
  • 《Linux(Ubuntu)下51单片机的开发环境的配置及详细的操作步骤》,CSDN博主:逗比小憨憨,时间:2021年2月4日;
  • 《如何在Linux下进行51单片机的开发》,微信公众号:我的一九九三,时间:2021年12月16日;
  • 《还在用keil做51单片机开发马?快来试试开源的SDCC吧》,微信公众号:STEM创造家,时间:2021年12月10日;
  • 《CentOS7设置环境变量》(该篇博文在哔哩哔哩里有博主配套的视频讲解,强烈推荐观看!),博主:C语言技术网-码农有道,时间:2020年3月27日;
  • 《一文带你学会Linux系统的变量》,微信公众号:生信喵实验柴,时间:2021年12月14日;
  • 《如何设置与查看Linux系统中的环境变量》,微信公众号:良许,时间:2020年8月17日;
  • 《Linux目录详解,软件应该安装到哪个目录》,博主:Deshun,时间:2019年6月13日。

文章更新时间记录

  • 文章框架搭好;「2023.2.2.19 19:10」
  • 九篇参考博文打列完毕;「2023.2.19 19:26」
  • “安装开源编译器SDCC”一节完毕。「2023.2.19 21:07」
  • “STC MCU ISP闪存工具 stcgal 的安装”一节完毕。「2023.2.19 21:25」
  • “单片机代码的编译与测试”一节里的三小节完毕。「2023.2.20 13:33」
  • “使用SDCC进行单片机程序编写的不同点”一节完成。「2023.2.20 13:53」
  • “单片机效果展示”一节完成。「2023.2.20 18:43」
  • “使用sdcc编译”该小节内增加了BIN文件相关内容。「2023.2.20 19:45」
  • 文章首次发布于CSDN。「2023.2.21 12:20」

P.S.1 2022年12月下旬在别人的同类型的博文下了留言,如今自己在两个月也写了一篇。两个月,两个月,两年,两年,唉。「2023.2.20 19:47」

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/360551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于龙芯 2K1000 的嵌入式 Linux 系统移植和驱动程序设计

2.1 需求分析 本课题以龙芯 2K1000 处理器为嵌入式系统的处理器&#xff0c;需要实现一个完成的嵌 入式软件系统&#xff0c;系统能够正常启动并可以稳定运行嵌入式 Linux。设计网络设备驱 动&#xff0c;可以实现板卡与其他网络设备之间的网络连接和文件传输。设计 PCIE 设备驱…

新时代下的医疗行业新基建研讨会

1、会议纪要 2023年2月17日&#xff0c;HIT专家网进行了《新时代下的医疗行业新基建研讨会》的会议。 对会议内容进行了记录。 会议中有友谊医院、301、北肿主任进行了分享。大纲如下所示 2、本人所想 本人的所想所感&#xff1a; 1、301在多院区的医疗信息建设&#xff0c…

程序员遇到人生低谷期怎么做?

每个人的一生都是起起伏伏的&#xff0c;你不会天天高潮&#xff0c;总会经历一段又一段的不如意&#xff0c;你怎么把握这一段段时间&#xff0c;如何掌控人生节奏&#xff0c;都源于对人生低谷期的回答。 尤其是2022年&#xff0c;程序员受到的冲击并不小&#xff0c;从年初…

车辆调度混乱?看DHTMLX Gantt 如何提高企业调度管理效率

我们公司有一个车辆调度系统&#xff0c;目前能对小规模车队的进行简单管理。但是随着公司的业务越来越复杂&#xff0c;需要调度的车辆种类和人员安排越来越困难&#xff0c;经常出现因安排不当导致货物无车可送或车辆集中闲置情况发生&#xff0c;非常影响货运效率&#xff0…

谈谈接口和抽象类有什么区别?

第13讲 | 谈谈接口和抽象类有什么区别&#xff1f; Java 是非常典型的面向对象语言&#xff0c;曾经有一段时间&#xff0c;程序员整天把面向对象、设计模式挂在嘴边。虽然如今大家对这方面已经不再那么狂热&#xff0c;但是不可否认&#xff0c;掌握面向对象设计原则和技巧&am…

论文投稿指南——中文核心期刊推荐(法律)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

python小程序课程在线学习平台系统vue

可定制框架:ssm/Springboot/vue/python/PHP/小程序/安卓均可 目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2.1 JAVA简介 4 2.2MyEclipse环境配置 4 2.3 B/S结构简介 4 2.4MySQL数据库 5 2.5 SPRINGBOOT框架…

基于Spring Boot的校园志愿者服务网站

文章目录项目介绍主要功能截图&#xff1a;登录个人中心志愿者管理活动类型管理活动报名管理活动心得部分代码展示设计总结项目获取方式&#x1f345; 作者主页&#xff1a;Java韩立 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试…

vulnhub LordOfTheRoot_1.0.1

总结&#xff1a;端口敲门&#xff0c;CVE-2015-8660提权&#xff0c; 目录 下载地址 漏洞分析 信息收集 端口敲门 网站分析 方法一 ssh登录提权 方法二 下载地址 LordOfTheRoot_1.0.1.ova (Size: 1.6 GB)Download: http://www.mediafire.com/download/m5tbx0dua05szjm…

【项目精选】百货中心供应链管理系统(论文+源码+视频)

点击下载源码 随着国内物流行业的迅速发展&#xff0c;越来越多的企业认识到了“供应链”一词的含义以及它对整个企业物流活动的重大意义&#xff0c;于是&#xff0c;“供应链管理”也逐渐受到了大家的重视。供应链管理主要涉及到四个领域&#xff1a;供应、生产计划、物流、需…

#Paper Reading# Language Models are Few-Shot Learner

论文题目: Language Models are Few-Shot Learner 论文地址: https://proceedings.neurips.cc/paper/2020/hash/1457c0d6bfcb4967418bfb8ac142f64a-Abstract.html 论文发表于: NIPS 2020 论文所属单位: OpenAI 论文大体内容 本文主要提出了GPT-3&#xff08;Generative Pre-Tr…

I.MX6ULL_Linux_系统篇(17) uboot分析-启动linux

bootz 启动 Linux 内核 images 全局变量 不管是 bootz 还是 bootm 命令&#xff0c;在启动 Linux 内核的时候都会用到一个重要的全局变量&#xff1a;images&#xff0c; images 在文件 cmd/bootm.c 中有如下定义&#xff1a; images 是 bootm_headers_t 类型的全局变量&…

[Golang实战]github.io部署个人博客hugo[新手开箱可用][小白教程]

[Golang实战]github.io部署个人博客hugo[新手开箱可用][小白教程]1.新手教程(小白也能学会)2.开始准备2.1myBlog是hugo的项目1.安装Hugo2.创建hugo项目2.2 xxxx.github.io是github.io中规定的pages项目3.成功部署4.TODO自动化workflows部署github.io1.新手教程(小白也能学会) …

linux-rockchip-电池相关

可以先安装d-feet&#xff0c;可以查看系统里所有的DBus连接 sudo apt-get install d-feet查看电池的状态&#xff1a; cat /sys/class/power_supply/BAT0/status查看剩余电量百分比&#xff1a; cat /sys/class/power_supply/BAT0/capacity如果有多块电池&#xff0c;查看第…

【Spark分布式内存计算框架——Spark SQL】15. Catalyst 优化器

第九章 Catalyst 优化器 在第四章【案例&#xff1a;电影评分数据分析】中&#xff0c;运行应用程序代码&#xff0c;通过WEB UI界面监控可以看出&#xff0c;无论使用DSL还是SQL&#xff0c;构建Job的DAG图一样的&#xff0c;性能是一样的&#xff0c;原因在于SparkSQL中引擎…

图卷积神经网络分类的pytorch实现

图神经网络(GNN)目前的主流实现方式就是节点之间的信息汇聚&#xff0c;也就是类似于卷积网络的邻域加权和&#xff0c;比如图卷积网络(GCN)、图注意力网络(GAT)等。下面根据GCN的实现原理使用Pytorch张量&#xff0c;和调用torch_geometric包&#xff0c;分别对Cora数据集进行…

Java函数式接口

3 函数式接口 3.1 函数式接口概述 函数式接口&#xff1a;有且仅有一个抽象方法的接口 Java中的函数式编程体现就是Lambda表达式&#xff0c;所以函数式接口就是可以适用于Lambda使用的接口只有确保接口中有且仅有一个抽象方法&#xff0c; Java中的Lambda才能顺利地进行推导…

不容错过!飞桨深度学习与大模型产业应用专场24日等你来!

人工智能教父Hinton曾评价&#xff0c;“深度学习将无所不能”&#xff0c;从聊天机器人、自动驾驶到语音助手&#xff0c;深度学习早已在不知不觉中渗透进我们的生活&#xff0c;而AI大模型又是一项深度学习技术的新突破。深度学习、大模型作为人工智能发展的重要方向&#xf…

前端开发项目规范写法介绍

1. 基本原则 结构、样式、行为分离 尽量确保文档和模板只包含 HTML 结构,样式都放到样式表里,行为都放到脚本里。 缩进 统一两个空格缩进(总之缩进统一即可),不要使用 Tab 或者 Tab、空格混搭。 文件编码 使用不带 BOM 的 UTF-8 编码。 在 HTML中指定编码 <meta c…

C# 利用FluentFTP实现FTP上传下载功能

FTP作为日常工作学习中&#xff0c;非常重要的一个文件传输存储空间&#xff0c;想必大家都非常的熟悉了&#xff0c;那么如何快速的实现文件的上传下载功能呢&#xff0c;本文以一个简单的小例子&#xff0c;简述如何通过FluentFTP实现文件的上传和下载功能。仅供学习分享使用…