Улучшена логика отправки сообщений и реализованы новые функции пакетной передачи

Реализована обработка разных типов сообщений
(BroadCast, Discrete, Analog) в функции PROTOCAN_SEND_CORE
с корректной разбивкой данных на пакеты размером до 8 байт.

Добавлены новые функции для пакетной отправки данных
по диапазону регистров (PROTOCAN_SEND_GENERAL_ADDRESS_SPACE)
и модбас-данных (PROTOCAN_SEND_MODBUS).
This commit is contained in:
2026-06-19 15:34:23 +03:00
parent 6e7dd22cac
commit cbf7dee1cf
2 changed files with 226 additions and 89 deletions

View File

@@ -252,14 +252,20 @@ typedef struct{
unsigned LastPulseStep; /**< Шаг последнего импульса. */
} ProtoCanDevice_t;
struct RXMsg{
struct INFO{
unsigned EXT:1;
unsigned RTR:1;
}info;
ProtoCanId_t eID;
uint16_t DLC;
uint8_t Data[8];
/**
* @brief Структура для хранения сообщения RXCAN.
*/
struct RXMsg {
/**
* @brief Структура, содержащая флаги информации.
*/
struct INFO {
unsigned EXT : 1; /**< Бит расширенного идентификатора (EXT). */
unsigned RTR : 1; /**< Бит запроса на передачу (RTR). */
} info; /**< Информационные флаги сообщения. */
ProtoCanId_t eID; /**< Идентификатор CAN-сообщения. */
uint16_t DLC; /**< Длина данных (Data Length Code). */
uint8_t Data[8]; /**< Массив данных (до 8 байт). */
};
uint16_t AvailableCanRxMsg(void);

View File

@@ -635,6 +635,107 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogTSens(struct RXMsg _rxMsg)
return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Отправляет сообщение по CAN-шине в соответствии с типом протокола.
* Эта функция формирует и отправляет сообщение через CAN-шину, основываясь на типе
* переданного ID и данных. Поддерживаются типы сообщений: широковещательные, дискретные
* и аналоговые.
* @param id Идентификатор сообщения, содержащий тип сообщения и другую информацию.
* @param coreData Структура данных, включающая тип, тело и данные для передачи.
* @return PROTOCAN_StatusTypeDef Статус операции.
* @details
* В зависимости от типа сообщения (панель broadcast, discrete или analog),
* формируется соответствующий пакет данных и отправляется на шину CAN.
* Для сообщений, превышающих длину 8 байт, передача выполняется пакетами.
* В случае ошибок при добавлении сообщения в очередь передачи, возвращается ошибка.
*/
PROTOCAN_StatusTypeDef PROTOCAN_SEND_CORE(const ProtoCanId_t id, const struct ProtoCanCoreData coreData)
{
ProtoCanId_t localId = id;
uint8_t msgType = localId.Fields.MsgType;
CAN_TxHeaderTypeDef TxHeader;
TxHeader.IDE = CAN_ID_EXT;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
uint32_t TxMailBox = 0;
uint8_t canData[8];
switch(id.Fields.MsgType)
{
case PROTOCAN_MSGTYPE_BROADCAST:
{
msgBodyBroadcastType body;
body.Fields.Type = coreData.Type;
body.Fields.Body = coreData.Body;
localId.Fields.MsgBody = body.Body;
TxHeader.ExtId = localId.BitAll;
TxHeader.DLC = 0;
if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{
return PROTOCAN_ERROR;
}
}
case PROTOCAN_MSGTYPE_DISCRETE:
{
msgBodyDiscreteType body;
body.Fields.Type = coreData.Type;
uint8_t regsRemaining = coreData.DataCount;
uint8_t currentIndex = 0;
while(regsRemaining > 0)
{
uint8_t regsInPacket = (regsRemaining > 8) ? 8 : regsRemaining;
body.Fields.Body = coreData.Body + currentIndex;
localId.Fields.MsgBody = body.Body;
TxHeader.ExtId = localId.BitAll;
TxHeader.DLC = regsInPacket;
for(int i = 0; i < regsInPacket; i++)
{
canData[i] = coreData.Data[coreData.DataCount - regsRemaining];
}
HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox);
if(status != HAL_OK)
{
return (PROTOCAN_StatusTypeDef)status;
}
regsRemaining -= regsInPacket;
currentIndex += regsInPacket;
}
break;
}
case PROTOCAN_MSGTYPE_ANALOG:
{
msgBodyAnalogType body;
body.Fields.Type = coreData.Type;
uint8_t regsRemaining = coreData.DataCount;
uint8_t currentIndex = 0;
while(regsRemaining > 0)
{
uint8_t regsInPacket = (regsRemaining > 8) ? 8 : regsRemaining;
body.Fields.SensorID = coreData.Body + currentIndex;
localId.Fields.MsgBody = body.Body;
TxHeader.ExtId = localId.BitAll;
TxHeader.DLC = regsInPacket;
for(int i = 0; i < regsInPacket; i++)
{
canData[i] = coreData.Data[coreData.DataCount - regsRemaining];
}
HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox);
if(status != HAL_OK)
{
return (PROTOCAN_StatusTypeDef)status;
}
regsRemaining -= regsInPacket;
currentIndex += regsInPacket;
}
break;
}
default:
{
return PROTOCAN_ERROR;
}
}
return PROTOCAN_OK;
}
/**
* @brief Обработка и ответ на запрос общего адресного пространства.
* Формирует и передает сообщение с данными о статусе "GAS-XXXX", где XXXX — значение из сообщения _rxMsg.
@@ -674,15 +775,12 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToGeneralAddressSpace(struct RXMsg _rxM
/**
* @brief Отправляет произвольный диапазон регистров в пакетах по, максимум, 4 регистра за раз через CAN.
*
* Функция разбивает передаваемый массив регистров на пакеты по 4 или меньше и отправляет их по отдельности.
* Для каждого пакета формируется уникальный MsgBody, равный адресу первого регистра в пакете.
*
* @param priority Приоритет сообщения CAN. ProtoCan_Priority_TypeDef
* @param regStartAdr Адрес первого регистра из всего массива.
* @param data Указатель на массив регистров для отправки.
* @param regCount Общее количество регистров для отправки.
*
* @return Возвращает статус отправки: HAL_OK при успехе, или код ошибки HAL_StatusTypeDef при сбое.
*/
PROTOCAN_StatusTypeDef PROTOCAN_SEND_GENERAL_ADDRESS_SPACE(ProtoCanId_t id, uint16_t regStartAdr, uint16_t *data, uint8_t regCount)
@@ -896,14 +994,21 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusInput(struct RXMsg _rxMsg)
}
/**
*/
int PROTOCAN_SEND_MODBUS(const ProtoCanId_t* id, const struct ProtoCanModbusData* modbusData)
* @brief Отправляет данные Modbus через CAN-шину в соответствии с типом сообщения.
* Эта функция формирует и отправляет Modbus-данные по CAN-шине, поддерживая
* различные типы сообщений: Coil, Discrete, Holding Register и Input Register.
* Для сообщений, превышающих 4 регистров, данные отправляются пакетами.
* @param id Идентификатор сообщения, содержащий тип сообщения и другую информацию.
* @param modbusData Структура с данными Modbus: адрес, количество регистров и сами данные.
* @return PROTOCAN_StatusTypeDef Статус операции.
* @details
* В зависимости от типа Modbus (coil/discrete или holding/input), формируется
* соответствующий CAN-пакет. Для coil/discrete отправляется один пакет, для holding/input —
* несколько, по 4 регистра в пакете. В случае ошибок при добавлении сообщения API HAL возвращается ошибка.
*/
PROTOCAN_StatusTypeDef PROTOCAN_SEND_MODBUS(const ProtoCanId_t id, const struct ProtoCanModbusData modbusData)
{
if (!id || !modbusData)
{
return PROTOCAN_ERROR;
}
ProtoCanId_t localId = *id;
ProtoCanId_t localId = id;
uint8_t msgType = localId.Fields.MsgType;
// Структура для отправки
CAN_TxHeaderTypeDef TxHeader;
@@ -913,20 +1018,20 @@ int PROTOCAN_SEND_MODBUS(const ProtoCanId_t* id, const struct ProtoCanModbusData
{
// Формируем body
msgBodyModbusType body;
body.Fields.StrAdr = modbusData->StrAdr;
body.Fields.RegCount = modbusData->RegCount;
body.Fields.StrAdr = modbusData.StrAdr;
body.Fields.RegCount = modbusData.RegCount;
// Устанавливаем коллизию ExtId с body.Body
localId.Fields.MsgBody = (body.Body & 0xFFFF);
// Обновляем ExtId
TxHeader.ExtId = localId.BitAll;
// Остальные настройки
TxHeader.IDE = CAN_ID_EXT; // Предположим, что используете расширенные ID
TxHeader.IDE = CAN_ID_EXT;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.DLC = modbusData->RegCount % 8 + 1;
TxHeader.DLC = modbusData.RegCount % 8 + 1;
// Формируем CAN данные
canData[0] = (*modbusData->Data) & 0xFF;
canData[1] = (*modbusData->Data >> 8) & 0xFF;
canData[0] = LowByteOfWord(modbusData.Data[0]);
canData[1] = HighByteOfWord(modbusData.Data[0]);
if (HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{
return PROTOCAN_ERROR;
@@ -934,10 +1039,10 @@ int PROTOCAN_SEND_MODBUS(const ProtoCanId_t* id, const struct ProtoCanModbusData
}
else if(msgType == PROTOCAN_MSGTYPE_MODBUS_HOLDING || msgType == PROTOCAN_MSGTYPE_MODBUS_INPUT)
{
uint16_t* dataPtr = modbusData->Data;
unsigned totalRegs = modbusData->RegCount;
uint16_t* dataPtr = modbusData.Data;
unsigned totalRegs = modbusData.RegCount;
unsigned regsProcessed = 0;
unsigned startAddress = modbusData->StrAdr;
unsigned startAddress = modbusData.StrAdr;
while (regsProcessed < totalRegs)
{
uint8_t regsInPacket = (totalRegs - regsProcessed) > 4 ? 4 : (totalRegs - regsProcessed);
@@ -1001,6 +1106,40 @@ __weak PROTOCAN_StatusTypeDef CanRequestError(struct RXMsg _rxMsg)
return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Отправляет сообщение об ошибке по CAN-шине.
* Эта функция формирует и отправляет сообщение об ошибке с кодом и информацией,
* объединёнными в поле MsgBody идентификатора сообщения, и передаёт его через CAN.
* @param id Идентификатор сообщения, содержащий базовую информацию.
* @param errorData Структура с данными ошибки: код и дополнительная информация.
* @return PROTOCAN_StatusTypeDef Статус операции.
* @details
* В качестве данных передаётся 2 байта: старшие биты — информация об ошибке, младшие — код ошибки.
* Размер данных в CANFrames устанавливается нулевым, так как все данные инкапсулированы в MsgBody.
*/
PROTOCAN_StatusTypeDef PROTOCAN_SEND_ERROR(const ProtoCanId_t id, const struct ProtoCanErrorData errorData)
{
ProtoCanId_t localId = id;
uint8_t msgType = localId.Fields.MsgType;
// Структура для отправки
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t canData[8];
localId.Fields.MsgBody = ((errorData.Info << 8) | errorData.Code);
// Обновляем ExtId
TxHeader.ExtId = localId.BitAll;
// Остальные настройки
TxHeader.IDE = CAN_ID_EXT;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.DLC = 0;
if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{
return PROTOCAN_ERROR;
}
return PROTOCAN_OK;
}
/**
* @brief Записывает полученное сообщение CAN в буфер rxMsg.
* Копирует параметры расширенного ID, типы, длину данных и сам данные в указанный элемент буфера.
@@ -1183,32 +1322,22 @@ void PROTOCAN_FILTERS()
/**
* @brief Отправляет CAN-сообщение в зависимости от типа сообщения.
*
* Эта функция анализирует тип сообщения в идентификаторе и вызывает
* соответствующую функцию отправки. В текущей реализации поддерживается только
* тип PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE.
*
* @param id Идентификатор CAN-сообщения, содержащий информацию о типе.
* @param data Структура с данными, которые необходимо отправить. Зависит от типа сообщения.
*
* @return Статус выполнения операции:
* - PROTOCAN_OK при успешной отправке,
* - PROTOCAN_ERROR, если тип сообщения не поддерживается или произошла ошибка.
* @return Статус выполнения операции.
*/
PROTOCAN_StatusTypeDef PROTOCAN_SEND(ProtoCanId_t id, ProtoCanData_t data)
{
switch (id.Fields.MsgType)
switch(id.Fields.MsgType)
{
case PROTOCAN_MSGTYPE_BROADCAST:
{
break;
}
case PROTOCAN_MSGTYPE_DISCRETE:
{
break;
}
case PROTOCAN_MSGTYPE_ANALOG:
{
return PROTOCAN_SEND_CORE(id, data.CoreData);
break;
}
case PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE:
@@ -1227,10 +1356,12 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND(ProtoCanId_t id, ProtoCanData_t data)
case PROTOCAN_MSGTYPE_MODBUS_HOLDING:
case PROTOCAN_MSGTYPE_MODBUS_INPUT:
{
return PROTOCAN_SEND_MODBUS(id, data.ModbusData);
break;
}
case PROTOCAN_MSGTYPE_ERROR:
{
return PROTOCAN_SEND_ERROR(id, data.ErrorData);
break;
}
default: