diff --git a/Inc/modbus.h b/Inc/modbus.h index e05c225..afb51dd 100644 --- a/Inc/modbus.h +++ b/Inc/modbus.h @@ -27,7 +27,7 @@ //----------------Прием модбас----------------// #include "modbus.h" -MODBUS_SetupHardware(&hmodbus1, &huart1, &htim3); +MODBUS_FirstInit(&hmodbus1, &huart1, &htim3); MODBUS_SlaveStart(&hmodbus1, NULL); // или если нужно переключится на другой @endverbatim @@ -68,6 +68,7 @@ MODBUS_SlaveStart(&hmodbus1, NULL); #include "modbus_holdregs.h" #include "modbus_inputregs.h" #include "modbus_devid.h" +#include "modbus_diag.h" @@ -83,11 +84,11 @@ MODBUS_SlaveStart(&hmodbus1, NULL); @{ */ /* Инициализация периферии модбас. */ -void MODBUS_SetupHardware(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim); +HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim); /* Программная конфигурация модбас. */ -void MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master); +HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master); /* Запуск слейв устройства */ -void MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg); +HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg); /** MODBUS_INIT_FUNCTIONS * @} */ diff --git a/Inc/modbus_coils.h b/Inc/modbus_coils.h index 7dff15d..01e2da3 100644 --- a/Inc/modbus_coils.h +++ b/Inc/modbus_coils.h @@ -81,7 +81,7 @@ typedef enum /** * @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS Modbus Data Access * @ingroup MODBUS_FUNCTIONS -* @brief Функции для доступа к данным модбас (коилы) +* @brief Функции для доступа к данным модбас @{ */ diff --git a/Inc/modbus_devid.h b/Inc/modbus_devid.h index 7d4361b..6acee42 100644 --- a/Inc/modbus_devid.h +++ b/Inc/modbus_devid.h @@ -1,7 +1,7 @@ /** ****************************************************************************** * @file modbus_devid.h -* @brief Идентификация устройства Modbus +* @brief Идентификаторы устройства Modbus ****************************************************************************** @addtogroup MODBUS_DEVID Device Identificators Tools @ingroup MODBUS_INTERNAL @@ -53,30 +53,6 @@ extern MB_DeviceIdentificationTypeDef MB_DEVID; void MB_DeviceInentificationInit(void); ///////////////---DEVICE IDENTIVICATIONS DEFINES---////////////////// -///////////////////////////////////////////////////////////////////// -/////////////////---DEVICE DIAGNOSTICS DEFINES---//////////////////// - -/** @brief Структура со диагностической информацией устройства модбас */ -typedef struct -{ - uint16_t DiagnosticRegister; - struct - { - uint16_t BusMessage; - uint16_t BusCommunicationErr; - uint16_t BusExceptionErr; - uint16_t SlaveMessage; - uint16_t SlaveNoResponse; - uint16_t SlaveNAK; - uint16_t SlaveBusy; - uint16_t BusCharacterOverrun; - }Counters; -}MB_DiagnosticsInfoTypeDef; -extern MB_DiagnosticsInfoTypeDef MB_DINFO; - - -/////////////////---DEVICE DIAGNOSTICS DEFINES---//////////////////// - ///////////////////////////////////////////////////////////////////// ////////////////////---MODBUS FUNCTION DEFINES---//////////////////// diff --git a/Inc/modbus_diag.h b/Inc/modbus_diag.h new file mode 100644 index 0000000..8f85db0 --- /dev/null +++ b/Inc/modbus_diag.h @@ -0,0 +1,107 @@ +/** +****************************************************************************** +* @file modbus_diag.h +* @brief Диагностика устройства Modbus +****************************************************************************** +@addtogroup MODBUS_DIAG Diagnostics Tools +@ingroup MODBUS_INTERNAL +@{ +****************************************************************************** +* @details +Модуль реализации Diagnostics (Serial Line only) (0x08): +- Полная поддержка всех подфункций диагностики +- Возможность выставить/сбросить любой бит в диагностическом регистре +- Сбор статистики работы устройства +- Управление режимами работы +******************************************************************************/ +#ifndef __MODBUS_DIAG_H_ +#define __MODBUS_DIAG_H_ +#include "modbus_core.h" + +///////////////////////////////////////////////////////////////////// +/////////////////---DEVICE DIAGNOSTICS DEFINES---//////////////////// + +/** @brief Режимы работы устройства */ +typedef enum +{ + MODBUS_NORMAL_MODE = 0, + MODBUS_LISTEN_ONLY_MODE = 1 +} MB_DeviceModeTypeDef; + + +/** @brief Структура со диагностической информацией устройства модбас */ +typedef struct +{ + uint16_t DiagnosticRegister; + MB_DeviceModeTypeDef DeviceMode; + uint8_t AsciiDelimiter; + struct + { + uint16_t BusMessage; + uint16_t BusCommunicationErr; + uint16_t BusExceptionErr; + uint16_t SlaveMessage; + uint16_t SlaveNoResponse; + uint16_t SlaveNAK; + uint16_t SlaveBusy; + uint16_t BusCharacterOverrun; + } Counters; +} MB_DiagnosticsInfoTypeDef; + +extern MB_DiagnosticsInfoTypeDef MB_DIAG; + +/////////////////---DEVICE DIAGNOSTICS DEFINES---//////////////////// + +///////////////////////////////////////////////////////////////////// +/////////////////////////---FUNCTIONS---///////////////////////////// + +/* Инициализация диагностических счетчиков */ +void MB_DiagnosticsInit(void); + +/** +* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS Modbus Data Access +@{ +*/ + +/* Выставить бит в регистре диагностике */ +int MB_Diagnostics_WriteBit(int bit_num, int bit_state); +/*ь Прочитать состояние бита диагностического регистра */ +int MB_Diagnostics_GetBit(int bit_num); +/* Получение текущего режима устройства */ +MB_DeviceModeTypeDef MB_GetDeviceMode(void); +/** MODBUS_CMD_PROCESS_FUNCTIONS + * @} + */ + +//---------PROCESS MODBUS COMMAND FUNCTIONS--------- +/** + * @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS + @{ + */ + +/* Обработка команды диагностики (0x08) */ +uint8_t MB_Proccess_Diagnostics(RS_MsgTypeDef *modbus_msg); + +/** MODBUS_CMD_PROCESS_FUNCTIONS + * @} + */ + + +/* Функции для обновления счетчиков диагностики */ +void MB_Diagnostics_BusMessageCnt(void); +void MB_Diagnostics_CommunicationErrorCnt(void); +void MB_Diagnostics_ExceptionErrorCnt(void); +void MB_Diagnostics_CharacterOverrunCnt(void); +void MB_Diagnostics_SlaveMessageCnt(void); +void MB_Diagnostics_SlaveNoResponseCnt(void); +void MB_Diagnostics_SlaveNAKCnt(void); +void MB_Diagnostics_SlaveBusyCnt(void); + + +/////////////////////////---FUNCTIONS---///////////////////////////// + +#endif //__MODBUS_DIAG_H_ + +/** MODBUS_DIAG + * @} + */ \ No newline at end of file diff --git a/README.md b/README.md index 6eac78e..321b7c3 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Modbus/ 1. **Склонируйте субмодуль** в ваш проект: ```bash -git submodule add https://git.arktika.cyou/Razvalyaev/STM32_ExtendedLibs path/to/ExtendedLibs +git submodule add https://git.arktika.cyou/set506/STM32_Modbus path/to/Modbus git submodule update --init --recursive ``` diff --git a/Src/modbus.c b/Src/modbus.c index a5aad72..2c6514a 100644 --- a/Src/modbus.c +++ b/Src/modbus.c @@ -9,8 +9,9 @@ @section Функции и макросы ### Инициализация: -- MODBUS_SetupHardware() — Инициализация модуля Modbus. +- MODBUS_FirstInit() — Инициализация модуля Modbus. - MODBUS_Config() — Инициализация модуля Modbus. +- MODBUS_SlaveStart() — Запуск Modbus как Slave. ### Функции для Modbus - MB_Slave_Response() @@ -22,45 +23,50 @@ ### Функции для работы с RS (UART): - RS_Parse_Message() / RS_Collect_Message() — Парсинг и сборка сообщения. - RS_Response() — Отправка ответа. - ******************************************************************************/ - #include "modbus.h" /* MODBUS HANDLES */ -RS_HandleTypeDef hmodbus1; -RS_MsgTypeDef MODBUS_MSG; +RS_HandleTypeDef hmodbus1; ///< Default Handle for Modbus +RS_MsgTypeDef MODBUS_MSG; ///< Default Message Struct for Modbus -/* DEFINE REGISTERS/COILS */ -MB_DeviceIdentificationTypeDef MB_DEVID; -MB_DataStructureTypeDef MB_DATA = {0};; +/* DEFINE DATA FOR MODBUS */ +MB_DataStructureTypeDef MB_DATA = {0};; ///< Coils & Registers //------------------------------------------------------------------- //-----------------------------FOR USER------------------------------ /** * @brief Инициализация периферии модбас. * @param hmodbus Указатель на хендлер RS + * @param huart Указатель на хендлер UART + * @param htim Указатель на хендлер TIM * @details Подключает хендлы периферии к hmodbus * Конфигурация выставляется по умолчанию из modbus_config.h */ -void MODBUS_SetupHardware(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim) +HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim) { if((hmodbus == NULL) || (huart == NULL)) { - return; + return HAL_ERROR; } MB_DeviceInentificationInit(); + MB_DiagnosticsInit(); //-----------SETUP MODBUS------------- // set up modbus: MB_RX_Size_NotConst and Timeout enable - hmodbus1.ID = MODBUS_DEVICE_ID; - hmodbus1.sRS_Timeout = MODBUS_TIMEOUT; - hmodbus1.sRS_Mode = RS_SLAVE_ALWAYS_WAIT; - hmodbus1.sRS_RX_Size_Mode = RS_RX_Size_NotConst; + hmodbus->ID = MODBUS_DEVICE_ID; + hmodbus->sRS_Timeout = MODBUS_TIMEOUT; + hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT; + hmodbus->sRS_RX_Size_Mode = RS_RX_Size_NotConst; // INIT - hmodbus1.RS_STATUS = RS_Init(hmodbus, huart, htim, 0); + hmodbus->RS_STATUS = RS_Init(hmodbus, huart, htim, 0); RS_EnableReceive(); + + if(hmodbus->RS_STATUS == RS_OK) + return HAL_OK; + else + return HAL_ERROR; } /** * @brief Программная конфигурация модбас. @@ -69,11 +75,15 @@ void MODBUS_SetupHardware(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, * @param master Режим мастер (пока не сделан) * @details Конфигурирует ID, таймаут и режим hmodbus */ -void MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master) +HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master) { if(hmodbus == NULL) { - return; + return HAL_ERROR; + } + if((ID < 1) || (ID > 247)) + { + return HAL_ERROR; } //-----------SETUP MODBUS------------- // set up modbus: MB_RX_Size_NotConst and Timeout enable @@ -84,30 +94,45 @@ void MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint else hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT; hmodbus->sRS_RX_Size_Mode = RS_RX_Size_NotConst; + + return HAL_OK; } /** * @brief Запуск слейв модбас. * @param hmodbus Указатель на хендлер RS. - * @param modbus_msg Указатель на структуру сообщения. + * @param modbus_msg Указатель на структуру сообщения. + (NULL чтобы использовать дефолтную) * @details Конфигурирует ID, таймаут и режим hmodbus */ -void MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) +HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) { if(hmodbus == NULL) { - return; + return HAL_ERROR; } + if(hmodbus->sRS_Mode >= RS_MASTER_START) + { + return HAL_ERROR; + } if(modbus_msg) - RS_Receive_IT(hmodbus, modbus_msg); + hmodbus->RS_STATUS = RS_Receive_IT(hmodbus, modbus_msg); else - RS_Receive_IT(hmodbus, &MODBUS_MSG); + hmodbus->RS_STATUS = RS_Receive_IT(hmodbus, &MODBUS_MSG); + + if(hmodbus->RS_STATUS == RS_OK) + return HAL_OK; + else + return HAL_ERROR; } +//------------------------------------------------------------------- +//-----------------------------INTERNAL------------------------------ + /** * @brief Ответ на сообщение в режиме слейва. * @param hmodbus Указатель на хендлер RS. @@ -121,12 +146,15 @@ static RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeD hmodbus->f.EchoResponse = 0; RS_Reset_TX_Flags(hmodbus); // reset flag for correct transmit - if(hmodbus->ID == 0) + MB_Diagnostics_BusMessageCnt(); + if(hmodbus->ID == 0 || modbus_msg->MbAddr == 0) { + MB_Diagnostics_SlaveNoResponseCnt(); // <-- Устройство не отвечает на широковещательные сообщения hmodbus->RS_STATUS = RS_SKIP; - return MB_RES; + return RS_Handle_Receive_Start(hmodbus, modbus_msg); } - + MB_Diagnostics_SlaveMessageCnt(); + if(modbus_msg->Func_Code < ERR_VALUES_START)// if no errors after parsing { switch (modbus_msg->Func_Code) @@ -192,12 +220,30 @@ static RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeD hmodbus->f.MessageHandled = MB_Proccess_Read_Device_Identification(hmodbus->pMessagePtr); break; + // Добавить в switch-case после других case: + case MB_R_DIAGNOSTIC: + hmodbus->f.MessageHandled = MB_Proccess_Diagnostics(hmodbus->pMessagePtr); + break; + /* unknown func code */ - default: modbus_msg->Except_Code = 0x01; /* set exception code: illegal function */ + default: + modbus_msg->Except_Code = 0x01; /* set exception code: illegal function */ + MB_Diagnostics_SlaveNAKCnt(); } - + + + // Проверяем режим устройства - если Listen Only, не обрабатываем команды + if (MB_GetDeviceMode() == MODBUS_LISTEN_ONLY_MODE) + { + MB_Diagnostics_SlaveNoResponseCnt(); + hmodbus->RS_STATUS = RS_SKIP; + return RS_Handle_Receive_Start(hmodbus, modbus_msg);; + } + + // Проверяем статус обработки запроса if(hmodbus->f.MessageHandled == 0) { + MB_Diagnostics_ExceptionErrorCnt(); TrackerCnt_Warn(hmodbus->rs_err); modbus_msg->Func_Code |= ERR_VALUES_START; } @@ -282,6 +328,14 @@ static RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_M } } + else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC) + { + // Diagnostics special format: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO] + modbus_uart_buff[ind++] = modbus_msg->DATA[0] >> 8; // Sub-function HI + modbus_uart_buff[ind++] = modbus_msg->DATA[0] & 0xFF; // Sub-function LO + modbus_uart_buff[ind++] = modbus_msg->DATA[1] >> 8; // Data HI + modbus_uart_buff[ind++] = modbus_msg->DATA[1] & 0xFF; // Data LO + } else // modbus data header { // set size of received data @@ -344,24 +398,23 @@ static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef * RS_StatusTypeDef MB_RES = 0; int mb_func_size = 0; - if ((modbus_msg->Func_Code & ~ERR_VALUES_START) < 0x0F) + if (modbus_msg->Func_Code == MB_R_DIAGNOSTIC) { - modbus_msg->ByteCnt = 0; mb_func_size = 1; - - } - else - { - modbus_msg->ByteCnt = hmodbus->pBufferPtr[RX_FIRST_PART_SIZE-1]; // get numb of data in command - // +1 because that defines is size, not ind. - mb_func_size = modbus_msg->ByteCnt + 2; - } - - - if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) + } + else if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) { mb_func_size = 0; } + else if ((modbus_msg->Func_Code & ~ERR_VALUES_START) < 0x0F) + { + mb_func_size = 1; + } + else + { + mb_func_size = modbus_msg->ByteCnt + 2; + } + mb_func_size = RX_FIRST_PART_SIZE + mb_func_size; // size of whole message return mb_func_size; @@ -381,20 +434,25 @@ static RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_Msg uint32_t check_empty_buff; int ind = 0; // ind for modbus-uart buffer hmodbus->f.RX_Continue = 0; + int expected_size = 0; //-----INFO ABOUT DATA/MESSAGE------- //-----------[first bits]------------ // get ID of message/user if(modbus_uart_buff[ind] != hmodbus->ID) { modbus_msg->MbAddr = 0; - return RS_SKIP; + ind++; + } + else + { + modbus_msg->MbAddr = modbus_uart_buff[ind++]; } - modbus_msg->MbAddr = modbus_uart_buff[ind++]; // get func code modbus_msg->Func_Code = modbus_uart_buff[ind++]; if(modbus_msg->Func_Code & ERR_VALUES_START) // явная херня { + MB_Diagnostics_SlaveNAKCnt(); modbus_msg->MbAddr = 0; return RS_SKIP; } @@ -406,6 +464,18 @@ static RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_Msg modbus_msg->DevId.NextObjId = modbus_uart_buff[ind++]; modbus_msg->ByteCnt = 0; } + else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC) + { + // Diagnostics: читаем 4 байта в DATA[0] и DATA[1] + // Sub-function + modbus_msg->DATA[0] = modbus_uart_buff[ind++] << 8; + modbus_msg->DATA[0] |= modbus_uart_buff[ind++]; + // Data + modbus_msg->DATA[1] = modbus_uart_buff[ind++] << 8; + modbus_msg->DATA[1] |= modbus_uart_buff[ind++]; + modbus_msg->Addr = 0; // не использует Addr + modbus_msg->Qnt = 0; // не использует Qnt + } else // if its classic modbus request { // get address from CMD @@ -422,6 +492,20 @@ static RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_Msg else hmodbus->pMessagePtr->ByteCnt = 0; + expected_size = MB_Define_Size_of_Function(hmodbus, modbus_msg); + // если размер меньше ожидаемого - продолжаем принимать + if(hmodbus->RS_Message_Size < expected_size) + { + hmodbus->f.RX_Continue = 1; + return RS_SKIP; + } + // если больше Ошибка + else if (hmodbus->RS_Message_Size > expected_size) + { + MB_Diagnostics_CommunicationErrorCnt(); + return RS_PARSE_MSG_ERR; + } + //---------------DATA---------------- // (optional) if (modbus_msg->ByteCnt != 0) @@ -431,6 +515,7 @@ static RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_Msg { TrackerCnt_Err(hmodbus->rs_err); modbus_msg->Func_Code |= ERR_VALUES_START; + MB_Diagnostics_CommunicationErrorCnt(); return RS_PARSE_MSG_ERR; } uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA; @@ -456,6 +541,7 @@ static RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_Msg // compare crc if (modbus_msg->MB_CRC != CRC_VALUE) { + MB_Diagnostics_CommunicationErrorCnt(); TrackerCnt_Err(hmodbus->rs_err); modbus_msg->Func_Code |= ERR_VALUES_START; } @@ -468,17 +554,6 @@ static RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_Msg // if(check_empty_buff == 0) // hmodbus->MB_RESPONSE = MB_EMPTY_MSG; // - // если размер меньше ожидаемого - продолжаем принимать - if(hmodbus->RS_Message_Size < MB_Define_Size_of_Function(hmodbus, modbus_msg)) - { - hmodbus->f.RX_Continue = 1; - return RS_SKIP; - } - // если больше Ошибка - else if (hmodbus->RS_Message_Size > MB_Define_Size_of_Function(hmodbus, modbus_msg)) - { - return RS_PARSE_MSG_ERR; - } return RS_OK; } diff --git a/Src/modbus_devid.c b/Src/modbus_devid.c index 31829aa..ad70117 100644 --- a/Src/modbus_devid.c +++ b/Src/modbus_devid.c @@ -1,7 +1,7 @@ /** ****************************************************************************** * @file modbus_devid.c -* @brief Реализация идентификации устройства Modbus +* @brief Реализация идентификаторов устройства Modbus ****************************************************************************** * @details Модуль обработки запросов идентификации устройства через MEI-тип 0x0E: @@ -14,9 +14,10 @@ сообщений с установкой флага MoreFollows и указанием NextObjId для продолжения чтения в следующем запросе. ******************************************************************************/ - #include "modbus_devid.h" +MB_DeviceIdentificationTypeDef MB_DEVID; ///< Device Identificatino= + /** * @brief Write Object of Device Identification to MessageData @@ -115,6 +116,11 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg) break; case MB_EXTENDED_IDENTIFICATION: + if(MODBUS_NUMB_OF_USEROBJECTS <= 0 || MODBUS_NUMB_OF_USEROBJECTS > 128) + { + return 0; + } + if (modbus_msg->DevId.NextObjId == 0) { modbus_msg->DevId.NextObjId = 0x80; diff --git a/Src/modbus_diag.c b/Src/modbus_diag.c new file mode 100644 index 0000000..0b7ab30 --- /dev/null +++ b/Src/modbus_diag.c @@ -0,0 +1,293 @@ +/** +****************************************************************************** +* @file modbus_diag.c +* @brief Реализация диагностики устройства Modbus +****************************************************************************** +* @details +Модуль обработки запросов диагностической информации (0x08): +- Полная поддержка всех подфункций диагностики согласно спецификации Modbus +- Выставление любого бита в Diagnostics Register +- Сбор статистики работы устройства +- Управление режимами работы (Normal/Listen Only) +******************************************************************************/ +#include "modbus_diag.h" + +// Глобальная структура диагностики +MB_DiagnosticsInfoTypeDef MB_DIAG = {0}; + +/** + * @brief Инициализация диагностических счетчиков + */ +void MB_DiagnosticsInit(void) +{ + MB_DIAG.DiagnosticRegister = 0; + MB_DIAG.DeviceMode = MODBUS_NORMAL_MODE; + MB_DIAG.AsciiDelimiter = '\n'; // LF по умолчанию + + // Инициализация счетчиков + MB_DIAG.Counters.BusMessage = 0; + MB_DIAG.Counters.BusCommunicationErr = 0; + MB_DIAG.Counters.BusExceptionErr = 0; + MB_DIAG.Counters.SlaveMessage = 0; + MB_DIAG.Counters.SlaveNoResponse = 0; + MB_DIAG.Counters.SlaveNAK = 0; + MB_DIAG.Counters.SlaveBusy = 0; + MB_DIAG.Counters.BusCharacterOverrun = 0; +} + +/** + * @brief Выставить бит в регистре диагностике + * @param bit_num Номер бита для выставления (1-15, 0 бит нельзя выставить) + * @param bit_state Состояние бита для выставления (Выставить/Сбросить) + * @return >0 - номер выставленного бита, 0 - ошибка + */ +int MB_Diagnostics_WriteBit(int bit_num, int bit_state) +{ + if(bit_num == 0 || bit_num > 15) + return 0; + + if(bit_state) + MB_DIAG.DiagnosticRegister |= (1 << bit_num); + else + MB_DIAG.DiagnosticRegister &= ~(1 << bit_num); + + return bit_num; +} +/** + * @brief Прочитать состояние бита диагностического регистра + * @param bit_num Номер бита (0-15) + * @return 1 - бит установлен, 0 - бит сброшен или ошибка + */ +int MB_Diagnostics_GetBit(int bit_num) +{ + if(bit_num < 0 || bit_num > 15) + return 0; + + return (MB_DIAG.DiagnosticRegister >> bit_num) & 0x01; +} + +/** + * @brief Обработка команды диагностики (0x08) + * @param modbus_msg Указатель на структуру сообщения modbus + * @return fMessageHandled Статус обработки команды + */ +uint8_t MB_Proccess_Diagnostics(RS_MsgTypeDef *modbus_msg) +{ + uint16_t sub_function = modbus_msg->DATA[0]; + uint16_t request_data = modbus_msg->DATA[1]; + + // Если устройство в режиме Listen Only, отвечаем только на sub-function 0x01 + if (MB_DIAG.DeviceMode == MODBUS_LISTEN_ONLY_MODE && sub_function != 0x0001) + { + return 0; // Не отвечаем в режиме Listen Only + } + + switch(sub_function) + { + case 0x0000: // Return Query Data + // Эхо-ответ с теми же данными + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = request_data; + modbus_msg->ByteCnt = 4; + break; + + case 0x0001: // Restart Communications + // Перезапуск коммуникаций - выходим из Listen Only режима + MB_DIAG.DeviceMode = MODBUS_NORMAL_MODE; + + // Если request_data = 0xFF00, очищаем лог событий + if (request_data == 0xFF00) + { + MB_DiagnosticsInit(); // Полный сброс + } + else + { + // Очищаем только счетчики, но не регистр диагностики + MB_DIAG.Counters.BusMessage = 0; + MB_DIAG.Counters.BusCommunicationErr = 0; + MB_DIAG.Counters.BusExceptionErr = 0; + MB_DIAG.Counters.SlaveMessage = 0; + MB_DIAG.Counters.SlaveNoResponse = 0; + MB_DIAG.Counters.SlaveNAK = 0; + MB_DIAG.Counters.SlaveBusy = 0; + MB_DIAG.Counters.BusCharacterOverrun = 0; + } + + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = request_data; + modbus_msg->ByteCnt = 4; + break; + + case 0x0002: // Return Diagnostic Register + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = MB_DIAG.DiagnosticRegister; + modbus_msg->ByteCnt = 4; + break; + + case 0x0003: // Change ASCII Input Delimiter + // В RTU режиме не поддерживается + modbus_msg->Func_Code |= ERR_VALUES_START; + modbus_msg->Except_Code = ILLEGAL_FUNCTION; + return 0; + + case 0x0004: // Force Listen Only Mode + MB_DIAG.DeviceMode = MODBUS_LISTEN_ONLY_MODE; + // В режиме Listen Only не отправляем ответ + return 0; + + case 0x000A: // Clear Counters and Diagnostic Register + MB_DiagnosticsInit(); // Полный сброс + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = 0; + modbus_msg->ByteCnt = 4; + break; + + case 0x000B: // Return Bus Message Count + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = MB_DIAG.Counters.BusMessage; + modbus_msg->ByteCnt = 4; + break; + + case 0x000C: // Return Bus Communication Error Count + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = MB_DIAG.Counters.BusCommunicationErr; + modbus_msg->ByteCnt = 4; + break; + + case 0x000D: // Return Bus Exception Error Count + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = MB_DIAG.Counters.BusExceptionErr; + modbus_msg->ByteCnt = 4; + break; + + case 0x000E: // Return Server Message Count + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = MB_DIAG.Counters.SlaveMessage; + modbus_msg->ByteCnt = 4; + break; + + case 0x000F: // Return Slave No Response Count + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = MB_DIAG.Counters.SlaveNoResponse; + modbus_msg->ByteCnt = 4; + break; + + case 0x0010: // Return Slave NAK Count + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = MB_DIAG.Counters.SlaveNAK; + modbus_msg->ByteCnt = 4; + break; + + case 0x0011: // Return Slave Busy Count + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = MB_DIAG.Counters.SlaveBusy; + modbus_msg->ByteCnt = 4; + break; + + case 0x0012: // Return Bus Character Overrun Count + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = MB_DIAG.Counters.BusCharacterOverrun; + modbus_msg->ByteCnt = 4; + break; + + case 0x0014: // Clear Overrun Counter and Flag + MB_DIAG.Counters.BusCharacterOverrun = 0; + // Сбрасываем флаг переполнения в DiagnosticRegister + MB_DIAG.DiagnosticRegister &= ~(1<<0); + modbus_msg->DATA[0] = sub_function; + modbus_msg->DATA[1] = 0; + modbus_msg->ByteCnt = 4; + break; + + default: + modbus_msg->Func_Code |= ERR_VALUES_START; + modbus_msg->Except_Code = ILLEGAL_FUNCTION; + return 0; + } + + return 1; +} + +/** + * @brief Увеличивает счетчик сообщений на шине + */ +void MB_Diagnostics_BusMessageCnt(void) +{ + MB_DIAG.Counters.BusMessage++; +} + +/** + * @brief Увеличивает счетчик ошибок связи + */ +void MB_Diagnostics_CommunicationErrorCnt(void) +{ + if (MB_DIAG.Counters.BusCommunicationErr < 0xFFFF) + MB_DIAG.Counters.BusCommunicationErr++; +} + +/** + * @brief Увеличивает счетчик исключений + */ +void MB_Diagnostics_ExceptionErrorCnt(void) +{ + if (MB_DIAG.Counters.BusExceptionErr < 0xFFFF) + MB_DIAG.Counters.BusExceptionErr++; +} + +/** + * @brief Увеличивает счетчик переполнения символов + */ +void MB_Diagnostics_CharacterOverrunCnt(void) +{ + if (MB_DIAG.Counters.BusCharacterOverrun < 0xFFFF) + { + MB_DIAG.Counters.BusCharacterOverrun++; + // Устанавливаем флаг переполнения в DiagnosticRegister + MB_DIAG.DiagnosticRegister |= (1 << 0); + } +} + +/** + * @brief Увеличивает счетчик отсутствия ответов + */ +void MB_Diagnostics_SlaveMessageCnt(void) +{ + if (MB_DIAG.Counters.SlaveMessage < 0xFFFF) + MB_DIAG.Counters.SlaveMessage++; +} + +/** + * @brief Увеличивает счетчик отсутствия ответов + */ +void MB_Diagnostics_SlaveNoResponseCnt(void) +{ + if (MB_DIAG.Counters.SlaveNoResponse < 0xFFFF) + MB_DIAG.Counters.SlaveNoResponse++; +} + +/** + * @brief Увеличивает счетчик NAK ответов + */ +void MB_Diagnostics_SlaveNAKCnt(void) +{ + if (MB_DIAG.Counters.SlaveNAK < 0xFFFF) + MB_DIAG.Counters.SlaveNAK++; +} + +/** + * @brief Увеличивает счетчик занятости устройства + */ +void MB_Diagnostics_SlaveBusyCnt(void) +{ + if (MB_DIAG.Counters.SlaveBusy < 0xFFFF) + MB_DIAG.Counters.SlaveBusy++; +} + +/** + * @brief Получение текущего режима устройства + * @return Текущий режим работы устройства + */ +MB_DeviceModeTypeDef MB_GetDeviceMode(void) +{ + return MB_DIAG.DeviceMode; +} \ No newline at end of file diff --git a/Src/modbus_holdregs.c b/Src/modbus_holdregs.c index 2f293d3..8524536 100644 --- a/Src/modbus_holdregs.c +++ b/Src/modbus_holdregs.c @@ -21,6 +21,8 @@ #include "modbus_inputregs.h" + + /** * @brief Proccess command Read Holding Registers (03 - 0x03). * @param modbus_msg Указатель на структуру собщения modbus. diff --git a/Src/modbus_inputregs.c b/Src/modbus_inputregs.c index cf300df..d6ed111 100644 --- a/Src/modbus_inputregs.c +++ b/Src/modbus_inputregs.c @@ -9,7 +9,6 @@ Копирование данных из структур устройства в буфер ответа - Поддержка знаковых и беззнаковых значений ******************************************************************************/ - #include "modbus_inputregs.h" diff --git a/Src/rs_message.c b/Src/rs_message.c index d1133c6..39a8647 100644 --- a/Src/rs_message.c +++ b/Src/rs_message.c @@ -23,6 +23,7 @@ - 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 @@ -356,20 +357,9 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS) // Парсим наше сообщение RS_StatusTypeDef parse_res = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr); - - // Проверяем адрес Modbus перед обработкой - if(hRS->pMessagePtr->MbAddr != hRS->ID) - { - // Чужое сообщение - игнорируем и начинаем новый прием - RS_Abort(hRS, ABORT_RX); - RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); - return; - } - - // Если сообещине принято корректно - отвечаем на него - if(parse_res != RS_SKIP) + if(parse_res == RS_OK) { if(hRS->htim) { @@ -425,6 +415,10 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS) //----------------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);