前言
在实习公司遇到一个需求,需要用到ymodem协议来传输文件(仪表传输到控制器)。在网上找了很多资料,但是符合的不太多
于是写下这个博客来记录一下。
一、ymodem协议是什么?
这个就直接打开浏览器,然后百度或者啥,一搜索就一大推,我就不废话再去讲什么是ymodem协议了。
简单的附上一个图片:
二、直接贴代码
#include "ymodem_read_file.h"
#include "ymodem.h"
//#include "ebike_util.h"
//#include "..\..\ebike_uart.h"
#include "string.h"
// #include "..\..\ebike_list.h"
#define UPDATA_FILE_NAME 'u', 'p', 'd', 'a', 't', 'a', '.', 'b', 'i', 'n', 0x00, '1', 'f', '4', 0x00
#define UART_YMODEM_READ_BUFFER_LENGTH 1
#define UART_YMODEM_READ_OK_BUFFER_LENGTH 2
#define UART_YMODEM_WRITE_BUFFER_LENGTH 128
#define UART_YMODEM_ALL_WRITE_BUFFER_LENGTH 133
#define UART_YMODEM_BOOT_SEND_COUNT 120
#define UART_YMODEM_BOOTING_READ_BUFFER_LENGTH 80
static kal_uint8 blockon = 0x00;
static kal_uint8 files_size = 500; // byte
static kal_bool files_over_flag = KAL_FALSE;
static kal_uint8 ymodem_read_buffer[UART_YMODEM_READ_BUFFER_LENGTH] = {0}; // 装CAN,NAK,ACK,C
static kal_uint8 ymodem_read_OK_signal[UART_YMODEM_READ_OK_BUFFER_LENGTH] = {0}; // 装ok
static kal_uint8 ymodem_booting_read_buffer[UART_YMODEM_BOOTING_READ_BUFFER_LENGTH] = {0}; // 版本号厂家序列号、
static kal_bool first_pkg_flag = KAL_FALSE;
static kal_bool last_pkg_flag = KAL_FALSE;
static ymodem_send_data ymodem_send_data_pkg = {0};
static kal_uint8 ymodem_wrote_len = 0;
static kal_uint8 ymodem_unwrite_len = 0;
static kal_uint8 ymodem_write_buffer[UART_YMODEM_ALL_WRITE_BUFFER_LENGTH] = {0};
static ymodem_crc_chk ymodem_crc_check = {0};
static EBIKE_TIMER_ID boot_timer_id;
static kal_uint8 status = STEP_0;
// kal_bool go_on_read_flag = KAL_FALSE;
// extern kal_uint8 read_buffer[];
extern read_updata_file read_updata_file_info;
extern kal_uint16 *bin_file_size;
// 校验计算crc。
static kal_uint16
YModemCrc(kal_uint8 *pdata, kal_uint16 slen)
{
kal_uint16 crc = 0;
kal_uint16 i;
if (slen)
{
crc = crc ^ (kal_uint32)(*pdata++) << 8;
for (i = 8; i != 0; i--)
{
if (crc & 0x8000)
crc = crc << 1;
else
crc = crc << 1;
}
}
return crc;
}
// 发送数据包到串口。
void ymodem_send_pgk(kal_uint8 *data, kal_uint8 data_len)
{
kal_uint8 unwrite;
kal_uint8 *ptr = NULL;
kal_int16 wrote = 0;
ptr = data;
unwrite = ymodem_unwrite_len - ymodem_wrote_len;
if (0 == unwrite)
{
wrote = ebike_uart_write_data(ptr, data_len);//这个是向串口发送的函数
if (wrote < data_len)
{
kal_uint8 need_write = data_len - wrote;
if (ymodem_unwrite_len + need_write <= UART_YMODEM_WRITE_BUFFER_LENGTH)
{
memcpy(ymodem_write_buffer + ymodem_unwrite_len, ptr + wrote, need_write);
ymodem_unwrite_len += need_write;
DBG("uart writing is not completed.");
}
else
{
DBG("the write buffer is full.");
}
}
}
else if (0 < unwrite)
{
if (ymodem_unwrite_len + data_len <= UART_YMODEM_WRITE_BUFFER_LENGTH)
{
memcpy(ymodem_write_buffer + ymodem_unwrite_len, ptr, data_len);
ymodem_unwrite_len += data_len;
}
else
{
DBG("the write buffer is full.");
}
}
}
/* memcpy memset 的头文件string.h还没有确定加了没*/
// 发送!@#$ 让从机进入boost。
static void ymodem_send_boot_signal()
{
kal_uint8 boost_signal[] = {0x21, 0x40, 0x23, 0x24};
// 每120ms发送一次。
ymodem_send_pgk(boost_signal, sizeof(boost_signal));
boot_timer_id = ebike_timer_start(ymodem_send_boot_signal, NULL, UART_YMODEM_BOOT_SEND_COUNT);
}
// 主机接收到ok后发送?。
static void ymodem_send_question_mark()
{
kal_uint8 question_marksignal[] = {0x3f}; // ?
ymodem_send_pgk(ymodem_send_question_mark, sizeof(ymodem_send_question_mark));
return;
}
// 文件传输结束后主机发送EOT
static void ymodem_send_EOT()
{
kal_uint8 EOTsignal[] = {0x04}; // EOT命令
ymodem_send_pgk(EOTsignal, sizeof(EOTsignal));
return;
}
// 接收串口函数
static void ymodem_read_uart_info(kal_uint8 *dptr, kal_uint16 data_size)
{
kal_uint16 read_length;
memset(dptr, 0, data_size); // 先clean存放数据的buffer,免得干扰。
read_length = ebike_uart_read_data(dptr, data_size);
if (0 >= read_length)
{
DBG("dnot read anything\n");
}
return;
}
// 发送第1帧
static void ymodem_send_first_pkg()
{
kal_uint8 blockoff = (~blockon & 0xff);
// 发送第一帧。初始化。
memset(&ymodem_send_data_pkg, 0, sizeof(ymodem_send_data)); // clean数据帧。
ymodem_crc_check.summary = YModemCrc(ymodem_send_data_pkg.data, sizeof(ymodem_send_data_pkg.data)); // 注意大小端。
ymodem_send_data_pkg.total_bytes = YMODEM_CODE_SOH;
ymodem_send_data_pkg.block_on = blockon;
ymodem_send_data_pkg.block_off = blockoff;
ymodem_send_data_pkg.data[UART_YMODEM_WRITE_BUFFER_LENGTH] = {UPDATA_FILE_NAME}; // 文件名与文件大小具体还需要考虑。
ymodem_send_data_pkg.crc_h = ymodem_crc_check.crc_h_byte;
ymodem_send_data_pkg.crc_l = ymodem_crc_check.crc_l_byte;
// 发送第一帧
ymodem_send_pgk((kal_uint8 *)&ymodem_send_data_pkg, sizeof(ymodem_send_data_pkg));
}
static void ymodem_send_old_pkg()
{
ymodem_send_pgk((kal_uint8 *)&ymodem_send_data_pkg, sizeof(ymodem_send_data_pkg));
}
static void ymodem_send_new_pkg()
{
kal_uint8 blockoff;
blockon++;
blockoff = (~blockon & 0xff);
memset(ymodem_send_data_pkg.data, 0, sizeof(ymodem_send_data_pkg.data));
ymodem_read_file();
if (read_updata_file_info.data_filled)//数据填充完成标志位
{
ymodem_crc_check.summary = YModemCrc(ymodem_send_data_pkg.data, sizeof(ymodem_send_data_pkg.data));
ymodem_send_data_pkg.block_on = blockon;
ymodem_send_data_pkg.block_off = blockoff;
ymodem_send_data_pkg.data = read_updata_file_info.read_buffer;
ymodem_send_data_pkg.crc_h = ymodem_crc_check.crc_h_byte;
ymodem_send_data_pkg.crc_l = ymodem_crc_check.crc_l_byte;
}
/*
if (files_size > UART_YMODEM_WRITE_BUFFER_LENGTH)
{
// 判断文件是否发送完成。
if (blockon * 128 < files_size)
{
// 写入:500/128=取整。
// memset( ymodem_send_data_pkg.data, 0, sizeof( ymodem_send_data_pkg.data));
memcpy(ymodem_send_data_pkg.data, files_ptr, UART_YMODEM_WRITE_BUFFER_LENGTH);
ymodem_crc_check.summary = YModemCrc(ymodem_send_data_pkg.data, sizeof(ymodem_send_data_pkg.data));
ymodem_send_data_pkg.crc_h = ymodem_crc_check.crc_h_byte;
ymodem_send_data_pkg.crc_l = ymodem_crc_check.crc_l_byte;
files_ptr = files_ptr + UART_YMODEM_WRITE_BUFFER_LENGTH;
}
else
{
// 写入的500%128=116,余数。写入的最后一次,可以判断为文件结束。
memcpy(ymodem_send_data_pkg.data, files_ptr, files_size - (blockon - 1) * 128);
ymodem_crc_check.summary = YModemCrc(ymodem_send_data_pkg.data, sizeof(ymodem_send_data_pkg.data));
ymodem_send_data_pkg.crc_h = ymodem_crc_check.crc_h_byte;
ymodem_send_data_pkg.crc_l = ymodem_crc_check.crc_l_byte;
files_over_flag = KAL_TRUE;
}
}
else // 文件总共字节数不足128
{
memcpy(ymodem_send_data_pkg.data, files_ptr, files_size);
ymodem_crc_check.summary = YModemCrc(ymodem_send_data_pkg.data, sizeof(ymodem_send_data_pkg.data));
ymodem_send_data_pkg.crc_h = ymodem_crc_check.crc_h_byte;
ymodem_send_data_pkg.crc_l = ymodem_crc_check.crc_l_byte;
files_over_flag = KAL_TRUE;
}
*/
ymodem_send_pgk((kal_uint8 *)&ymodem_send_data_pkg, sizeof(ymodem_send_data_pkg));
}
static void ymodem_send_last_pkg()
{
memset(&ymodem_send_data_pkg, 0, sizeof(ymodem_send_data)); // 清除数据帧。
ymodem_crc_check.summary = YModemCrc(ymodem_send_data_pkg.data, sizeof(ymodem_send_data_pkg.data)); // 注意大小端。
ymodem_send_data_pkg.total_bytes = YMODEM_CODE_SOH;
ymodem_send_data_pkg.block_on = 0x00;
ymodem_send_data_pkg.block_off = 0xff;
ymodem_send_data_pkg.data[UART_YMODEM_WRITE_BUFFER_LENGTH] = {0};
ymodem_send_data_pkg.crc_h = ymodem_crc_check.crc_h_byte;
ymodem_send_data_pkg.crc_l = ymodem_crc_check.crc_l_byte;
// 发送结束帧
ymodem_send_pgk((kal_uint8 *)&ymodem_send_data_pkg, sizeof(ymodem_send_data_pkg));
}
static void ymodem_recv_signal()
{
kal_uint16 read_length;
kal_uint16 pre_read_signl = 0;
kal_uint16 pre_read_old_signl = 0;
kal_uint8 OKsignal[] = {0x4f, 0x4b}; // OK
kal_uint8 cmp_result = 1;
switch (status)
{
case STEP_0:
ymodem_send_boot_signal();
status = STEP_1;
break;
case STEP_1:
ymodem_read_uart_info(ymodem_read_OK_signal, sizeof(ymodem_read_OK_signal));
cmp_result = memcmp(OKsignal, ymodem_read_OK_signal, 2);
if (cmp_result == 0) // 主机接收到的是OK。
{
ebike_timer_stop(boot_timer_id); // 关闭120ms发送的timer。
ymodem_send_question_mark();
status = STEP_2;
}
break;
case STEP_2:
ymodem_read_uart_info(ymodem_booting_read_buffer, sizeof(ymodem_booting_read_buffer)); // 接收从机发送过来的版本号厂家序列号等、
status = STEP_3;
break;
case STEP_3:
ymodem_read_uart_info(ymodem_read_buffer, sizeof(ymodem_read_buffer));
pre_read_signl = ymodem_read_buffer[0];
if (pre_read_signl == YMODEM_CODE_CRC)
{
ymodem_send_first_pkg();
status = STEP_4;
}
break;
case STEP_4:
ymodem_read_uart_info(ymodem_read_buffer, sizeof(ymodem_read_buffer));
pre_read_signl = ymodem_read_buffer[0];
if (pre_read_signl == YMODEM_CODE_CAN)
{
return; // 结束通信
}
if (pre_read_signl == YMODEM_CODE_CRC || pre_read_signl == YMODEM_CODE_NAK)
{
// 重新发送第一帧。
ymodem_send_first_pkg();
}
if (pre_read_signl == YMODEM_CODE_ACK)
{
pre_read_old_signl = pre_read_signl;
status = STEP_5;
}
break;
case STEP_5:
ymodem_read_uart_info(ymodem_read_buffer, sizeof(ymodem_read_buffer));
pre_read_signl = ymodem_read_buffer[0];
if (pre_read_signl == YMODEM_CODE_CAN || pre_read_signl == YMODEM_CODE_ACK)
{
return; // 结束通信
}
if (pre_read_signl == YMODEM_CODE_CRC && pre_read_old_signl == YMODEM_CODE_ACK) // pre_read_old_signl这里用完后要不要fu0?
{
// 发送接下来的帧。
ymodem_send_new_pkg();
pre_read_old_signl = 0;
status = STEP_6;
}
break;
case STEP_6:
ymodem_read_uart_info(ymodem_read_buffer, sizeof(ymodem_read_buffer));
pre_read_signl = ymodem_read_buffer[0];
if (pre_read_signl == YMODEM_CODE_CAN)
{
return;
}
if (pre_read_signl == YMODEM_CODE_NAK)
{
ymodem_send_old_pkg();
}
if (pre_read_signl == YMODEM_CODE_ACK)
{
if (read_updata_file_info.file_over)
{
read_updata_file_info.file_over = KAL_FALSE;
ymodem_send_EOT();
status = STEP_7;
}
else
{
ymodem_send_new_pkg();
// return;
}
}
break;
case STEP_7:
ymodem_read_uart_info(ymodem_read_buffer, sizeof(ymodem_read_buffer));
pre_read_signl = ymodem_read_buffer[0];
if (pre_read_signl == YMODEM_CODE_CAN)
{
return;
}
if (pre_read_signl == YMODEM_CODE_NAK)
{
ymodem_send_EOT();
status = STEP_8;
}
break;
case STEP_8:
ymodem_read_uart_info(ymodem_read_buffer, sizeof(ymodem_read_buffer));
pre_read_signl = ymodem_read_buffer[0];
if (pre_read_signl == YMODEM_CODE_CAN)
{
return;
}
if (pre_read_signl == YMODEM_CODE_ACK)
{
pre_read_old_signl = pre_read_signl;
status = STEP_9;
}
if (pre_read_signl == YMODEM_CODE_NAK)
{
ymodem_send_EOT();
}
break;
case STEP_9:
ymodem_read_uart_info(ymodem_read_buffer, sizeof(ymodem_read_buffer));
pre_read_signl = ymodem_read_buffer[0];
if (pre_read_signl == YMODEM_CODE_CAN)
{
return;
}
if (pre_read_signl == YMODEM_CODE_CRC && pre_read_old_signl == YMODEM_CODE_ACK)
{
// 发送最后一帧。
ymodem_send_last_pkg();
pre_read_old_signl = 0;
}
if (pre_read_signl == YMODEM_CODE_ACK)
return;
break;
}
}
// 升级init.
void controller_upgrade()
{
ymodem_send_boot_signal();
ymodem_recv_signal();
}
#ifndef _YMODEM_H_
#define _YMODEM_H_
#include "kal_public_defs.h"
#pragma pack(1)
typedef enum
{
YMODEM_CODE_NONE = 0x0,
YMODEM_CODE_SOH = 0x01, /* start of 128-byte data packet */
YMODEM_CODE_STX = 0x02, /* start of 1024-byte data packet */
YMODEM_CODE_EOT = 0x04, /* end of transmission */
YMODEM_CODE_ACK = 0x06, /* receive OK */
YMODEM_CODE_NAK = 0x015, /* receiver error; retry */
YMODEM_CODE_CAN = 0x018, /* two of these in succession aborts transfer */
YMODEM_CODE_CRC = 0x43, /* use in place of first NAK for CRC mode */
} ymodem_cmd;
typedef enum
{
STEP_0,
STEP_1,
STEP_2,
STEP_3,
STEP_4,
STEP_5,
STEP_6,
STEP_7,
STEP_8,
STEP_9,
} ymodem_step;
typedef struct
{
kal_uint16 total_bytes;
kal_uint8 block_on;
kal_uint8 block_off;
kal_uint8 data[128]; //
kal_uint8 crc_h;
kal_uint8 crc_l;
} ymodem_send_data, *ymodem_send_data_ptr;
typedef union
{
kal_uint16 summary;
struct
{
kal_uint8 crc_h_byte;
kal_uint8 crc_l_byte;
} check;
} ymodem_crc_chk;
#pragma pack()
void controller_upgrade();
#endif
读文件操作:(传输这个文件之前要先进行读文件大小和读文件内容)
#include "ymodem_read_file.h"
#include "fs_func.h"
#define EBIKE_YMODEM_UPDATA_PATH L"Z:\\update.bin"
#define READ_UPDATA_FILE_BUF_LENGTH 128
static FS_HANDLE file_handle = -1;
kal_uint16 *bin_file_size = NULL;
read_updata_file read_updata_file_info = {0};
int pos = 0;
void ymodem_open_file()
{
file_handle = FS_Open(EBIKE_YMODEM_UPDATA_PATH, FS_READ_ONLY);
if (file_handle == FS_NO_ERROR)
{
FS_GetFileSize(file_handle, bin_file_size);
}
else if (file_handle == FS_FILE_NOT_FOUND)
{
DBG("this uodate file dont find\n");
FS_Close(file_handle);
return;
}
}
void ymodem_read_file()
{
unsigned int *rely_read_length = NULL;
memset(&read_updata_file_info, 0, sizeof(read_updata_file_info)); // clean数据帧。
if (*bin_file_size - pos > 128)
{
FS_Seek(file_handle, pos, FS_FILE_BEGIN);
FS_Read(file_handle, read_updata_file_info.read_buffer, READ_UPDATA_FILE_BUF_LENGTH, rely_read_length);
pos += 128;
}
else
{
FS_Seek(file_handle, pos, FS_FILE_BEGIN);
FS_Read(file_handle, read_updata_file_info.read_buffer, *bin_file_size - pos, rely_read_length);
// read end.
read_updata_file_info.file_over = KAL_TRUE;
FS_Close(file_handle);
}
// if (*rely_read_length < READ_UPDATA_FILE_BUF_LENGTH)
// {
// // read end.
// read_updata_file_info.file_over = KAL_TRUE;
// FS_Close(file_handle);
// }
}
/*
读bin文件,将文件内容读到一个缓冲区,然后再发送这个缓冲区。再seek后读,在发送。
*/
#ifndef __YMODEM_READ_FILE_H__
#define __YMODEM_READ_FILE_H__
#include "kal_public_defs.h"
#pragma pack(1)
typedef struct
{
kal_uint16 read_buffer[128];
kal_bool file_over;
kal_bool data_filled;
} read_updata_file;
// 文件总共大小,文件128字节的数组(这样就不需要extern这个数组了),是否文件传输结束,128填充是否完成,
#pragma pack()
void ymodem_open_file();
void ymodem_read_file();
#endif
结束:
希望接下来的事情顺利!!!!