Arduino_Modbus/rs_message.cpp
Razvalyaev ef623e8b0b +freertos
но надо еще коменты подправить
2025-08-31 13:24:11 +03:00

331 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.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; }