/** ************************************************************************** * @file rs_message.c * @brief Модуль для реализации протоколов по RS/UART. **************************************************************************\ * @details * Данный модуль реализует основные функции для приема и передачи сообщений * по протоколу RS через UART в режиме прерываний. Реализована обработка * приема и передачи данных, управление состояниями RS, а также функции для * инициализации и управления периферией. * * Реализованы следующие функции: * - RS_Receive_IT() — запуск приема данных в прерывании по UART. * - RS_Transmit_IT() — запуск передачи данных в прерывании по UART. * - RS_Init() — инициализация структуры RS и привязка периферии. * - RS_ReInit_UART() — переинициализация UART и перезапуск приема данных. * - RS_Abort() — остановка работы RS/UART с очисткой флагов и структур. * - RS_Handle_Receive_Start() — обработка старта приема данных по RS. * * В модуле также определен буфер RS_Buffer[] для хранения принимаемых/передаваемых данных. * * @note * Для корректной работы модуля предполагается использование соответствующих * обработчиков прерываний UART и таймера (RS_UART_Handler(), RS_TIM_Handler()), * которые надо вызывать с обработчиках используемой периферии @verbatim //-------------------Функции-------------------// Functions: users - RS_Parse_Message/RS_Collect_Message Заполнение структуры сообщения и буфера - RS_Response Ответ на сообщение - RS_Define_Size_of_RX_Message Определение размера принимаемых данных Functions: general - RS_Receive_IT Ожидание комманды и ответ на неё - RS_Transmit_IT Отправление комманды и ожидание ответа - RS_Init Инициализация переферии и структуры для RS - RS_ReInit_UART Реинициализация UART для RS - RS_Abort Отмена приема/передачи по ЮАРТ - RS_Init Инициализация периферии и modbus handler Functions: callback/handler - RS_Handle_Receive_Start Функция для запуска приема или остановки RS - RS_Handle_Transmit_Start Функция для запуска передачи или остановки RS - RS_UART_RxCpltCallback Коллбек при окончании приема или передачи RS_UART_TxCpltCallback - RS_UART_Handler Обработчик прерывания для UART - RS_TIM_Handler Обработчик прерывания для TIM @endverbatim *************************************************************************/ #include "rs_message.h" uint8_t RS_Buffer[MSG_SIZE_MAX]; // uart buffer //------------------------------------------------------------------- //-------------------------GENERAL FUNCTIONS------------------------- /** * @brief Start receive IT. * @param hRS - указатель на хендлер RS. * @param RS_msg - указатель на структуру сообщения. * @return RS_RES - статус о состоянии RS после инициализации приема. */ RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) { if (hRS->f.RS_Busy || hRS->f.RX_Busy) return RS_BUSY; RS_EnableReceive(); RS_Set_Busy(hRS); RS_Set_RX_Flags(hRS); hRS->pMessagePtr = RS_msg; hRS->lastByteTime = millis(); hRS->RS_STATUS = RS_OK; return RS_OK; } /** * @brief Start transmit IT. * @param hRS - указатель на хендлер RS. * @param RS_msg - указатель на структуру сообщения. * @return RS_RES - статус о состоянии RS после инициализации передачи. */ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) { if (hRS->f.RS_Busy || hRS->f.TX_Busy) return RS_BUSY; RS_StatusTypeDef RS_RES = RS_Collect_Message(hRS, RS_msg, hRS->pBufferPtr); if (RS_RES != RS_OK) { RS_Abort(hRS, ABORT_RS); RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); return RS_RES; } RS_EnableTransmit(); RS_Set_Busy(hRS); RS_Set_TX_Flags(hRS); hRS->pMessagePtr = RS_msg; hRS->huart->write(hRS->pBufferPtr, hRS->RS_Message_Size); hRS->lastByteTime = millis(); return RS_OK; } /** * @brief Initialize UART and handle RS stucture. * @param hRS - указатель на хендлер RS. * @param suart - указатель на структуру с настройками UART. * @param stim - указатель на структуру с настройками таймера. * @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) 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 = millis(); 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 Отмена работы UART в целом или отмена приема/передачи RS. Также очищается хендл hRS. */ RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode) { if ((AbortMode & ABORT_RS) == 0) { if ((AbortMode & ABORT_RX) == ABORT_RX) RS_Reset_RX_Flags(hRS); if ((AbortMode & ABORT_TX) == ABORT_TX) RS_Reset_TX_Flags(hRS); } else { RS_Clear_All(hRS); } RS_Set_Free(hRS); hRS->RS_STATUS = RS_ABORTED; return RS_ABORTED; } //-------------------------GENERAL FUNCTIONS------------------------- //------------------------------------------------------------------- //------------------------------------------------------------------- //--------------------CALLBACK/HANDLER FUNCTIONS--------------------- /** * @brief Handle for starting receive. * @param hRS - указатель на хендлер RS. * @param RS_msg - указатель на структуру сообщения. * @return RS_RES - статус о состоянии RS после инициализации приема или окончания общения. * @note Определяет начинать прием команды/ответа или нет. */ RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) { RS_StatusTypeDef RS_RES = RS_OK; switch(hRS->sRS_Mode) { case SLAVE_ALWAYS_WAIT: // in slave mode with permanent waiting RS_RES = RS_Receive_IT(hRS, RS_msg); break; // start receiving again case SLAVE_TIMEOUT_WAIT: // in slave mode with timeout waiting (start receiving cmd by request) RS_Set_Free(hRS); RS_RES = RS_OK; break; // end RS communication (set RS unbusy) } if(RS_RES != RS_OK) { } return RS_RES; } /** * @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) { RS_StatusTypeDef RS_RES = RS_OK; switch(hRS->sRS_Mode) { case SLAVE_ALWAYS_WAIT: // in slave mode always response case SLAVE_TIMEOUT_WAIT: // transmit response RS_RES = RS_Transmit_IT(hRS, RS_msg); break; } if(RS_RES != RS_OK) { } return RS_RES; } /** * @brief UART RX Callback: define behaviour after receiving parts of message. * @param hRS - указатель на хендлер RS. * @return RS_RES - статус о состоянии RS после обработки приема. * @note Контролирует прием сообщения: определяет размер принимаемой посылки и обрабатывает его. */ RS_StatusTypeDef RS_UART_RxCpltCallback(RS_HandleTypeDef *hRS) { if (!hRS->f.RX_Half && hRS->sRS_RX_Size_Mode == RS_RX_Size_NotConst) { hRS->f.RX_Half = 1; uint32_t restSize = 0xFFFF; RS_StatusTypeDef res = RS_Define_Size_of_RX_Message(hRS, &restSize); if (res == RS_SKIP || restSize == 0xFFFF) { RS_Abort(hRS, ABORT_RX); return RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); } // if there is no bytes to receive if(NuRS_of_Rest_Bytes == 0) { hRS->f.RX_Half = 0; //---------PROCESS DATA & ENDING RECEIVING-------- RS_Set_RX_End(hRS); // parse received data RS_RES = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr); // parse message hRS->RS_Message_Size = NuRS_of_Rest_Bytes; // RESPONSE RS_RES = RS_Response(hRS, hRS->pMessagePtr); return RS_RES; } } else // if we had received whole message { hRS->f.RX_Half = 0; //---------PROCESS DATA & ENDING RECEIVING-------- RS_Set_RX_End(hRS); // parse received data RS_RES = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr); // parse message // RESPONSE RS_RES = RS_Response(hRS, hRS->pMessagePtr); } return RS_RES; } /** * @brief UART TX Callback: define behaviour after transmiting message. * @param hRS - указатель на хендлер RS. * @return RS_RES - статус о состоянии RS после обработки приема. * @note Определяет поведение RS после передачи сообщения. */ RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS) { RS_StatusTypeDef RS_RES = RS_OK; HAL_StatusTypeDef uart_res = 0; //--------------ENDING TRANSMITTING------------- RS_Set_TX_End(hRS); RS_EnableReceive(); // for(int i = 0; i < hRS->sRS_Timeout; i++); //-----------START RECEIVING or END RS---------- RS_RES = RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); return RS_RES; } /** * @brief Handler for UART. * @param hRS - указатель на хендлер RS. * @note Обрабатывает ошибки если есть и вызывает RS Коллбеки. * Добавить вызов этой функции в UARTx_IRQHandler() после HAL_UART_IRQHandler(). */ void RS_UART_Handler(RS_HandleTypeDef *hRS) { while (hRS->huart->available()) { uint8_t b = hRS->huart->read(); hRS->pBufferPtr[hRS->RS_Message_Size++] = b; hRS->lastByteTime = millis(); if (hRS->f.RX_Busy && (hRS->RS_Message_Size >= RX_FIRST_PART_SIZE) && !hRS->f.RX_Half) RS_UART_RxCpltCallback(hRS); } } /** * @brief Handler for TIM (timeout check). * @param hRS - указатель на хендлер RS. * @note Проверяет таймаут между байтами. Если превышен, сбрасывает RX и перезапускает прием. * Вызывать в TIMx_IRQHandler() после HAL_TIM_IRQHandler(). */ void RS_TIM_Handler(RS_HandleTypeDef *hRS) { if (!hRS) return; unsigned long now = millis(); // Если идет прием данных и есть таймаут if (hRS->f.RX_Busy && hRS->sRS_Timeout > 0) { if ((now - hRS->lastByteTime) >= hRS->sRS_Timeout) { // таймаут истек → abort RX и restart RS_Abort(hRS, ABORT_RX); RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); } } // Можно также проверять TX, если нужна логика таймаута передачи if (hRS->f.TX_Busy && hRS->sRS_Timeout > 0) { if ((now - hRS->lastByteTime) >= hRS->sRS_Timeout) { RS_Abort(hRS, ABORT_TX); // TX не рестартим автоматически } } } // weak functions __attribute__((weak)) RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) { return RS_ERR; } __attribute__((weak)) RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff) { return RS_ERR; } __attribute__((weak)) RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff) { return RS_ERR; } __attribute__((weak)) RS_StatusTypeDef RS_Define_Size_of_RX_Message(RS_HandleTypeDef *hRS, uint32_t *rx_data_size) { return RS_ERR; }