/** ******************************************************************************* * @file modbus_diag.c * @brief Реализация диагностики устройства Modbus ******************************************************************************* * @details Модуль обработки запросов диагностической информации (0x08): - Полная поддержка всех подфункций диагностики согласно спецификации Modbus - Выставление любого бита в Diagnostics Register - Сбор статистики работы устройства - Управление режимами работы (Normal/Listen Only) ******************************************************************************/ #include "modbus_diag.h" #ifdef MODBUS_ENABLE_DIAGNOSTICS MB_DiagnosticsInfoTypeDef MB_DIAG = {0}; ///< Глобальная структура диагностики /** * @brief Инициализация диагностических счетчиков */ void MB_DiagnosticsInit(void) { MB_DIAG.DiagnosticRegister = 0; MB_DIAG.DeviceMode = MODBUS_NORMAL_MODE; // Инициализация счетчиков 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 Обработать функцию Diagnostics (Serial Line only) (0x08) * @param modbus_msg Указатель на структуру сообщения modbus * @return fMessageHandled Статус обработки команды */ uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg) { uint16_t sub_function = modbus_msg->MbData[0]; uint16_t request_data = modbus_msg->MbData[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->MbData[0] = sub_function; modbus_msg->MbData[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->MbData[0] = sub_function; modbus_msg->MbData[1] = request_data; modbus_msg->ByteCnt = 4; break; case 0x0002: // Return Diagnostic Register modbus_msg->MbData[0] = sub_function; modbus_msg->MbData[1] = MB_DIAG.DiagnosticRegister; modbus_msg->ByteCnt = 4; break; case 0x0003: // Change ASCII Input Delimiter // В RTU режиме не поддерживается modbus_msg->FuncCode |= FC_ERR_VALUES_START; modbus_msg->Except_Code = ET_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->MbData[0] = sub_function; modbus_msg->MbData[1] = 0; modbus_msg->ByteCnt = 4; break; case 0x000B: // Return Bus Message Count modbus_msg->MbData[0] = sub_function; modbus_msg->MbData[1] = MB_DIAG.Counters.BusMessage; modbus_msg->ByteCnt = 4; break; case 0x000C: // Return Bus Communication Error Count modbus_msg->MbData[0] = sub_function; modbus_msg->MbData[1] = MB_DIAG.Counters.BusCommunicationErr; modbus_msg->ByteCnt = 4; break; case 0x000D: // Return Bus Exception Error Count modbus_msg->MbData[0] = sub_function; modbus_msg->MbData[1] = MB_DIAG.Counters.BusExceptionErr; modbus_msg->ByteCnt = 4; break; case 0x000E: // Return Server Message Count modbus_msg->MbData[0] = sub_function; modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveMessage; modbus_msg->ByteCnt = 4; break; case 0x000F: // Return Slave No Response Count modbus_msg->MbData[0] = sub_function; modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveNoResponse; modbus_msg->ByteCnt = 4; break; case 0x0010: // Return Slave NAK Count modbus_msg->MbData[0] = sub_function; modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveNAK; modbus_msg->ByteCnt = 4; break; case 0x0011: // Return Slave Busy Count modbus_msg->MbData[0] = sub_function; modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveBusy; modbus_msg->ByteCnt = 4; break; case 0x0012: // Return Bus Character Overrun Count modbus_msg->MbData[0] = sub_function; modbus_msg->MbData[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->MbData[0] = sub_function; modbus_msg->MbData[1] = 0; modbus_msg->ByteCnt = 4; break; default: modbus_msg->FuncCode |= FC_ERR_VALUES_START; modbus_msg->Except_Code = ET_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; } #endif //MODBUS_ENABLE_DIAGNOSTICS