#include "protocan.h" ProtoCanDevice_t CurrentDevice; ProtoCanDevice_t Device_on_the_Network[8][16]; struct controlflags ControlFlags; CAN_HandleTypeDef *_HCAN = 0; RTC_HandleTypeDef *_HRTC = 0; TIM_HandleTypeDef *_HTIM = 0; uint8_t CurrentStep = 1; uint8_t LastStep = 0; struct RXMsg rxMsg[PROTOCAN_RX_BUFFER_SIZE]; /** * @brief Проверяет, является ли год високосным. * В системе год хранится как смещение от 2000. Например, значение 24 соответствует 2024. * Эта функция добавляет 2000 к значению и проверяет, високосный ли получившийся год. * @param year Смещение от 2000, тип uint8_t (от 0 до 255). * @return _Bool true, если год високосный; false — иначе. */ _Bool IsLeapYear(uint8_t year) { year += 2000; return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0)); } /** * @brief Возвращает количество доступных сообщений в буфере приёма CAN. * Используется кольцевой буфер. * Возвращаемое значение — количество элементов между указателями LastStep и CurrentStep. * @return uint16_t Количество доступных сообщений, ожидающих обработки, в буфере. */ uint16_t AvailableCanRxMsg(void) { return ((uint16_t)(PROTOCAN_RX_BUFFER_SIZE + (LastStep - CurrentStep + 1))) % PROTOCAN_RX_BUFFER_SIZE; } void PROTOCAN_DEINIT(uint8_t stage) { switch(stage) { case 3: #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) HAL_TIM_UnRegisterCallback(_HTIM, HAL_TIM_PERIOD_ELAPSED_CB_ID); #endif case 2: case 1: #if (USE_HAL_CAN_REGISTER_CALLBACKS == 1) HAL_CAN_UnRegisterCallback(_HCAN, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID); #endif } return; } /** * @brief Инициализация переферии * @details Инициализация указателей на HCAN, HRTC, установка фильтров CAN. * @note Фильтры CAN описаны в разделе PROTOCAN_CAN_FILTERS(). */ PROTOCAN_INIT_StatusTypeDef PROTOCAN_INIT(CAN_HandleTypeDef *tmp_hcan, RTC_HandleTypeDef *tmp_hrtc, TIM_HandleTypeDef *tmp_tim) { unsigned initStage = 0; if(tmp_hcan) { _HCAN = tmp_hcan; #if (USE_HAL_CAN_REGISTER_CALLBACKS == 1) HAL_StatusTypeDef CAN_RC_RESULT = HAL_CAN_RegisterCallback(_HCAN, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID, ProtoCanRxFifo0MsgPendingCallback); if(CAN_RC_RESULT != HAL_OK) { PROTOCAN_DEINIT(initStage); return PROTOCAN_INIT_HRTC_ERROR; } #endif } else { PROTOCAN_DEINIT(initStage); return PROTOCAN_INIT_HCAN_ERROR; } initStage++; if(tmp_hrtc) { _HRTC = tmp_hrtc; } else { PROTOCAN_DEINIT(initStage); return PROTOCAN_INIT_HRTC_ERROR; } initStage++; if(tmp_tim) { _HTIM = tmp_tim; #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) HAL_StatusTypeDef TIM_RC_RESULT = HAL_TIM_RegisterCallback(_HTIM, HAL_TIM_PERIOD_ELAPSED_CB_ID, ProtoCanPulseCallback); if(TIM_RC_RESULT != HAL_OK) { PROTOCAN_DEINIT(initStage); return PROTOCAN_INIT_HRTC_ERROR; } #endif } else { PROTOCAN_DEINIT(initStage); return PROTOCAN_INIT_TIM_ERROR; } initStage++; PROTOCAN_FILTERS(); ControlFlags.IsPulse = 1; return PROTOCAN_INIT_OK; } /** * @brief Функция с обработкой полученных запросов * @details В бесконечном цикле функция ожидает приёма сообщения. После этого сообщение распределяется в зависимости от DataType. * Обработка запроса аналоговых значений - PROTOCAN_AnalogProcessing(). * Обработка широковещательных запросов - PROTOCAN_BroadcastProcessing(). * Обработка запроса дискретных значений - PROTOCAN_DiscreticProcessing(). * Обработка запроса к общему адресному пространству - PROTOCAN_GeneralAddressSpace_Answer(). * Обработка Modbus - PROTOCAN_ModbusProcessing(). */ void PROTOCAN_LOOP(void) { while(1) { if(AvailableCanRxMsg()) { if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_ANALOG) { PROTOCAN_AnalogProcessing(rxMsg[CurrentStep]); } else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_BROADCAST) { PROTOCAN_BroadcastProcessing(rxMsg[CurrentStep]); } else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_DISCRETE) { PROTOCAN_DiscreticProcessing(rxMsg[CurrentStep]); } else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE) { ProtoCanMsgToGeneralAddressSpace(rxMsg[CurrentStep]); } else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_COIL || rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_DISCRETE || rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_HOLDING || rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_INPUT) { PROTOCAN_ModbusProcessing(rxMsg[CurrentStep]); } else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_ERROR) { CanRequestError(rxMsg[CurrentStep]); } CurrentStep = (uint16_t)(CurrentStep + 1) % PROTOCAN_RX_BUFFER_SIZE; } } } /** * @brief Функция обработки широковещательных запросов. * @param struct RXMsg _rxMsg - структура для полученного сообщения. * @details Функция, выполняющая команды, переданные в широковещательном формате с головного (master) устройства. Типы команд: Запрос статуса, запрос на включение или выключение, рестарт устройств, установка времени. * @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка). */ PROTOCAN_StatusTypeDef PROTOCAN_BroadcastProcessing(struct RXMsg _rxMsg) { msgBodyBroadcastType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; switch(msg.Fields.Type) { case PROTOCAN_BROADCAST_STATUS: { //Обработка запроса статуса устройства return ProtoCanMsgToBroadcastStatus(_rxMsg); } case PROTOCAN_BROADCAST_ONOFF: { //Обработка запроса на вкл/выкл ProtoCanMsgToBroadcastOnOff(_rxMsg); break; } case PROTOCAN_BROADCAST_RESTARTDEVICE: { ProtoCanMsgToBroadcastRestart(_rxMsg); break; } case PROTOCAN_BROADCAST_RTCSETUP: { //Обработка запроса на синхронизацию времени //С головным устройством ProtoCanMsgToBroadcastRtcSetup(_rxMsg); break;; } default: //RESERVE SENSOR TYPE. return PROTOCAN_ERROR; break; } return PROTOCAN_OK; } /** * @brief Формирует и отправляет широковещательное CAN-сообщение для ответа на запрос статуса. * Устанавливает идентификатор, собирает текущие параметры времени и даты, и отправляет сообщение. * @param _rxMsg Структура полученного по CAN запроса. * @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успехе. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastStatus(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.DLC = 7; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; tmp_eID.Fields.DeviceType = CURRENT_TYPE_DEVICE; tmp_eID.Fields.DeviceID = CURRENT_ID_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; RTC_TimeTypeDef sTime = {0}; HAL_RTC_GetTime(_HRTC, &sTime, RTC_FORMAT_BIN); data[0] = sTime.Hours; data[1] = sTime.Minutes; data[2] = sTime.Seconds; RTC_DateTypeDef DateToUpdate = {0}; HAL_RTC_GetDate(_HRTC, &DateToUpdate, RTC_FORMAT_BIN); data[3] = DateToUpdate.Year; data[4] = DateToUpdate.Month; data[5] = DateToUpdate.Date; data[6] = DateToUpdate.WeekDay; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief Виртуальная слабая функция для переключения режима пульсации ON/OFF. * Меняет состояние флага IsPulse в структуре ControlFlags. * В данном случае — просто инвертирует его логическое значение. * @param _rxMsg Структура полученного CAN-сообщения (Пока не используется в функции). */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastOnOff(struct RXMsg _rxMsg) { ControlFlags.IsPulse = !ControlFlags.IsPulse; return PROTOCAN_OK; } /** * @brief __weak функция для обработки широковещательного сообщения перезапуска устройства. * Проверяет длину сообщения, извлекает ID из данных и, если она совпадает с текущим устройством, * вызывает системный перезапуск. * @param _rxMsg Структура полученного CAN-сообщения с командой перезапуска. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastRestart(struct RXMsg _rxMsg) { if(_rxMsg.DLC == 0) { return PROTOCAN_ERROR; } msgBodyBroadcastType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; if(msg.Fields.Body == (CURRENT_ID_DEVICE / (_rxMsg.DLC*8))) { uint64_t page = 0; for(int i = 0; i < _rxMsg.DLC; i++) { page += (_rxMsg.Data[i] << (i * 8)); } if((page >> CURRENT_ID_DEVICE) & 0b1) { NVIC_SystemReset(); } } return PROTOCAN_OK; } /** * @brief __weak функция для обработки запроса на синхронизацию RTC через широковещательное сообщение. * Проверяет длину сообщения и корректность данных (время и дата). Если все проверки пройдены, * вызывает функцию синхронизации RTC. * @param _rxMsg Структура полученного CAN-сообщения с данными для установки времени и даты. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastRtcSetup(struct RXMsg _rxMsg) { if(_rxMsg.DLC > 7) { return PROTOCAN_ERROR; } else { int DaysCount_Normal[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; if( _rxMsg.Data[0] > 23 || _rxMsg.Data[1] > 59 || _rxMsg.Data[2] > 59 || _rxMsg.Data[3] > 99 || _rxMsg.Data[4] > 12 || _rxMsg.Data[5] > DaysCount_Normal[IsLeapYear(_rxMsg.Data[3])][_rxMsg.Data[4]] || _rxMsg.Data[6] > 6) { return PROTOCAN_ERROR; } else { PROTOCAN_RTC_SYNC(_rxMsg.Data); } } return PROTOCAN_OK; } /** * @brief Функция обработки дискретных запросов. * @param struct RXMsg _rxMsg - структура для полученного сообщения. * @details Функция, формирующая и отправляющая ответ на запросы. Типы запросов: Аварии, Предупреждения, Управляющие сигналы, Флаги, Рестарт устройства, Изменение режима работы устройства, Запрос на устройство. * @note Запрос на устройство. Головное (master) устройство запрашивает некоторое колличество параметров. В Data - 64 битовых адресса параметров, тип которых задаётся в Sensor ID. Имеется возможность запрашивать непоследовательные параметры. * @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка). */ PROTOCAN_StatusTypeDef PROTOCAN_DiscreticProcessing(struct RXMsg _rxMsg) { msgBodyDiscreteType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; switch(msg.Fields.Type) { case PROTOCAN_DISCRETE_ACCIDENT: { ProtoCanMsgToDiscreteAccident(_rxMsg); break; } case PROTOCAN_DISCRETE_WARNING: { ProtoCanMsgToDiscreteWarning(_rxMsg); break; } case PROTOCAN_DISCRETE_CONTROL_SIGNALS: { ProtoCanMsgToDiscreteControlSignals(_rxMsg); break; } case PROTOCAN_DISCRETE_FLAGS: { ProtoCanMsgToDiscreteFlags(_rxMsg); break; } case PROTOCAN_DISCRETE_RESET: { ProtoCanMsgToDiscreteReset(_rxMsg); break; } case PROTOCAN_DISCRETE_CHANGE_MODE: { ProtoCanMsgToDiscreteChangeMode(_rxMsg); break; } case PROTOCAN_DISCRETE_REQUEST_LIST_OF_PARAMETERS: { ProtoCanMsgToDiscreteRequestListOfParameters(_rxMsg); break; } default: //RESERVE SENSOR TYPE. return PROTOCAN_ERROR; break; } return PROTOCAN_OK; } /** * @brief Обработка запроса аварийных ситуаций. * В текущей реализации — заглушка, ничего не выполняет. * Можно реализовать отправку или обработку аварийных данных. * @param _rxMsg Входящее сообщение с данными запроса. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteAccident(struct RXMsg _rxMsg) { return PROTOCAN_OK; } /** * @brief Обработка запроса предупреждений. * В текущей реализации — заглушка. * Можно реализовать обработку или ответ на предупреждения. * @param _rxMsg Входящее сообщение. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteWarning(struct RXMsg _rxMsg) { return PROTOCAN_OK; } /** * @brief Обработка запроса управляющих сигналов. * В текущей реализации — заглушка. * Можно реализовать управление сигналами. * @param _rxMsg Входящее сообщение. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteControlSignals(struct RXMsg _rxMsg) { return PROTOCAN_OK; } /** * @brief Обработка запроса флагов. * В текущей реализации — заглушка. * Можно реализовать работу с флагами. * @param _rxMsg Входящее сообщение. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteFlags(struct RXMsg _rxMsg) { return PROTOCAN_OK; } /** * @brief Обработка рестарта системы по CAN-запросу. * Выполняет программный рестарт системы. * @param _rxMsg Входящее сообщение. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteReset(struct RXMsg _rxMsg) { NVIC_SystemReset(); return PROTOCAN_ERROR; } /** * @brief Обработка запроса на изменение режима. * В текущей реализации — заглушка. * Можно реализовать изменение режима работы. * @param _rxMsg Входящее сообщение. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteChangeMode(struct RXMsg _rxMsg) { return PROTOCAN_OK; } /** * @brief Обработка запроса на получение списка параметров. * В текущей реализации — заглушка. * Можно реализовать отправку списка параметров. * @param _rxMsg Входящее сообщение. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToDiscreteRequestListOfParameters(struct RXMsg _rxMsg) { return PROTOCAN_OK; } /** * @brief Функция обработки аналоговых запросов. * @param struct RXMsg _rxMsg - структура для полученного сообщения. * @details Функция, сортирующая запросы по типам и вызывающая соответствующие функции. Типы запросов: Универсальный, Уставки, Напряжение, Ток, Температура. * @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка). */ PROTOCAN_StatusTypeDef PROTOCAN_AnalogProcessing(struct RXMsg _rxMsg) { msgBodyAnalogType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; switch (msg.Fields.Type) { case PROTOCAN_ANALOG_UNIVERSAL: { return ProtoCanMsgToAnalogUniversal(_rxMsg); break; } case PROTOCAN_ANALOG_SETTINGS: { return ProtoCanMsgToAnalogSettings(_rxMsg); break; } case PROTOCAN_ANALOG_U: { return ProtoCanMsgToAnalogUSens(_rxMsg); break; } case PROTOCAN_ANALOG_I: { return ProtoCanMsgToAnalogISens(_rxMsg); break; } case PROTOCAN_ANALOG_T: { return ProtoCanMsgToAnalogTSens(_rxMsg); break; } default: //RESERVE SENSOR TYPE return PROTOCAN_ERROR; break; } return PROTOCAN_ERROR; } /** * @brief Функция ответа на запрос данных со всех датчиков. * Создает сообщение с определенными данными и отправляет его через CAN-шину. * @param _rxMsg Структура входящего сообщения, используется для формирования ID. * @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogUniversal(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 6; data[0] = 'U'; data[1] = 'N'; data[2] = 'I'; data[3] = 'V'; data[4] = 'E'; data[5] = 'R'; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief Функция ответа на запрос уставок по CAN. * Создает сообщение с определенными данными и отправляет его через CAN-шину. * @param _rxMsg Структура входящего сообщения, используется только для формирования ID. * @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogSettings(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 7; data[0] = 'U'; data[1] = 'S'; data[2] = 'T'; data[3] = 'A'; data[4] = 'V'; data[5] = 'K'; data[6] = 'I'; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief Функция ответа на запрос данных с датчиков напряжения. * Создает сообщение с указанными параметрами и отправляет его через CAN-шину. * @param _rxMsg Структура входящего сообщения, используемая для формирования идентификатора и данных. * @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK при успешной отправке. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogUSens(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 6; msgBodyAnalogType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; data[0] = 'U'; data[1] = 'S'; data[2] = 47 + msg.Fields.SensorID / 1000; data[3] = 47 + msg.Fields.SensorID / 100 % 10; data[4] = 47 + msg.Fields.SensorID / 10 % 10; data[5] = 47 + msg.Fields.SensorID % 10; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief Функция ответа на запрос данных с датчиков тока. * Создает сообщение с определенными параметрами и отправляет его через CAN-шину. * @param _rxMsg Структура входящего сообщения, используется для формирования идентификатора и данных. * @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успешной отправке. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogISens(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 6; msgBodyAnalogType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; data[0] = 'I'; data[1] = 'S'; data[2] = 47 + msg.Fields.SensorID / 1000; data[3] = 47 + msg.Fields.SensorID / 100 % 10; data[4] = 47 + msg.Fields.SensorID / 10 % 10; data[5] = 47 + msg.Fields.SensorID % 10; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief Функция ответа на запрос данных с датчиков температуры. * Создает сообщение с определенным заголовком, формирует данные по ID сенсора и отправляет его через CAN. * @param _rxMsg Структура полученного сообщения, которая используется для формирования идентификатора. * @return HAL_StatusTypeDef Статус выполнения функции HAL, например HAL_OK при успехе. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToAnalogTSens(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 6; msgBodyAnalogType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; data[0] = 'T'; data[1] = 'S'; data[2] = 47 + msg.Fields.SensorID / 1000; data[3] = 47 + msg.Fields.SensorID / 100 % 10; data[4] = 47 + msg.Fields.SensorID / 10 % 10; data[5] = 47 + msg.Fields.SensorID % 10; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief Обработка и ответ на запрос общего адресного пространства. * Формирует и передает сообщение с данными о статусе "GAS-XXXX", где XXXX — значение из сообщения _rxMsg. * @param _rxMsg Структура входящего сообщения, содержит идентификатор и тело сообщения. */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToGeneralAddressSpace(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 8; data[0] = 'G'; data[1] = 'A'; data[2] = 'S'; data[3] = '-'; for(int i = 0; i < 4; i++) { unsigned sym = (_rxMsg.eID.Fields.MsgBody >> (12 - (i * 4))) & 0xF; if(sym >= 10) { data[4 + i] = sym % 10 + 'A'; } else { data[4 + i] = sym + '0'; } } return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @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) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t canData[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = id.BitAll; uint8_t regsRemaining = regCount; uint8_t currentIndex = 0; while(regsRemaining > 0) { uint8_t regsInPacket = (regsRemaining > 4) ? 4 : regsRemaining; tmp_eID.Fields.MsgBody = regStartAdr + currentIndex; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = regsInPacket*2; for(int i = 0; i < regsInPacket; i++) { canData[(i * 2)] = LowByteOfWord(data[currentIndex + i]); canData[(i * 2) + 1] = HighByteOfWord(data[currentIndex + i]); } HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox); if(status != HAL_OK) { return (PROTOCAN_StatusTypeDef)status; } regsRemaining -= regsInPacket; currentIndex += regsInPacket; } return (PROTOCAN_StatusTypeDef)HAL_OK; } /** * @brief Обработка запроса на основе типа Modbus. * В зависимости от типа сообщения, вызывает соответствующую функцию * для отправки Modbus-запроса по CAN-шине. * @param _rxMsg Структура входящего сообщения RXMsg. * @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка). */ PROTOCAN_StatusTypeDef PROTOCAN_ModbusProcessing(struct RXMsg _rxMsg) { switch(_rxMsg.eID.Fields.MsgType) { case PROTOCAN_MSGTYPE_MODBUS_COIL: { return ProtoCanMsgToModbusCoil(_rxMsg); break; } case PROTOCAN_MSGTYPE_MODBUS_DISCRETE: { return ProtoCanMsgToModbusDiscrete(_rxMsg); break; } case PROTOCAN_MSGTYPE_MODBUS_HOLDING: { return ProtoCanMsgToModbusHolding(_rxMsg); break; } case PROTOCAN_MSGTYPE_MODBUS_INPUT: { return ProtoCanMsgToModbusInput(_rxMsg); break; } default: //ERROR return PROTOCAN_ERROR; break; } return PROTOCAN_ERROR; } /** * @brief __weak Функция отправки ответа на запрос Coil Modbus по CAN. * Формирует CAN ответ на запрос Coil Modbus и отправляет его. * @param _rxMsg Структура входящего сообщения RXMsg. * @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка). */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusCoil(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 8; msgBodyModbusType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; data[0] = 'M'; data[1] = 'C'; data[2] = ' '; data[3] = 'S'; data[4] = msg.Fields.StrAdr; data[5] = ' '; data[6] = 'C'; data[7] = msg.Fields.RegCount; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief __weak Функция отправки ответа на запрос Discrete Modbus по CAN. * Формирует CAN ответ на запрос Discrete Modbus и отправляет его. * @param _rxMsg Структура входящего сообщения RXMsg. * @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка). */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusDiscrete(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 8; msgBodyModbusType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; data[0] = 'M'; data[1] = 'D'; data[2] = ' '; data[3] = 'S'; data[4] = msg.Fields.StrAdr; data[5] = ' '; data[6] = 'C'; data[7] = msg.Fields.RegCount; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief __weak Функция отправки ответа на запрос Holding Modbus по CAN. * Формирует CAN ответ на запрос Holding Modbus и отправляет его. * @param _rxMsg Структура входящего сообщения RXMsg. * @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка). */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusHolding(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 8; msgBodyModbusType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; data[0] = 'M'; data[1] = 'H'; data[2] = ' '; data[3] = 'S'; data[4] = msg.Fields.StrAdr; data[5] = ' '; data[6] = 'C'; data[7] = msg.Fields.RegCount; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief __weak Функция отправки ответа на запрос Input Modbus по CAN. * Формирует CAN ответ на запрос Input Modbus и отправляет его. * @param _rxMsg Структура входящего сообщения RXMsg. * @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка). */ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusInput(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 8; msgBodyModbusType msg; msg.Body = _rxMsg.eID.Fields.MsgBody; data[0] = 'M'; data[1] = 'I'; data[2] = ' '; data[3] = 'S'; data[4] = msg.Fields.StrAdr; data[5] = ' '; data[6] = 'C'; data[7] = msg.Fields.RegCount; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** */ int PROTOCAN_SEND_MODBUS(const ProtoCanId_t* id, const struct ProtoCanModbusData* modbusData) { if (!id || !modbusData) { 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) { // Формируем 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; // Предположим, что используете расширенные ID 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) { 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; } /** * @brief __weak Функция отправки сообщения об ошибке. * Посылает CAN сообщение с кодом ошибки. Используется, когда необходимо оповестить о неуспешной операции. * @param _rxMsg Структура входящего сообщения RXMsg, данными которого формируется ответ-ошибка. * @return HAL_StatusTypeDef Статус выполнения команды CAN. */ __weak PROTOCAN_StatusTypeDef CanRequestError(struct RXMsg _rxMsg) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; ProtoCanId_t tmp_eID; tmp_eID.BitAll = _rxMsg.eID.BitAll; tmp_eID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; tmp_eID.Fields.MsgType = PROTOCAN_MSGTYPE_ERROR; msgBodyErrorType msg; msg.Fields.ErrorCode = 0xFF; msg.Fields.Info = 0; tmp_eID.Fields.MsgBody = msg.Body; TxHeader.ExtId = tmp_eID.BitAll; TxHeader.DLC = 0; return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } /** * @brief Записывает полученное сообщение CAN в буфер rxMsg. * Копирует параметры расширенного ID, типы, длину данных и сам данные в указанный элемент буфера. * @param tmp_eID Расширенный ID сообщения. * @param tmp_IDE Тип идентификатора (например, CAN_ID_EXT). * @param tmp_RTR Тип запроса (например, CAN_RTR_DATA). * @param tmp_DLC Длина данных в сообщении. * @param tmp_DATA Указатель на массив данных, полученных из CAN. * @param tmp_LastStep Индекс позиции в буфере, куда записывать сообщение. */ void TakeRxMsgToBuffer(ProtoCanId_t tmp_eID, uint32_t tmp_IDE, uint32_t tmp_RTR, uint32_t tmp_DLC, uint8_t *tmp_DATA, uint16_t tmp_LastStep) { rxMsg[tmp_LastStep].eID.BitAll = tmp_eID.BitAll; rxMsg[tmp_LastStep].info.EXT = tmp_IDE; rxMsg[tmp_LastStep].info.RTR = tmp_RTR; rxMsg[tmp_LastStep].DLC = tmp_DLC; for(int i = 0; i < tmp_DLC; i++) { rxMsg[tmp_LastStep].Data[i] = tmp_DATA[i]; } LastStep = tmp_LastStep; } /** * @brief Обработка сообщений, ожидающих в FIFO, по прерыванию HAL_CAN_RxFifo0MsgPendingCallback. * Читает все сообщения из FIFO, проверяет их тип, обновляет статус устройств сети или сохраняет сообщение в буфер. * @param hcan Указатель на структуру управления CAN-Hardware. */ void ProtoCanRxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { //Обработка всех сообщений в FIFO CAN_RxHeaderTypeDef RxHeader; uint8_t RCAN_Data[8]; while(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RCAN_Data) == HAL_OK) { //Расширенный ID if(RxHeader.IDE == CAN_ID_EXT) { if(!((PROTOCAN_RX_BUFFER_SIZE + LastStep - (CurrentStep - 1)) & PROTOCAN_RX_BUFFER_SIZE)) { //Буффер переполнен return; } uint16_t tmp_LastStep = (uint16_t)(LastStep + 1) % PROTOCAN_RX_BUFFER_SIZE; ProtoCanId_t ExtID_Of_RX_MSG; ExtID_Of_RX_MSG.BitAll = RxHeader.ExtId; //Полученное сообщение - пульс устройств в сети if(ExtID_Of_RX_MSG.Fields.MsgType == PROTOCAN_MSGTYPE_PULSE) { Device_on_the_Network[ExtID_Of_RX_MSG.Fields.DeviceType][ExtID_Of_RX_MSG.Fields.DeviceID].Status = ONLINE; Device_on_the_Network[ExtID_Of_RX_MSG.Fields.DeviceType][ExtID_Of_RX_MSG.Fields.DeviceID].TimeFromLastPulse = 0; return; } TakeRxMsgToBuffer(ExtID_Of_RX_MSG, RxHeader.IDE, RxHeader.RTR, RxHeader.DLC, RCAN_Data, tmp_LastStep); } } } /** * @brief Функция отправки пульса устройства. * @details Пульс устройства. Есть возможность отключить пульс по запросу. */ void ProtoCanPulseCallback(TIM_HandleTypeDef *htim) { if(ControlFlags.IsPulse) { static unsigned PulseStage = 0; CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailBox = 0; ProtoCanId_t currentID; currentID.BitAll = 0; currentID.Fields.Priority = PROTOCAN_PRIORITY_STANDARD; currentID.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE; currentID.Fields.DeviceType = CURRENT_TYPE_DEVICE; currentID.Fields.DeviceID = CURRENT_ID_DEVICE; currentID.Fields.MsgType = PROTOCAN_MSGTYPE_PULSE; currentID.Fields.MsgBody = 0; TxHeader.ExtId = currentID.BitAll; uint8_t data[8]; TxHeader.IDE = CAN_ID_EXT; TxHeader.TransmitGlobalTime = DISABLE; TxHeader.RTR = CAN_RTR_DATA; TxHeader.DLC = 1; PulseStage++; unsigned int debugID; debugID = currentID.BitAll; if(PulseStage > 0xFF) { PulseStage = 0; } data[0] = PulseStage; HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox); } } /** * @brief Функция установки в RTC полученной даты/время из запроса. * @param uint8_t *data - Байтовый массив, 7 элементов. [0] - Часы. [1] - Минуты. [2] - Секунды. [3] - Год. [4] - Месяц. [5] - Дата. [6] - День недели. */ void PROTOCAN_RTC_SYNC(uint8_t *data) { __HAL_RTC_WRITEPROTECTION_DISABLE(_HRTC); RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef DateToUpdate = {0}; sTime.Hours = data[0]; sTime.Minutes = data[1]; sTime.Seconds = data[2]; if(HAL_RTC_SetTime(_HRTC, &sTime, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } DateToUpdate.Year = data[3]; DateToUpdate.Month = data[4]; DateToUpdate.Date = data[5]; DateToUpdate.WeekDay = data[6]; if(HAL_RTC_SetDate(_HRTC, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } __HAL_RTC_WRITEPROTECTION_ENABLE(_HRTC); } /** * @brief Настройка CAN-Фильтра. * На основе переданных ID и маски настраивает фильтр CAN для приема только определённых сообщений. * @param filterBank Номер фильтра (обычно 0..27). * @param idFilter 32-битный идентификатор, используемый для фильтрации. * @param idMask 32-битная маска, определяющая биты, проверяемые фильтром. */ void PROTOCAN_CONFIG_FILTER(uint8_t filterBank, uint32_t idFilter, uint32_t idMask) { CAN_FilterTypeDef canFilterConfig; canFilterConfig.FilterBank = filterBank; canFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; canFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; canFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; canFilterConfig.FilterActivation = ENABLE; canFilterConfig.SlaveStartFilterBank = 14; #define CAN_IDE_32 0b00000100 // Для 32-х битного масштаба // Разбиваем 32-битный ID и маску для фильтрации на High и Low 16 бит canFilterConfig.FilterIdHigh = (uint16_t)(((idFilter) >> 13)); // верхние 16 бит canFilterConfig.FilterIdLow = (uint16_t)((((idFilter) << 3)) | CAN_IDE_32); // низкие 16 бит, canFilterConfig.FilterMaskIdHigh = (uint16_t)(((idMask) >> 13)); canFilterConfig.FilterMaskIdLow = (uint16_t)((((idMask) << 3)) | CAN_IDE_32); if(HAL_CAN_ConfigFilter(_HCAN, &canFilterConfig) != HAL_OK) { Error_Handler(); } } /** * @brief Конфигурирует три фильтра CAN для различных типов сообщений. */ void PROTOCAN_FILTERS() { #define BIT27 (1UL << 27) #define BITS_20_23_MASK (0xFUL << 20) // 4 бита (20..23) #define BITS_24_26_MASK (0x7UL << 24) // 3 бита (24..26) uint32_t filter1_id = BIT27 | (CURRENT_TYPE_DEVICE << 24) | (CURRENT_ID_DEVICE << 20); uint32_t filter1_mask = BIT27 | BITS_24_26_MASK | BITS_20_23_MASK; // все проверяемые биты #define BITS_16_19_MASK (0xFUL << 16) uint32_t filter2_id = PROTOCAN_MSGTYPE_BROADCAST << 16; uint32_t filter2_mask = BITS_16_19_MASK; uint32_t filter3_id = PROTOCAN_MSGTYPE_PULSE << 16; uint32_t filter3_mask = BITS_16_19_MASK; // Первый фильтр: проверяем 20-27 биты, где 27-й всегда 1 PROTOCAN_CONFIG_FILTER(0, filter1_id, filter1_mask); // Второй фильтр: проверяем 16-19 биты, равны PROTOCAN_MSGTYPE_BROADCASTE PROTOCAN_CONFIG_FILTER(1, filter2_id, filter2_mask); // Третий фильтр: проверяем 16-19 биты, равны PROTOCAN_MSGTYPE_PULSE PROTOCAN_CONFIG_FILTER(2, filter3_id, filter3_mask); } /** * @brief Отправляет CAN-сообщение в зависимости от типа сообщения. * * Эта функция анализирует тип сообщения в идентификаторе и вызывает * соответствующую функцию отправки. В текущей реализации поддерживается только * тип PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE. * * @param id Идентификатор CAN-сообщения, содержащий информацию о типе. * @param data Структура с данными, которые необходимо отправить. Зависит от типа сообщения. * * @return Статус выполнения операции: * - PROTOCAN_OK при успешной отправке, * - PROTOCAN_ERROR, если тип сообщения не поддерживается или произошла ошибка. */ PROTOCAN_StatusTypeDef PROTOCAN_SEND(ProtoCanId_t id, ProtoCanData_t data) { switch (id.Fields.MsgType) { case PROTOCAN_MSGTYPE_BROADCAST: { break; } case PROTOCAN_MSGTYPE_DISCRETE: { break; } case PROTOCAN_MSGTYPE_ANALOG: { break; } case PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE: { /* Если есть регистры для отправки, указатель data должен быть валиден. */ if((data.GeneralAddressSpaceData.RegCount > 0U) && data.GeneralAddressSpaceData.Data == 0) { return PROTOCAN_ERROR; } return PROTOCAN_SEND_GENERAL_ADDRESS_SPACE(id, data.GeneralAddressSpaceData.RegStartAdr, data.GeneralAddressSpaceData.Data, data.GeneralAddressSpaceData.RegCount); } case PROTOCAN_MSGTYPE_MODBUS_COIL: case PROTOCAN_MSGTYPE_MODBUS_DISCRETE: case PROTOCAN_MSGTYPE_MODBUS_HOLDING: case PROTOCAN_MSGTYPE_MODBUS_INPUT: { break; } case PROTOCAN_MSGTYPE_ERROR: { break; } default: return PROTOCAN_ERROR; } return PROTOCAN_ERROR; }