一.前言
前面我们移植了FATFS文件系统,此时就可以方便地读取TF卡上的文件了。为了和开发板进行交互,我们继续实现通过xmodem导入导出文件的功能。xmodem的实现和shell的实现参考公众号文章。
https://mp.weixin.qq.com/s/QSYKxND3DTyQZ0JtnSqkzQ XMODEM协议介绍与高效高可移植非阻塞版本实现
https://mp.weixin.qq.com/s/XLmbJn0SKoDT1aLdxHDrbg一个超级精简高可移植的shell命令行C实现
二.Xmodem导入文件
shell_func.c中
添加fatfs头文件
#include "ff.h"
中申明实现函数
static void rxfilefunc(uint8_t* param);
g_shell_cmd_list_ast中添加一行
{ (uint8_t*)"rxfile", rxfilefunc, (uint8_t*)"rxfile name len"},
实现函数rxfilefunc
void rxfilefunc(uint8_t* param)
{
char name[64];
uint32_t len;
int res = 0;
if(2 == sscanf((const char*)param, "%*s %s %ld", name, &len))
{
xprintf("rxfile %s %ld\r\n",name,len);
if(0 == rx_file_open(name))
{
xmodem_cfg_st cfg=
{
.buffer = rxtx_buf,
.crccheck = 1,
.getms = getms,
.io_read = io_read,
.io_read_flush = io_read_flush,
.io_write = io_write,
.start_timeout = 60,
.packet_timeout = 1000,
.ack_timeout = 1000,
.mem_write = rx_file_write,
.addr = 0,
.totallen = len,
};
xmodem_init_rx(&cfg);
while((res = xmodem_rx()) == 0);
rx_file_close(name);
xprintf("res:%d\r\n",res);
}
else
{
xprintf("open:%s err\r\n",name);
}
}
}
需要实现相关的接口
static uint8_t rxtx_buf[1029];
static uint32_t getms(void)
{
return os_boot_time32();
}
static uint32_t io_read(uint8_t* buffer, uint32_t len)
{
return fifo_cdc_rx_read(buffer, len);
}
static void io_read_flush(void)
{
uint8_t tmp;
while(0 != fifo_cdc_rx_read(&tmp, 1));
}
static uint32_t io_write(uint8_t* buffer, uint32_t len)
{
fifo_cdc_tx_write(buffer, len);
return len;
}
文件相关接口
static FIL rx_fil; /* File object */
static int rx_file_open_flag = 0;
static int rx_file_open(char* name)
{
FRESULT res = f_open(&rx_fil, name, FA_CREATE_NEW | FA_WRITE);
if (FR_OK == 0)
{
rx_file_open_flag = 1;
return 0;
}
else
{
xprintf("open %s err %d\r\n",name,res);
return -1;
}
}
static int rx_file_close(char* name)
{
if(rx_file_open_flag != 0)
{
rx_file_open_flag = 0;
FRESULT res = f_close(&rx_fil);
if(res != FR_OK)
{
xprintf("close err %d\r\n",res);
}
return 0;
}
else
{
return -1;
}
}
static uint32_t rx_file_write(uint32_t addr, uint8_t* buffer, uint32_t len)
{
if(rx_file_open_flag != 0)
{
UINT bw;
FRESULT res = f_write(&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 FATFS fs; /* Filesystem object */
if(0 == (r = sd_itf_init()))
{
if(FR_OK != (res = f_mount (&fs, "0:", 1)))
{
printf("mount err %d, mkfs\r\n",res);
res = f_mkfs("0:",0,work,sizeof(work));
if(res == 0)
{
printf("mkfs ok\r\n");
if(FR_OK == f_mount (&fs, "0:", 1))
{
printf("mount ok\r\n");
}
else
{
printf("mount err\r\n");
}
}
else
{
printf("mkfs err %d\r\n",res);
}
}
else
{
printf("mount ok\r\n");
} }
else
{
printf("sd init err:%d\r\n",r);
}
测试输入help查看新添加的命令
输入
rxfile zhcy093.pdf 2573119回车
Crt中菜单栏
Transfer->Send Xmodem...
选择对应的文件,打开开始发送
开始传输
传输完成
用读卡器查看导入的文件
计算校验信息和原始文件一样
三. Xmodem导出文件
shell_func.c中
中申明实现函数
static void sxfilefunc(uint8_t* param);
g_shell_cmd_list_ast中添加一行
{ (uint8_t*)"sxfile", sxfilefunc, (uint8_t*)"sxfile name len"},
实现函数sxfilefunc
void sxfilefunc(uint8_t* param)
{
char name[64];
uint32_t len;
int res = 0;
if(2 == sscanf((const char*)param, "%*s %s %ld", name, &len))
{
xprintf("sxfile %s %ld\r\n",name,len);
if(0 == tx_file_open(name))
{
xmodem_cfg_st cfg=
{
.buffer = rxtx_buf,
.plen = 1024,
.getms = getms,
.io_read = io_read,
.io_read_flush = io_read_flush,
.io_write = io_write,
.start_timeout = 60,
.packet_timeout = 1000,
.ack_timeout = 5000,
.mem_read = tx_file_read,
.addr = 0,
.totallen = len,
};
xmodem_init_tx(&cfg);
while((res = xmodem_tx()) == 0);
tx_file_close(name);
xprintf("res:%d\r\n",res);
}
else
{
xprintf("open:%s err\r\n",name);
}
}
}
文件读接口实现
static FIL tx_fil; /* File object */
static int tx_file_open_flag = 0;
static int tx_file_open(char* name)
{
FRESULT res = f_open(&tx_fil, name, FA_READ);
if (FR_OK == 0)
{
tx_file_open_flag = 1;
return 0;
}
else
{
xprintf("open %s err %d\r\n",name,res);
return -1;
}
}
static int tx_file_close(char* name)
{
if(tx_file_open_flag != 0)
{
tx_file_open_flag = 0;
FRESULT res = f_close(&tx_fil);
if(res != FR_OK)
{
xprintf("close err %d\r\n",res);
}
return 0;
}
else
{
return -1;
}
}
static uint32_t tx_file_read(uint32_t addr, uint8_t* buffer, uint32_t len)
{
UINT br;
FRESULT res = f_read(&tx_fil, buffer, len, &br);
if(res != 0)
{
xprintf("read err %d\r\n",res);
}
return br;
}
测试输入help查看新添加的命令
输入
sxfile zhcy093.pdf 2573119 回车
Crt中菜单栏
Transfer->Receive Xmodem...
选择保存为文件zhcy093-1.pdf
传输中
传输完成
Xmodem协议最后不足包长需要填充0x1A,所以接收的文件中需要删除这部分,我们实现的xmodem会根据长度信息只保存指定长度没有这个问题。
删除这部分填充
检查校验信息,完全一致
四. 总结
得益于我们前面构造的shell和xmodem的轮子,可以很快的就实现文件传输的功能,这也是之前文章分享中要不断积累自己的轮子的原因。平常功能模块注意设计好框架接口,按照面向对象思想设计,考虑可移植性,这样就可以快速的移植使用。