一. 前言
前面我们实现了xmodem进行文件传输,xmodem一次只能传输一个文件,有时候希望一次传输多个文件,可以使用ymodem。Ymodem的实现见公众号文章
https://mp.weixin.qq.com/s/uDwVYJRDGrH6MZmUXn2Q3A YMODEM协议介绍与高效高可移植非阻塞版本实现。
二. 实现
上述Ymodem实现采用的是面向对象思想,模块化设计,只需要实现几个简单的接口即可。
2.1接收
shell_func.c中
#include "ymodem.h"
添加命令函数申明
static void ryfilefunc(uint8_t* param);
g_shell_cmd_list_ast中添加命令信息,
{ (uint8_t*)"ryfile", ryfilefunc, (uint8_t*)"ryfile"},
命令函数实现如下
void ryfilefunc(uint8_t* param)
{
(void)param;
int res = 0;
ymodem_rx_cfg_st cfg=
{
.buffer = rxtx_buf,
.getms = getms,
.io_read = io_read,
.io_write = io_write,
.start_timeout = 60,
.packet_timeout = 2000,
.ack_timeout = 1000,
.mem_start = ymodem_rx_file_start,
.mem_write = ymodem_rx_file_write,
.mem_done = ymodem_rx_file_done,
};
ymodem_rx_init(&cfg);
while((res = ymodem_rx()) == YMODEM_RX_ERR_NEED_CONTINUE);
xprintf("\r\nres:%d\r\n",res);
}
其中
.mem_start = ymodem_rx_file_start,
.mem_write = ymodem_rx_file_write,
.mem_done = ymodem_rx_file_done,
实现如下
static FIL ymodem_rx_fil; /* File object */
static int ymodem_rx_file_open_flag = 0;
static int ymodem_rx_file_start(uint32_t addr, uint8_t** name, uint32_t* len)
{
(void)addr;
(void)len;
int res = 0;
if(ymodem_rx_file_open_flag == 0)
{
FRESULT res = f_open(&ymodem_rx_fil, (const char*)(*name), FA_CREATE_NEW | FA_WRITE);
if (FR_OK == res)
{
ymodem_rx_file_open_flag = 1;
return 0;
}
else
{
xprintf("open %s err %d\r\n",name,res);
return -1;
}
}
return res;
}
static uint32_t ymodem_rx_file_write(uint32_t addr, uint8_t* buffer, uint32_t len)
{
(void)addr;
if(ymodem_rx_file_open_flag != 0)
{
UINT bw;
FRESULT res = f_write(&ymodem_rx_fil, buffer, len, &bw);
if ((bw != len) || (res != FR_OK))
{
xprintf("write err %d %d\r\n",bw,res);
}
return bw;
}
else
{
return 0;
}
}
static int ymodem_rx_file_done(void)
{
if(0 != ymodem_rx_file_open_flag)
{
ymodem_rx_file_open_flag = 0;
return f_close(&ymodem_rx_fil);
}
return 0;
}
其他的接口和xmodem一样
.getms = getms,
.io_read = io_read,
.io_write = io_write,
Help查看命令
ryfile接收多个文件,电脑端ymodem发送
crt第二个文件后会以小包发送,所以速率慢很多。
然后ls查看,收到的文件
计算MD5sum,原始数据和收到的数据一致。
2.2发送
shell_func.c中
#include "ymodem.h"
添加命令函数申明
static void syfilefunc(uint8_t* param);
g_shell_cmd_list_ast中添加命令信息,
{ (uint8_t*)"syfile", syfilefunc, (uint8_t*)"syfile len path1 path2"},
命令函数实现如下,这里实现最多一次发送2个文件,可以修改支持在命令行中解析支持更多文件即可。
void syfilefunc(uint8_t* param)
{
(void)param;
int plen = 0;
int res;
int num = 0;
ymodem_tx_cfg_st cfg=
{
.buffer = rxtx_buf,
.getms = getms,
.io_read = io_read,
.io_write = io_write,
.start_timeout = 60,
.packet_timeout = 2000,
.ack_timeout = 2000,
.plen = 1024,
.mem_start = ymodem_tx_file_start,
.mem_read = ymodem_tx_file_read,
.mem_done = ymodem_tx_file_done,
};
memset(ymodem_sfile_name,0,sizeof(ymodem_sfile_name));
ymodem_tx_file_num = 0;
num = sscanf((const char*)param, "%*s %d %s %s", &plen, ymodem_sfile_name[0],ymodem_sfile_name[1]);
if((num == 2) || (num == 3))
{
cfg.plen = plen;
ymodem_tx_init(&cfg);
while((res = ymodem_tx()) == YMODEM_TX_ERR_NEED_CONTINUE);
xprintf("\r\nres:%d\r\n",res);
}
}
其中接口
.mem_start = ymodem_tx_file_start,
.mem_read = ymodem_tx_file_read,
.mem_done = ymodem_tx_file_done,
实现如下
static int8_t ymodem_sfile_name[2][64];
static int ymodem_tx_file_open_flag = 0;
static uint32_t ymodem_tx_file_num = 0;
static FIL ymodem_tx_file;
static uint8_t ymodem_sfile_name_len_att[128];
/* 获取文件名 */
static int ymodem_tx_file_start(uint32_t addr, uint8_t** name, uint32_t* len)
{
(void)addr;
(void)len;
uint32_t flen;
uint32_t fnamelen;
int res = 0;
if(ymodem_tx_file_open_flag != 0)
{
f_close(&ymodem_tx_file);
ymodem_tx_file_open_flag = 0;
}
if(ymodem_tx_file_num >= sizeof(ymodem_sfile_name)/sizeof(ymodem_sfile_name[0]))
{
return -1;
}
if(0 == (res = f_open(&ymodem_tx_file, (const char*)ymodem_sfile_name[ymodem_tx_file_num], FA_READ)))
{
flen = f_size(&ymodem_tx_file);
fnamelen = strlen((const char*)ymodem_sfile_name[ymodem_tx_file_num]);
memcpy(ymodem_sfile_name_len_att,ymodem_sfile_name[ymodem_tx_file_num],fnamelen);
ymodem_sfile_name_len_att[fnamelen]=0;
*len = fnamelen + 1 + snprintf((char*)(&ymodem_sfile_name_len_att[fnamelen+1]),sizeof(ymodem_sfile_name_len_att)-(fnamelen+1),"%ld %o %d",flen,1715670058,0);
*name = ymodem_sfile_name_len_att;
ymodem_tx_file_open_flag = 1;
}
ymodem_tx_file_num++;
return res;
}
static uint32_t ymodem_tx_file_read(uint32_t addr, uint8_t* buffer, uint32_t len)
{
(void)addr;
UINT br;
FRESULT res = f_read(&ymodem_tx_file, buffer, len, &br);
if(res != 0)
{
xprintf("read err %d\r\n",res);
}
return br;
}
static int ymodem_tx_file_done(void)
{
if(0 != ymodem_tx_file_open_flag)
{
ymodem_tx_file_open_flag = 0;
return f_close(&ymodem_tx_file);
}
return 0;
}
其他接口和xmodem一样
.buffer = rxtx_buf,
.getms = getms,
.io_read = io_read,
.io_write = io_write,
Help查看命令
syfile 发送文件到电脑
MD5sum计算校验信息。
三. 总结
得益于之前设计的ymodem的轮子,只需要几个简单的接口,即可快速集成ymodem传输文件功能。