demo3/tcl.c
2024-12-03 08:33:43 +08:00

429 lines
11 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 透传缆
#include <stdio.h>
#include <stdlib.h>
#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();
}