From cbf7dee1cfa3df3795b015e27c13c373cff71194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D1=8F=D1=87=D0=B5=D1=81=D0=BB=D0=B0=D0=B2=20=D0=A8?= =?UTF-8?q?=D1=82=D0=B5=D0=B9=D0=B1=D0=B5=D0=B7=D0=B0=D0=BD=D0=B4=D1=82?= Date: Fri, 19 Jun 2026 15:34:23 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20=D1=81=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B8=20=D1=80=D0=B5=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D1=8B=20=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D1=8B=D0=B5=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=D0=BD=D0=BE=D0=B9=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D1=87=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Реализована обработка разных типов сообщений (BroadCast, Discrete, Analog) в функции PROTOCAN_SEND_CORE с корректной разбивкой данных на пакеты размером до 8 байт. Добавлены новые функции для пакетной отправки данных по диапазону регистров (PROTOCAN_SEND_GENERAL_ADDRESS_SPACE) и модбас-данных (PROTOCAN_SEND_MODBUS). --- Core/Inc/protocan.h | 22 ++-- Core/Src/protocan.c | 293 ++++++++++++++++++++++++++++++++------------ 2 files changed, 226 insertions(+), 89 deletions(-) diff --git a/Core/Inc/protocan.h b/Core/Inc/protocan.h index d6e8506..55a225c 100644 --- a/Core/Inc/protocan.h +++ b/Core/Inc/protocan.h @@ -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); diff --git a/Core/Src/protocan.c b/Core/Src/protocan.c index e7778a1..2d4f861 100644 --- a/Core/Src/protocan.c +++ b/Core/Src/protocan.c @@ -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) @@ -712,8 +810,8 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_GENERAL_ADDRESS_SPACE(ProtoCanId_t id, uint for(int i = 0; i < regsInPacket; 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); @@ -896,82 +994,89 @@ __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) + 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; - } - 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) + } + } + else if(msgType == PROTOCAN_MSGTYPE_MODBUS_HOLDING || msgType == PROTOCAN_MSGTYPE_MODBUS_INPUT) + { + uint16_t* dataPtr = modbusData.Data; + unsigned totalRegs = modbusData.RegCount; + unsigned regsProcessed = 0; + unsigned startAddress = modbusData.StrAdr; + while (regsProcessed < totalRegs) { + uint8_t regsInPacket = (totalRegs - regsProcessed) > 4 ? 4 : (totalRegs - regsProcessed); // Формируем body msgBodyModbusType body; - body.Fields.StrAdr = modbusData->StrAdr; - body.Fields.RegCount = modbusData->RegCount; - // Устанавливаем коллизию ExtId с body.Body - localId.Fields.MsgBody = (body.Body & 0xFFFF); + body.Fields.StrAdr = startAddress; + body.Fields.RegCount = regsInPacket; // Обновляем ExtId + localId.Fields.MsgBody = (body.Body & 0xFFFF); 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; - // Формируем CAN данные - canData[0] = (*modbusData->Data) & 0xFF; - canData[1] = (*modbusData->Data >> 8) & 0xFF; - if (HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK) + 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; } - } - else if(msgType == PROTOCAN_MSGTYPE_MODBUS_HOLDING || msgType == PROTOCAN_MSGTYPE_MODBUS_INPUT) - { - uint16_t* dataPtr = modbusData->Data; - unsigned totalRegs = modbusData->RegCount; - unsigned regsProcessed = 0; - unsigned startAddress = modbusData->StrAdr; - while (regsProcessed < totalRegs) - { - 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; + 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); } +/** + * @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: