// 透传缆 #include #include #include "MultiTimer.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 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) // 客户端重试时间间隔 static int devMode = HOST_MODE; static int curMode = R_MODE; static int tarMode = R_MODE; static int trySwt = 0; // 是否尝试切换到发送模式 static int tarDevMode = R_MODE; // 目标端工作模式 static MultiTimer retryTimer, timeoutTimer; // 重试定时器、超时定时器 /// @brief 主从模式,主机拥有更高优先级 enum { HOST_MODE = 0, CLIENT_MODE }; /// @brief 工作模式,收、发、休眠、唤醒 enum { T_MODE = 0, R_MODE, SLEEP_MODE, WORK_MODE }; // 设置GPIO电平 void setGpioLevel(int gpio, int level) { // 设置GPIO电平 } /// @brief 初始化工作模式,仅设置对应GPIO电平 /// @param cfg void initWorkMode(int 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) { // 发送数据前后需初始化为发送模式 initWorkMode(T_MODE); // 发送数据 // 发送完毕后需恢复为接收模式 initWorkMode(R_MODE); } /// @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 retryTimerCallback(MultiTimer *timer, void *userData) { if (tarDevMode == R_MODE && curMode == R_MODE && tarMode == T_MODE) // 两端为收方可发起请求 { trySwt = 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->有效 1->帧头无效 2->帧尾无效 3->校验位无效 int chkDataValid(uint8_t *data, uint8_t len) { // 按帧发送数据 // 判断帧的完整性及校验位 if (data[0] != 0x5A || data[1] != 0xA5) // 帧头校验 { return 1; } if (data[len - 1] != 0xED) // 帧尾校验 { return 2; } if (bccCRC(data + 2, len - 4) != data[len - 2]) // 校验位校验 { return 3; } return 0; } /// @brief 用户接口接收数据回调 /// @param data /// @param len void userRecDataCallback(uint8_t *data, uint8_t len) { if (len == 1) { switch (data[0]) { case CMD_REC_MODE: switchToRecMode(); break; case CMD_TRA_MODE: if (curMode == R_MODE && tarDevMode == R_MODE) { switchToTraMode(); // ok } break; case CMD_SLP_MODE: initWorkMode(SLEEP_MODE); break; case CMD_WKP_MODE: initWorkMode(WORK_MODE); break; default: break; } } else { if (chkDataValid(data, len)) { lineTransmitData(data, len); } } } /// @brief 透传缆接收数据回调 /// @param data /// @param len void lineRecCallback(uint8_t *data, uint8_t len) { if (len == 1) { switch (data[0]) { case CMD_REC_MODE: lineTransmitData(CMD_ACK_OK, 1); tarDevMode = R_MODE; break; case CMD_TRA_MODE: // 对端请求切换为发送模式 if (curMode == R_MODE) { lineTransmitData(CMD_ACK_OK, 1); tarDevMode = T_MODE; } else { lineTransmitData(CMD_ACK_NOK, 1); } break; default: lineTransmitData(CMD_ACK_NOK, 1); break; } } else { if (chkDataValid(data, len)) { userTransmitData(data, len); } } } /// @brief void handleTR() { // 初始化外设 uint8_t retryTime = 100; if (devMode == HOST_MODE) { // 间隔0.1s retryTime = 100; } else { retryTime = 1000; } // 初始化MultiTimer multiTimerInstall(getPlatformTicks); // 定时器始终开启 multiTimerStart(&retryTimer, retryTime, retryTimerCallback, NULL); // Start timer while (1) { multiTimerYield(); if (trySwt) { trySwt = 0; // clear flag switchToTraMode(); multiTimerStart(&retryTimer, retryTime, retryTimerCallback, NULL); // always restart timer } } } void main() { handleTR(); }