prj to mdks
This commit is contained in:
@@ -0,0 +1,336 @@
|
||||
|
||||
|
||||
#include "ymodem.h"
|
||||
|
||||
struct ymodem_info_t {
|
||||
uint8_t isBusy; /* 正在处理主机数据的标志位 */
|
||||
uint8_t enRecv; /* 使能接收主机的指令包 */
|
||||
|
||||
uint16_t rxLen; /* 接收到的数据长度 */
|
||||
uint8_t *rxData; /* 协议解析的数据来源 */
|
||||
|
||||
uint8_t numPkt; /* 记录 YModem 协议的 packet number */
|
||||
|
||||
eYmFLOW flow; /* 记录协议的执行流程 */
|
||||
|
||||
union message_raw_t *msg; /* 接收主机数据包的缓存池,称为主机消息 */
|
||||
|
||||
struct tx_raw_t txPkt; /* 用于存放设备上发数据组包的部分参数 */
|
||||
|
||||
ymSend send; /* 数据发送接口 */
|
||||
ymPrepareCallback prepare; /* 收到主机指令时的预备处理接口 */
|
||||
ymReplyCallback reply; /* 正在执行指令时响应主机查询执行过程和结果的接口 */
|
||||
};
|
||||
|
||||
static struct ymodem_info_t yM;
|
||||
|
||||
static struct drv_timer_t timerSendC; /* 用于定时向主机发送数据的定时器 */
|
||||
|
||||
|
||||
|
||||
static void timerSendCCallback (void *user_data)
|
||||
{
|
||||
static uint8_t c[1] = {YMODEM_C};
|
||||
yM.send(c, 1, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 复位一些全局变量和标志信息
|
||||
* @note
|
||||
* @retval None
|
||||
*/
|
||||
static void ymodemReset (void)
|
||||
{
|
||||
yM.isBusy = 0;
|
||||
yM.enRecv = 1;
|
||||
yM.numPkt = 0;
|
||||
yM.flow = YMODEM_FLOW_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 对主机下发的心跳包的处理
|
||||
* @note 对主机下发的数据帧进行回复, ymodem 没有心跳包,此处只是模拟
|
||||
* @retval None
|
||||
*/
|
||||
static void ymodemHeartBeatProcess (void)
|
||||
{
|
||||
static eRESULT result;
|
||||
|
||||
yM.reply((eYmCMD)yM.msg->pkg.header, &result, NULL, NULL);
|
||||
|
||||
switch (result) {
|
||||
case YM_RESULT_OK:
|
||||
//yM.txPkt.response = YMODEM_ACK;
|
||||
break;
|
||||
|
||||
case YM_RESULT_FAILED: {
|
||||
/* 因前面已经加 1 ,此处是由于对数据处理有问题,非协议本身问题,因此需要减回 */
|
||||
yM.numPkt--;
|
||||
yM.txPkt.response = YMODEM_NAK;
|
||||
break;
|
||||
}
|
||||
|
||||
case YM_RESULT_CANCEL:
|
||||
yM.txPkt.response = YMODEM_CAN;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* 业务层还在处理数据,暂时不回复主机 */
|
||||
return;
|
||||
}
|
||||
|
||||
yM.isBusy = 0;
|
||||
yM.send(&yM.txPkt.response, 1, HAL_MAX_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 对主机下发指令的处理
|
||||
* @note
|
||||
* @retval None
|
||||
*/
|
||||
static void ymodemCommandProcess (void)
|
||||
{
|
||||
uint16_t data_len = 0;
|
||||
|
||||
yM.isBusy = 1;
|
||||
|
||||
|
||||
if ((yM.msg->pkg.header == YMODEM_SOH)||(yM.msg->pkg.header == YMODEM_STX)) {
|
||||
data_len = yM.rxLen - YMODEM_FRAME_FIXED_LEN;
|
||||
}
|
||||
|
||||
yM.prepare((eYmCMD)yM.msg->pkg.header, yM.msg->pkg.data, data_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置协议执行流程
|
||||
* @note
|
||||
* @param[in] cmd: 主机的指令
|
||||
* @retval eErrCode
|
||||
*/
|
||||
static eErrCode ymodemExeFlow (eYmCMD cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case YM_CMD_SOH: {
|
||||
if (yM.flow == YMODEM_FLOW_NONE) { /* 第一个 SOH 数据帧 */
|
||||
yM.flow = YMODEM_FLOW_START;
|
||||
yM.txPkt.response = YMODEM_ACK;
|
||||
} else if (yM.flow == YMODEM_FLOW_SECOND_EOT) { /* 最后一个空的 SOH 数据帧 */
|
||||
drv_timer_pause(&timerSendC);
|
||||
yM.flow = YMODEM_FLOW_SUCCESS;
|
||||
yM.numPkt = 0;
|
||||
yM.txPkt.response = YMODEM_ACK;
|
||||
} else { /* 正在传输数据的 SOH 数据帧 */
|
||||
/* 暂停发送字符“ C ”的定时器 */
|
||||
drv_timer_pause(&timerSendC);
|
||||
yM.txPkt.response = YMODEM_ACK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case YM_CMD_STX: {
|
||||
/* 因为第一个有数据的数据帧可能是 STX ,因此此处有必要暂停发送字符“ C ”的定时器 */
|
||||
drv_timer_pause(&timerSendC);
|
||||
yM.txPkt.response = YMODEM_ACK;
|
||||
break;
|
||||
}
|
||||
|
||||
case YM_CMD_EOT: {
|
||||
if (yM.flow == YMODEM_FLOW_START) { /* 第一个 EOT */
|
||||
yM.flow = YMODEM_FLOW_FIRST_EOT;
|
||||
yM.txPkt.response = YMODEM_NAK;
|
||||
} else if (yM.flow == YMODEM_FLOW_FIRST_EOT) { /* 第二个 EOT */
|
||||
yM.flow = YMODEM_FLOW_SECOND_EOT;
|
||||
yM.numPkt = 0;
|
||||
yM.txPkt.response = YMODEM_ACK;
|
||||
/* 发完 ACK 需要继续发“ C ” */
|
||||
drv_timer_restart(&timerSendC);
|
||||
} else {
|
||||
return ERR_EXE_FLOW;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case YM_CMD_CAN: {
|
||||
yM.flow = YMODEM_FLOW_CANCEL;
|
||||
yM.txPkt.response = YMODEM_ACK;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 协议解析处理函数
|
||||
* @note 需要循环调用
|
||||
* @retval eErrCode
|
||||
*/
|
||||
eErrCode ymodemHandler (uint8_t *data, uint16_t len)
|
||||
{
|
||||
eErrCode err_code;
|
||||
|
||||
/* 有数据 */
|
||||
if (data && len && yM.enRecv) {
|
||||
/* 暂存和格式化 */
|
||||
yM.rxData = data;
|
||||
yM.rxLen = len;
|
||||
yM.msg = (union message_raw_t *)yM.rxData;
|
||||
//hex_printf(data, len);
|
||||
/* 只有数据帧才做以下错误检查 */
|
||||
if (yM.msg->pkg.header == YMODEM_SOH || yM.msg->pkg.header == YMODEM_STX) {
|
||||
/* 判断序列号是否符合顺序 */
|
||||
if (yM.msg->pkg.pkt_num != yM.numPkt) {
|
||||
if (yM.msg->pkg.pkt_num == (yM.numPkt - 1)) {
|
||||
printf("error: duplicate frame %d %d\r\n", yM.msg->pkg.pkt_num, yM.numPkt);
|
||||
err_code = ERR_DUPLICATE_FRAME;
|
||||
} else {
|
||||
printf("error: omission frame %d %d\r\n", yM.msg->pkg.pkt_num, yM.numPkt);
|
||||
err_code = ERR_OMISSION_FRAME;
|
||||
}
|
||||
goto __error_exit;
|
||||
}
|
||||
|
||||
/* 判断正反序列号是否正确 */
|
||||
uint8_t pkt_num = ~(yM.msg->pkg.pkt_num);
|
||||
if (yM.msg->pkg.not_pkt_num != pkt_num) {
|
||||
printf("error: packet number: %.2X %.2X\r\n", yM.msg->pkg.pkt_num, yM.msg->pkg.not_pkt_num);
|
||||
err_code = ERR_PKT_NUM_ERR;
|
||||
goto __error_exit;
|
||||
}
|
||||
|
||||
/* 获取协议帧中的数据字段长度 */
|
||||
uint16_t data_len;
|
||||
if (yM.msg->pkg.header == YMODEM_SOH) {
|
||||
data_len = YMODEM_SOH_DATA_LEN;
|
||||
} else if (yM.msg->pkg.header == YMODEM_STX) {
|
||||
data_len = YMODEM_STX_DATA_LEN;
|
||||
}
|
||||
|
||||
/* 帧长度判断 */
|
||||
/* 奇怪的是, Xshell 在发送最后一个空 SOH 数据帧时会附加两个字节的 0x4F ,原因未知。
|
||||
* 为了处理这个问题,避免误判为数据帧长度有误,此处嵌套了“ ymodem_pkt_num != 0 ”的判断 */
|
||||
if (yM.numPkt != 0) {
|
||||
if (yM.rxLen != (data_len + YMODEM_FRAME_FIXED_LEN)) {
|
||||
//printf("error: _dev_rx_len: %d\r\n", yM.rxLen);
|
||||
err_code = ERR_FRAME_LENGTH;
|
||||
goto __error_exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* 校验数据是否正确 */
|
||||
uint16_t crc16 = crc16_xmodem(yM.msg->pkg.data, data_len);
|
||||
printf("%04X", crc16);
|
||||
uint16_t raw_crc16 = (yM.msg->pkg.data[data_len] << 8) | yM.msg->pkg.data[data_len + 1];
|
||||
if (crc16 != raw_crc16) {
|
||||
printf("error: crc16: %.4X\r\n", crc16);
|
||||
printf("error: raw crc16: %.4X\r\n", raw_crc16);
|
||||
err_code = ERR_FRAME_VERIFY_ERR;
|
||||
goto __error_exit;
|
||||
}
|
||||
//printf("crc16 ok.\r\n");
|
||||
|
||||
/* 执行到此处记录序列号加1 */
|
||||
yM.numPkt++;
|
||||
}
|
||||
//printf("Ymodem recv len: %d (%02X %02X %02X)\r\n",
|
||||
// yM.rxLen, yM.msg->pkg.header, yM.msg->pkg.pkt_num, yM.msg->pkg.not_pkt_num);
|
||||
|
||||
/* 设置流程 */
|
||||
if (ymodemExeFlow((eYmCMD)yM.msg->pkg.header)) {
|
||||
//printf("error: flow illegal\r\n");
|
||||
err_code = ERR_EXE_FLOW;
|
||||
goto __error_exit;
|
||||
}
|
||||
|
||||
/* 除了最后一个空的SOH数据帧,其他都会执行 ,包括 EOT CAN */
|
||||
/* 需要注意是, 执行完后仍未回复主机,回复部分由 ymodemHeartBeatProcess 处理 */
|
||||
ymodemCommandProcess();
|
||||
return ERR_OK;
|
||||
|
||||
__error_exit:
|
||||
|
||||
yM.txPkt.response = YMODEM_NAK;
|
||||
yM.send(&yM.txPkt.response, 1, HAL_MAX_DELAY);
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
if (yM.isBusy) {
|
||||
/* 模拟主机的心跳处理,查询是否对协议包处理完毕,以向主机回复 */
|
||||
ymodemHeartBeatProcess();
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 配置协议析构层的参数
|
||||
* @note
|
||||
* @param[in] para: 需要进行配置的选型或参数
|
||||
* @param[in] value: 对应的数据或值
|
||||
* @retval None
|
||||
*/
|
||||
void ymodemConfig (eYmMODE mode, void *value)
|
||||
{
|
||||
switch (mode) {
|
||||
case YM_MODE_RESET:
|
||||
ymodemReset();
|
||||
drv_timer_restart(&timerSendC);
|
||||
break;
|
||||
|
||||
case YM_MODE_RECV: {
|
||||
uint8_t *enable = (uint8_t *)value;
|
||||
yM.enRecv = *enable;
|
||||
if (yM.enRecv == 0) {
|
||||
drv_timer_pause(&timerSendC);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 协议析构层的初始化
|
||||
* @note
|
||||
* @param[in] Send: 底层数据发送接口
|
||||
* @param[in] HeartbeatCallback: 心跳包的响应接口
|
||||
* @param[in] PrepareCallback: 指令包的处理接口
|
||||
* @param[in] Set_ReplyInfo: 查询指令执行结果的处理接口
|
||||
* @retval None
|
||||
*/
|
||||
void ymodem_init (ymSend Send,
|
||||
ymPrepareCallback PrepareCallback,
|
||||
ymReplyCallback Set_ReplyInfo)
|
||||
{
|
||||
yM.send = Send;
|
||||
yM.prepare = PrepareCallback;
|
||||
yM.reply = Set_ReplyInfo;
|
||||
|
||||
ymodemReset();
|
||||
|
||||
drv_timer_init(&timerSendC,
|
||||
timerSendCCallback,
|
||||
1000,
|
||||
TIMER_RUN_FOREVER);
|
||||
drv_timer_start(&timerSendC);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user