297 lines
9.7 KiB
C
297 lines
9.7 KiB
C
/**
|
||
*******************************************************************************
|
||
* @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
|