历史上的今天
返回首页

历史上的今天

今天是:2025年04月22日(星期二)

正在发生

2018年04月22日 | STM32物联网之TFTP文件传输

2018-04-22 来源:eefocus

  • 感言:专注物联网应用开发,分享物联网技术经验。

  • 软件平台: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主要是实现文件传输,在固件升级、程序调试中极大提高效率,有重要的意义。


推荐阅读

史海拾趣

Gustav Klauke GmbH公司的发展小趣事

Gustav Klauke GmbH在电子行业中的五个发展故事

故事一:创立与电气时代的崛起

1879年,Gustav Klauke GmbH在德国雷姆沙伊德成立,正值第二次工业革命带来的“电气时代”大爆发。创始人Gustav Klauke凭借其对精工品质的执着追求,将公司的业务聚焦于电气连接技术和电缆作业工具的研发。随着全球电气工业的迅速发展,Klauke迅速成为该领域的佼佼者,为供电系统的各个环节提供可靠且实用的解决方案,满足了社会对电气产品日益增长的多元化需求。

故事二:产品创新与多元化发展

历经百年开拓,Klauke的产品线不断丰富和完善。从最初的钟表维修钳子,到电缆接线端子的生产,再到电池驱动的液压工具的研发,Klauke始终走在行业前沿。特别是1994年推出的首款电池驱动液压工具,标志着公司在电气连接技术上的重大突破。至今,Klauke已拥有多达280种工具,2000多个型号,超过10000个优质部件,广泛应用于电力、铁路和工业等多个领域。

故事三:品质与认证的国际认可

Klauke深知电气连接作业对安全可靠性的极高要求,因此始终将产品质量放在首位。公司不仅拥有IEC(国际电工委员会)、UL(美国保险商试验所)、DNV(挪威船级社)和GL(德国劳氏船级社)等特殊行业的权威资质认证,还通过严格的产品测试报告,确保每一款产品都能达到国际最高标准。这种对品质的坚持,让Klauke在全球客户中赢得了极高的信誉和口碑。

故事四:全球化布局与业务拓展

随着全球市场的不断扩大,Klauke积极实施全球化战略。从德国本土出发,公司在全球范围内设立了多个分公司和销售办事处,如印度、奥地利、西班牙等。同时,公司还通过并购和合作等方式,不断拓展业务领域和市场份额。例如,2018年Gustav Klauke GmbH被艾默生收购,现隶属于“专业工具”部门,这一举措进一步提升了公司在全球电气工具市场的竞争力。

故事五:未来展望与技术创新

面对未来,Klauke继续秉承德国精工品质的优良传统,致力于技术创新和服务优化。公司正密切关注物联网和工业4.0等下一代趋势,并制定相应的战略计划。例如,新型电池供电液压工具配备了蓝牙接口,实现了移动数据读取和工具配置的便捷性。此外,Klauke还计划推出更多高适应性、精准性的电气连接解决方案,以更好地满足全球日益多元的施工作业需求。展望未来,Klauke将继续向下一个百年目标迈进,为电子行业的发展贡献更多力量。

华大北斗(Allystar)公司的发展小趣事

在电子行业中,华大北斗(Allystar)公司以其卓越的技术实力和市场表现脱颖而出,其发展历程中充满了许多引人注目的故事。以下是关于华大北斗发展起来的五个相关故事:

一、华大北斗的南迁之旅

华大北斗的发展史上,一个重要的节点是2016年的南迁。当时,华大北斗主动选择深圳作为新的研发总部所在地,这是基于深圳电子制造业的雄厚基础和良好的创新环境。在短短几个月内,华大北斗完成了研发环境的选址与装修、研发团队的组建以及实验设备的部署,展现了其高效的执行力。这一举措得到了深圳市政府及各级领导的高度关注与认可,华大北斗也被列为深圳市重点引进的战略项目。

二、北斗芯片技术的突破

华大北斗在北斗芯片技术方面取得了重大突破。公司成功研发出支持北斗三号信号体制的多系统多频高精度SoC芯片,这一成果在芯片多频定位方面取得了重要进展。该芯片集成了射频、基带、处理器、存储器等单元,不仅大幅缩小了芯片尺寸,还优化了功耗,实现了单芯片双频北斗高精度定位一体化SoC解决方案。这一技术的突破为北斗应用的自主可控提供了关键支持,推动了北斗产业化、国际化、规模化应用的进程。

三、高精度芯片进入共享单车市场

随着共享经济的兴起,共享单车市场蓬勃发展。华大北斗凭借其高精度芯片技术,成功进入了共享单车市场。其高精度芯片为共享单车提供了精准的定位服务,有效提升了用户体验和车辆管理效率。这一成功应用不仅展示了华大北斗技术的市场竞争力,也为其在更多领域的应用提供了广阔的空间。

四、双频北斗手机的面市

基于华大北斗双频北斗芯片研发的全球首款双频北斗手机的面市,是华大北斗发展历程中的又一里程碑。这款手机的推出,标志着北斗导航技术在智能手机领域的成功应用,也展示了华大北斗在推动北斗产业化进程中的积极作用。双频北斗手机的出现,不仅提升了用户体验,也为北斗导航技术的普及和推广做出了贡献。

五、斩获香港环球创新奖

2023年,华大北斗荣获首届香港环球创新奖年度创新大奖,这是对其在北斗导航芯片领域技术创新和产业化应用的高度认可。这一奖项的获得,不仅提升了华大北斗的品牌形象和市场影响力,也为其在未来的发展中注入了强大的动力。华大北斗将继续坚持技术创新,针对行业应用的不同需求,用领先的技术研发和应用能力推动北斗产业的高质量规模化发展。

这些故事只是华大北斗发展历程中的一部分,但它们足以展现出华大北斗在电子行业中的技术实力和市场竞争力。华大北斗将继续秉持创新精神,不断推动北斗导航技术的发展和应用,为电子行业的进步做出更大的贡献。

BCD Semi(Diodes)公司的发展小趣事

在电子行业的发展过程中,市场波动和风险挑战是不可避免的。某一年,全球半导体市场出现了严重的产能过剩,导致产品价格大幅下跌。面对这一挑战,BCD Semi(Diodes)公司及时调整生产策略,优化产品结构,降低生产成本,成功度过了这一行业寒冬。

Dialog公司的发展小趣事

近年来,Dialog公司通过一系列收购活动实现了快速扩张。例如,Dialog公司收购了Adesto公司,进一步拓展了工业物联网市场。这次收购为Dialog公司带来了一系列新的智能楼宇自动化工业解决方案,为其现有的制造自动化产品提供了非常好的补充。此外,收购Adesto还为Dialog公司带来了近5,000家客户公司,这些公司中大部分对Dialog公司来说是新客户。

General Electric Company公司的发展小趣事
根据应用需求设计合适的电路,注意电源滤波、信号隔离和保护措施等。
BAE Systems公司的发展小趣事

作为一个大型企业,BAE Systems公司不仅关注经济效益,也积极履行社会责任,推动可持续发展。公司注重环境保护和安全生产,严格遵守相关法律法规和行业标准。同时,公司也积极参与社会公益事业,为社区发展和员工福利做出了贡献。此外,公司还注重员工的培训和发展,为员工提供了广阔的职业发展空间和良好的工作环境。这些举措不仅提升了公司的社会形象,也为公司的可持续发展奠定了坚实的基础。

这五个故事只是BAE Systems公司在电子行业发展历程中的一部分,它们展示了公司的成长、创新、合作、应对挑战以及履行社会责任等方面的努力和成果。这些故事也充分说明了BAE Systems公司在电子行业中的重要地位和影响力。

问答坊 | AI 解惑

谁用过LCD近进来帮我一下吧!

我现在需要做个电话机!可以显示大头贴的!需要用块彩色的LCD,不知道市场上卖的带驱动的那种LCD是不是可以直接接到ARM处理器的IO口上?一般黑白的LCD也是可以的吧!主要的部分是在软件的编程吧!…

查看全部问答>

学习PLC的2个简单方法

  有的初学者在理论上花了很多功夫,结果半年下来还是没有把PLC搞懂,其实他们只是缺少了一些PLC的实践经验,只要再进行一些实际的梯形图编写、程序下载、调试等操作,增加对PLC的感性认识,很快就可以掌握PLC这项技术了。开始阶段可以先学习 ...…

查看全部问答>

WINCE 能实现每秒5000次中断吗?有没有高手做过,请赐教。

Wince5.0 硬件每秒钟5000个脉冲触发X86CUP的7号中断,Wince能完全捕获到每一个中断吗,保证不丢,请做过的兄弟姐妹帮帮忙,提提思路,或者给 一段代码,不胜感激…

查看全部问答>

求助:WINCE下2440串口读写问题(急)

小弟正在调试WINCE+2440下串口程序,现在COM1已经打开,我把2440的板子COM1直接用串口线连接到PC的COM3,发现通过COM1写到PC的全是???,通过PC写到COM1的是ff,或者77之类的乱码,是怎么回事?先谢了。…

查看全部问答>

小菜求教

我想学习windows ce方面的开发,我以前学C++的,没有学过C#,我经常用的是vs2005。 我想问一下,用vs2005进行windows ce方面的开发,是否要学些C#? 学习C#又什么好的教材吗? 谢谢了…

查看全部问答>

侃侃我的成果

本人长期从事单片机开发应用,积累了一定的经验。本人将毕生研究心得集成了文字,放在我的博客里,希望大家光临指导,并给点意见。我的博客地址;http://blog.mcuol.com/user/Article/500.html…

查看全部问答>

介绍 具有pwm模块的常用单片机

各位高手们,你们经验比较丰富,见的单片机种类也多。请问一下有没有常见的,具有PWM功能的单片机,可以输出50kHz~100kHz的pwm脉冲信号,介绍几个。最近在做这方面的实验,谢谢各位了…

查看全部问答>

新手关于MSP430LaunchPad在CCS下Debug的问题

首先,我是msp430绝对新手,因为之前我从来没用过ti的单片机。关于msp430程序下载以及Debug的方式,我看了相关资料,资料显示是使用Jtag方式进行的,有相应的下载器。收到开发板以后,我发现套件里面只有一根usb的线缆,板子上有一块usb-串口的芯片 ...…

查看全部问答>

MSP430程序库<十三>硬件乘法器使用

硬件乘法器不占用CPU周期,有硬件实现,速度比软件实现的乘法速度快很多。msp430f14x、msp430f16x中都含有硬件乘法器模块,方便用户需要速度的时候使用。     硬件介绍: 在MSP430系列单片机中,硬件乘法器是外围模块,而不是CPU ...…

查看全部问答>

请教DCDC升压SY7208的参数问题

最近有一个项目需要将DC5V升到DC18V,之前想过用MC34063,可是MC34063的效率低、纹波大所以就放弃了。现在想用SY7208,可是SY7208用的人很少,资料里只有输出电压的计算公式,其他参数我不知道怎么确定,请各位工程师帮我看看。 其中R1R2可以用输 ...…

查看全部问答>