使用Xshell进行ymodem传输时的一个小trick(用于IAP编程中的bootloader程序
Ymodem通信信号
符号 | 数值 | 含义 |
---|---|---|
SOH | 0x01 | 128字节数据包 |
STX | 0x02 | 1024字节数据包 |
EOT | 0x04 | 结束传输 |
ACK | 0x06 | 正确接收回应 |
NAK | 0x15 | 错误接收回应 |
CAN | 0x18 | 传输终止 |
C | 0x43 | 请求数据 |
1、MCU发送"C"命令
cs
else
{
// 没有收到数据,根据状态发送请求
if (t_sts == 0 || t_sts == 3)
{
fwt_cnt = 0;
UART_Send_Byte('C'); // 请求发送
}
if (t_sts == 1 || t_sts == 2)
{
fwt_cnt = 0;
UART_Send_Byte(0X15); // 发送NAK
}
// 超时处理
if (ot++ > 20) t_cmp = ERR_YMODEM_OVERTIME;
}
2、处理Xshell发送的起始帧
cs
case 133:
case 134:
case 136:
// 处理文件头帧
{
int off = 0;
int index = 0;
if (fwt_cnt > 133)
{
while (fwt_buf[index++] != 0X0D);
off = index;
}
crc = calcrc(fwt_buf + 3 + off, 128);
fwt_cnt = 0;
if (t_sts == 0)
{
if (fwt_buf[1 + off] == 0X00 && fwt_buf[2 + off] == 0xff)
{
if ((((unsigned short)crc) == (((int)fwt_buf[131 + off]) << 8) | (fwt_buf[132 + off])))
{
// 解析文件名
strcpy(filename + 1, fwt_buf + 3 + off);
// 创建新文件
f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
t_sts = 1;
fwt_cnt = 0;
UART_Send_Byte(0x06); // 发送ACK
}
else UART_Send_Byte(0x15); // CRC错误,发送NAK
}
else UART_Send_Byte(0x15); // 帧头错误,发送NAK
}
break;
}
3、t_sts==0执行一次1步骤
4、处理Xshell发送的数据帧
cs
case 1029:
// 处理数据帧
t_sts = 2;
crc = calcrc(fwt_buf + 3, 1024);
fwt_cnt = 0;
{
if (fwt_buf[1] == to_fn)
{
to_fn++;
if ((fwt_buf[1] + fwt_buf[2]) == 0xff)
{
if ((((unsigned short)crc) == (((int)fwt_buf[1027]) << 8) | (fwt_buf[1028])))
{
unsigned int nw = 0;
// 写入数据到文件
f_write(&file, fwt_buf + 3, 1024, &nw);
UART_Send_Byte(0x06); // 发送ACK
}
else UART_Send_Byte(0x15); // CRC错误
}
else UART_Send_Byte(0x15); // 序号校验错误
}
else UART_Send_Byte(0x15); // 序号错误
}
break;
default:
fwt_cnt = 0;
// 未知帧类型
// if(t_sts==0 || t_sts==1) UART_Send_Byte(0X15);
break;
}
}
5、处理Xshell发送的EOT命令
cs
case 1:
// 处理EOT帧
fwt_cnt = 0;
if (0X04 == fwt_buf[0])
{
if (t_sts == 2)
{
t_sts = 3;
UART_Send_Byte(0x15); // 发送NAK
}
else if (t_sts == 3)
{
t_sts = 4;
fwt_cnt = 0;
UART_Send_Byte(0x06); // 发送ACK
UART_Send_Byte('C'); // 发送'C'
}
}
break;
7、t_sts== 3再一次接收到EOT执行步骤5
8、处理Xshell发送的结束帧
cs
case 135:
// 处理结束帧
if (t_sts == 4)
{
if (fwt_buf[1] == 0X00 && fwt_buf[2] == 0xff)
{
if (fwt_buf[131] == 0 && fwt_buf[132] == 0)
{
t_sts = 5;
fwt_cnt = 0;
UART_Send_Byte(0x06); // 发送ACK
{
UART_Send_Byte(0X4F);//MCU回应表示接收完成
UART_Send_Byte(0X4F);
}
}
else UART_Send_Byte(0x15);
}
else UART_Send_Byte(0x15);
}
break;
**135字节的原因:**按照Ymodem协议标准是133,但是经过Xshell的传输完成会添加两个0x4F做结尾,表示传输完成。
延时设计
cs
{
unsigned short i = 0;
// 根据状态设置等待时间
if (t_sts == 0)
wt = 1000;
else
wt = 120;
for (i = 0; i < wt; i++) HAL_Delay(1);
}
CAN命令处理
cs
case 10:
// CAN处理取消帧
fwt_cnt = 0;
if (0X18 == fwt_buf[0])
{
unsigned char buf[21] = {0X18, 0X18, 0X18, 0X18, 0X18, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08};
unsigned char i = 0;
t_sts = 6;
for (i = 0; i < 20; i++)
UART_Send_Byte(buf[i]);
t_cmp = ERR_YMODEM_SUCCESS;
}
break;
CRC校验算法
cs
/**
* @brief 计算CRC
* @param ptr : 数据缓冲指针
* count : 数据长度
* @retval uint32_t : CRC值
* @verbatim
* @note CRC16_XMODEM
* @endverbatim
*/
int calcrc(char *ptr, int count)
{
int crc = 0;//CRC寄存器初始值为0x0000
char i = 0;
while ((--count) >= 0)
{
crc = crc ^ (int)*ptr++ << 8;
i = 8;
do
{
if (crc & 0x8000)
crc = crc << 1 ^ 0x1021;//^异或 相异为1 相同为0
else
crc = crc << 1;
} while (--i);
}
return crc;
}
完整代码
cs
static FIL file;
extern char filename[20];
/**
* @brief Ymodem下载文件
* @param 无
* @retval err_code : 参考ERR_YMODEM定义
* @verbatim
* 具体实现参考YMODEM协议标准与XSHELL协议扩展
* @endverbatim
*/
int ymodem_down_file(void)
{
unsigned char t_cmp = 0; // 传输完成标志
unsigned char to_fn = 1; // 文件块序号
unsigned char frame_n = 0; // 帧号(未使用)
unsigned char t_sts = 0; // 传输状态
unsigned short wt = 0; // 等待时间
unsigned int ot = 0; // 超时计数
unsigned short crc = 0; // CRC校验值
fw_transfering = 1;
fwt_cnt = 0;
printf("Download File via Ymodem\r\n");
{
while (!t_cmp)
{
{
unsigned short i = 0;
// 根据状态设置等待时间
if (t_sts == 0)
wt = 1000;
else
wt = 120;
for (i = 0; i < wt; i++) HAL_Delay(1);
}
if (fwt_cnt > 0)
{
ot = 0;
switch (fwt_cnt)
{
case 1:
// 处理EOT帧
fwt_cnt = 0;
if (0X04 == fwt_buf[0])
{
if (t_sts == 2)
{
t_sts = 3;
UART_Send_Byte(0x15); // 发送NAK
}
else if (t_sts == 3)
{
t_sts = 4;
fwt_cnt = 0;
UART_Send_Byte(0x06); // 发送ACK
UART_Send_Byte('C'); // 发送'C'
}
}
break;
case 10:
// CAN处理取消帧
fwt_cnt = 0;
if (0X18 == fwt_buf[0])
{
unsigned char buf[21] = {0X18, 0X18, 0X18, 0X18, 0X18, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08, 0X08, 0X20, 0X08};
unsigned char i = 0;
t_sts = 6;
for (i = 0; i < 20; i++)
UART_Send_Byte(buf[i]);
t_cmp = ERR_YMODEM_SUCCESS;
}
break;
case 133:
case 134:
case 136:
// 处理文件头帧
{
int off = 0;
int index = 0;
if (fwt_cnt > 133)
{
while (fwt_buf[index++] != 0X0D);
off = index;
}
crc = calcrc(fwt_buf + 3 + off, 128);
fwt_cnt = 0;
if (t_sts == 0)
{
if (fwt_buf[1 + off] == 0X00 && fwt_buf[2 + off] == 0xff)
{
if ((((unsigned short)crc) == (((int)fwt_buf[131 + off]) << 8) | (fwt_buf[132 + off])))
{
// 解析文件名
strcpy(filename + 1, fwt_buf + 3 + off);
// 创建新文件
f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
t_sts = 1;
fwt_cnt = 0;
UART_Send_Byte(0x06); // 发送ACK
}
else UART_Send_Byte(0x15); // CRC错误,发送NAK
}
else UART_Send_Byte(0x15); // 帧头错误,发送NAK
}
break;
}
case 135:
// 处理结束帧
if (t_sts == 4)
{
if (fwt_buf[1] == 0X00 && fwt_buf[2] == 0xff)
{
if (fwt_buf[131] == 0 && fwt_buf[132] == 0)
{
t_sts = 5;
fwt_cnt = 0;
UART_Send_Byte(0x06); // 发送ACK
{
UART_Send_Byte(0X4F);//O
UART_Send_Byte(0X4F);
}
}
else UART_Send_Byte(0x15);
}
else UART_Send_Byte(0x15);
}
break;
case 1029:
// 处理数据帧
t_sts = 2;
crc = calcrc(fwt_buf + 3, 1024);
fwt_cnt = 0;
{
if (fwt_buf[1] == to_fn)
{
to_fn++;
if ((fwt_buf[1] + fwt_buf[2]) == 0xff)
{
if ((((unsigned short)crc) == (((int)fwt_buf[1027]) << 8) | (fwt_buf[1028])))
{
unsigned int nw = 0;
// 写入数据到文件
f_write(&file, fwt_buf + 3, 1024, &nw);
UART_Send_Byte(0x06); // 发送ACK
}
else UART_Send_Byte(0x15); // CRC错误
}
else UART_Send_Byte(0x15); // 序号校验错误
}
else UART_Send_Byte(0x15); // 序号错误
}
break;
default:
fwt_cnt = 0;
// 未知帧类型
// if(t_sts==0 || t_sts==1) UART_Send_Byte(0X15);
break;
}
}
else
{
// 没有收到数据,根据状态发送请求
if (t_sts == 0 || t_sts == 3)
{
fwt_cnt = 0;
UART_Send_Byte('C'); // 请求发送
}
if (t_sts == 1 || t_sts == 2)
{
fwt_cnt = 0;
UART_Send_Byte(0X15); // 发送NAK
}
// 超时处理
if (ot++ > 20) t_cmp = ERR_YMODEM_OVERTIME;
}
}
// 文件同步与关闭
f_sync(&file);
f_close(&file);
fw_transfering = 0;
}
printf("\r\nFirmware Transfer Complete\r\n");
return t_cmp;
}