Работает Modbus, проверено на ESP32-S3-Zero
Заготовка для модбас на ИВР
This commit is contained in:
parent
51f6216eb7
commit
1c4f4d689a
@ -2,9 +2,6 @@
|
||||
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
// Пример: RX=16, TX=17, скорость 115200
|
||||
rs_huart.begin(115200, SERIAL_8N1, 8, 9);
|
||||
rs_huart.println("start1");
|
||||
MODBUS_FirstInit();
|
||||
|
||||
|
||||
|
50
modbus.cpp
50
modbus.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file modbus.c
|
||||
* @file modbus.cpp
|
||||
* @brief Модуль для реализации MODBUS.
|
||||
**************************************************************************
|
||||
* @details Файл содержит реализацию функций работы с Modbus, включая:
|
||||
@ -50,16 +50,18 @@
|
||||
* Таким образом, сами коилы могут представлять собой как массив так и структуру.
|
||||
*
|
||||
* @section Инструкция по подключению
|
||||
* Для корректной работы надо подключить обработчики RS_UART_Handler(), RS_TIM_Handler(),
|
||||
* в соответствубщие низкоуровневые прерывания UART_IRQHandler, TIM_IRQHandler. После HAL'овского обработчика
|
||||
*
|
||||
* Также необходимо в modbus_config.h настроить дефайны для нужной работы UART
|
||||
* Настройте modbus_config.h дефайны для нужной работы UART
|
||||
*
|
||||
* После для запуска Modbus:
|
||||
* @verbatim
|
||||
//----------------Прием модбас----------------//
|
||||
#include "rs_message.h"
|
||||
MODBUS_FirstInit();
|
||||
RS_Receive_IT(&hmodbus1, &MODBUS_MSG);
|
||||
|
||||
void loop()
|
||||
{
|
||||
RS_Process(&hmodbus1);(&hmodbus1, &MODBUS_MSG);
|
||||
}
|
||||
* @endverbatim
|
||||
*
|
||||
******************************************************************************/
|
||||
@ -73,6 +75,7 @@ MB_DeviceIdentificationTypeDef MB_INFO;
|
||||
MB_DataStructureTypeDef MB_DATA;
|
||||
RS_MsgTypeDef MODBUS_MSG;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
//-----------------------------FOR USER------------------------------
|
||||
/**
|
||||
@ -91,6 +94,7 @@ void MODBUS_FirstInit(void)
|
||||
hmodbus1.sRS_RX_Size_Mode = RS_RX_Size_NotConst;
|
||||
hmodbus1.pMessagePtr = &MODBUS_MSG;
|
||||
// INIT
|
||||
rs_huart.begin(115200, SERIAL_8N1, 8, 9);
|
||||
hmodbus1.RS_STATUS = RS_Init(&hmodbus1, &rs_huart, 0);
|
||||
|
||||
RS_EnableReceive();
|
||||
@ -188,6 +192,7 @@ MB_ExceptionTypeDef MB_Check_Address_For_Arr(uint16_t Addr, uint16_t Qnt, uint16
|
||||
// if quantity too big return error
|
||||
if ((Addr - R_ARR_ADDR) + Qnt > R_ARR_NUMB)
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Illegal Data Address");
|
||||
return ILLEGAL_DATA_ADDRESS; // return exception code
|
||||
}
|
||||
// if all ok - return no errors
|
||||
@ -195,7 +200,10 @@ MB_ExceptionTypeDef MB_Check_Address_For_Arr(uint16_t Addr, uint16_t Qnt, uint16
|
||||
}
|
||||
// if address isnt from this array return error
|
||||
else
|
||||
return ILLEGAL_DATA_ADDRESS; // return exception code
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Illegal Data Address");
|
||||
return ILLEGAL_DATA_ADDRESS; // return exception code
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Define Address Origin for Input/Holding Registers
|
||||
@ -213,6 +221,7 @@ MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, u
|
||||
/* check quantity error */
|
||||
if (Qnt > 125)
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Illegal Data Value");
|
||||
return ILLEGAL_DATA_VALUE; // return exception code
|
||||
}
|
||||
|
||||
@ -226,6 +235,7 @@ MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, u
|
||||
// if address doesnt match any array - return illegal data address response
|
||||
else
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Illegal Data Address");
|
||||
return ILLEGAL_DATA_ADDRESS;
|
||||
}
|
||||
}
|
||||
@ -239,11 +249,13 @@ MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, u
|
||||
// if address doesnt match any array - return illegal data address response
|
||||
else
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Illegal Data Address");
|
||||
return ILLEGAL_DATA_ADDRESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Illegal Function");
|
||||
return ILLEGAL_FUNCTION;
|
||||
}
|
||||
// if found requeried array return no err
|
||||
@ -266,6 +278,7 @@ MB_ExceptionTypeDef MB_DefineCoilsAddress(uint16_t **pCoils, uint16_t Addr, uint
|
||||
/* check quantity error */
|
||||
if (Qnt > 2000)
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Illegal Data Value");
|
||||
return ILLEGAL_DATA_VALUE; // return exception code
|
||||
}
|
||||
|
||||
@ -277,6 +290,7 @@ MB_ExceptionTypeDef MB_DefineCoilsAddress(uint16_t **pCoils, uint16_t Addr, uint
|
||||
// if address doesnt match any array - return illegal data address response
|
||||
else
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Illegal Data Address");
|
||||
return ILLEGAL_DATA_ADDRESS;
|
||||
}
|
||||
|
||||
@ -611,20 +625,24 @@ RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_ms
|
||||
{
|
||||
// Read Coils
|
||||
case MB_R_COILS:
|
||||
MB_DEBUG_PRINT("[MB] Read Coils request");
|
||||
hmodbus->f.MessageHandled = MB_Read_Coils(hmodbus->pMessagePtr);
|
||||
break;
|
||||
|
||||
// Read Hodling Registers
|
||||
case MB_R_HOLD_REGS:
|
||||
MB_DEBUG_PRINT("[MB] Read Hodling Registers request");
|
||||
hmodbus->f.MessageHandled = MB_Read_Hold_Regs(hmodbus->pMessagePtr);
|
||||
break;
|
||||
case MB_R_IN_REGS:
|
||||
MB_DEBUG_PRINT("[MB] Read Input Registers request");
|
||||
hmodbus->f.MessageHandled = MB_Read_Input_Regs(hmodbus->pMessagePtr);
|
||||
break;
|
||||
|
||||
|
||||
// Write Single Coils
|
||||
case MB_W_COIL:
|
||||
MB_DEBUG_PRINT("[MB] Write Single Coil request");
|
||||
hmodbus->f.MessageHandled = MB_Write_Single_Coil(hmodbus->pMessagePtr);
|
||||
if(hmodbus->f.MessageHandled)
|
||||
{
|
||||
@ -634,6 +652,7 @@ RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_ms
|
||||
break;
|
||||
|
||||
case MB_W_HOLD_REG:
|
||||
MB_DEBUG_PRINT("[MB] Write Single Hodling Register request");
|
||||
hmodbus->f.MessageHandled = MB_Write_Single_Reg(hmodbus->pMessagePtr);
|
||||
if(hmodbus->f.MessageHandled)
|
||||
{
|
||||
@ -644,6 +663,7 @@ RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_ms
|
||||
|
||||
// Write Multiple Coils
|
||||
case MB_W_COILS:
|
||||
MB_DEBUG_PRINT("[MB] Write Multiple Coils request");
|
||||
hmodbus->f.MessageHandled = MB_Write_Miltuple_Coils(hmodbus->pMessagePtr);
|
||||
if(hmodbus->f.MessageHandled)
|
||||
{
|
||||
@ -654,6 +674,7 @@ RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_ms
|
||||
|
||||
// Write Multiple Registers
|
||||
case MB_W_HOLD_REGS:
|
||||
MB_DEBUG_PRINT("[MB] Write Multiple Hodling Registers request");
|
||||
hmodbus->f.MessageHandled = MB_Write_Miltuple_Regs(hmodbus->pMessagePtr);
|
||||
if(hmodbus->f.MessageHandled)
|
||||
{
|
||||
@ -663,22 +684,29 @@ RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_ms
|
||||
break;
|
||||
|
||||
case MB_R_DEVICE_INFO:
|
||||
MB_DEBUG_PRINT("[MB] Read Device Information request");
|
||||
hmodbus->f.MessageHandled = MB_Read_Device_Identification(hmodbus->pMessagePtr);
|
||||
break;
|
||||
|
||||
|
||||
/* unknown func code */
|
||||
default: modbus_msg->Except_Code = ILLEGAL_FUNCTION; /* set exception code: illegal function */
|
||||
default:
|
||||
MB_DEBUG_PRINT("[MB] Illegal function");
|
||||
modbus_msg->Except_Code = ILLEGAL_FUNCTION; /* set exception code: illegal function */
|
||||
}
|
||||
|
||||
if(hmodbus->f.MessageHandled == 0)
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Function not handled");
|
||||
modbus_msg->Func_Code = static_cast<MB_FunctonTypeDef>(
|
||||
static_cast<int>(modbus_msg->Func_Code) + ERR_VALUES_START
|
||||
);
|
||||
MB_ERR_DEBUG_PRINT2_HEX("[MB] Function Code:", modbus_msg->Func_Code);
|
||||
MB_ERR_DEBUG_PRINT2_DEC("[MB] Error Code:", modbus_msg->Except_Code);
|
||||
}
|
||||
else
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Function handled, responsing...");
|
||||
}
|
||||
|
||||
|
||||
@ -733,6 +761,7 @@ RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mo
|
||||
|
||||
if (modbus_msg->ByteCnt > DATA_SIZE*2) // if ByteCnt less than DATA_SIZE
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Response is invalid (ByteCnt not match DataSize)");
|
||||
return RS_COLLECT_MSG_ERR;
|
||||
}
|
||||
|
||||
@ -754,6 +783,7 @@ RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mo
|
||||
modbus_uart_buff[ind++] = modbus_msg->ByteCnt;
|
||||
else // otherwise return data_size err
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Response is invalid (ByteCnt not match DataSize)");
|
||||
return RS_COLLECT_MSG_ERR;
|
||||
}
|
||||
|
||||
@ -844,6 +874,7 @@ RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modb
|
||||
modbus_msg->Func_Code = static_cast<MB_FunctonTypeDef>(
|
||||
static_cast<int>(modbus_msg->Func_Code) + ERR_VALUES_START
|
||||
);
|
||||
MB_DEBUG_PRINT("[MB] Request is invalid (ByteCnt not match DataSize)");
|
||||
return RS_PARSE_MSG_ERR;
|
||||
}
|
||||
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA;
|
||||
@ -869,6 +900,9 @@ RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modb
|
||||
// compare crc
|
||||
if (modbus_msg->MB_CRC != CRC_VALUE)
|
||||
{
|
||||
MB_DEBUG_PRINT("[MB] Request CRC is invalid");
|
||||
MB_ERR_DEBUG_PRINT2_HEX("[MB] Request CRC: ", modbus_msg->MB_CRC);
|
||||
MB_ERR_DEBUG_PRINT2_HEX("[MB] Calced CRC: ", CRC_VALUE);
|
||||
modbus_msg->Func_Code = static_cast<MB_FunctonTypeDef>(
|
||||
static_cast<int>(modbus_msg->Func_Code) + ERR_VALUES_START
|
||||
);
|
||||
|
29
modbus.h
29
modbus.h
@ -369,4 +369,33 @@ uint8_t MB_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg);
|
||||
#define Trace_MB_TIM_Exit()
|
||||
#endif //Trace_MB_TIM_Exit
|
||||
|
||||
|
||||
#if MODBUS_DEBUG
|
||||
#define MB_DEBUG_PRINT(msg) Serial.println(msg)
|
||||
#define MB_DEBUG_PRINT_HEX(val) Serial.println(val, HEX)
|
||||
#define MB_DEBUG_PRINT_DEC(val) Serial.println(val, DEC)
|
||||
#define MB_DEBUG_PRINT2_HEX(msg,val) do { Serial.print(msg); Serial.println(val, HEX); } while(0)
|
||||
#define MB_DEBUG_PRINT2_DEC(msg,val) do { Serial.print(msg); Serial.println(val, DEC); } while(0)
|
||||
#else
|
||||
#define MB_DEBUG_PRINT(msg) ((void)0)
|
||||
#define MB_DEBUG_PRINT_HEX(val) ((void)0)
|
||||
#define MB_DEBUG_PRINT_DEC(val) ((void)0)
|
||||
#define MB_DEBUG_PRINT2_HEX(msg,val) ((void)0)
|
||||
#define MB_DEBUG_PRINT2_DEC(msg,val) ((void)0)
|
||||
#endif
|
||||
|
||||
#if MODBUS_ERR_DEBUG
|
||||
#define MB_ERR_DEBUG_PRINT(msg) Serial.println(msg)
|
||||
#define MB_ERR_DEBUG_PRINT_HEX(val) Serial.println(val, HEX)
|
||||
#define MB_ERR_DEBUG_PRINT_DEC(val) Serial.println(val, DEC)
|
||||
#define MB_ERR_DEBUG_PRINT2_HEX(msg,val) do { Serial.print(msg); Serial.println(val, HEX); } while(0)
|
||||
#define MB_ERR_DEBUG_PRINT2_DEC(msg,val) do { Serial.print(msg); Serial.println(val, DEC); } while(0)
|
||||
#else
|
||||
#define MB_ERR_DEBUG_PRINT(msg) ((void)0)
|
||||
#define MB_ERR_DEBUG_PRINT_HEX(val) ((void)0)
|
||||
#define MB_ERR_DEBUG_PRINT_DEC(val) ((void)0)
|
||||
#define MB_ERR_DEBUG_PRINT2_HEX(msg,val) ((void)0)
|
||||
#define MB_ERR_DEBUG_PRINT2_DEC(msg,val) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif //__MODBUS_H_
|
||||
|
@ -7,17 +7,22 @@
|
||||
#ifndef _MODBUS_CONFIG_H_
|
||||
#define _MODBUS_CONFIG_H_
|
||||
|
||||
// Включить/выключить debug
|
||||
#define RS_DEBUG 0 ///< Отладка приема/передачи UART
|
||||
#define MODBUS_DEBUG 0 ///< Отладка обработки запросов Modbus
|
||||
#define MODBUS_ERR_DEBUG 1 ///< Отладка ошибок по CAN
|
||||
|
||||
// MODBUS PARAMS
|
||||
#define MODBUS_DEVICE_ID 1 ///< девайс текущего устройства
|
||||
#define MODBUS_TIMEOUT 5000 ///< максимальнйы тайтаут MB в тиках таймера
|
||||
|
||||
// STRING OBJECTS MODBUS
|
||||
#define MODBUS_VENDOR_NAME "NIO-12"
|
||||
#define MODBUS_VENDOR_NAME "NIO PRIBOR"
|
||||
#define MODBUS_PRODUCT_CODE ""
|
||||
#define MODBUS_REVISION "Ver. 1.0"
|
||||
#define MODBUS_VENDOR_URL ""
|
||||
#define MODBUS_PRODUCT_NAME ""
|
||||
#define MODBUS_MODEL_NAME "STM32F103"
|
||||
#define MODBUS_MODEL_NAME "Arduino"
|
||||
#define MODBUS_USER_APPLICATION_NAME ""
|
||||
|
||||
// PERIPH FUNCTIONS AND HANDLERS
|
||||
|
210
rs_message.cpp
210
rs_message.cpp
@ -1,56 +1,49 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file rs_message.c
|
||||
* @brief Модуль для реализации протоколов по RS/UART.
|
||||
* @file rs_message.cpp
|
||||
* @brief Модуль для реализации протоколов по RS/UART (Arduino).
|
||||
**************************************************************************\
|
||||
* @details
|
||||
* Данный модуль реализует основные функции для приема и передачи сообщений
|
||||
* по протоколу RS через UART в режиме прерываний. Реализована обработка
|
||||
* приема и передачи данных, управление состояниями RS, а также функции для
|
||||
* инициализации и управления периферией.
|
||||
* по протоколу RS через UART. Реализована обработка приёма и передачи данных,
|
||||
* управление состояниями RS, а также функции для инициализации и управления
|
||||
* периферией Arduino Serial.
|
||||
*
|
||||
* Реализованы следующие функции:
|
||||
* - 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_Init() — инициализация структуры RS и привязка к Serial-порту.
|
||||
* - RS_Abort() — остановка работы RS/UART с очисткой флагов и структур.
|
||||
* - RS_Handle_Transmit_Start() — запуск передачи данных.
|
||||
* - RS_Process() — обработка входящих данных (вызов в loop()).
|
||||
*
|
||||
* В модуле также определен буфер RS_Buffer[] для хранения принимаемых/передаваемых данных.
|
||||
* В модуле также определён буфер RS_Buffer[] для хранения принимаемых/передаваемых данных.
|
||||
*
|
||||
* Пользователь должен определить функции:
|
||||
* - RS_Response() — формирование ответа на принятое сообщение.
|
||||
* - RS_Collect_Message() — подготовка сообщения для передачи.
|
||||
* - RS_Parse_Message() — разбор принятого сообщения.
|
||||
* - RS_Define_Size_of_RX_Message() — определение размера входящих данных.
|
||||
*
|
||||
* @note
|
||||
* Для корректной работы модуля предполагается использование соответствующих
|
||||
* обработчиков прерываний UART и таймера (RS_UART_Handler(), RS_TIM_Handler()),
|
||||
* которые надо вызывать с обработчиках используемой периферии
|
||||
|
||||
* Для корректной работы требуется вызывать RS_Process() в основном цикле программы.
|
||||
* UART используется через стандартный Arduino Stream API (write(), read(), available()).
|
||||
*
|
||||
@verbatim
|
||||
//-------------------Функции-------------------//
|
||||
Functions: users
|
||||
- RS_Parse_Message/RS_Collect_Message Заполнение структуры сообщения и буфера
|
||||
- RS_Response Ответ на сообщение
|
||||
- RS_Define_Size_of_RX_Message Определение размера принимаемых данных
|
||||
- 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
|
||||
- RS_Init Инициализация структуры RS и привязка Serial
|
||||
- RS_Abort Остановка приёма/передачи
|
||||
- RS_Handle_Transmit_Start Запуск передачи сообщения
|
||||
- RS_Process Обработка приёма сообщений (в loop)
|
||||
|
||||
@endverbatim
|
||||
*************************************************************************/
|
||||
|
||||
#include "rs_message.h"
|
||||
|
||||
uint8_t RS_Buffer[MSG_SIZE_MAX]; // uart buffer
|
||||
@ -60,15 +53,14 @@ uint8_t RS_Buffer[MSG_SIZE_MAX]; // uart buffer
|
||||
/**
|
||||
* @brief Initialize UART and handle RS stucture.
|
||||
* @param hRS - указатель на хендлер RS.
|
||||
* @param suart - указатель на структуру с настройками UART.
|
||||
* @param stim - указатель на структуру с настройками таймера.
|
||||
* @param SerialPort - указатель на структуру с настройками UART.
|
||||
* @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) {
|
||||
Serial.println("[RS] Init error: null handler or port");
|
||||
RS_DEBUG_PRINT("[RS] Init error: null handler or port");
|
||||
return RS_ERR;
|
||||
}
|
||||
|
||||
@ -82,26 +74,44 @@ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, HUART_TypeDef *SerialPort, uint8
|
||||
hRS->f.RX_Half = 0;
|
||||
hRS->lastByteTime = 0;
|
||||
|
||||
Serial.println("[RS] Initialized successfully");
|
||||
RS_EnableReceive();
|
||||
RS_Set_Busy(hRS);
|
||||
RS_Set_RX_Flags(hRS);
|
||||
|
||||
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 после аборта.
|
||||
* @note В Arduino аппаратный UART (Serial) не отключается физически,
|
||||
* т.к. стандартный API HardwareSerial не даёт прямого доступа
|
||||
* к регистрах UART. RS_Abort лишь очищает внутренние флаги
|
||||
* и структуры, имитируя "отключение".
|
||||
*/
|
||||
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode) {
|
||||
if (hRS == nullptr) {
|
||||
Serial.println("[RS] Abort error: null handler");
|
||||
RS_DEBUG_PRINT("[RS] Abort error: null handler");
|
||||
return RS_ERR;
|
||||
}
|
||||
if ((AbortMode & ABORT_RS) == 0) {
|
||||
if ((AbortMode & ABORT_RX) == ABORT_RX) {
|
||||
Serial.println("[RS] Abort RX");
|
||||
RS_DEBUG_PRINT("[RS] Abort RX");
|
||||
RS_Reset_RX_Flags(hRS);
|
||||
}
|
||||
if ((AbortMode & ABORT_TX) == ABORT_TX) {
|
||||
Serial.println("[RS] Abort TX");
|
||||
RS_DEBUG_PRINT("[RS] Abort TX");
|
||||
RS_Reset_TX_Flags(hRS);
|
||||
}
|
||||
} else {
|
||||
Serial.println("[RS] Abort RS (full reset)");
|
||||
RS_DEBUG_PRINT("[RS] Abort RS (full reset)");
|
||||
RS_Clear_All(hRS);
|
||||
}
|
||||
|
||||
@ -110,91 +120,124 @@ RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode) {
|
||||
return RS_ABORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
if (hRS == nullptr || RS_msg == nullptr) {
|
||||
Serial.println("[RS] TX start error: null ptr");
|
||||
RS_DEBUG_PRINT("[RS] TX start error: null ptr");
|
||||
return RS_ERR;
|
||||
}
|
||||
|
||||
if (RS_Is_TX_Busy(hRS)) {
|
||||
Serial.println("[RS] TX busy, cannot start");
|
||||
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) {
|
||||
Serial.println("[RS] TX collect message failed");
|
||||
RS_DEBUG_PRINT("[RS] TX collect message failed");
|
||||
return status;
|
||||
}
|
||||
|
||||
Serial.print("[RS] TX start, size=");
|
||||
Serial.println(hRS->RS_Message_Size);
|
||||
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);
|
||||
|
||||
Serial.println("[RS] TX finished");
|
||||
RS_DEBUG_PRINT("[RS] TX finished");
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Main RS processing function.
|
||||
* @param hRS - указатель на хендлер RS.
|
||||
* @return None.
|
||||
* @note Функция должна вызываться в основном цикле (loop()).
|
||||
* Выполняет проверку таймаутов, обработку поступающих байт из UART
|
||||
* и вызов пользовательских функций:
|
||||
* - RS_Define_Size_of_RX_Message() для определения размера пакета;
|
||||
* - RS_Parse_Message() для разбора принятого сообщения;
|
||||
* - RS_Response() для ответа, если сообщение адресовано текущему устройству.
|
||||
* В случае таймаута выполняется RS_Abort() с режимом ABORT_RX.
|
||||
*/
|
||||
void RS_Process(RS_HandleTypeDef *hRS)
|
||||
{
|
||||
if (hRS == nullptr) {
|
||||
Serial.println("[RS] Process error: null handler");
|
||||
return;
|
||||
}
|
||||
// Локальные статические переменные для индекса приёма и ожидаемого размера пакета
|
||||
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) {
|
||||
Serial.println("[RS] RX timeout");
|
||||
RS_DEBUG_PRINT("[RS] RX timeout");
|
||||
RS_Abort(hRS, ABORT_RX);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка наличия данных в UART
|
||||
if (hRS->huart->available()) {
|
||||
// Если приём ещё не активен — начинаем новый пакет
|
||||
if (!hRS->f.RX_Ongoing) {
|
||||
Serial.println("[RS] RX start");
|
||||
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;
|
||||
|
||||
Serial.print("[RS] RX byte: 0x");
|
||||
Serial.println(b, HEX);
|
||||
RS_DEBUG_PRINT2_HEX("[RS] RX byte: 0x", b);
|
||||
|
||||
if (rx_index == RX_FIRST_PART_SIZE && hRS->sRS_RX_Size_Mode == RS_RX_Size_NotConst) {
|
||||
// Если достигнут размер первой части пакета — определяем полный размер
|
||||
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;
|
||||
Serial.println("Defining size...");
|
||||
RS_DEBUG_PRINT("[RS] Defining size...");
|
||||
RS_Define_Size_of_RX_Message(hRS, &data_size);
|
||||
expected_size = RX_FIRST_PART_SIZE + data_size;
|
||||
Serial.print("[RS] RX expected size=");
|
||||
Serial.println(expected_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);
|
||||
Serial.println("[RS] RX complete, parsing...");
|
||||
RS_DEBUG_PRINT("[RS] RX complete, parsing...");
|
||||
|
||||
// Разбираем сообщение
|
||||
RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
|
||||
|
||||
// Если адрес совпадает — отвечаем
|
||||
if (hRS->pMessagePtr->MbAddr == hRS->ID) {
|
||||
Serial.println("[RS] RX for me, sending response");
|
||||
RS_DEBUG_PRINT("[RS] RX for me, sending response");
|
||||
RS_Response(hRS, hRS->pMessagePtr);
|
||||
} else {
|
||||
Serial.print("[RS] RX not for me, Addr=");
|
||||
Serial.println(hRS->pMessagePtr->MbAddr);
|
||||
RS_DEBUG_PRINT2_DEC("[RS] RX not for me, Addr=", hRS->pMessagePtr->MbAddr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -204,8 +247,43 @@ void RS_Process(RS_HandleTypeDef *hRS)
|
||||
|
||||
|
||||
|
||||
// weak functions
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
|
||||
/**
|
||||
* @brief Respond accord to received message.
|
||||
* @param hRS - указатель на хендлер RS.
|
||||
* @param RS_msg - указатель на структуру сообщения.
|
||||
* @return RS_RES - статус о результате ответа на комманду.
|
||||
* @note Обработка принятой комманды и ответ на неё.
|
||||
*/
|
||||
__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 - статус о результате заполнения буфера.
|
||||
* @note Заполнение буффера 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 - статус о результате заполнения структуры.
|
||||
* @note Заполнение структуры сообщения из буффера 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 - статус о корректности рассчета кол-ва байт для принятия.
|
||||
* @note Определение сколько байтов надо принять по протоколу.
|
||||
*/
|
||||
__attribute__((weak)) RS_StatusTypeDef RS_Define_Size_of_RX_Message(RS_HandleTypeDef *hRS, uint32_t *rx_data_size) { return RS_ERR; }
|
||||
|
21
rs_message.h
21
rs_message.h
@ -14,9 +14,6 @@
|
||||
- Подключить этот файл в раздел rs_message.h.
|
||||
- Определить функции для обработки сообщения: RS_Parse_Message(),
|
||||
RS_Collect_Message(), RS_Response(), RS_Define_Size_of_RX_Message()
|
||||
- Добавить UART/TIM Handler в Хендлер используемых UART/TIM.
|
||||
|
||||
Так же данный модуль использует счетчики
|
||||
**************************************************************************
|
||||
@verbatim
|
||||
Визуальное описание. Форматирование сохраняется как в коде.
|
||||
@ -164,6 +161,7 @@ typedef struct // RS_HandleTypeDef
|
||||
|
||||
/* RS STATUS */
|
||||
unsigned long lastByteTime;
|
||||
unsigned long baudRate;
|
||||
RS_StatusTypeDef RS_STATUS; ///< RS status
|
||||
}RS_HandleTypeDef;
|
||||
extern RS_HandleTypeDef hmodbus1;
|
||||
@ -200,11 +198,18 @@ RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *
|
||||
///////////////////////////---FUNCTIONS---///////////////////////////
|
||||
|
||||
|
||||
#ifndef printf_rs_err
|
||||
#define printf_rs_err(...)
|
||||
#if RS_DEBUG
|
||||
#define RS_DEBUG_PRINT(msg) Serial.println(msg)
|
||||
#define RS_DEBUG_PRINT_HEX(val) Serial.println(val, HEX)
|
||||
#define RS_DEBUG_PRINT_DEC(val) Serial.println(val, DEC)
|
||||
#define RS_DEBUG_PRINT2_HEX(msg,val) do { Serial.print(msg); Serial.println(val, HEX); } while(0)
|
||||
#define RS_DEBUG_PRINT2_DEC(msg,val) do { Serial.print(msg); Serial.println(val, DEC); } while(0)
|
||||
#else
|
||||
#define RS_DEBUG_PRINT(msg) ((void)0)
|
||||
#define RS_DEBUG_PRINT_HEX(val) ((void)0)
|
||||
#define RS_DEBUG_PRINT_DEC(val) ((void)0)
|
||||
#define RS_DEBUG_PRINT2_HEX(msg,val) ((void)0)
|
||||
#define RS_DEBUG_PRINT2_DEC(msg,val) ((void)0)
|
||||
#endif
|
||||
|
||||
#ifndef printf_rs
|
||||
#define printf_rs(...)
|
||||
#endif
|
||||
#endif // __RS_LIB_H_
|
||||
|
Loading…
Reference in New Issue
Block a user