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

Реализована обработка разных типов сообщений
(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; /**< Шаг последнего импульса. */ unsigned LastPulseStep; /**< Шаг последнего импульса. */
} ProtoCanDevice_t; } ProtoCanDevice_t;
struct RXMsg{ /**
struct INFO{ * @brief Структура для хранения сообщения RXCAN.
unsigned EXT:1; */
unsigned RTR:1; struct RXMsg {
}info; /**
ProtoCanId_t eID; * @brief Структура, содержащая флаги информации.
uint16_t DLC; */
uint8_t Data[8]; 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); 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); 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 Обработка и ответ на запрос общего адресного пространства. * @brief Обработка и ответ на запрос общего адресного пространства.
* Формирует и передает сообщение с данными о статусе "GAS-XXXX", где XXXX — значение из сообщения _rxMsg. * Формирует и передает сообщение с данными о статусе "GAS-XXXX", где XXXX — значение из сообщения _rxMsg.
@@ -674,15 +775,12 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToGeneralAddressSpace(struct RXMsg _rxM
/** /**
* @brief Отправляет произвольный диапазон регистров в пакетах по, максимум, 4 регистра за раз через CAN. * @brief Отправляет произвольный диапазон регистров в пакетах по, максимум, 4 регистра за раз через CAN.
*
* Функция разбивает передаваемый массив регистров на пакеты по 4 или меньше и отправляет их по отдельности. * Функция разбивает передаваемый массив регистров на пакеты по 4 или меньше и отправляет их по отдельности.
* Для каждого пакета формируется уникальный MsgBody, равный адресу первого регистра в пакете. * Для каждого пакета формируется уникальный MsgBody, равный адресу первого регистра в пакете.
*
* @param priority Приоритет сообщения CAN. ProtoCan_Priority_TypeDef * @param priority Приоритет сообщения CAN. ProtoCan_Priority_TypeDef
* @param regStartAdr Адрес первого регистра из всего массива. * @param regStartAdr Адрес первого регистра из всего массива.
* @param data Указатель на массив регистров для отправки. * @param data Указатель на массив регистров для отправки.
* @param regCount Общее количество регистров для отправки. * @param regCount Общее количество регистров для отправки.
*
* @return Возвращает статус отправки: HAL_OK при успехе, или код ошибки HAL_StatusTypeDef при сбое. * @return Возвращает статус отправки: HAL_OK при успехе, или код ошибки HAL_StatusTypeDef при сбое.
*/ */
PROTOCAN_StatusTypeDef PROTOCAN_SEND_GENERAL_ADDRESS_SPACE(ProtoCanId_t id, uint16_t regStartAdr, uint16_t *data, uint8_t regCount) PROTOCAN_StatusTypeDef PROTOCAN_SEND_GENERAL_ADDRESS_SPACE(ProtoCanId_t id, uint16_t regStartAdr, uint16_t *data, uint8_t regCount)
@@ -712,8 +810,8 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_GENERAL_ADDRESS_SPACE(ProtoCanId_t id, uint
for(int i = 0; i < regsInPacket; i++) for(int i = 0; i < regsInPacket; i++)
{ {
canData[(i * 2)] = LowByteOfWord(data[currentIndex + i]); canData[(i * 2)] = LowByteOfWord(data[currentIndex + i]);
canData[(i * 2) + 1] = HighByteOfWord(data[currentIndex + i]); canData[(i * 2) + 1] = HighByteOfWord(data[currentIndex + i]);
} }
HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox); HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox);
@@ -896,82 +994,89 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusInput(struct RXMsg _rxMsg)
} }
/** /**
*/ * @brief Отправляет данные Modbus через CAN-шину в соответствии с типом сообщения.
int PROTOCAN_SEND_MODBUS(const ProtoCanId_t* id, const struct ProtoCanModbusData* modbusData) * Эта функция формирует и отправляет 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) ProtoCanId_t localId = id;
uint8_t msgType = localId.Fields.MsgType;
// Структура для отправки
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t canData[8];
if(msgType == PROTOCAN_MSGTYPE_MODBUS_COIL || msgType == PROTOCAN_MSGTYPE_MODBUS_DISCRETE)
{
// Формируем body
msgBodyModbusType body;
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;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.DLC = modbusData.RegCount % 8 + 1;
// Формируем CAN данные
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; return PROTOCAN_ERROR;
} }
ProtoCanId_t localId = *id; }
uint8_t msgType = localId.Fields.MsgType; else if(msgType == PROTOCAN_MSGTYPE_MODBUS_HOLDING || msgType == PROTOCAN_MSGTYPE_MODBUS_INPUT)
// Структура для отправки {
CAN_TxHeaderTypeDef TxHeader; uint16_t* dataPtr = modbusData.Data;
uint32_t TxMailBox = 0; unsigned totalRegs = modbusData.RegCount;
uint8_t canData[8]; unsigned regsProcessed = 0;
if(msgType == PROTOCAN_MSGTYPE_MODBUS_COIL || msgType == PROTOCAN_MSGTYPE_MODBUS_DISCRETE) unsigned startAddress = modbusData.StrAdr;
while (regsProcessed < totalRegs)
{ {
uint8_t regsInPacket = (totalRegs - regsProcessed) > 4 ? 4 : (totalRegs - regsProcessed);
// Формируем body // Формируем body
msgBodyModbusType body; msgBodyModbusType body;
body.Fields.StrAdr = modbusData->StrAdr; body.Fields.StrAdr = startAddress;
body.Fields.RegCount = modbusData->RegCount; body.Fields.RegCount = regsInPacket;
// Устанавливаем коллизию ExtId с body.Body
localId.Fields.MsgBody = (body.Body & 0xFFFF);
// Обновляем ExtId // Обновляем ExtId
localId.Fields.MsgBody = (body.Body & 0xFFFF);
TxHeader.ExtId = localId.BitAll; TxHeader.ExtId = localId.BitAll;
// Остальные настройки TxHeader.IDE = CAN_ID_EXT;
TxHeader.IDE = CAN_ID_EXT; // Предположим, что используете расширенные ID
TxHeader.RTR = CAN_RTR_DATA; TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE; TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.DLC = modbusData->RegCount % 8 + 1; TxHeader.DLC = regsInPacket * 2;
// Формируем CAN данные // Добавляем регистры
canData[0] = (*modbusData->Data) & 0xFF; for (int i = 0; i < regsInPacket; i++)
canData[1] = (*modbusData->Data >> 8) & 0xFF; {
if (HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK) canData[i * 2] = LowByteOfWord(dataPtr[regsProcessed + i]);
canData[i * 2 + 1] = HighByteOfWord(dataPtr[regsProcessed + i]);
}
// Отправляем сообщение
if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{ {
return PROTOCAN_ERROR; return PROTOCAN_ERROR;
} }
} regsProcessed += regsInPacket;
else if(msgType == PROTOCAN_MSGTYPE_MODBUS_HOLDING || msgType == PROTOCAN_MSGTYPE_MODBUS_INPUT) startAddress += regsInPacket;
{ }
uint16_t* dataPtr = modbusData->Data; }
unsigned totalRegs = modbusData->RegCount; else
unsigned regsProcessed = 0; {
unsigned startAddress = modbusData->StrAdr; return PROTOCAN_ERROR; // Неверный тип сообщения
while (regsProcessed < totalRegs) }
{ return PROTOCAN_OK;
uint8_t regsInPacket = (totalRegs - regsProcessed) > 4 ? 4 : (totalRegs - regsProcessed);
// Формируем body
msgBodyModbusType body;
body.Fields.StrAdr = startAddress;
body.Fields.RegCount = regsInPacket;
// Обновляем ExtId
localId.Fields.MsgBody = (body.Body & 0xFFFF);
TxHeader.ExtId = localId.BitAll;
TxHeader.IDE = CAN_ID_EXT;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.DLC = regsInPacket * 2;
// Добавляем регистры
for (int i = 0; i < regsInPacket; i++)
{
canData[i * 2] = LowByteOfWord(dataPtr[regsProcessed + i]);
canData[i * 2 + 1] = HighByteOfWord(dataPtr[regsProcessed + i]);
}
// Отправляем сообщение
if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{
return PROTOCAN_ERROR;
}
regsProcessed += regsInPacket;
startAddress += regsInPacket;
}
}
else
{
return PROTOCAN_ERROR; // Неверный тип сообщения
}
return PROTOCAN_OK;
} }
/** /**
@@ -1001,6 +1106,40 @@ __weak PROTOCAN_StatusTypeDef CanRequestError(struct RXMsg _rxMsg)
return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); 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. * @brief Записывает полученное сообщение CAN в буфер rxMsg.
* Копирует параметры расширенного ID, типы, длину данных и сам данные в указанный элемент буфера. * Копирует параметры расширенного ID, типы, длину данных и сам данные в указанный элемент буфера.
@@ -1183,32 +1322,22 @@ void PROTOCAN_FILTERS()
/** /**
* @brief Отправляет CAN-сообщение в зависимости от типа сообщения. * @brief Отправляет CAN-сообщение в зависимости от типа сообщения.
*
* Эта функция анализирует тип сообщения в идентификаторе и вызывает * Эта функция анализирует тип сообщения в идентификаторе и вызывает
* соответствующую функцию отправки. В текущей реализации поддерживается только * соответствующую функцию отправки. В текущей реализации поддерживается только
* тип PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE. * тип PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE.
*
* @param id Идентификатор CAN-сообщения, содержащий информацию о типе. * @param id Идентификатор CAN-сообщения, содержащий информацию о типе.
* @param data Структура с данными, которые необходимо отправить. Зависит от типа сообщения. * @param data Структура с данными, которые необходимо отправить. Зависит от типа сообщения.
* * @return Статус выполнения операции.
* @return Статус выполнения операции:
* - PROTOCAN_OK при успешной отправке,
* - PROTOCAN_ERROR, если тип сообщения не поддерживается или произошла ошибка.
*/ */
PROTOCAN_StatusTypeDef PROTOCAN_SEND(ProtoCanId_t id, ProtoCanData_t data) PROTOCAN_StatusTypeDef PROTOCAN_SEND(ProtoCanId_t id, ProtoCanData_t data)
{ {
switch (id.Fields.MsgType) switch(id.Fields.MsgType)
{ {
case PROTOCAN_MSGTYPE_BROADCAST: case PROTOCAN_MSGTYPE_BROADCAST:
{
break;
}
case PROTOCAN_MSGTYPE_DISCRETE: case PROTOCAN_MSGTYPE_DISCRETE:
{
break;
}
case PROTOCAN_MSGTYPE_ANALOG: case PROTOCAN_MSGTYPE_ANALOG:
{ {
return PROTOCAN_SEND_CORE(id, data.CoreData);
break; break;
} }
case PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE: 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_HOLDING:
case PROTOCAN_MSGTYPE_MODBUS_INPUT: case PROTOCAN_MSGTYPE_MODBUS_INPUT:
{ {
return PROTOCAN_SEND_MODBUS(id, data.ModbusData);
break; break;
} }
case PROTOCAN_MSGTYPE_ERROR: case PROTOCAN_MSGTYPE_ERROR:
{ {
return PROTOCAN_SEND_ERROR(id, data.ErrorData);
break; break;
} }
default: default: