Files
UKSI_TEST/AllLibs/Modbus/Src/rs_message.c
2025-12-16 17:57:59 +03:00

564 lines
21 KiB
C
Raw Permalink 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
Модуль реализует асинхронный обмен сообщениями через UART с использованием:
- Прерываний по приему/передаче
- Детектирования конца фрейма по IDLE линии
- Таймаутов через таймер
- Двухстадийного приема (заголовок + данные)
@section arch Архитектура:
В режиме слейв:
- Инициализация приема с сообщения с максимальным размером RS_MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного сообщения
В режиме мастер (пока не реализовано):
- Отправка запроса и переход в режим приема сообщения с максимальным размером RS_MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного ответа
@section ithandler Необходимые обработчики:
- RS_UART_Handler() в UARTx_IRQHandler вместо HAL_UART_IRQHandler()
- RS_TIM_Handler() в TIMx_IRQHandler вместо HAL_TIM_IRQHandler()
******************************************************************************/
#include "rs_message.h"
#include "modbus_diag.h"
uint8_t RS_Buffer[RS_MSG_SIZE_MAX]; // uart buffer
extern void RS_UART_Init(void);
extern void RS_UART_DeInit(UART_HandleTypeDef *huart);
extern void RS_TIM_Init(void);
extern void RS_TIM_DeInit(TIM_HandleTypeDef *htim);
//-------------------------------------------------------------------
//-------------------------GENERAL FUNCTIONS-------------------------
/**
* @brief Начать прием по прерываниям.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации приема.
*/
RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
RS_StatusTypeDef RS_RES = 0;
HAL_StatusTypeDef uart_res = 0;
//-------------CHECK RS LINE----------------
// check that receive isnt busy
if( RS_Is_RX_Busy(hRS) ) // if tx busy - return busy status
return RS_BUSY;
//-----------INITIALIZE RECEIVE-------------
// if all OK: start receiving
RS_EnableReceive();
RS_Set_Busy(hRS); // set RS busy
RS_Set_RX_Flags(hRS); // initialize flags for receive
hRS->pMessagePtr = RS_msg; // set pointer to message structire for filling it from UARTHandler fucntions
if(!hRS->f.RX_Continue) // if not continue receiving
hRS->RS_Message_Size = 0; // set ptr to start buffer
// start receiving
__HAL_UART_ENABLE_IT(hRS->huart, UART_IT_IDLE);
uart_res = HAL_UART_Receive_IT(hRS->huart, &hRS->pBufferPtr[hRS->RS_Message_Size], RS_MSG_SIZE_MAX); // receive until ByteCnt+1 byte,
// then in Callback restart receive for rest bytes
// if receive isnt started - abort RS
if(uart_res != HAL_OK)
{
RS_RES = RS_Abort(hRS, ABORT_RS);
printf_rs_err("Failed to start RS receiving...");
TrackerCnt_Err(hRS->rs_err);
}
else
{
RS_RES = RS_OK;
printf_rs("Start Receiving...");
TrackerCnt_Ok(hRS->rs_err);
}
hRS->RS_STATUS = RS_RES;
return RS_RES; // returns result of receive init
}
/**
* @brief Начать передачу по прерываниям.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации передачи.
*/
RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
RS_StatusTypeDef RS_RES = 0;
HAL_StatusTypeDef uart_res = 0;
//-------------CHECK RS LINE----------------
// check that transmit isnt busy
if( RS_Is_TX_Busy(hRS) ) // if tx busy - return busy status
return RS_BUSY;
// check receive line
//------------COLLECT MESSAGE---------------
RS_RES = RS_Collect_Message(hRS, RS_msg, hRS->pBufferPtr);
if (RS_RES != RS_OK) // if message isnt collect - stop RS and return error in RS_RES
{// need collect message status, so doesnt write abort to RS_RES
RS_Abort(hRS, ABORT_RS);
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); // restart receive
}
else // if collect successful
{
//----------INITIALIZE TRANSMIT-------------
RS_EnableTransmit();
RS_Set_Busy(hRS); // set RS busy
RS_Set_TX_Flags(hRS); // initialize flags for transmit IT
hRS->pMessagePtr = RS_msg; // set pointer for filling given structure from UARTHandler fucntion
if(hRS->RS_Message_Size <= 0)
{
RS_RES = RS_Abort(hRS, ABORT_RS);
TrackerCnt_Err(hRS->rs_err);
return RS_ERR;
}
// if all OK: start transmitting
uart_res = HAL_UART_Transmit_IT(hRS->huart, hRS->pBufferPtr, hRS->RS_Message_Size);
// if transmit isnt started - abort RS
if(uart_res != HAL_OK)
{
RS_RES = RS_Abort(hRS, ABORT_RS);
printf_rs_err("Failed to start RS transmitting...");
TrackerCnt_Err(hRS->rs_err);
}
else
{
RS_RES = RS_OK;
printf_rs("Start Transmitting...");
TrackerCnt_Ok(hRS->rs_err);
}
}
hRS->RS_STATUS = RS_RES;
return RS_RES; // returns result of transmit init
}
/**
* @brief Инициалазация структуры @ref RS_HandleTypeDef.
* @param hRS Указатель на хендлер RS.
* @param suart Указатель на структуру с настройками UART.
* @param stim Указатель на структуру с настройками таймера.
* @param pRS_BufferPtr Указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер.
* @return RS_RES Статус о состоянии RS после инициализации.
* @details Инициализация перефирии и структуры для приема-передачи по RS.
*/
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr)
{
// check that hRS is defined
if (hRS == NULL)
return RS_ERR;
// check that huart is defined
if (huart == NULL)
return RS_ERR;
hRS->huart = huart;
hRS->htim = htim;
// check that buffer is defined
if (hRS->pBufferPtr == NULL)
{
hRS->pBufferPtr = RS_Buffer; // if no - set default
}
else
hRS->pBufferPtr = pRS_BufferPtr; // if yes - set by user
return RS_OK;
}
/**
* @brief Отменить прием/передачу RS/UART.
* @param hRS Указатель на хендлер RS.
* @param AbortMode Выбор, что надо отменить.
- ABORT_TX: Отмена передачи по ЮАРТ, с очищением флагов TX,
- ABORT_RX: Отмена приема по ЮАРТ, с очищением флагов RX,
- ABORT_RX_TX: Отмена приема и передачи по ЮАРТ,
- ABORT_RS: Отмена приема-передачи RS, с очищением всей структуры.
* @return RS_RES Статус о состоянии RS после аборта.
* @details Отмена работы UART в целом или отмена приема/передачи RS.
Также очищается хендл hRS.
*/
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode)
{
HAL_StatusTypeDef uart_res = 0;
RS_Timeout_Stop(hRS);
if((AbortMode&ABORT_RS) == 0x00)
{
if((AbortMode&ABORT_RX) == ABORT_RX)
{
uart_res = HAL_UART_AbortReceive(hRS->huart); // abort receive
RS_Reset_RX_Flags(hRS);
}
if((AbortMode&ABORT_TX) == ABORT_TX)
{
uart_res = HAL_UART_AbortTransmit(hRS->huart); // abort transmit
RS_Reset_TX_Flags(hRS);
}
}
else
{
uart_res = HAL_UART_Abort(hRS->huart);
RS_Clear_All(hRS);
}
hRS->RS_STATUS = RS_ABORTED;
return RS_ABORTED;
}
//-------------------------GENERAL FUNCTIONS-------------------------
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
/**
* @brief Обработчик для начала приема.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации приема или окончания общения.
* @details Определяет начинать прием команды/ответа или нет.
*/
RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
RS_StatusTypeDef RS_RES = 0;
switch(hRS->sRS_Mode)
{
// В режиме мастер
case RS_MASTER_REQUEST:
RS_Timeout_Start(hRS); // сразу запускаем таймаут и начинаем прием
// В режиме слейв
case RS_SLAVE_ALWAYS_WAIT:
RS_RES = RS_Receive_IT(hRS, RS_msg); // Просто запускаем фоновый прием
break;
case RS_RESERVED:
RS_Set_Free(hRS); RS_RES = RS_OK; break; // end RS communication (set RS unbusy)
}
if(RS_RES != RS_OK)
{
TrackerCnt_Err(hRS->rs_err);
}
return RS_RES;
}
/**
* @brief Обработчик для начала передачи.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации передачи.
* @details Определяет отвечать ли на команду или нет.
*/
RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
RS_StatusTypeDef RS_RES = 0;
switch(hRS->sRS_Mode)
{
case RS_SLAVE_ALWAYS_WAIT: // in slave mode always response
case RS_RESERVED: // transmit response
case RS_MASTER_REQUEST: // transmit response
RS_RES = RS_Transmit_IT(hRS, RS_msg); break;
}
if(RS_RES != RS_OK)
{
if(hRS->sRS_Mode < RS_MASTER_MODE_START)
{
RS_Handle_Receive_Start(hRS, RS_msg);
}
TrackerCnt_Err(hRS->rs_err);
}
return RS_RES;
}
/**
* @brief UART TX Callback: коллбек после окончания передачи.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус о состоянии RS после обработки приема.
* @details Определяет поведение 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);
//-----------START RECEIVING or END RS----------
RS_RES = RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
return RS_RES;
}
/**
* @brief Обработчик прерывания UART.
* @param hRS Указатель на хендлер RS.
* @details Обрабатывает ошибки если есть и вызывает RS Коллбеки.
* Добавить вызов этой функции в UARTx_IRQHandler() ВМЕСТО HAL_UART_IRQHandler().
*/
void RS_UART_Handler(RS_HandleTypeDef *hRS)
{
if(hRS->huart == NULL)
{
return;
}
RS_UART_Handler_ENTER();
//-------------CHECK IDLE FLAG FIRST-------------
/* Проверяем флаг IDLE в первую очередь - это гарантирует обработку только после idle */
if(__HAL_UART_GET_FLAG(hRS->huart, UART_FLAG_IDLE) && __HAL_UART_GET_IT_SOURCE(hRS->huart, UART_IT_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(hRS->huart); // Важно: очистить флаг IDLE
//-------------STANDARD UART HANDLING-------------
HAL_UART_IRQHandler(hRS->huart);
hRS->f.RX_Continue = 0;
// Если прием активен и мы получили IDLE - это конец фрейма
if(RS_Is_RX_Busy(hRS) && hRS->f.RX_Ongoing)
{
// Получаем количество фактически принятых байтов
hRS->RS_Message_Size += hRS->huart->RxXferSize - hRS->huart->RxXferCount;
if(hRS->RS_Message_Size > 0)
{
// Принудительно завершаем прием (получили сообщение)
HAL_UART_AbortReceive(hRS->huart); // abort receive
// Завершаем прием в нашей структуре
RS_Set_RX_End(hRS);
// Парсим наше сообщение
hRS->RS_STATUS = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
// Если сообещине принято корректно
if(hRS->RS_STATUS == RS_OK)
{
RS_Timeout_Stop(hRS);
hRS->lastPacketTick = local_time();
if(hRS->sRS_Mode < RS_MASTER_MODE_START)
{
RS_Response(hRS, hRS->pMessagePtr); // отвечаем на запрос
}
else
{
RS_Set_Free(hRS); // освобожднаем RS
if(hRS->pCallback)
{
hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
}
}
}
else
{
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); // если сообщение пришло не полностью - продолжаем прием до таймаута
}
}
}
return; // Выходим после обработки IDLE
}
else
{
//-------------STANDARD UART HANDLING-------------
HAL_UART_IRQHandler(hRS->huart);
}
//-------------CALL RS CALLBACKS------------
/* IF NO ERROR OCCURS */
if(hRS->huart->ErrorCode == 0)
{
// if first byte is received and receive is active
if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS))
{
RS_Timeout_Start(hRS);
}
RS_Timeout_Update(hRS);
/* RX Callback - теперь НЕ вызываем здесь, ждем IDLE */
/* TX Callback */
if ((hRS->huart->TxXferCount == 0U) && RS_Is_TX_Busy(hRS) && // if all bytes are transmited and transmit is active
hRS->huart->gState != HAL_UART_STATE_BUSY_TX) // also check that receive "REALLY" isnt busy
RS_UART_TxCpltCallback(hRS);
/* NOTE: RX Callback больше не вызывается здесь - ждем IDLE для гарантии конца фрейма */
}
//----------------ERRORS HANDLER----------------
else
{
if (hRS->huart->ErrorCode & HAL_UART_ERROR_ORE)
{
MB_Diagnostics_CharacterOverrunCnt(); // <-- Обнаружено переполнение
}
//TrackerCnt_Err(hRS->rs_err);
/* de-init uart transfer */
RS_Abort(hRS, ABORT_RS);
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
// later, maybe, will be added specific handlers for err
}
RS_UART_Handler_EXIT();
}
/**
* @brief Обработчик прерывания TIM.
* @param hRS Указатель на хендлер RS.
* @details Попадание сюда = таймаут и перезапуск RS приема
* Добавить вызов этой функции в TIMx_IRQHandler() ВМЕСТО HAL_TIM_IRQHandler().
*/
void RS_TIM_Handler(RS_HandleTypeDef *hRS)
{
if(hRS->htim == NULL)
{
return;
}
RS_TIM_Handler_ENTER();
HAL_TIM_IRQHandler(hRS->htim);
RS_Abort(hRS, ABORT_RS);
hRS->RS_STATUS = RS_TIMEOUT;
if(hRS->sRS_Mode < RS_MASTER_MODE_START)
if(hRS->pMessagePtr->DeviceAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
TrackerCnt_Err(hRS->rs_err);
if(hRS->sRS_Mode >= RS_MASTER_MODE_START)
{ // Мастер: коллбек и освобождение для нового запроса
RS_Set_Free(hRS);
if(hRS->pCallback)
{
hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
}
} else {
// Слейв: перезапускаем прием
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
}
RS_TIM_Handler_EXIT();
}
/**
* @brief Запуск таймаута приема.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус операции.
* @details Запускает таймер для отсчета времени ожидания следующего байта.
*/
RS_StatusTypeDef RS_Timeout_Start(RS_HandleTypeDef *hRS)
{
if(hRS->htim)
{
hRS->htim->Instance->CNT = 0; // reset cnt;
if(hRS->sRS_Timeout) // if timeout setted
{
hRS->htim->Instance->ARR = hRS->sRS_Timeout;
HAL_TIM_Base_Start_IT(hRS->htim);
RS_Set_RX_Active_Flags(hRS);
}
}
return RS_OK;
}
/**
* @brief Остановка таймаута приема.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус операции.
* @details Останавливает таймер ожидания.
*/
RS_StatusTypeDef RS_Timeout_Stop(RS_HandleTypeDef *hRS)
{
if(hRS->htim)
{
// Останавливаем таймаут
if(hRS->sRS_Timeout)
HAL_TIM_Base_Stop_IT(hRS->htim);
hRS->htim->Instance->CNT = 0;
__HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE);
}
return RS_OK;
}
/**
* @brief Обновление (сброс) таймаута приема.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус операции.
* @details Сбрасывает счетчик таймера в 0.
*/
RS_StatusTypeDef RS_Timeout_Update(RS_HandleTypeDef *hRS)
{
if(hRS->htim)
{
hRS->htim->Instance->CNT = 0; // reset cnt;
}
return RS_OK;
}
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
/**
* @brief Пользовательская функция для ответа на запрос по UART.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о результате ответа на комманду.
*/
__weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
/* Redefine function for user purposes */
return RS_ERR;
}
/**
* @brief Пользовательская функция для сбора сообщения в буфер UART.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @param msg_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения буфера.
*/
__weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{
/* Redefine function for user purposes */
return RS_ERR;
}
/**
* @brief Пользовательская функция для парса сообщения из буфера UART.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @param msg_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения структуры.
*/
__weak RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{
/* Redefine function for user purposes */
return RS_ERR;
}
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
//-------------------------------------------------------------------