历史上的今天
今天是: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初始化,并启动网卡后进行:
LwIP_Config();
printf("ipaddr:%d.%d.%d.%d\r\n", net_ip[0], net_ip[1], net_ip[2], net_ip[3]);
tftpd_init();
在tftpd_init函数中创建TFTP服务器:
void tftpd_init(void)
{
err_t err;
unsigned port = 69;
/* create a new UDP PCB structure */
UDPpcb = udp_new();
if (!UDPpcb)
{ /* Error creating PCB. Out of Memory */
return;
}
/* Bind this PCB to port 69 */
err = udp_bind(UDPpcb, IP_ADDR_ANY, port);
if (err != ERR_OK)
{ /* Unable to bind to port */
return;
}
/* TFTP server start */
udp_recv(UDPpcb, recv_callback_tftp, NULL);
}
OK,到这里就完成了TFTP服务器在LWIP中建立起来了,接下来的主要事情就是根据TFTP协议进行协议解释、数据处理。
2、TFTP协议分析
2.1、TFTP通信基本流程(摘自网络)
2.2、TFTP报文格式(摘自网络)
2.3、TFTP协议理解
从以上两张图片,我们了解到什么有用信息呢?
每一次文件传输,首先需要发起一个请求,根据请求帧的操作码判断是读文件还是写文件。
每一帧都有一个操作码用来标识读写。
数据包的长度有一个块编号用来表示数据包的顺序。
数据包中的数据长度为512个字节(在后面的软件我们可以了解到这个长度是可以设定的)。
3、实现TFTP文件传输
3.1、文件传输协议实现
有了第2节的协议分析,我们基本了解了TFTP通信的协议,在这里,我们来实现TFTP的服务器端代码。
在监听的回调函数被触发调用时,首先从请求帧中获取操作码:
typedef enum {
TFTP_RRQ = 1,
TFTP_WRQ = 2,
TFTP_DATA = 3,
TFTP_ACK = 4,
TFTP_ERROR = 5
} tftp_opcode;
tftp_opcode tftp_decode_op(char *buf)
{
return (tftp_opcode)(buf[1]);
}
根据操作码进行相应的处理:
tftp_opcode op = tftp_decode_op(pkt_buf->payload);
switch (op)
{
case TFTP_RRQ: /* TFTP RRQ (read request) */
tftp_extract_filename(FileName, pkt_buf->payload);
tftp_process_read(upcb, addr, port, FileName);
break;
case TFTP_WRQ: /* TFTP WRQ (write request) */
tftp_extract_filename(FileName, pkt_buf->payload);
//在这个加入擦FALSH
tftp_process_write(upcb, addr, port, FileName);
break;
default:
/* sEndTransfera generic access violation message */
tftp_send_error_message(upcb, addr, port, TFTP_ERR_ACCESS_VIOLATION);
/* TFTP unknown request op */
/* no need to use tftp_cleanup_wr because no "tftp_connection_args" struct has been malloc'd */
udp_remove(upcb);
break;
}
这里当STM32接收到写操作请求时,通过tftp_extract_filename函数把文件名读出来。接下来通过tftp_process_write函数来完成文件数据的传输:
int tftp_process_write(struct udp_pcb *upcb, struct ip_addr *to, int to_port, char *FileName)
{
... ...
udp_recv(upcb, wrq_recv_callback, args);
tftp_send_ack_packet(upcb, to, to_port, args->block);
return 0;
}
设定数据传输回调函数后,根据TFTP协议,回复一个ACK,之后TFTP客户端开始传输文件数据,从而触发调用wrq_recv_callback
void wrq_recv_callback(void *_args, struct udp_pcb *upcb, struct pbuf *pkt_buf, struct ip_addr *addr, u16_t port)
{
tftp_connection_args *args = (tftp_connection_args *)_args;
int n = 0;
if (pkt_buf->len != pkt_buf->tot_len)
{
return;
}
/* Does this packet have any valid data to write? */
if ((pkt_buf->len > TFTP_DATA_PKT_HDR_LEN) &&
(tftp_extract_block(pkt_buf->payload) == (args->block + 1)))
{
/* 在这里处理接收到的数据pkt_buf->payload */
/* update our block number to match the block number just received */
args->block++;
/* update total bytes */
(args->tot_bytes) += (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);
/* This is a valid pkt but it has no data. This would occur if the file being
written is an exact multiple of 512 bytes. In this case, the args->block
value must still be updated, but we can skip everything else. */
}
else if (tftp_extract_block(pkt_buf->payload) == (args->block + 1))
{
/* update our block number to match the block number just received */
args->block++;
}
/* SEndTransferthe appropriate ACK pkt (the block number sent in the ACK pkt echoes
* the block number of the DATA pkt we just received - see RFC1350)
* NOTE!: If the DATA pkt we received did not have the appropriate block
* number, then the args->block (our block number) is never updated and
* we simply sEndTransfera "duplicate ACK" which has the same block number as the
* last ACK pkt we sent. This lets the host know that we are still waiting
* on block number args->block+1. */
tftp_send_ack_packet(upcb, addr, port, args->block);
/* If the last write returned less than the maximum TFTP data pkt length,
* then we've received the whole file and so we can quit (this is how TFTP
* signals the EndTransferof a transfer!)
*/
if (pkt_buf->len < TFTP_DATA_PKT_LEN_MAX)
{
tftp_cleanup_wr(upcb, args);
pbuf_free(pkt_buf);
}
else
{
pbuf_free(pkt_buf);
return;
}
}
OK!至此STM32就完成了整个TFTP协议文件的接收。
3.2、保存文件数据
接收到完整的文件数据之后,我们需要把数据写到STM32的FLASH中,保存起来。
由于STM32内存较小,不可能开辟一个大的内存空间把文件数据保存起来再写到FLASH,
所以需要边接收边写FLASH。
首先在接收到写操作请求后,把存储区域的FLASH擦除:
case TFTP_WRQ: /* TFTP WRQ (write request) */
... ...
FlashDestination = HtmlDataAddress;
* Erase the needed pages where the user application will be loaded */
/* Define the number of page to be erased */
NbrOfPage = FLASH_PagesMask(HtmlTotalSize);//擦除HTML区域
/* Erase the FLASH pages */
FLASH_Unlock();
for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
{
FLASHStatus = FLASH_ErasePage(HtmlSizeAddress + (PageSize * EraseCounter));
}
FLASH_Lock();
在文件数据传输过程中,把<=512BYTE的数据写到FLASH:
filedata = (uint32_t)pkt_buf->payload + TFTP_DATA_PKT_HDR_LEN;
FLASH_Unlock();
for (n = 0;n < (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);n += 4)
{
/* Program the data received into STM32F10x Flash */
FLASH_ProgramWord(FlashDestination, *(uint32_t*)filedata);
if (*(uint32_t*)FlashDestination != *(uint32_t*)filedata)
{
/* End session */
p_send_error_message(upcb, addr, port, FLASH_VERIFICATION_FAILED);
/* close the connection */
tftp_cleanup_wr(upcb, args); /* close the connection */
}
FlashDestination += 4;
filedata += 4;
}
FLASH_Lock();
到这里,就实现了STM32接收TFTP客户端传输的文件数据,并保存到FLASH地址为FlashDestination的区域中。
3.3、演示操作
将通信板连接到与电脑在同一局域的路由器,并正确配置好IP信息。在电脑端打开软件Tftpd32.exe:
点击“上传”按键,就会把文件html.bin文件发送到STM32通信板:
可以在STM32通信板中把文件内容读出来使用,在下一篇博客物联网WEB开发中会使用TFTP传输HTML文件。
4、TFTP的应用
TFTP主要是实现文件传输,在固件升级、程序调试中极大提高效率,有重要的意义。
上一篇:STM32移植lwip之硬件连接
下一篇:STM32CubeMX:ETH
史海拾趣
|
我现在需要做个电话机!可以显示大头贴的!需要用块彩色的LCD,不知道市场上卖的带驱动的那种LCD是不是可以直接接到ARM处理器的IO口上?一般黑白的LCD也是可以的吧!主要的部分是在软件的编程吧!… 查看全部问答> |
|
有的初学者在理论上花了很多功夫,结果半年下来还是没有把PLC搞懂,其实他们只是缺少了一些PLC的实践经验,只要再进行一些实际的梯形图编写、程序下载、调试等操作,增加对PLC的感性认识,很快就可以掌握PLC这项技术了。开始阶段可以先学习 ...… 查看全部问答> |
|
WINCE 能实现每秒5000次中断吗?有没有高手做过,请赐教。 Wince5.0 硬件每秒钟5000个脉冲触发X86CUP的7号中断,Wince能完全捕获到每一个中断吗,保证不丢,请做过的兄弟姐妹帮帮忙,提提思路,或者给 一段代码,不胜感激… 查看全部问答> |
|
小弟正在调试WINCE+2440下串口程序,现在COM1已经打开,我把2440的板子COM1直接用串口线连接到PC的COM3,发现通过COM1写到PC的全是???,通过PC写到COM1的是ff,或者77之类的乱码,是怎么回事?先谢了。… 查看全部问答> |
|
各位高手们,你们经验比较丰富,见的单片机种类也多。请问一下有没有常见的,具有PWM功能的单片机,可以输出50kHz~100kHz的pwm脉冲信号,介绍几个。最近在做这方面的实验,谢谢各位了… 查看全部问答> |
|
新手关于MSP430LaunchPad在CCS下Debug的问题 首先,我是msp430绝对新手,因为之前我从来没用过ti的单片机。关于msp430程序下载以及Debug的方式,我看了相关资料,资料显示是使用Jtag方式进行的,有相应的下载器。收到开发板以后,我发现套件里面只有一根usb的线缆,板子上有一块usb-串口的芯片 ...… 查看全部问答> |
|
硬件乘法器不占用CPU周期,有硬件实现,速度比软件实现的乘法速度快很多。msp430f14x、msp430f16x中都含有硬件乘法器模块,方便用户需要速度的时候使用。 硬件介绍: 在MSP430系列单片机中,硬件乘法器是外围模块,而不是CPU ...… 查看全部问答> |
|
最近有一个项目需要将DC5V升到DC18V,之前想过用MC34063,可是MC34063的效率低、纹波大所以就放弃了。现在想用SY7208,可是SY7208用的人很少,资料里只有输出电压的计算公式,其他参数我不知道怎么确定,请各位工程师帮我看看。 其中R1R2可以用输 ...… 查看全部问答> |




