Files
STM32_Modbus/Src/rs_message.c
Razvalyaev bd34ace028 release 0.2
Добавлен модуль диагностически модбас (функция 0x08)

+ мелкие кореркции
2025-11-04 13:05:52 +03:00

502 lines
19 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
Модуль реализует асинхронный обмен сообщениями через UART с использованием:
- Прерываний по приему/передаче
- Детектирования конца фрейма по IDLE линии
- Таймаутов через таймер
- Двухстадийного приема (заголовок + данные)
@section Архитектура:
В режиме слейв:
- Инициализация приема с сообщения с максимальным размером MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного сообщения
В режиме мастер (пока не реализовано):
- Отправка запроса и переход в режим приема сообщения с максимальным размером MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного ответа
@section Необходимые обработчики:
- 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[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 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)
{
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], 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 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)
{
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();
// for(int i = 0; i < hRS->sRS_Timeout; i++);
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 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, 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;
if (hRS->sRS_RX_Size_Mode == NULL)
return RS_ERR;
// 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 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)
{
HAL_StatusTypeDef uart_res = 0;
if(hRS->htim)
{
if(hRS->sRS_Timeout) // if timeout setted
HAL_TIM_Base_Stop_IT(hRS->htim); // stop timeout
hRS->htim->Instance->CNT = 0;
__HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE);
}
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 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 = 0;
switch(hRS->sRS_Mode)
{
case RS_SLAVE_ALWAYS_WAIT: // in slave mode with permanent waiting
RS_RES = RS_Receive_IT(hRS, RS_msg); break; // start receiving again
case RS_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)
{
TrackerCnt_Err(hRS->rs_err);
}
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 = 0;
switch(hRS->sRS_Mode)
{
case RS_SLAVE_ALWAYS_WAIT: // in slave mode always response
case RS_SLAVE_TIMEOUT_WAIT: // transmit response
RS_RES = RS_Transmit_IT(hRS, RS_msg); break;
}
if(RS_RES != RS_OK)
{
if(hRS->sRS_Mode < RS_MASTER_START)
{
RS_Handle_Receive_Start(hRS, RS_msg);
}
TrackerCnt_Err(hRS->rs_err);
}
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);
// if(RS_RES != RS_OK)
// {
// __NOP();
// }
return RS_RES;
}
/**
* @brief Handler for UART.
* @param hRS Указатель на хендлер RS.
* @note Обрабатывает ошибки если есть и вызывает RS Коллбеки.
* Добавить вызов этой функции в UARTx_IRQHandler() после HAL_UART_IRQHandler().
*/
void RS_UART_Handler(RS_HandleTypeDef *hRS)
{
if(hRS->huart == NULL)
{
return;
}
//-------------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);
// Если прием активен и мы получили 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);
// Парсим наше сообщение
RS_StatusTypeDef parse_res = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
// Если сообещине принято корректно - отвечаем на него
if(parse_res == RS_OK)
{
if(hRS->htim)
{
// Останавливаем таймаут
if(hRS->sRS_Timeout)
HAL_TIM_Base_Stop_IT(hRS->htim);
}
hRS->lastPacketTick = uwTick;
RS_Response(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(hRS->htim)
{
hRS->htim->Instance->CNT = 0; // reset cnt;
/* Start timeout при получении первого байта */
if(hRS->sRS_Timeout) // if timeout setted
if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS)) // if first byte is received and receive is active
{
hRS->htim->Instance->ARR = hRS->sRS_Timeout;
HAL_TIM_Base_Start_IT(hRS->htim);
RS_Set_RX_Active_Flags(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
}
}
/**
* @brief Handler for TIM.
* @param hRS Указатель на хендлер RS.
* @note Попадание сюда = таймаут и перезапуск RS приема
* Добавить вызов этой функции в TIMx_IRQHandler() после HAL_TIM_IRQHandler().
*/
void RS_TIM_Handler(RS_HandleTypeDef *hRS)
{
if(hRS->htim == NULL)
{
return;
}
HAL_TIM_IRQHandler(hRS->htim);
RS_Abort(hRS, ABORT_RS);
if(hRS->pMessagePtr->MbAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
TrackerCnt_Err(hRS->rs_err);
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
}
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
/**
* @brief Пользовательская функция для ответа на запрос по UART.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о результате ответа на комманду.
* @note Обработка принятой комманды и ответ на неё.
*/
__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 Статус о результате заполнения буфера.
* @note Заполнение буффера UART из структуры сообщения.
*/
__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 Статус о результате заполнения структуры.
* @note Заполнение структуры сообщения из буффера UART.
*/
__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---------------
//-------------------------------------------------------------------