// 透传缆 #include #include #include "MultiTimer.h" #include "lwrb/lwrb.h" #include "linkedlist.h" #define CMD_TRA_MODE 0xA1 #define CMD_REC_MODE 0xA2 #define CMD_SLP_MODE 0xA3 #define CMD_WKP_MODE 0xA4 #define CMD_ACK_OK 0xAB #define CMD_ACK_NOK 0xAF #define USING_ACK 1 #define MIN_TR_PERIOD_MS (1000 * 5) // 单字节最小传输时间 #define HOST_RETRY_PERIOD_MS (MIN_TR_PERIOD_MS + 1000) // 主机重试时间间隔 #define CLIENT_RETRY_PERIOD_MS (MIN_TR_PERIOD_MS + 1000 * 10) // 客户端重试时间间隔 #define MAX_RETRY_CNT 3 #define RING_BUFFER_SIZE (1024 * 3) #define MAX_FRAME_LENGTH (200) /// @brief 主从模式,主机拥有更高优先级 typedef enum { HOST_MODE = 0, CLIENT_MODE } devmode_t; /// @brief 工作模式,收、发、休眠、唤醒 typedef enum { T_MODE = 0, R_MODE, SLEEP_MODE, WORK_MODE } devstatus_t; typedef enum { ERR_OK = 0, ERR_NOK, ERR_TIMEOUT, ERR_HEADER, ERR_TAIL, ERR_CRC, ERR_LENGTH, ERR_NREADY } err_t; static devmode_t devMode = HOST_MODE; static devstatus_t curMode = R_MODE; static devstatus_t tarMode = R_MODE; volatile int isOK2Swt = 1; // 是否尝试切换到发送模式 static devstatus_t tarDevMode = R_MODE; // 目标端工作模式 static MultiTimer swtModeTimer, retryTimer, timeoutTimer; // 重试定时器、超时定时器 static int isNewLineRx = 0; static uint8_t lstLineRxData[MAX_FRAME_LENGTH]; // 透传缆最后一个接收到的数据 static uint8_t lstLineRxDataLen = 0; // 透传缆最后一个接收到的数据长度 static Node *userRxBuffList = NULL; // user端接收缓冲链表 // static err_t lstState = ERR_OK; // 设置GPIO电平 void setGpioLevel(int gpio, int level) { // 设置GPIO电平 } /// @brief 初始化工作模式,仅设置对应GPIO电平 /// @param cfg void initWorkStatus(devstatus_t cfg) { switch (cfg) { case T_MODE: /* code */ break; case R_MODE: /* code */ break; case SLEEP_MODE: /* code */ break; case WORK_MODE: /* code */ break; default: break; } } /// @brief 通过透传缆串口发送数据 /// @param data 待发送数据 /// @param len 待发送数据长度 void lineTransmitData(uint8_t *data, uint8_t len) { // 发送数据前后需初始化为发送模式 initWorkStatus(T_MODE); // 发送数据 // 发送完毕后需恢复为接收模式 msdelay(len * 10); initWorkStatus(R_MODE); } // 透传缆发送逻辑 // 0. 用户串口接收待发送数据,放入缓存链表队列。 // 1. 通过透传缆串口发送数据 // 2. 发送完成后切换为接收模式 // 3. 通过透传缆串口接收回复的ACK // 4. ACK正常则发送成功,否则发送失败 // 5. 发送成功反馈ACK,删除链表数据并准备发送链表中下一帧数据 // 6. 发送失败则等待指定时间t后重发当前帧 // 7. 重试次数达到上限则发送失败,删除链表数据并准备发送链表中下一帧数据 // 8. /// @brief 通过用户串口发送数据 /// @param data 待发送数据 /// @param len 待发送数据长度 void userTransmitData(uint8_t *data, uint8_t len) { } /// @brief 切换为接收模式 void switchToRecMode() { lineTransmitData(CMD_REC_MODE, 1); // 不需要ACK curMode = R_MODE; tarMode = R_MODE; } /// @brief 切换为发送模式 // // 以ACK回复判断是否切换成功 // 切换失败则定时重试 void switchToTraMode() { // tarMode = T_MODE; lineTransmitData(CMD_REC_MODE, 1); // 接收ACK uint8_t ack[] = {}; uint8_t len = 0; recvData(ack, len, 2000); // 阻塞等待 if (len == 1 && ack[0] == CMD_ACK_OK) { // 切换发送模式成功 curMode = T_MODE; tarMode = R_MODE; // initWorkMode(T_MODE); // return 0; } else { // 切换发送模式失败 // 恢复为接收模式 switchToRecMode(); tarMode = T_MODE; // 触发定时重试 } } uint64_t getPlatformTicks(void) { /* Platform-specific implementation */ } // Callback functions for the timers void swtModeTimerCallback(MultiTimer *timer, void *userData) { isOK2Swt = 1; //提示保护期已过,收端可以发送数据 printf("time of protect expired and can send data.\n"); } void timeOutTimerCallback(MultiTimer *timer, void *userData) { isTimeOut = 1; printf("Timer 1 fired at %lu ms\n", getPlatformTicks()); } void reTryTimerCallback(MultiTimer *timer, void *userData) { userData = 1; printf("Timer 1 fired at %lu ms\n", getPlatformTicks()); } /// @brief 计算异或XOR校验 /// @param data 待校验数据 /// @param len 数据长度 /// @return 校验结果 uint8_t bccCRC(uint8_t *data, uint8_t len) { uint8_t crc = 0; for (int i = 0; i < len; i++) { crc ^= data[i]; } return crc; } /// @brief 检查数据有效性 /// @param data 待校验数据 /// @param len 数据长度 /// @return 0->有效 err_t chkDataValid(uint8_t *data, uint8_t len) { // 按帧发送数据 // 判断帧的完整性及校验位 if (len > MAX_FRAME_LENGTH) { return ERR_LENGTH; } if (data[0] != 0x5A || data[1] != 0xA5) // 帧头校验 { return ERR_HEADER; } if (data[len - 1] != 0xED) // 帧尾校验 { return ERR_TAIL; } if (bccCRC(data + 2, len - 4) != data[len - 2]) // 校验位校验 { return ERR_CRC; } return ERR_OK; } /// @brief 用户接口接收数据回调 /// @param data /// @param len void userRecDataCallback(uint8_t *data, uint8_t len) { //等待时间内不能发送数据 if (!isOK2Swt) { userTransmitData(ERR_NREADY,1); } err_t rst= chkDataValid(data, len); if (rst == ERR_OK) { //校验通过则加入待发列表 appendNode(&userRxBuffList, data, len); //由发送函数反馈发送结果 } else { //校验不通过立即反馈 userTransmitData(rst, 1); } } /// @brief 透传缆接收数据回调 /// @param data /// @param len void lineRecCallback(uint8_t *data, uint8_t len) { isNewLineRx = 1; memcpy(lstLineRxData, data, len); lstLineRxDataLen = len; } /// @brief 阻塞方式接收ACK数据 /// @param timeOut 指定超时时间,单位ms /// @return 返回ACK状态 err_t getACK(int timeOut) { uint8_t isTimeOut = 0; multiTimerStop(&timeoutTimer); multiTimerStart(&timeoutTimer, timeOut, timeOutTimerCallback, &isTimeOut); // Start timer while (1) { if (isTimeOut) { return ERR_TIMEOUT; } if (isNewLineRx) { isNewLineRx = 0; //ACK为单个字节 if (lstLineRxDataLen == 1 && lstLineRxData[0] == CMD_ACK_OK) { return ERR_OK; } else { return ERR_NOK; } } else { continue; } } } /// @brief void handleTR() { // 初始化外设 uint16_t time2Retry = 0; if (devMode == HOST_MODE) { // 间隔1s time2Retry = 1000; } else { time2Retry = 1000 * 10; } // 初始化MultiTimer multiTimerInstall(getPlatformTicks); while (1) { multiTimerYield(); // 收到新数据 if (isNewLineRx) { if (lstLineRxDataLen == 1) { /* 判断为无效,只有ACK为单个字节 */ } else { if (chkDataValid(lstLineRxData, lstLineRxDataLen) == ERR_OK) { //校验通过 lineTransmitData(CMD_ACK_OK, 1); userTransmitData(lstLineRxData, lstLineRxDataLen); //每次建立通信后10s内收端不能发起通信 isOK2Swt = 0; multiTimerStop(&retryTimer); multiTimerStart(&retryTimer, 10*1000, swtModeTimerCallback, NULL); // Start timer } else { //数据校验不过 lineTransmitData(CMD_ACK_NOK, 1); } } } // 有数据需要发送 if (isOK2Swt && getListSize(userRxBuffList) > 0) { // 发送数据 err_t lstState = ERR_OK; while (userRxBuffList != NULL) // 遍历链表 { static uint8_t isRetryTimeOut = 0; if (lstState == ERR_NOK) { if (!isRetryTimeOut) { // 跳过后续代码进入下次循环 continue; } else { isRetryTimeOut = 0; } } lineTransmitData(userRxBuffList->data, userRxBuffList->size); if (USING_ACK == 1) // 使用ACK { err_t rst = getACK(userRxBuffList->size * 10); if (rst == ERR_TIMEOUT) { lstState = ERR_TIMEOUT; userTransmitData(ERR_TIMEOUT, 1); // 超时错误大概率是透传缆损坏,不主动重试,终止发送 break; } else if (rst == ERR_OK) // ACK正常 { lstState = ERR_OK; userRxBuffList = userRxBuffList->next; // 正常则移动指针 deleteFirstNode(&userRxBuffList); userTransmitData(ERR_OK, 1); } else { lstState = ERR_NOK; static uint8_t reTryCnt = 0;//当前重试次数 reTryCnt++; if (reTryCnt > MAX_RETRY_CNT) { reTryCnt = 0; lstState = ERR_OK; userRxBuffList = userRxBuffList->next; // 次数超出阈值则移动指针 deleteFirstNode(&userRxBuffList); // userTransmitData(CMD_ACK_NOK, 1); userTransmitData(ERR_NOK,1); break; } //失败的原因可能是数据碰撞,需要根据优先级等待不同的时间后进行重试 multiTimerStop(&timeoutTimer); multiTimerStart(&timeoutTimer, time2Retry, reTryTimerCallback, &isRetryTimeOut); // Start timer } } else // 无ACK { lstState = ERR_OK; userRxBuffList = userRxBuffList->next; // 移动指针 deleteFirstNode(&userRxBuffList); } } } } } void main() { handleTR(); }