Arduino_Modbus/rs_message.cpp

340 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
**************************************************************************
* @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; }