331 lines
14 KiB
C++
331 lines
14 KiB
C++
/**
|
||
**************************************************************************
|
||
* @file rs_message.cpp
|
||
* @brief Модуль для работы с протоколами RS/UART на Arduino.
|
||
**************************************************************************
|
||
* @details
|
||
* Данный модуль обеспечивает приём и передачу сообщений по протоколу RS
|
||
* через UART на платформах Arduino. Поддерживаются два режима работы:
|
||
* - обычный (Arduino loop);
|
||
* - FreeRTOS (через StreamBuffer и отдельную задачу RS_Task).
|
||
*
|
||
* Основные возможности:
|
||
* - Инициализация и управление структурой RS_HandleTypeDef.
|
||
* - Обработка приёма байтов, сборка пакетов и определение их длины.
|
||
* - Разбор полученных сообщений и формирование ответов.
|
||
* - Управление состояниями приёма и передачи (флаги RX/TX, busy/free).
|
||
* - Таймаут приёма и аварийная остановка передачи/приёма.
|
||
*
|
||
* Предоставляемые функции:
|
||
* - RS_Init() — инициализация структуры RS и Serial порта.
|
||
* - RS_Abort() — остановка работы RS/UART, очистка флагов.
|
||
* - RS_Handle_Transmit_Start() — подготовка и отправка сообщения.
|
||
* - RS_Process() — основной цикл обработки (Arduino loop).
|
||
* - RS_Process_Byte() — обработка одного принятого байта (для FreeRTOS).
|
||
* - RS_UART_RX_Handler() — ISR-приём данных UART (FreeRTOS).
|
||
* - RS_Task() — FreeRTOS задача обработки UART.
|
||
*
|
||
* Пользователь должен реализовать слабые функции:
|
||
* - RS_Response() — формирование ответа на сообщение.
|
||
* - RS_Collect_Message() — заполнение буфера перед передачей.
|
||
* - RS_Parse_Message() — разбор принятого пакета.
|
||
* - RS_Define_Size_of_RX_Message() — определение длины ожидаемого пакета.
|
||
*
|
||
* Буфер для приёма/передачи: RS_Buffer[MSG_SIZE_MAX].
|
||
*
|
||
* @note
|
||
* - В режиме Arduino loop необходимо вызывать RS_Process() в основном цикле.
|
||
* - В режиме FreeRTOS требуется определить макрос RS_IN_FREERTOS,
|
||
* создать задачу RS_Task и подключить RS_UART_RX_Handler() к ISR UART.
|
||
* - UART используется через стандартный Arduino Stream API (write, read, available).
|
||
*
|
||
* Пример использования (Arduino loop):
|
||
* @verbatim
|
||
RS_HandleTypeDef hRS;
|
||
RS_MsgTypeDef msg;
|
||
hRS.pMessagePtr = &msg;
|
||
RS_Init(&hRS, &Serial, 115200, NULL);
|
||
|
||
void loop() {
|
||
RS_Process(&hRS);
|
||
}
|
||
* @endverbatim
|
||
* Пример использования с FreeRTOS:
|
||
* @verbatim
|
||
RS_HandleTypeDef hRS;
|
||
RS_MsgTypeDef msg;
|
||
hRS.pMessagePtr = &msg;
|
||
RS_Init(&hRS, &Serial, 115200, NULL);
|
||
|
||
xTaskCreate(RS_Task, "RS_Task", 256, &hRS, 1, NULL);
|
||
//...
|
||
void eventSerial() {
|
||
RS_UART_RX_Handler(&hmodbus1);
|
||
}
|
||
* @endverbatim
|
||
**************************************************************************/
|
||
|
||
#include "rs_message.h"
|
||
|
||
#ifdef RS_IN_FREERTOS
|
||
#include <FreeRTOS.h>
|
||
#endif
|
||
|
||
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, uint32_t baudRate, uint8_t *pRS_BufferPtr) {
|
||
if (!hRS || !SerialPort) {
|
||
RS_DEBUG_PRINT("[RS] Init error: null handler or port");
|
||
return RS_ERR;
|
||
}
|
||
|
||
hRS->huart = SerialPort;
|
||
hRS->baudRate = baudRate;
|
||
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);
|
||
|
||
hRS->huart->begin(hRS->baudRate, SERIAL_8N1, hRS->rx_pin, hRS->tx_pin);
|
||
|
||
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 после аборта.
|
||
* @details В 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 после инициализации передачи.
|
||
* @details Определяет отвечать ли на команду или нет.
|
||
*/
|
||
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;
|
||
}
|
||
|
||
#ifdef RS_IN_FREERTOS
|
||
|
||
/**
|
||
* @brief Задача обработки UART приёма (FreeRTOS).
|
||
* @param pvParameters - указатель на RS_HandleTypeDef (передаётся при создании задачи).
|
||
* @details Вызывает функцию обработки приема UART по протоколу.
|
||
*/
|
||
void RS_Task(void *pvParameters) {
|
||
RS_HandleTypeDef *hRS = (RS_HandleTypeDef *)pvParameters;
|
||
uint8_t rxBuf[64];
|
||
|
||
for (;;) {
|
||
RS_Process(hRS);
|
||
vTaskDelay(pdMS_TO_TICKS(hRS->taskDelay));
|
||
}
|
||
}
|
||
#endif // RS_IN_FREERTOS
|
||
|
||
|
||
/**
|
||
* @brief Основная функция обработки RS (вариант без FreeRTOS).
|
||
* @param hRS - указатель на хендлер RS.
|
||
* @note Используется в Arduino loop(), если проект без FreeRTOS.
|
||
* @details Выполняет проверку таймаутов, обработку поступающих байт из UART
|
||
* по протоколу
|
||
*/
|
||
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 - статус о результате ответа на комманду.
|
||
* @details Обработка принятой комманды и ответ на неё.
|
||
*/
|
||
__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 - статус о результате заполнения буфера.
|
||
* @details Заполнение буффера 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 - статус о результате заполнения структуры.
|
||
* @details Заполнение структуры сообщения из буффера 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 - статус о корректности рассчета кол-ва байт для принятия.
|
||
* @details Определение сколько байтов надо принять по протоколу.
|
||
*/
|
||
__attribute__((weak)) RS_StatusTypeDef RS_Define_Size_of_RX_Message(RS_HandleTypeDef *hRS, uint32_t *rx_data_size) { return RS_ERR; }
|