/** ************************************************************************** * @file rs_message.cpp * @brief Модуль для реализации протоколов по RS/UART (Arduino). **************************************************************************\ * @details * Данный модуль реализует основные функции для приема и передачи сообщений * по протоколу RS через UART. Реализована обработка приёма и передачи данных, * управление состояниями RS, а также функции для инициализации и управления * периферией Arduino Serial. * * Реализованы следующие функции: * - RS_Init() — инициализация структуры RS и привязка к Serial-порту. * - RS_Abort() — остановка работы RS/UART с очисткой флагов и структур. * - RS_Handle_Transmit_Start() — запуск передачи данных. * - RS_Process() — обработка входящих данных (вызов в loop()). * * В модуле также определён буфер RS_Buffer[] для хранения принимаемых/передаваемых данных. * * Пользователь должен определить функции: * - RS_Response() — формирование ответа на принятое сообщение. * - RS_Collect_Message() — подготовка сообщения для передачи. * - RS_Parse_Message() — разбор принятого сообщения. * - RS_Define_Size_of_RX_Message() — определение размера входящих данных. * * @note * Для корректной работы требуется вызывать RS_Process() в основном цикле программы. * UART используется через стандартный Arduino Stream API (write(), read(), available()). * @verbatim //-------------------Функции-------------------// Functions: users - RS_Parse_Message Разбор принятого сообщения - RS_Collect_Message Заполнение структуры сообщения и буфера - RS_Response Ответ на сообщение - RS_Define_Size_of_RX_Message Определение размера принимаемых данных Functions: general - RS_Init Инициализация структуры RS и привязка Serial - RS_Abort Остановка приёма/передачи - RS_Handle_Transmit_Start Запуск передачи сообщения - RS_Process Обработка приёма сообщений (в loop) @endverbatim *************************************************************************/ #include "rs_message.h" uint8_t RS_Buffer[MSG_SIZE_MAX]; // uart buffer //------------------------------------------------------------------- //-------------------------GENERAL FUNCTIONS------------------------- /** * @brief Initialize UART and handle RS stucture. * @param hRS - указатель на хендлер RS. * @param SerialPort - указатель на структуру с настройками UART. * @param pRS_BufferPtr - указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер. * @return RS_RES - статус о состоянии RS после инициализации. * @note Инициализация перефирии и структуры для приема-передачи по RS. */ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, HUART_TypeDef *SerialPort, uint8_t *pRS_BufferPtr) { if (!hRS || !SerialPort) { RS_DEBUG_PRINT("[RS] Init error: null handler or port"); return RS_ERR; } hRS->huart = SerialPort; hRS->pBufferPtr = pRS_BufferPtr ? pRS_BufferPtr : RS_Buffer; hRS->RS_STATUS = RS_OK; RS_Set_Free(hRS); RS_Reset_RX_Flags(hRS); RS_Reset_TX_Flags(hRS); hRS->f.RX_Half = 0; hRS->lastByteTime = 0; RS_EnableReceive(); RS_Set_Busy(hRS); RS_Set_RX_Flags(hRS); RS_DEBUG_PRINT("[RS] Initialized successfully"); return RS_OK; } /** * @brief Abort RS/UART. * @param hRS - указатель на хендлер RS. * @param AbortMode - выбор, что надо отменить. - ABORT_TX: Отмена передачи по ЮАРТ, с очищением флагов TX, - ABORT_RX: Отмена приема по ЮАРТ, с очищением флагов RX, - ABORT_RX_TX: Отмена приема и передачи по ЮАРТ, - ABORT_RS: Отмена приема-передачи RS, с очищением всей структуры. * @return RS_RES - статус о состоянии RS после аборта. * @note В Arduino аппаратный UART (Serial) не отключается физически, * т.к. стандартный API HardwareSerial не даёт прямого доступа * к регистрах UART. RS_Abort лишь очищает внутренние флаги * и структуры, имитируя "отключение". */ RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode) { if (hRS == nullptr) { RS_DEBUG_PRINT("[RS] Abort error: null handler"); return RS_ERR; } if ((AbortMode & ABORT_RS) == 0) { if ((AbortMode & ABORT_RX) == ABORT_RX) { RS_DEBUG_PRINT("[RS] Abort RX"); RS_Reset_RX_Flags(hRS); } if ((AbortMode & ABORT_TX) == ABORT_TX) { RS_DEBUG_PRINT("[RS] Abort TX"); RS_Reset_TX_Flags(hRS); } } else { RS_DEBUG_PRINT("[RS] Abort RS (full reset)"); RS_Clear_All(hRS); } RS_Set_Free(hRS); hRS->RS_STATUS = RS_ABORTED; return RS_ABORTED; } /** * @brief Handle for starting transmit. * @param hRS - указатель на хендлер RS. * @param RS_msg - указатель на структуру сообщения. * @return RS_RES - статус о состоянии RS после инициализации передачи. * @note Определяет отвечать ли на команду или нет. */ RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) { if (hRS == nullptr || RS_msg == nullptr) { RS_DEBUG_PRINT("[RS] TX start error: null ptr"); return RS_ERR; } if (RS_Is_TX_Busy(hRS)) { RS_DEBUG_PRINT("[RS] TX busy, cannot start"); return RS_BUSY; } RS_StatusTypeDef status = RS_Collect_Message(hRS, RS_msg, hRS->pBufferPtr); if (status != RS_OK) { RS_DEBUG_PRINT("[RS] TX collect message failed"); return status; } RS_DEBUG_PRINT2_DEC("[RS] TX start, size=", hRS->RS_Message_Size); RS_EnableTransmit(); RS_Set_TX_Flags(hRS); hRS->huart->write(hRS->pBufferPtr, hRS->RS_Message_Size); RS_Set_TX_End(hRS); RS_Set_RX_Flags(hRS); RS_Set_RX_Flags(hRS); RS_DEBUG_PRINT("[RS] TX finished"); return RS_OK; } /** * @brief Main RS processing function. * @param hRS - указатель на хендлер RS. * @return None. * @note Функция должна вызываться в основном цикле (loop()). * Выполняет проверку таймаутов, обработку поступающих байт из UART * и вызов пользовательских функций: * - RS_Define_Size_of_RX_Message() для определения размера пакета; * - RS_Parse_Message() для разбора принятого сообщения; * - RS_Response() для ответа, если сообщение адресовано текущему устройству. * В случае таймаута выполняется RS_Abort() с режимом ABORT_RX. */ void RS_Process(RS_HandleTypeDef *hRS) { // Локальные статические переменные для индекса приёма и ожидаемого размера пакета static uint32_t rx_index = 0; static uint32_t expected_size = RX_FIRST_PART_SIZE; if (hRS == nullptr) { RS_DEBUG_PRINT("[RS] Process error: null handler"); return; } // Проверка таймаута при активном приёме if (hRS->f.RX_Ongoing) { if (millis() - hRS->lastByteTime > hRS->sRS_Timeout) { RS_DEBUG_PRINT("[RS] RX timeout"); RS_Abort(hRS, ABORT_RX); return; } } // Проверка наличия данных в UART if (hRS->huart->available()) { // Если приём ещё не активен — начинаем новый пакет if (!hRS->f.RX_Ongoing) { RS_DEBUG_PRINT("[RS] RX start"); RS_Set_RX_Active_Flags(hRS); rx_index = 0; expected_size = RX_FIRST_PART_SIZE; RS_Clear_Buff(hRS->pBufferPtr); } // Обновляем время получения последнего байта hRS->lastByteTime = millis(); // Считываем доступные байты из UART while (hRS->huart->available() && rx_index < MSG_SIZE_MAX) { uint8_t b = hRS->huart->read(); hRS->pBufferPtr[rx_index++] = b; RS_DEBUG_PRINT2_HEX("[RS] RX byte: 0x", b); // Если достигнут размер первой части пакета — определяем полный размер if (rx_index == RX_FIRST_PART_SIZE && (hRS->f.RX_Half == 0) && hRS->sRS_RX_Size_Mode == RS_RX_Size_NotConst) { hRS->f.RX_Half = 1; uint32_t data_size; RS_DEBUG_PRINT("[RS] Defining size..."); RS_Define_Size_of_RX_Message(hRS, &data_size); expected_size = RX_FIRST_PART_SIZE + data_size; RS_DEBUG_PRINT2_DEC("[RS] RX expected size=", expected_size); } // Если пакет полностью получен if (rx_index >= expected_size) { hRS->f.RX_Half = 0; RS_Set_RX_End(hRS); RS_DEBUG_PRINT("[RS] RX complete, parsing..."); // Разбираем сообщение RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr); // Если адрес совпадает — отвечаем if (hRS->pMessagePtr->MbAddr == hRS->ID) { RS_DEBUG_PRINT("[RS] RX for me, sending response"); RS_Response(hRS, hRS->pMessagePtr); } else { RS_DEBUG_PRINT2_DEC("[RS] RX not for me, Addr=", hRS->pMessagePtr->MbAddr); } break; } } } } //------------------------------------------------------------------- //--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE--------------- /** * @brief Respond accord to received message. * @param hRS - указатель на хендлер RS. * @param RS_msg - указатель на структуру сообщения. * @return RS_RES - статус о результате ответа на комманду. * @note Обработка принятой комманды и ответ на неё. */ __attribute__((weak)) RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) { return RS_ERR; } /** * @brief Collect message in buffer to transmit it. * @param hRS - указатель на хендлер RS. * @param RS_msg - указатель на структуру сообщения. * @param msg_uart_buff - указатель на буффер UART. * @return RS_RES - статус о результате заполнения буфера. * @note Заполнение буффера UART из структуры сообщения. */ __attribute__((weak)) RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff) { return RS_ERR; } /** * @brief Parse message from buffer to process it. * @param hRS - указатель на хендлер RS. * @param RS_msg - указатель на структуру сообщения. * @param msg_uart_buff - указатель на буффер UART. * @return RS_RES - статус о результате заполнения структуры. * @note Заполнение структуры сообщения из буффера UART. */ __attribute__((weak)) RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff) { return RS_ERR; } /** * @brief Define size of RX Message that need to be received. * @param hRS - указатель на хендлер RS. * @param rx_data_size - указатель на переменную для записи кол-ва байт для принятия. * @return RS_RES - статус о корректности рассчета кол-ва байт для принятия. * @note Определение сколько байтов надо принять по протоколу. */ __attribute__((weak)) RS_StatusTypeDef RS_Define_Size_of_RX_Message(RS_HandleTypeDef *hRS, uint32_t *rx_data_size) { return RS_ERR; }