STM32物联网之TFTP文件传输

发布者:不羁少年最新更新时间:2018-04-22 来源: eefocus关键字:STM32  物联网  TFTP文件传输 手机看文章 扫描二维码
随时随地手机看文章
  • 感言:专注物联网应用开发,分享物联网技术经验。

  • 软件平台:IAR6.5

  • TCP/IP协议栈:LWIP1.4.1

  • 硬件平台:STM32F103C8T6有线通信板



1、TCP/IP协议栈LWIP

1.1、LWIP认识

       LWIP是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈,是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。LWIP提供三种API,分别是RAW API、LWIP API 、BSD API。其中RAW API把协议栈和应用程序放到一个进程里边,该接口基于函数回调技术来实现的,适合于无操作系统的场合运行,如单片机。本文使用的就是LWIP的RAW API来实现网络层的通信的。

1.2、TFTP在LWIP中的实现

        关于LWIP的移植,就不在本文中多讲,读者可以在网上找到众多资料或在另外的专题中再详细讲解,在这里我们专注其应用。在LWIP中实现一个TFTP服务器非常简单,根据RAW API的编程方法,在初始化的时候创建一个UDP PCB(TFTP使用UDP协议通信),且绑定69端口(TFTP默认通信端口),最后指定该UDP PCB的数据接收回调函数即可。

以上的创建TFTP服务器的方法需要在LWIP初始化,并启动网卡后进行:


  1. LwIP_Config();  

  2.    printf("ipaddr:%d.%d.%d.%d\r\n", net_ip[0], net_ip[1], net_ip[2], net_ip[3]);  

  3.   

  4.    tftpd_init();  

在tftpd_init函数中创建TFTP服务器:


  1. void tftpd_init(void)  

  2. {  

  3.   err_t err;  

  4.   unsigned port = 69;  

  5.   

  6.   /* create a new UDP PCB structure  */  

  7.   UDPpcb = udp_new();  

  8.   if (!UDPpcb)  

  9.   {  /* Error creating PCB. Out of Memory  */  

  10.     return;  

  11.   }  

  12.   

  13.   /* Bind this PCB to port 69  */  

  14.   err = udp_bind(UDPpcb, IP_ADDR_ANY, port);  

  15.   if (err != ERR_OK)  

  16.   {    /* Unable to bind to port  */  

  17.     return;  

  18.   }  

  19.   

  20.   /* TFTP server start  */  

  21.   udp_recv(UDPpcb, recv_callback_tftp, NULL);  

  22. }  

OK,到这里就完成了TFTP服务器在LWIP中建立起来了,接下来的主要事情就是根据TFTP协议进行协议解释、数据处理。


2、TFTP协议分析

2.1、TFTP通信基本流程(摘自网络)


2.2、TFTP报文格式(摘自网络)


2.3、TFTP协议理解

     从以上两张图片,我们了解到什么有用信息呢?

  • 每一次文件传输,首先需要发起一个请求,根据请求帧的操作码判断是读文件还是写文件。

  • 每一帧都有一个操作码用来标识读写。

  • 数据包的长度有一个块编号用来表示数据包的顺序。

  • 数据包中的数据长度为512个字节(在后面的软件我们可以了解到这个长度是可以设定的)。

3、实现TFTP文件传输

3.1、文件传输协议实现

有了第2节的协议分析,我们基本了解了TFTP通信的协议,在这里,我们来实现TFTP的服务器端代码。

在监听的回调函数被触发调用时,首先从请求帧中获取操作码:


  1. typedef enum {  

  2.   TFTP_RRQ = 1,  

  3.   TFTP_WRQ = 2,  

  4.   TFTP_DATA = 3,  

  5.   TFTP_ACK = 4,  

  6.   TFTP_ERROR = 5  

  7. } tftp_opcode;  

  8.   

  9. tftp_opcode tftp_decode_op(char *buf)  

  10. {  

  11.   return (tftp_opcode)(buf[1]);  

  12. }  

根据操作码进行相应的处理:

  1. tftp_opcode op = tftp_decode_op(pkt_buf->payload);  

  2.   

  3. switch (op)  

  4.   {  

  5.   

  6.     case TFTP_RRQ:    /* TFTP RRQ (read request) */  

  7.       tftp_extract_filename(FileName, pkt_buf->payload);  

  8.       tftp_process_read(upcb, addr, port, FileName);  

  9.       break;  

  10.   

  11.     case TFTP_WRQ:    /* TFTP WRQ (write request) */   

  12.       tftp_extract_filename(FileName, pkt_buf->payload);  

  13.       //在这个加入擦FALSH  

  14.        tftp_process_write(upcb, addr, port, FileName);  

  15.       break;  

  16.   

  17.     default:  

  18.       /* sEndTransfera generic access violation message */  

  19.       tftp_send_error_message(upcb, addr, port, TFTP_ERR_ACCESS_VIOLATION);  

  20.       /* TFTP unknown request op */  

  21.       /* no need to use tftp_cleanup_wr because no "tftp_connection_args" struct has been malloc'd   */  

  22.       udp_remove(upcb);  

  23.   

  24.       break;  

  25.   }  

这里当STM32接收到写操作请求时,通过tftp_extract_filename函数把文件名读出来。接下来通过tftp_process_write函数来完成文件数据的传输:


  1. int tftp_process_write(struct udp_pcb *upcb, struct ip_addr *to, int to_port, char *FileName)  

  2. {  

  3.   ... ...  

  4.   udp_recv(upcb, wrq_recv_callback, args);  

  5.   tftp_send_ack_packet(upcb, to, to_port, args->block);  

  6.   

  7.   return 0;  

  8. }  

设定数据传输回调函数后,根据TFTP协议,回复一个ACK,之后TFTP客户端开始传输文件数据,从而触发调用wrq_recv_callback


  1. void wrq_recv_callback(void *_args, struct udp_pcb *upcb, struct pbuf *pkt_buf, struct ip_addr *addr, u16_t port)  

  2. {  

  3.   tftp_connection_args *args = (tftp_connection_args *)_args;  

  4.   int n = 0;  

  5.   

  6.   if (pkt_buf->len != pkt_buf->tot_len)  

  7.   {  

  8.     return;  

  9.   }  

  10.   

  11.   /* Does this packet have any valid data to write? */  

  12.   if ((pkt_buf->len > TFTP_DATA_PKT_HDR_LEN) &&  

  13.       (tftp_extract_block(pkt_buf->payload) == (args->block + 1)))  

  14.   {  

  15.     /* 在这里处理接收到的数据pkt_buf->payload */  

  16.   

  17.     /* update our block number to match the block number just received */  

  18.     args->block++;  

  19.     /* update total bytes  */  

  20.     (args->tot_bytes) += (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);  

  21.   

  22.     /* This is a valid pkt but it has no data.  This would occur if the file being 

  23.        written is an exact multiple of 512 bytes.  In this case, the args->block 

  24.        value must still be updated, but we can skip everything else.    */  

  25.   }  

  26.   else if (tftp_extract_block(pkt_buf->payload) == (args->block + 1))  

  27.   {  

  28.     /* update our block number to match the block number just received  */  

  29.     args->block++;  

  30.   }  

  31.   

  32.   /* SEndTransferthe appropriate ACK pkt (the block number sent in the ACK pkt echoes 

  33.    * the block number of the DATA pkt we just received - see RFC1350) 

  34.    * NOTE!: If the DATA pkt we received did not have the appropriate block 

  35.    * number, then the args->block (our block number) is never updated and 

  36.    * we simply sEndTransfera "duplicate ACK" which has the same block number as the 

  37.    * last ACK pkt we sent.  This lets the host know that we are still waiting 

  38.    * on block number args->block+1. */  

  39.   tftp_send_ack_packet(upcb, addr, port, args->block);  

  40.   

  41.   /* If the last write returned less than the maximum TFTP data pkt length, 

  42.    * then we've received the whole file and so we can quit (this is how TFTP 

  43.    * signals the EndTransferof a transfer!) 

  44.    */  

  45.   if (pkt_buf->len < TFTP_DATA_PKT_LEN_MAX)  

  46.   {  

  47.     tftp_cleanup_wr(upcb, args);  

  48.     pbuf_free(pkt_buf);  

  49.   }  

  50.   else  

  51.   {  

  52.     pbuf_free(pkt_buf);  

  53.     return;  

  54.   }  

  55.   

  56. }  

OK!至此STM32就完成了整个TFTP协议文件的接收。


3.2、保存文件数据

接收到完整的文件数据之后,我们需要把数据写到STM32的FLASH中,保存起来。

由于STM32内存较小,不可能开辟一个大的内存空间把文件数据保存起来再写到FLASH,

所以需要边接收边写FLASH。

首先在接收到写操作请求后,把存储区域的FLASH擦除:


  1. case TFTP_WRQ:    /* TFTP WRQ (write request) */   

  2.  ... ...  

  3.   FlashDestination = HtmlDataAddress;  

  4. * Erase the needed pages where the user application will be loaded */  

  5.   /* Define the number of page to be erased */  

  6.   NbrOfPage = FLASH_PagesMask(HtmlTotalSize);//擦除HTML区域  

  7.   

  8.   /* Erase the FLASH pages */  

  9.   FLASH_Unlock();  

  10.   for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)  

  11.    {  

  12.      FLASHStatus = FLASH_ErasePage(HtmlSizeAddress + (PageSize * EraseCounter));  

  13.    }  

  14.   FLASH_Lock();  

在文件数据传输过程中,把<=512BYTE的数据写到FLASH:


  1. filedata = (uint32_t)pkt_buf->payload + TFTP_DATA_PKT_HDR_LEN;  

  2. FLASH_Unlock();  

  3. for (n = 0;n < (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);n += 4)  

  4. {  

  5.  /* Program the data received into STM32F10x Flash */  

  6.  FLASH_ProgramWord(FlashDestination, *(uint32_t*)filedata);  

  7.   

  8.   if (*(uint32_t*)FlashDestination != *(uint32_t*)filedata)  

  9.    {  

  10.      /* End session */  

  11. p_send_error_message(upcb, addr, port, FLASH_VERIFICATION_FAILED);  

  12.     /* close the connection */  

  13.     tftp_cleanup_wr(upcb, args); /* close the connection */  

  14.    }  

  15.    FlashDestination += 4;  

  16.    filedata += 4;  

  17. }  

  18. FLASH_Lock();  

到这里,就实现了STM32接收TFTP客户端传输的文件数据,并保存到FLASH地址为FlashDestination的区域中。


3.3、演示操作

将通信板连接到与电脑在同一局域的路由器,并正确配置好IP信息。在电脑端打开软件Tftpd32.exe:


点击“上传”按键,就会把文件html.bin文件发送到STM32通信板:



可以在STM32通信板中把文件内容读出来使用,在下一篇博客物联网WEB开发中会使用TFTP传输HTML文件。

4、TFTP的应用

    TFTP主要是实现文件传输,在固件升级、程序调试中极大提高效率,有重要的意义。


关键字:STM32  物联网  TFTP文件传输 引用地址:STM32物联网之TFTP文件传输

上一篇:STM32移植lwip之硬件连接
下一篇:STM32CubeMX:ETH

推荐阅读最新更新时间:2024-03-16 16:00

STM32的串口函数_库函数
个人记录: 昨天做串口实验的时候一直没有成功的原因,连续调用USART_SendData总是会出现前一个被后一个覆盖的情况。 之前觉得ST的官方库应该没有问题就没往这方面想,现在查查,确实有库的问题,还是自己对库不太理解。 还有遇到的硬件复位以后,发送第一个字符丢失的情况。 1、后字节覆盖前字节 -----------------加判断while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET){} 2、硬件复位之后第一个字符丢失 -----------------USART_ClearFlag(USART2,USART_FLAG_TC); ----
[单片机]
<font color='red'>STM32</font>的串口函数_库函数
构建中国物联网 好一个“快”字了得
笔者从由中国电子学会主办的物联网产业与技术应用峰会上获悉,如今在学术界、企业界普遍达成共识,吸取以往经验教训,不再把目光和精力仅聚焦在物联网的各种概念之争上,而是集中力量,快、高、稳地突破物联网关键技术,快速地构建物联网络,以期占领未来的制高点。    2009年,国务院温家宝总理再三强调,我国需要以物联网的发展带动整个产业链的发展,借助信息产业的第三次浪潮实现经济的发展的再一次腾飞,要着力突破物联网关键技术,在后IP时代把物联网作为推进信息产业迈向信息社会的“发动机”。    有鉴于此,工信部提出了四点意见,第一,突破物联网关键核心技术,实现科技创新; 第二,制订中国物联网发展规划,全面布局;第三,推动典型物联网应用示
[网络通信]
2016年哪些物联网技术和方案会带来惊喜?
未来几年是物联网冲向爆发的重要年份,你所拥有的一切设备,以及几乎你能够想到的任何事物,都将连接到在物联网上。Gartner这张技术成熟度曲线图说明了这一切,IoT的发展速度可能比预期的还要快! Gartner还预测,今年全球使用中的物联网设备将达到64亿,比去年增加30%。到2020年实现物联网的事物数量将增长逾3倍,达到近210亿。另据CB Insights统计,过去六年中,迅速发展的物联网领域吸引了近75亿美元投资,发生近900起交易。2010至2014年期间,物联网初创公司吸引的投资金额翻番,交易数量由91起增至221起。物联网就像一个巨大的吸盘,将整个世界纳入其中。当我们在谈论物联网,除了用猎奇的眼光去憧憬它的美
[物联网]
2016年哪些<font color='red'>物联网</font>技术和方案会带来惊喜?
stm32模拟iic——引脚配置、代码
我的工程里要用到iic总线扩展rom,stm32是有硬件iic的,但是,网上有很多人说这个硬件iic有漏洞,甚至于有bug。 http://bbs.21ic.com/icview-184741-1-1.html http://blog.gkong.com/more.asp?name=zjcsharp&id=112878 。《例说stm32》的表述是:“非常复杂,不太好用”。那么我判断这个硬件iic可能确实有不足,因此选择直接用软件模拟出iic。 在做的过程中,遇到几个问题,记录下来。 1、引脚的模式与配置 iic的两个引脚SDA与SCL都要求既能输出又能输入。这对stm32来说问题不大,由参考手册给出的图来看,引脚是始终连着ID
[单片机]
<font color='red'>stm32</font>模拟iic——引脚配置、代码
(一)stm32之CMSIS标准、库目录、GPIO
一、CMSIS标准   ST公司的stm32采用的是cortex-m3内核,内核是整个微处理器的CPU。该内核是ARM公司设计的一种处理器体系架构。内核与外设的关系就像PC上的CPU与硬盘、主板、内存等的关系一样。 基于cortex系列的处理器内核都是一样的,区别在于除内核以外的外设的差异,由于这些差异,导致不同处理器移植起来比较麻烦,所以ARM与芯片厂商建立了CMSIS标准,CMSIS架构如下所示:   CMSIS标准中最主要的是CMSIS核心层;内核函数层中的内核函数寄存器以及地址主要由ARM公司提供;设备外设访问层核外外设和中断寄存器地址由芯片生产厂商定义。 二、库目录和文件简介 1、core_cm3.c文件   在Co
[单片机]
(一)<font color='red'>stm32</font>之CMSIS标准、库目录、GPIO
STM32之AHB与APB总线
AHB是高速总线,是一种系统总线,它主要负责连接处理器、DMA等一些内部接口。AHB 系统由主模块、从模块和基础结构3部分组成,整个AHB总线上的传输都由主模块发出,由从模块负责回应。 APB是低速总线,它主要负责连接外围设备,它又分为APB1和APB2,它的总线架构不像 AHB支持多个主模块,在APB里面唯一的主模块就是APB 桥。APB桥就是连接AHB和APB中间的玩意。 APB1最大时钟频率为36MHz APB2最大时钟频率为72MHz 看下图 在STM32F1中,不同的外设接在不同的APB总线上,以下是详细的分布: #define RCC_APB1Periph_TIM2 ((ui
[单片机]
<font color='red'>STM32</font>之AHB与APB总线
stm32---DS18b20
一个一线接口的温度传感器 DS18B20 发送所有的命令和数据都是字节的低位在前 每个器件都有自己的地址序列号 可以设置测量精度有四种,9---12位(0.5℃,0.25℃,0.125℃和 0.0625℃。),出场默认12位最高精度 高5位是0-- 温度大于0, 高5位是1-- 温度小于0 12位精度时,测量温度大于0,温度 = 测量数值 x 0.0625; 测量温度小于0, 温度 = (测量数值取反+1) x 0.0625 初始化时序 复位 : 引脚配置为输出模式。主机输出低电平 时间 480us ~ 960us,以产生复位脉冲后输出高电平线延时 15~60 us。 检查 :引脚配置为接收模式。接着 DS18B20 拉低
[单片机]
为什么学习STM32时还要学习汇编
不同的平台的汇编代码是不一样的,最早的汇编在50年代就发明了,比很多人的父母的年龄都大,老掉牙,不用学习怎么写汇编。一个公司有一个人知道怎么写汇编就够了。但要学习读汇编,为什么学习汇编? 1、性能 直接翻译为机器语言,性能最高。优秀的C语言效率只能达到汇编的80%左右。其他高级语言跟汇编一比差得更远。语言越高级性能越差。很多bootloader和BIOS用汇编写,汇编操作的是电脑,手机刚刚上电时,硬件和初始化的那些命令,它们的性能的要求比较高,效率高开机速度更快。 分析问题 个人认为,编程人与机器对话,我们写C,写JAVA,但是电脑并不认识这些语言,电脑只认识0和1;所以需要一个人来翻译这些语言,这个翻译官就是编译器,但是
[单片机]
为什么学习<font color='red'>STM32</font>时还要学习汇编
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved