diff --git a/Inc/modbus.h b/Inc/modbus.h index 107b80a..e9af0ac 100644 --- a/Inc/modbus.h +++ b/Inc/modbus.h @@ -5,7 +5,7 @@ ****************************************************************************** @addtogroup MODBUS Modbus tools ****************************************************************************** -@addtogroup MODBUS_FUNCTIONS Modbus library funtions +@addtogroup MODBUS_FUNCTIONS Main API for Modbus Library @ingroup MODBUS @{ ****************************************************************************** @@ -24,13 +24,28 @@ - Инициализировать хендл мобдас. По умолчанию глобально создается hmodbus1 - После для запуска Modbus: @verbatim - //----------------Прием модбас----------------// + //----------------Слейв модбас----------------// #include "modbus.h" MODBUS_FirstInit(&hmodbus1, &huart1, &htim3); - //MODBUS_Config(&hmodbus1, 1, 1000, 0); // - если нужны другие настройки, не из modbus_config.h + MODBUS_Config(&hmodbus1, 1, 1000, MODBUS_MODE_SLAVE); MODBUS_SlaveStart(&hmodbus1, NULL); @endverbatim + @verbatim + //----------------Мастер модбас----------------// + #include "modbus.h" + + MODBUS_FirstInit(&hmodbus1, &huart1, &htim3); + MODBUS_Config(&hmodbus1, 0, 1000, MODBUS_MODE_MASTER); // - если нужны другие настройки, не из modbus_config.h + // Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук + RS_MsgTypeDef msg = MB_REQUEST_READ_HOLDING_REGS(1, 0, 10); + MODBUS_MasterRequest(&hmodbus1, &msg, &callback_func); + + void callback_func(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) + { + // modbus_msg содержит ответ от устройства + } + @endverbatim @section Подключаемые модули: @@ -58,6 +73,8 @@ #define __MODBUS_H_ #include "rs_message.h" +#include "modbus_master.h" +#include "modbus_slave.h" #include "modbus_coils.h" #include "modbus_holdregs.h" #include "modbus_inputregs.h" @@ -71,21 +88,16 @@ //----------------FUNCTIONS FOR USER---------------- -/** - * @addtogroup MODBUS_INIT_FUNCTIONS Functions for Init - * @ingroup MODBUS_FUNCTIONS - * @brief Функции для инициализации - @{ - */ + /* Инициализация периферии модбас. */ HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim); /* Программная конфигурация модбас. */ HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master); /* Запуск слейв устройства */ HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg); -/** MODBUS_INIT_FUNCTIONS - * @} - */ +/* Реквест мастера модбас */ +HAL_StatusTypeDef MODBUS_MasterRequest(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, void (*pClbk)(RS_HandleTypeDef*, RS_MsgTypeDef*)); + //---------PROCESS MODBUS COMMAND FUNCTIONS--------- /////////////////////////---FUNCTIONS---///////////////////////////// diff --git a/Inc/modbus_coils.h b/Inc/modbus_coils.h index 38414ff..95f4a14 100644 --- a/Inc/modbus_coils.h +++ b/Inc/modbus_coils.h @@ -82,11 +82,46 @@ typedef enum /////////////////////////---FUNCTIONS---///////////////////////////// /** -* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS Modbus Data Access +* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS API for Data Access * @ingroup MODBUS_FUNCTIONS * @brief Функции для доступа к данным модбас @{ */ + + +/** + * @addtogroup MODBUS_REQ_COILS_API API for Coils + * @ingroup MODBUS_REQUEST_MSG + * @brief API для чтения coils из ответа в режиме мастер + * @details Примеры использования: + * + * @code + * // Пример: Запросили 10 coils с адреса 20, хотим узнать состояние coil 25 + * int coil_state; + * if(MB_GetCoilState(&MODBUS_MSG, 25, &coil_state)) + * { + * printf("Coil 25 state: %s\n", coil_state ? "ON" : "OFF"); + * } + * + * // Пример: Получить состояние всех запрошенных coils + * for(int addr = MODBUS_MSG.Addr; addr < MODBUS_MSG.Addr + MODBUS_MSG.Qnt; addr++) + * { + * int state; + * if(MB_GetCoilState(&MODBUS_MSG, addr, &state)) + * { + * printf("Coil %d: %s\n", addr, state ? "ON" : "OFF"); + * } + * } + * @endcode + */ + +int MB_GetCoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state); + +/** MODBUS_REQ_COILS_API + * @} + */ + + /** * @brief Считать коил по локальному адресу. @@ -137,9 +172,9 @@ uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception); @{ */ /* Обработать функцию Read Coils (01 - 0x01) */ -uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg); +uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg); /* Обработать функцию Write Single Coils (05 - 0x05) */ -uint8_t MB_Proccess_Write_Single_Coil(RS_MsgTypeDef *modbus_msg); +uint8_t MB_Process_Write_Single_Coil(RS_MsgTypeDef *modbus_msg); /* Обработать функцию Write Multiple Coils (15 - 0x0F) */ uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg); diff --git a/Inc/modbus_core.h b/Inc/modbus_core.h index c29400b..6a34751 100644 --- a/Inc/modbus_core.h +++ b/Inc/modbus_core.h @@ -117,39 +117,39 @@ typedef enum //MB_FunctonTypeDef /** @brief Structure for MEI func codes */ typedef enum //MB_FunctonTypeDef { - MEI_DEVICE_IDENTIFICATION = 0x0E, + MEI_DEVICE_IDENTIFICATIONS = 0x0E, }MB_MEITypeDef; /** @brief Structure for comformity */ typedef enum //MB_FunctonTypeDef { - MB_BASIC_IDENTIFICATION = 0x01, /*!< @brief Basic Device Identification. + MB_BASIC_IDENTIFICATIONS = 0x01, /*!< @brief Basic Device Identifications. @details All objects of this category are mandatory: VendorName,Product code, and revision number */ - MB_REGULAR_IDENTIFICATION = 0x02, /*!< @brief Regular Device Identification. + MB_REGULAR_IDENTIFICATIONS = 0x02, /*!< @brief Regular Device Identifications. @details The device provides additional and optional - identification and description data objects */ + identifications and description data objects */ - MB_EXTENDED_IDENTIFICATION = 0x03, /*!< @brief Extended Device Identification. + MB_EXTENDED_IDENTIFICATIONS = 0x03, /*!< @brief Extended Device Identifications. @details The device provides additional and optional - identification and description private data about the physical + identifications and description private data about the physical device itself. All of these data are device dependent. */ - MB_SPEDIFIC_IDENTIFICATION = 0x04, /*!< @brief Specific Device Identification. - @details The device provides one specific identification object. */ + MB_SPEDIFIC_IDENTIFICATIONS = 0x04, /*!< @brief Specific Device Identifications. + @details The device provides one specific identifications object. */ /* ERRORS */ - MB_ERR_BASIC_IDENTIFICATION = MB_BASIC_IDENTIFICATION + ERR_VALUES_START, - MB_ERR_REGULAR_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START, - MB_ERR_EXTENDED_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START, - MB_ERR_SPEDIFIC_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START, + MB_ERR_BASIC_IDENTIFICATIONS = MB_BASIC_IDENTIFICATIONS + ERR_VALUES_START, + MB_ERR_REGULAR_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START, + MB_ERR_EXTENDED_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START, + MB_ERR_SPEDIFIC_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START, }MB_ConformityTypeDef; -/** @brief Structure for decive identification message type */ +/** @brief Structure for decive identifications message type */ typedef struct { - MB_MEITypeDef MEI_Type; ///< MEI Type assigned number for Device Identification Interface + MB_MEITypeDef MEI_Type; ///< MEI Type assigned number for Device Identifications Interface MB_ConformityTypeDef ReadDevId; MB_ConformityTypeDef Conformity; uint8_t MoreFollows; @@ -163,7 +163,7 @@ typedef struct // RS_MsgTypeDef { uint8_t MbAddr; ///< Modbus Slave Address MB_FunctonTypeDef Func_Code; ///< Modbus Function Code - MB_DevIdMsgTypeDef DevId; ///< Read Device Identification Header struct + MB_DevIdMsgTypeDef DevId; ///< Read Device Identifications Header struct uint16_t Addr; ///< Modbus Address of data uint16_t Qnt; ///< Quantity of modbus data uint8_t ByteCnt; ///< Quantity of bytes of data in message to transmit/receive diff --git a/Inc/modbus_devid.h b/Inc/modbus_devid.h index 00f514e..b1c8b2b 100644 --- a/Inc/modbus_devid.h +++ b/Inc/modbus_devid.h @@ -3,12 +3,12 @@ * @file modbus_devid.h * @brief Идентификаторы устройства Modbus ****************************************************************************** -@addtogroup MODBUS_DEVID Device Identificators Tools +@addtogroup MODBUS_DEVID Device Identifications Tools @ingroup MODBUS_INTERNAL @{ ****************************************************************************** * @details -Модуль реализации функции Read Device Identification (0x2B): +Модуль реализации функции Read Device Identifications (0x2B): - Базовая идентификация (Vendor, Product, Revision) - Расширенная идентификация (URL, Model, User fields) - Поддержка потоковой передачи больших объектов @@ -48,8 +48,8 @@ typedef struct MB_DeviceObjectTypeDef Reserved[0x79]; MB_DeviceObjectTypeDef User[MODBUS_NUMB_OF_USEROBJECTS]; -}MB_DeviceIdentificationTypeDef; -extern MB_DeviceIdentificationTypeDef MB_DEVID; +}MB_DeviceIdentificationsTypeDef; +extern MB_DeviceIdentificationsTypeDef MB_DEVID; void MB_DeviceInentificationInit(void); ///////////////---DEVICE IDENTIVICATIONS DEFINES---////////////////// @@ -77,6 +77,52 @@ void MB_DeviceInentificationInit(void); ///////////////////////////////////////////////////////////////////// /////////////////////////---FUNCTIONS---///////////////////////////// +/** + * @addtogroup MODBUS_REQ_DEFID_API API for Device Identifications + * @ingroup MODBUS_REQUEST_MSG + * @brief Макросы для чтения идентификторов из ответа в режиме мастер + * @details Примеры использования: + * + * @code + * // Пример 1: Получить VendorName (ID = 0x00) + * uint8_t length; + * char vendor_name[64]; + * if(MB_FindObjectById(&MODBUS_MSG, 0x00, vendor_name, &length)) + * { + * // получено + * } + * + * // Пример 2: Перебрать все объекты в сообщении + * uint8_t obj_id, obj_length; + * char obj_data[256]; + * + * int obj_count = MB_GetNumberOfObjects(&MODBUS_MSG); + * printf("Total objects: %d\n", obj_count); + * + * for(int i = 0; i < obj_count; i++) + * { + * if(MB_GetObjectByIndex(&MODBUS_MSG, i, &obj_id, obj_data, &obj_length)) + * { + * // получено + * } + * } + * @endcode + */ + +/* Получить количество объектов в сообщении */ +int MB_GetNumberOfObjects(RS_MsgTypeDef *modbus_msg); +/* Найти объект по ID в сообщении */ +int MB_FindObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length); +/* Получить объект по индексу в сообщении */ +int MB_GetObjectByIndex(RS_MsgTypeDef *modbus_msg, int index, uint8_t *obj_id, char *obj_data, uint8_t *obj_length); + + +/** MODBUS_REQ_DEFID_API +* @} +*/ + + + /* Записать Один Объект Идентификатора в массив данных */ void MB_WriteSingleObjectToMessage(char *mbdata, unsigned *ind, MB_DeviceObjectTypeDef *obj); /* Записать Массив Объектов Идентификатора в массив данных */ @@ -88,8 +134,8 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj); @{ */ -/* Обработать функцию Read Device Identification (43/14 - 0x2B/0E) */ -uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg); +/* Обработать функцию Read Device Identifications (43/14 - 0x2B/0E) */ +uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg); /** MODBUS_CMD_PROCESS_FUNCTIONS * @} */ diff --git a/Inc/modbus_diag.h b/Inc/modbus_diag.h index ccba95c..b338c67 100644 --- a/Inc/modbus_diag.h +++ b/Inc/modbus_diag.h @@ -53,12 +53,39 @@ extern MB_DiagnosticsInfoTypeDef MB_DIAG; ///////////////////////////////////////////////////////////////////// /////////////////////////---FUNCTIONS---///////////////////////////// - + /* Инициализация диагностических счетчиков */ void MB_DiagnosticsInit(void); +/** + * @addtogroup MODBUS_REQ_DIAG_API API for Diagnostics + * @ingroup MODBUS_REQUEST_MSG + * @brief API для чтения диагностической информации из ответа в режиме мастер + * @details Примеры использования: + * + * @code + * Получить данные диагностики (значение счетчика) + * uint16_t counter_value; + * if(MB_GetDiagnosticResponse(&MODBUS_MSG, &counter_value)) + * { + * printf("Counter value: %d\n", counter_value); + * } + * @endcode + */ +int MB_GetDiagnosticResponse(RS_MsgTypeDef *modbus_msg, uint16_t *data); +/** MODBUS_REQ_DIAG_API + * @} + */ + + + + + + + + /** -* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS Modbus Data Access +* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS @{ */ @@ -79,7 +106,7 @@ MB_DeviceModeTypeDef MB_GetDeviceMode(void); */ /* Обработка команды диагностики (0x08) */ -uint8_t MB_Proccess_Diagnostics(RS_MsgTypeDef *modbus_msg); +uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg); /** MODBUS_CMD_PROCESS_FUNCTIONS * @} diff --git a/Inc/modbus_holdregs.h b/Inc/modbus_holdregs.h index e4ffca4..3a4e253 100644 --- a/Inc/modbus_holdregs.h +++ b/Inc/modbus_holdregs.h @@ -30,11 +30,11 @@ @{ */ /* Обработать функцию Read Holding Registers (03 - 0x03) */ -uint8_t MB_Proccess_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg); +uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg); /* Обработать функцию Write Single Coils (06 - 0x06) */ -uint8_t MB_Proccess_Write_Single_Reg(RS_MsgTypeDef *modbus_msg); +uint8_t MB_Process_Write_Single_Reg(RS_MsgTypeDef *modbus_msg); /* Обработать функцию Write Multiple Register (16 - 0x10) */ -uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg); +uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg); /** MODBUS_CMD_PROCESS_FUNCTIONS * @} diff --git a/Inc/modbus_inputregs.h b/Inc/modbus_inputregs.h index 3dc3010..0aba2a6 100644 --- a/Inc/modbus_inputregs.h +++ b/Inc/modbus_inputregs.h @@ -24,13 +24,13 @@ //---------PROCESS MODBUS COMMAND FUNCTIONS--------- /** - * @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS Proccess Functions + * @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS Internal Process Functions * @ingroup MODBUS_FUNCTIONS * @brief Функции обработки запросов модбас @{ */ /* Обработать функцию Read Input Registers (04 - 0x04) */ -uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg); +uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg); /** MODBUS_CMD_PROCESS_FUNCTIONS * @} diff --git a/Inc/modbus_master.h b/Inc/modbus_master.h new file mode 100644 index 0000000..70505ac --- /dev/null +++ b/Inc/modbus_master.h @@ -0,0 +1,121 @@ +/** +****************************************************************************** +* @file modbus_master.h +* @brief Главный заголовочный файл Modbus библиотеки +****************************************************************************** +@addtogroup MODBUS_MASTER Modbus master funtions +@ingroup MODBUS_CMD_PROCESS_FUNCTIONS +@{ +****************************************************************************** +* @details +Модуль реализации обработки UART сообщение в режиме мастер +******************************************************************************/ +#ifndef __MODBUS_MASTER_H_ +#define __MODBUS_MASTER_H_ + +#include "rs_message.h" + +#ifdef MODBUS_ENABLE_MASTER +#define MODBUS_MODE_MASTER 1 +#endif + +/** + * @addtogroup MODBUS_REQUEST_MSG API for Master Requests + * @ingroup MODBUS_FUNCTIONS + * @brief Макросы для создания запросов в режиме мастер + * @details Примеры использования: + * + * // Чтение 10 holding registers начиная с адреса 0 + * RS_MsgTypeDef read_msg = MB_REQUEST_READ_HOLDING_REGS(1, 0, 10); + * + * // Запись одного coil + * RS_MsgTypeDef write_coil_msg = MB_REQUEST_WRITE_SINGLE_COIL(1, 5, 1); + * + * // Диагностический запрос + * RS_MsgTypeDef diag_msg = MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(1); + * + * // Идентификация устройства + * RS_MsgTypeDef dev_id_msg = MB_REQUEST_READ_DEVICE_ID_BASIC(1); + */ + +//---------КЛАССИЧЕСКИЕ ДАННЫЕ----------- +RS_MsgTypeDef MB_REQUEST_READ_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity); +RS_MsgTypeDef MB_REQUEST_READ_DISCRETE_INPUTS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity); +RS_MsgTypeDef MB_REQUEST_READ_HOLDING_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity); +RS_MsgTypeDef MB_REQUEST_READ_INPUT_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity); +RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_COIL(uint8_t slave_addr, uint16_t coil_addr, uint8_t value); +RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_REG(uint8_t slave_addr, uint16_t reg_addr, uint16_t value); +RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint8_t *coils_data); +RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint16_t *regs_data); + +//---------ДИАГНОСТИЧЕСКИЕ ДАННЫЕ----------- +RS_MsgTypeDef MB_REQUEST_DIAGNOSTIC_QUERY(uint8_t slave_addr, uint16_t sub_function, uint16_t data); +RS_MsgTypeDef MB_REQUEST_RETURN_QUERY_DATA(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_RESTART_COMMUNICATIONS(uint8_t slave_addr, uint16_t data); +RS_MsgTypeDef MB_REQUEST_RETURN_DIAGNOSTIC_REGISTER(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_FORCE_LISTEN_ONLY_MODE(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_RETURN_BUS_COMMUNICATION_ERROR_COUNT(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_EXCEPTION_ERROR_COUNT(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_MESSAGE_COUNT(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NO_RESPONSE_COUNT(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NAK_COUNT(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_BUSY_COUNT(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_RETURN_BUS_CHARACTER_OVERRUN_COUNT(uint8_t slave_addr); + +//---------ИДЕНТИФИКАТОРЫ МОДБАС----------- +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_BASIC(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_REGULAR(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_EXTENDED(uint8_t slave_addr); +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_SPECIFIC(uint8_t slave_addr, uint8_t object_id); +/** MODBUS_REQUEST_MSG + * @} + */ + + +/** + * @addtogroup MODBUS_REGS_API API for Registers + * @ingroup MODBUS_REQUEST_MSG + * @brief API для чтения регистров из ответа в режиме мастер + * @details Примеры использования: + * + * @code + * // Пример: Запросили 10 регистров с адреса 100, хотим получить значение регистра 105 + * uint16_t reg_value; + * if(MB_GetRegisterValue(&MODBUS_MSG, 105, ®_value)) + * { + * printf("Register 105 value: %d\n", reg_value); + * } + * + * // Пример: Получить все запрошенные регистры + * for(int addr = MODBUS_MSG.Addr; addr < MODBUS_MSG.Addr + MODBUS_MSG.Qnt; addr++) + * { + * uint16_t value; + * if(MB_GetRegisterValue(&MODBUS_MSG, addr, &value)) + * { + * printf("Register %d: %d\n", addr, value); + * } + * } + * @endcode + */ + +int MB_GetRegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value); + + +/** MODBUS_REQ_REGS_API + * @} + */ + + +/* Сбор сообщения в буфер UART в режиме мастер (фрейм мастера из msg -> uart) */ +RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff); +/* Парс сообщения в режиме мастер (фрейм слейва из uart -> msg) */ +RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff); + + +#endif //__MODBUS_MASTER_H_ + +/** MODBUS_MASTER + * @} + */ \ No newline at end of file diff --git a/Inc/modbus_slave.h b/Inc/modbus_slave.h new file mode 100644 index 0000000..810d7ef --- /dev/null +++ b/Inc/modbus_slave.h @@ -0,0 +1,34 @@ +/** +****************************************************************************** +* @file modbus_slave.h +* @brief Главный заголовочный файл Modbus библиотеки +****************************************************************************** +@addtogroup MODBUS_SLAVE Modbus slave funtions +@ingroup MODBUS_CMD_PROCESS_FUNCTIONS +@{ +****************************************************************************** +* @details +Модуль реализации обработки UART сообщение в режиме слейв +******************************************************************************/ +#ifndef __MODBUS_SLAVE_H_ +#define __MODBUS_SLAVE_H_ + +#include "rs_message.h" + + +#ifdef MODBUS_ENABLE_SLAVE +#define MODBUS_MODE_SLAVE 0 +#endif + +/* Ответ на сообщение в режиме слейва */ +RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg); +/* Сбор сообщения в буфер UART в режиме слейв (фрейм слейва из msg -> uart) */ +RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff); +/* Парс сообщения в режиме слейв (фрейм мастера из uart -> msg) */ +RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff); + +#endif //__MODBUS_SLAVE_H_ + +/** MODBUS_MASTER + * @} + */ \ No newline at end of file diff --git a/Inc/rs_message.h b/Inc/rs_message.h index 0625fd4..98b9ea5 100644 --- a/Inc/rs_message.h +++ b/Inc/rs_message.h @@ -40,10 +40,6 @@ #error Define MSG_SIZE_MAX (Maximum size of message). This is necessary to create buffer for UART. #endif -#ifndef RX_FIRST_PART_SIZE -#error Define RX_FIRST_PART_SIZE (Size of first part of message). This is necessary to receive the first part of the message, from which determine the size of the remaining part of the message. -#endif - /** * @cond Заглушки и внутренний недокументированный стаф */ @@ -55,14 +51,14 @@ #define RS_Set_Free(_hRS_) _hRS_->f.RS_Busy = 0 #define RS_Set_Busy(_hRS_) _hRS_->f.RS_Busy = 1 -#define RS_Set_RX_Flags(_hRS_) _hRS_->f.RX_Busy = 1; _hRS_->f.RX_Done = 0; _hRS_->f.RX_Half = 0 +#define RS_Set_RX_Flags(_hRS_) _hRS_->f.RX_Busy = 1; _hRS_->f.RX_Done = 0; #define RS_Set_RX_Active_Flags(_hRS_) _hRS_->f.RX_Ongoing = 1 #define RS_Set_TX_Flags(_hRS_) _hRS_->f.TX_Busy = 1; _hRS_->f.TX_Done = 0 #define RS_Reset_RX_Active_Flags(_hRS_) _hRS_->f.RX_Ongoing = 0; _hRS_->f.RX_Continue = 0; -#define RS_Reset_RX_Flags(_hRS_) RS_Reset_RX_Active_Flags(_hRS_); _hRS_->f.RX_Busy = 0; _hRS_->f.RX_Done = 0; _hRS_->f.RX_Half = 0 +#define RS_Reset_RX_Flags(_hRS_) RS_Reset_RX_Active_Flags(_hRS_); _hRS_->f.RX_Busy = 0; _hRS_->f.RX_Done = 0; #define RS_Reset_TX_Flags(_hRS_) _hRS_->f.TX_Busy = 0; _hRS_->f.TX_Done = 0 #define RS_Set_RX_End_Flag(_hRS_) _hRS_->f.RX_Done = 1; @@ -79,13 +75,6 @@ #define RS_Is_RX_Busy(_hRS_) (_hRS_->f.RX_Busy == 1) #define RS_Is_TX_Busy(_hRS_) (_hRS_->f.TX_Busy == 1) -// направление передачи rs485 -#ifndef RS_EnableReceive -#define RS_EnableReceive() -#endif -#ifndef RS_EnableTransmit -#define RS_EnableTransmit() -#endif #ifndef printf_rs_err #define printf_rs_err(...) @@ -122,10 +111,20 @@ static int dummy; #include "mylibs_include.h" #endif + #ifndef RS_USER_VARS_NUMB #define RS_USER_VARS_NUMB 0 #endif /** @endcond */ + + +// направление передачи rs485 +#ifndef RS_EnableReceive +#define RS_EnableReceive() ///< Функция изменения направления передачи на ПРИЕМ для RS-485 +#endif +#ifndef RS_EnableTransmit +#define RS_EnableTransmit() ///< Функция изменения направления передачи на ПЕРЕДАЧУ для RS-485 +#endif ////////////////////////////---DEFINES---//////////////////////////// @@ -151,61 +150,44 @@ typedef enum // RS_StatusTypeDef }RS_StatusTypeDef; -#define RS_MASTER_START 0x3 +#define RS_MASTER_MODE_START 0x3 ///< Начало режимов мастера (до него - режим слейв) /** @brief Enums for RS Modes */ typedef enum // RS_ModeTypeDef { - RS_SLAVE_ALWAYS_WAIT = 0x01, ///< Slave mode with infinity waiting - RS_SLAVE_TIMEOUT_WAIT = 0x02, ///< Slave mode with waiting with timeout -// RS_MASTER = 0x03, ///< Master mode + RS_SLAVE_ALWAYS_WAIT = 0x01, ///< Слейв в постоянном ожидании + RS_RESERVED = 0x02, ///< резерв + RS_MASTER_REQUEST = 0x03, ///< Мастер с ручным запросом + //RS_MASTER_POLLING = 0x04, ///< Мастер с опросом в фоновом режиме }RS_ModeTypeDef; -/** @brief Enums for RS UART Modes */ -typedef enum // RS_ITModeTypeDef -{ - BLCK_MODE = 0x00, ///< Blocking mode - IT_MODE = 0x01, ///< Interrupt mode -}RS_ITModeTypeDef; - /** @brief Enums for Abort modes */ typedef enum // RS_AbortTypeDef { - ABORT_TX = 0x01, ///< Abort transmit - ABORT_RX = 0x02, ///< Abort receive - ABORT_RX_TX = 0x03, ///< Abort receive and transmit - ABORT_RS = 0x04, ///< Abort uart and reset RS structure + ABORT_TX = 0x01, ///< Отменить передачу + ABORT_RX = 0x02, ///< Отменить прием + ABORT_RX_TX = 0x03, ///< Отменить прием и передачу + ABORT_RS = 0x04, ///< Отменить любую работу UART в целом }RS_AbortTypeDef; -/** @brief Enums for RX Size modes */ -typedef enum // RS_RXSizeTypeDef -{ - RS_RX_Size_Const = 0x01, ///< size of receiving message is constant - RS_RX_Size_NotConst = 0x02, ///< size of receiving message isnt constant -}RS_RXSizeTypeDef; - - //-----------STRUCTURE FOR HANDLE RS------------ /** @brief Struct for flags RS */ typedef struct { - unsigned RX_Half:1; ///< flag: 0 - receiving msg before ByteCnt, 0 - receiving msg after ByteCnt + unsigned RS_Busy:1; ///< 1 - RS занят, 0 - RS свободен + unsigned RX_Ongoing:1; ///< 1 - Прием данных в активном состоянии, 0 - Ожидаем начало приема данных - unsigned RS_Busy:1; ///< flag: 1 - RS is busy, 0 - RS isnt busy - unsigned RX_Ongoing:1; ///< flag: 1 - receiving data right now, 0 - waiting for receiving data + unsigned RX_Busy:1; ///< 1 - Режим приема активен, 0 - Прием не активен + unsigned TX_Busy:1; ///< 1 - Режим передачи активен, 0 - Прием не активен - unsigned RX_Busy:1; ///< flag: 1 - receiving is active, 0 - receiving isnt active - unsigned TX_Busy:1; ///< flag: 1 - transmiting is active, 0 - transmiting isnt active - - unsigned RX_Done:1; ///< flag: 1 - receiving is done, 0 - receiving isnt done - unsigned TX_Done:1; ///< flag: 1 - transmiting is done, 0 - transmiting isnt done + unsigned RX_Done:1; ///< 1 - Прием закончен, 0 - Прием еще в процессе или не инициализирован + unsigned TX_Done:1; ///< 1 - Передача закончена, 0 - Передача еще в процессе или не инициализирована - // setted by user - unsigned RX_Continue:1; ///< flag: 0 - continue receiving, 0 - start receiving from ind = 0 - unsigned MessageHandled:1; ///< flag: 1 - RS command is handled, 0 - RS command isnt handled yet - unsigned EchoResponse:1; ///< flag: 1 - response with received msg, 0 - response with own msg - unsigned DeferredResponse:1; ///< flag: 1 - response not in interrupt, 0 - response in interrupt - unsigned DataUpdated:1; ///< flag: 1 - Received command to write colis/resg - unsigned ReInit_UART:1; ///< flag: 1 - need to reinitialize uart, 0 - nothing + // Выставление следующие флагов определяет пользователь + unsigned RX_Continue:1; ///< 0 - Продолжить принимать, 0 - Начать прием сначала + unsigned MessageHandled:1; ///< 1 - Обработка запроса успешна, 0 - Обработка запроса в процессе или ошибка + unsigned EchoResponse:1; ///< 1 - Ответить эхом, 0 - Ответить своим сообщением + unsigned DeferredResponse:1; ///< 1 - Не начинать передачу в IT, 0 - Ответить в прерывании + unsigned DataUpdated:1; ///< 1 - Данные были обновлены }RS_FlagsTypeDef; @@ -217,25 +199,24 @@ typedef struct typedef struct // RS_HandleTypeDef { /* MESSAGE */ - uint8_t ID; ///< ID of RS "channel" - RS_MsgTypeDef *pMessagePtr; ///< pointer to message struct - uint8_t *pBufferPtr; ///< pointer to message buffer + uint8_t ID; ///< ID хендла + RS_MsgTypeDef *pMessagePtr; ///< Указатель на структуру протокола + uint8_t *pBufferPtr; ///< Указатеь на буфер UART int32_t RS_Message_Size; ///< size of whole message, not only data /* HANDLERS and SETTINGS */ - UART_HandleTypeDef *huart; ///< handler for used uart - TIM_HandleTypeDef *htim; ///< handler for used tim - RS_ModeTypeDef sRS_Mode; ///< setting: slave or master @ref RS_ModeTypeDef - RS_ITModeTypeDef sRS_IT_Mode; ///< setting: 1 - IT mode, 0 - Blocking mode - uint16_t sRS_Timeout; ///< setting: timeout in ms - RS_RXSizeTypeDef sRS_RX_Size_Mode; ///< setting: 1 - not const, 0 - const + UART_HandleTypeDef *huart; ///< Хендл UART + TIM_HandleTypeDef *htim; ///< Хендл TIM + RS_ModeTypeDef sRS_Mode; ///< Настройка: слейв/мастер @ref RS_ModeTypeDef + uint16_t sRS_Timeout; ///< Настройка: Таймаут в тиках таймера + void (*pCallback)(void*, void*); ///< Указатель на коллбек: принят ответ в режиме мастер /* FLAGS */ - RS_FlagsTypeDef f; ///< These flags for controling receive/transmit + RS_FlagsTypeDef f; ///< Флаги для контроля приема/передачи /* RS STATUS */ - uint32_t lastPacketTick; - RS_StatusTypeDef RS_STATUS; ///< RS status + uint32_t lastPacketTick; ///< Время последнего принятого пакета + RS_StatusTypeDef RS_STATUS; ///< Статус RS TrackerTypeDef(RS_USER_VARS_NUMB) rs_err; }RS_HandleTypeDef; @@ -249,47 +230,53 @@ extern RS_HandleTypeDef hmodbus1; ///////////////////////////---FUNCTIONS---/////////////////////////// //----------------FUNCTIONS FOR PROCESSING MESSAGE------------------- /*--------------------Defined by users purposes--------------------*/ -/* Respond accord to received message */ +/* Пользовательская функция для ответа на запрос по UART */ RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); -/* Collect message in buffer to transmit it */ +/* Пользовательская функция для обработки принятого ответа по UART */ +__weak RS_StatusTypeDef RS_Response_Callback(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); + +/* Пользовательская функция для сбора сообщения в буфер UART */ RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff); -/* Parse message from buffer to process it */ +/* Пользовательская функция для парса сообщения из буфера UART */ RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff); //-------------------------GENERAL FUNCTIONS------------------------- /*-----------------Should be called from main code-----------------*/ -/* Start receive IT */ +/* Начать прием по прерываниям */ RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); -/* Start transmit IT */ +/* Начать передачу по прерываниям */ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); -/* Initialize UART and handle RS stucture */ +/* Инициалазация структуры @ref RS_HandleTypeDef */ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr); -/* ReInitialize UART and RS receive */ -HAL_StatusTypeDef RS_ReInit_UART(RS_HandleTypeDef *hRS, UART_HandleTypeDef *suart); - -/* Abort RS/UART */ +/* Отменить прием/передачу RS/UART */ RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode); //-------------------------GENERAL FUNCTIONS------------------------- //------------------------------------------------------------------- //--------------------CALLBACK/HANDLER FUNCTIONS--------------------- -/* Handle for starting receive */ +/* Обработчик для начала приема */ RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); -/* Handle for starting transmit */ +/* Обработчик для начала передачи */ RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); -/* UART TX Callback: define behaviour after transmiting message */ +/* UART TX Callback: коллбек после окончания передачи */ RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS); -/* Handler for UART */ +/* Обработчик прерывания UART */ void RS_UART_Handler(RS_HandleTypeDef *hRS); -/* Handler for TIM */ +/* Обработчик прерывания TIM */ void RS_TIM_Handler(RS_HandleTypeDef *hRS); +/* Запуск таймаута приема. */ +RS_StatusTypeDef RS_Timeout_Start(RS_HandleTypeDef *hRS); +/* Остановка таймаута приема. */ +RS_StatusTypeDef RS_Timeout_Stop(RS_HandleTypeDef *hRS); +/* Обновление (сброс) таймаута приема. */ +RS_StatusTypeDef RS_Timeout_Update(RS_HandleTypeDef *hRS); //--------------------CALLBACK/HANDLER FUNCTIONS--------------------- ///////////////////////////---FUNCTIONS---/////////////////////////// diff --git a/README.md b/README.md index 71b23e6..ff3ff85 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,8 @@ ProjectRoot/ 3.3. В `modbus_config.h` укажите параметры устройства 3.4. Инициализация в коде - - В `main()` после инициализации HAL: + + Чтобы настроить Slave-режим `main()` после инициализации HAL: ```c #include "modbus.h" @@ -97,10 +97,11 @@ ProjectRoot/ MX_TIM3_Init(); // Инициализация Modbus - MODBUS_SetupHardware(&hmodbus1, &mb_huart, &mb_htim); + MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim); + MODBUS_Config(&hmodbus1, 1, 1000, MODBUS_MODE_SLAVE); // Запуск приема Modbus - MODBUS_SlaveStart(&hmodbus1, &MODBUS_MSG); + MODBUS_SlaveStart(&hmodbus1, NULL); while (1) { @@ -109,6 +110,34 @@ ProjectRoot/ } ``` + Чтобы настроить Master-режим `main()` после инициализации HAL: + + ```c + #include "modbus.h" + + int main(void) + { + // Инициализация HAL + HAL_Init(); + SystemClock_Config(); + MX_GPIO_Init(); + MX_USART1_UART_Init(); + MX_TIM3_Init(); + + // Инициализация Modbus + MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim); + MODBUS_Config(&hmodbus1, 0, 1000, MODBUS_MODE_MASTER); + + // Запуск приема Modbus + // Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук + RS_MsgTypeDef msg = MB_MASTER_READ_HOLDING_REGS(1, 0, 10); + MODBUS_MasterRequest(&hmodbus1, &msg, &callback_func); + void callback_func(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) + { + // modbus_msg содержит ответ от устройства + } + } + ``` 3.5. Настройка карты данных В `modbus_data.h` настройте регистры и coils под ваше устройство: @@ -178,7 +207,7 @@ ProjectRoot/ 5. **Обновление библиотеки**: -После обновления субмодуля из Git, исходные файлы библиотеки будут обновлены, но ваши конфиги останутся в целевой папке и не перезапишутся: +После обновления субмодуля из Git, исходные файлы библиотеки будут обновлены, и ваши конфиги вне субмодуля не перезапишутся: ```bash git submodule update --remote diff --git a/Src/modbus.c b/Src/modbus.c index 06fe021..a39cf08 100644 --- a/Src/modbus.c +++ b/Src/modbus.c @@ -12,13 +12,7 @@ - MODBUS_FirstInit() — Инициализация Modbus (подключение UART, TIM) - MODBUS_Config() — Конфигурацмя Modbus (ID, Timeout). - MODBUS_SlaveStart() — Запуск Modbus как Slave. - -### Функции для Modbus -- MB_Slave_Response() — Ответ на запрос -- MB_Slave_Collect_Message() — Сбор сообщения в режиме слейва. -- MB_Slave_Parse_Message() — Парс сообщения в режиме слейва. -- MB_Master_Collect_Message() — Сбор сообщения в режиме мастера -- MB_Master_Parse_Message() — Парс сообщения в режиме мастера +- MODBUS_MasterRequest() — Отправить запрос в MODBUS как Master. ### Функции для работы с RS (UART): - RS_Parse_Message() / RS_Collect_Message() — Парсинг и сборка сообщения. @@ -33,6 +27,8 @@ RS_MsgTypeDef MODBUS_MSG; ///< Default Message Struct for Modbus /* DEFINE DATA FOR MODBUS */ MB_DataStructureTypeDef MB_DATA = {0};; ///< Coils & Registers +static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg); + //------------------------------------------------------------------- //-----------------------------FOR USER------------------------------ /** @@ -55,7 +51,6 @@ HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef 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 hmodbus->RS_STATUS = RS_Init(hmodbus, huart, htim, 0); @@ -80,19 +75,22 @@ HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t { return HAL_ERROR; } - if((ID < 1) || (ID > 247)) + if(!master) { - return HAL_ERROR; + if((ID < 1) || (ID > 247)) + { + return HAL_ERROR; + } + hmodbus->ID = ID; } - //-----------SETUP MODBUS------------- - // set up modbus: MB_RX_Size_NotConst and Timeout enable - hmodbus->ID = ID; + else + hmodbus->ID = 0; + hmodbus->sRS_Timeout = Timeout; if(master) - hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT; + hmodbus->sRS_Mode = RS_MASTER_REQUEST; else hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT; - hmodbus->sRS_RX_Size_Mode = RS_RX_Size_NotConst; return HAL_OK; } @@ -111,7 +109,7 @@ HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mo return HAL_ERROR; } - if(hmodbus->sRS_Mode >= RS_MASTER_START) + if(hmodbus->sRS_Mode >= RS_MASTER_MODE_START) { return HAL_ERROR; } @@ -129,467 +127,57 @@ HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mo return HAL_ERROR; } - - - -//------------------------------------------------------------------- -//-----------------------------INTERNAL------------------------------ - /** - * @brief Ответ на сообщение в режиме слейва. + * @brief Реквест мастера модбас. * @param hmodbus Указатель на хендлер RS. - * @param modbus_msg Указатель на структуру сообщения. - * @return RS_RES Статус о результате ответа на комманду. + * @param modbus_msg Указатель на структуру сообщения + * @details Конфигурирует ID, таймаут и режим hmodbus */ -static RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) +HAL_StatusTypeDef MODBUS_MasterRequest(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, void (*pClbk)(RS_HandleTypeDef*, RS_MsgTypeDef*)) { - RS_StatusTypeDef MB_RES = 0; - hmodbus->f.MessageHandled = 0; - hmodbus->f.EchoResponse = 0; - RS_Reset_TX_Flags(hmodbus); // reset flag for correct transmit - - MB_Diagnostics_BusMessageCnt(); - if(hmodbus->ID == 0 || modbus_msg->MbAddr == 0) + if(hmodbus == NULL) { - MB_Diagnostics_SlaveNoResponseCnt(); // <-- Устройство не отвечает на широковещательные сообщения - hmodbus->RS_STATUS = RS_SKIP; - return RS_Handle_Receive_Start(hmodbus, modbus_msg); + return HAL_ERROR; } - MB_Diagnostics_SlaveMessageCnt(); - - if(modbus_msg->Func_Code < ERR_VALUES_START)// if no errors after parsing + if(modbus_msg == NULL) { - switch (modbus_msg->Func_Code) - { - // Read Coils - case MB_R_COILS: - hmodbus->f.MessageHandled = MB_Proccess_Read_Coils(hmodbus->pMessagePtr); - break; - - // Read Hodling Registers - case MB_R_HOLD_REGS: - hmodbus->f.MessageHandled = MB_Proccess_Read_Hold_Regs(hmodbus->pMessagePtr); - break; - case MB_R_IN_REGS: - hmodbus->f.MessageHandled = MB_Proccess_Read_Input_Regs(hmodbus->pMessagePtr); - break; - - - // Write Single Coils - case MB_W_COIL: - hmodbus->f.MessageHandled = MB_Proccess_Write_Single_Coil(hmodbus->pMessagePtr); - if(hmodbus->f.MessageHandled) - { - hmodbus->f.DataUpdated = 1; - hmodbus->f.EchoResponse = 1; - hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes) - } - break; - - case MB_W_HOLD_REG: - hmodbus->f.MessageHandled = MB_Proccess_Write_Single_Reg(hmodbus->pMessagePtr); - if(hmodbus->f.MessageHandled) - { - hmodbus->f.DataUpdated = 1; - hmodbus->f.EchoResponse = 1; - hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes) - } - break; - - // Write Multiple Coils - case MB_W_COILS: - hmodbus->f.MessageHandled = MB_Write_Miltuple_Coils(hmodbus->pMessagePtr); - if(hmodbus->f.MessageHandled) - { - hmodbus->f.DataUpdated = 1; - hmodbus->f.EchoResponse = 1; - hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes) - } - break; - - // Write Multiple Registers - case MB_W_HOLD_REGS: - hmodbus->f.MessageHandled = MB_Proccess_Write_Miltuple_Regs(hmodbus->pMessagePtr); - if(hmodbus->f.MessageHandled) - { - hmodbus->f.DataUpdated = 1; - hmodbus->f.EchoResponse = 1; - hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes) - } - break; - - case MB_R_DEVICE_INFO: - 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 */ - } - - - // Проверяем режим устройства - если 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; - } - else - { - TrackerCnt_Ok(hmodbus->rs_err); - } - - - } - - // if we need response - check that transmit isnt busy - if( RS_Is_TX_Busy(hmodbus) ) - RS_Abort(hmodbus, ABORT_TX); // if tx busy - set it free - - // Transmit right there, or sets (fDeferredResponse) to transmit response in main code - if(hmodbus->f.DeferredResponse == 0) - { - MB_RES = RS_Handle_Transmit_Start(hmodbus, modbus_msg); - } - else - { - RS_Handle_Receive_Start(hmodbus, modbus_msg); - hmodbus->f.DeferredResponse = 0; - } - - hmodbus->RS_STATUS = MB_RES; - return MB_RES; -} - - - -/** - * @brief Сбор сообщения в буфер UART в режиме слейв. - * @param hmodbus Указатель на хендлер RS. - * @param modbus_msg Указатель на структуру сообщения. - * @param modbus_uart_buff Указатель на буффер UART. - * @return RS_RES Статус о результате заполнения буфера. - */ -static RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) -{ - int ind = 0; // ind for modbus-uart buffer - - if(hmodbus->f.EchoResponse && hmodbus->f.MessageHandled) // if echo response need - ind = hmodbus->RS_Message_Size; - else - { - //------INFO ABOUT DATA/MESSAGE------ - //-----------[first bytes]----------- - // set ID of message/user - modbus_uart_buff[ind++] = modbus_msg->MbAddr; - - // set dat or err response - modbus_uart_buff[ind++] = modbus_msg->Func_Code; - - if (modbus_msg->Func_Code < ERR_VALUES_START) // if no error occur - { - // fill modbus header - if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // devide identification header - { - modbus_uart_buff[ind++] = modbus_msg->DevId.MEI_Type; - modbus_uart_buff[ind++] = modbus_msg->DevId.ReadDevId; - modbus_uart_buff[ind++] = modbus_msg->DevId.Conformity; - modbus_uart_buff[ind++] = modbus_msg->DevId.MoreFollows; - modbus_uart_buff[ind++] = modbus_msg->DevId.NextObjId; - modbus_uart_buff[ind++] = modbus_msg->DevId.NumbOfObj; - - if (modbus_msg->ByteCnt > DATA_SIZE*2) // if ByteCnt less than DATA_SIZE - { - TrackerCnt_Err(hmodbus->rs_err); - return RS_COLLECT_MSG_ERR; - } - - - //---------------DATA---------------- - //-----------[data bytes]------------ - uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA; - for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data - { // set data - modbus_uart_buff[ind++] = *tmp_data_addr; - tmp_data_addr++; - } - - } - 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 - if (modbus_msg->ByteCnt <= DATA_SIZE*2) // if ByteCnt less than DATA_SIZE - modbus_uart_buff[ind++] = modbus_msg->ByteCnt; - else // otherwise return data_size err - { - TrackerCnt_Err(hmodbus->rs_err); - return RS_COLLECT_MSG_ERR; - } - - //---------------DATA---------------- - //-----------[data bytes]------------ - uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA; - for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data - { // set data - if (i%2 == 0) // HI byte - modbus_uart_buff[ind++] = (*tmp_data_addr)>>8; - else // LO byte - { - modbus_uart_buff[ind++] = *tmp_data_addr; - tmp_data_addr++; - } - } - - } - - } - else // if some error occur - { // send expection code - modbus_uart_buff[ind++] = modbus_msg->Except_Code; - } - } - if(ind < 0) - return RS_COLLECT_MSG_ERR; - - //---------------CRC---------------- - //---------[last 16 bytes]---------- - // calc crc of received data - uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind); - // write crc to message structure and modbus-uart buffer - modbus_msg->MB_CRC = CRC_VALUE; - modbus_uart_buff[ind++] = CRC_VALUE; - modbus_uart_buff[ind++] = CRC_VALUE >> 8; - - hmodbus->RS_Message_Size = ind; - - return RS_OK; // returns ok -} - -/** - * @brief Определить размер модбас запроса. - * @param hRS Указатель на хендлер RS. - * @param rx_data_size Указатель на переменную для записи кол-ва байт для принятия. - * @return RS_RES Статус о корректности рассчета кол-ва байт для принятия. - * @details Определение сколько байтов надо принять по протоколу. - */ -static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) -{ - RS_StatusTypeDef MB_RES = 0; - int mb_func_size = 0; - - if (modbus_msg->Func_Code == MB_R_DIAGNOSTIC) - { - mb_func_size = 1; + return HAL_ERROR; } - else if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) + if(hmodbus->sRS_Mode < RS_MASTER_MODE_START) { - mb_func_size = 0; - } - else if ((modbus_msg->Func_Code & ~ERR_VALUES_START) < 0x0F) - { - mb_func_size = 1; + return HAL_ERROR; } + + if(hmodbus->f.RS_Busy) + return HAL_BUSY; + + if(pClbk) // если задан используем пользовательский коллбек + hmodbus->pCallback = (void (*)(void*, void*))(pClbk); + else // иначе дефолтный + hmodbus->pCallback = (void (*)(void*, void*))(&MB_DefaultCallback); + + hmodbus->RS_STATUS = RS_Transmit_IT(hmodbus, modbus_msg); + + if(hmodbus->RS_STATUS == RS_OK) + return HAL_OK; 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; + return HAL_ERROR; } -/** - * @brief Парс сообщения в режиме слейв. - * @param hmodbus Указатель на хендлер RS. - * @param modbus_msg Указатель на структуру сообщения. - * @param modbus_uart_buff Указатель на буффер UART. - * @return RS_RES Статус о результате заполнения структуры. - * @details Заполнение структуры сообщения из буффера UART. - */ -static RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) + + +static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_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; - ind++; - } - else - { - 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; - } - - if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // if it device identification request - { - modbus_msg->DevId.MEI_Type = modbus_uart_buff[ind++]; - modbus_msg->DevId.ReadDevId = modbus_uart_buff[ind++]; - 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 - modbus_msg->Addr = modbus_uart_buff[ind++] << 8; - modbus_msg->Addr |= modbus_uart_buff[ind++]; - - // get address from CMD - modbus_msg->Qnt = modbus_uart_buff[ind++] << 8; - modbus_msg->Qnt |= modbus_uart_buff[ind++]; - } - - if((hmodbus->pMessagePtr->Func_Code == 0x0F) || (hmodbus->pMessagePtr->Func_Code == 0x10)) - hmodbus->pMessagePtr->ByteCnt = modbus_uart_buff[ind++]; - 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) - { - //check that data size is correct - if (modbus_msg->ByteCnt > DATA_SIZE*2) - { - 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; - for(int i = 0; i < modbus_msg->ByteCnt; i++) - { // set data - if (i%2 == 0) - *tmp_data_addr = ((uint16_t)modbus_uart_buff[ind++] << 8); - else - { - *tmp_data_addr |= modbus_uart_buff[ind++]; - tmp_data_addr++; - } - } - } - - //---------------CRC---------------- - //----------[last 16 bits]---------- - // calc crc of received data - uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind); - // get crc of received data - modbus_msg->MB_CRC = modbus_uart_buff[ind++]; - modbus_msg->MB_CRC |= modbus_uart_buff[ind++] << 8; - // compare crc - if (modbus_msg->MB_CRC != CRC_VALUE) - { - MB_Diagnostics_CommunicationErrorCnt(); - TrackerCnt_Err(hmodbus->rs_err); - modbus_msg->Func_Code |= ERR_VALUES_START; - } - - return RS_OK; + __NOP(); + return; } - - - - - - -/** - * @brief Сбор сообщения в буфер UART в режиме мастер. - * @param hmodbus Указатель на хендлер RS. - * @param modbus_msg Указатель на структуру сообщения. - * @param modbus_uart_buff Указатель на буффер UART. - * @return RS_RES Статус о результате заполнения буфера. - */ -static RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) -{ - return RS_PARSE_MSG_ERR; -} - - -/** - * @brief Парс сообщения в режиме мастер. - * @param hmodbus Указатель на хендлер RS. - * @param modbus_msg Указатель на структуру сообщения. - * @param modbus_uart_buff Указатель на буффер UART. - * @return RS_RES Статус о результате заполнения структуры. - */ -static RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) -{ - return RS_PARSE_MSG_ERR; -} - - - - - /* Реализация функций из rs_message.c для протокола */ RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) { - if(hmodbus->sRS_Mode >= RS_MASTER_START) + if(hmodbus->sRS_Mode >= RS_MASTER_MODE_START) { return RS_ERR; } @@ -598,7 +186,7 @@ RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_ms RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) { - if(hmodbus->sRS_Mode < RS_MASTER_START) + if(hmodbus->sRS_Mode < RS_MASTER_MODE_START) { return MB_Slave_Collect_Message(hmodbus, modbus_msg, modbus_uart_buff); } @@ -610,7 +198,7 @@ RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mo RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) { - if(hmodbus->sRS_Mode < RS_MASTER_START) + if(hmodbus->sRS_Mode < RS_MASTER_MODE_START) { return MB_Slave_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff); } diff --git a/Src/modbus_coils.c b/Src/modbus_coils.c index c6f6e7f..8c629a9 100644 --- a/Src/modbus_coils.c +++ b/Src/modbus_coils.c @@ -86,13 +86,48 @@ uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception) } + +/** + * @brief Получить состояние coil в ответе по его адресу + * @param modbus_msg Указатель на структуру сообщения + * @param coil_addr Адрес coil, состояние которого нужно получить + * @param coil_state Указатель для состояния coil (1 - ON, 0 - OFF) + * @return 1 - успех, 0 - ошибка или coil_addr вне диапазона запроса + */ +int MB_GetCoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state) +{ + if(modbus_msg == NULL || coil_state == NULL) + return 0; + + // Проверяем что coil_addr в пределах запрошенного диапазона + if(coil_addr < modbus_msg->Addr || coil_addr >= modbus_msg->Addr + modbus_msg->Qnt) + return 0; + + // Вычисляем индекс coil в полученных данных + uint16_t coil_index = coil_addr - modbus_msg->Addr; + + // Вычисляем байт и бит + uint8_t byte_index = coil_index / 8; + uint8_t bit_index = coil_index % 8; + + // Проверяем что байт существует в данных + if(byte_index >= modbus_msg->ByteCnt) + return 0; + + // Получаем байт и проверяем бит + uint8_t coil_byte = modbus_msg->DATA[byte_index] & 0xFF; + *coil_state = (coil_byte >> bit_index) & 0x01; + + return 1; +} + /** * @brief Обработать функцию Read Coils (01 - 0x01). * @param modbus_msg Указатель на структуру собщения modbus. * @return fMessageHandled Статус о результате обработки комманды. * @details Обработка команды Read Coils. */ -uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg) +uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg) { //---------CHECK FOR ERRORS---------- uint16_t *coils; @@ -145,7 +180,7 @@ uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg) * @return fMessageHandled Статус о результате обработки комманды. * @details Обработка команды Write Single Coils. */ -uint8_t MB_Proccess_Write_Single_Coil(RS_MsgTypeDef *modbus_msg) +uint8_t MB_Process_Write_Single_Coil(RS_MsgTypeDef *modbus_msg) { //---------CHECK FOR ERRORS---------- if ((modbus_msg->Qnt != 0x0000) && (modbus_msg->Qnt != 0xFF00)) @@ -238,10 +273,12 @@ uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg) } #else //MODBUS_ENABLE_COILS + +int MB_GetCoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state) {return 0;} MB_ExceptionTypeDef MB_Write_Coil_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal) {return ILLEGAL_FUNCTION;} uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception) {return 0;} -uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg) {return 0;} -uint8_t MB_Proccess_Write_Single_Coil(RS_MsgTypeDef *modbus_msg) {return 0;} +uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg) {return 0;} +uint8_t MB_Process_Write_Single_Coil(RS_MsgTypeDef *modbus_msg) {return 0;} uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg) {return 0;} #endif \ No newline at end of file diff --git a/Src/modbus_devid.c b/Src/modbus_devid.c index 8ed5ff6..9d08bfb 100644 --- a/Src/modbus_devid.c +++ b/Src/modbus_devid.c @@ -16,9 +16,115 @@ ******************************************************************************/ #include "modbus_devid.h" -#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATORS +#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS + +MB_DeviceIdentificationsTypeDef MB_DEVID; ///< Глобальная структура идентификаторов устройства + + +/** + * @brief Получить количество объектов в сообщении + * @param modbus_msg Указатель на структуру сообщения + * @return int Количество объектов + */ +int MB_GetNumberOfObjects(RS_MsgTypeDef *modbus_msg) +{ + return modbus_msg->DevId.NumbOfObj; +} + +/** + * @brief Найти объект по ID в сообщении + * @param modbus_msg Указатель на структуру сообщения + * @param obj_id ID искомого объекта + * @param obj_data Буфер для данных объекта (может быть NULL) + * @param obj_length Указатель для длины объекта + * @return int Найден ли объект (1 - да, 0 - нет) + */ +int MB_FindObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length) +{ + if((modbus_msg == NULL) || (obj_data == NULL)) + return 0; + + uint8_t *data = (uint8_t*)modbus_msg->DATA; + unsigned ind = 0; + + for(int i = 0; i < modbus_msg->DevId.NumbOfObj; i++) + { + uint8_t current_id = data[ind++]; + uint8_t current_length = data[ind++]; + + if(current_id == obj_id) + { + if(obj_length) + *obj_length = current_length; + + for(int j = 0; j < current_length; j++) + { + obj_data[j] = data[ind++]; + } + obj_data[current_length] = '\0'; // добавляем \0 + + return 1; + } + else + { + // Пропускаем данные этого объекта + ind += current_length; + } + } + + return 0; +} + +/** + * @brief Получить объект по индексу в сообщении + * @param modbus_msg Указатель на структуру сообщения + * @param index Индекс объекта (0..N-1) + * @param obj_id Указатель для ID объекта + * @param obj_data Буфер для данных объекта + * @param obj_length Указатель для длины объекта + * @return int Успешность получения (1 - получен, 0 - не найден) + */ +int MB_GetObjectByIndex(RS_MsgTypeDef *modbus_msg, int index, uint8_t *obj_id, char *obj_data, uint8_t *obj_length) +{ + if((modbus_msg == NULL) || (obj_data == NULL)) + return 0; + + if(index >= modbus_msg->DevId.NumbOfObj) + return 0; + + uint8_t *data = (uint8_t*)modbus_msg->DATA; + unsigned ind = 0; + + for(int i = 0; i <= index; i++) + { + uint8_t current_id = data[ind++]; + uint8_t current_length = data[ind++]; + + if(obj_id) + *obj_id = current_id; + if(obj_length) + *obj_length = current_length; + + if(i == index) + { + for(int j = 0; j < current_length; j++) + { + obj_data[j] = data[ind++]; + } + obj_data[current_length] = '\0'; // добавляем \0 + return 1; + } + else + { + // Пропускаем данные этого объекта + ind += current_length; + } + } + + return 0; +} + -MB_DeviceIdentificationTypeDef MB_DEVID; ///< Глобальная структура идентификаторов устройства /** @@ -88,16 +194,16 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj) /** - * @brief Обработать функцию Read Device Identification (43/14 - 0x2B/0E). + * @brief Обработать функцию Read Device Identifications (43/14 - 0x2B/0E). * @param modbus_msg Указатель на структуру собщения modbus. * @return fMessageHandled Статус о результате обработки комманды. * @details Обработка команды Write Single Register. */ -uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg) +uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg) { switch(modbus_msg->DevId.ReadDevId) { - case MB_BASIC_IDENTIFICATION: + case MB_BASIC_IDENTIFICATIONS: if (modbus_msg->DevId.NextObjId == 0) { modbus_msg->DevId.NextObjId = 0; @@ -107,7 +213,7 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg) modbus_msg->DevId.NumbOfObj = 3; break; - case MB_REGULAR_IDENTIFICATION: + case MB_REGULAR_IDENTIFICATIONS: if (modbus_msg->DevId.NextObjId == 0) { modbus_msg->DevId.NextObjId = 3; @@ -117,7 +223,7 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg) modbus_msg->DevId.NumbOfObj = 4; break; - case MB_EXTENDED_IDENTIFICATION: + case MB_EXTENDED_IDENTIFICATIONS: if(MODBUS_NUMB_OF_USEROBJECTS <= 0 || MODBUS_NUMB_OF_USEROBJECTS > 128) { return 0; @@ -132,7 +238,7 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg) modbus_msg->DevId.NumbOfObj = MODBUS_NUMB_OF_USEROBJECTS; break; - case MB_SPEDIFIC_IDENTIFICATION: + case MB_SPEDIFIC_IDENTIFICATIONS: MB_WriteObjectsToMessage(modbus_msg, modbus_msg->DevId.NextObjId); modbus_msg->DevId.NumbOfObj = 1; break; @@ -543,12 +649,15 @@ void MB_DeviceInentificationInit(void) } -#else //MODBUS_ENABLE_DEVICE_IDENTIFICATORS - +#else //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS +/* Получить количество объектов в сообщении */ +int MB_GetNumberOfObjects(RS_MsgTypeDef *modbus_msg) {return 0;} +int MB_FindObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length) {return 0;} +int MB_GetObjectByIndex(RS_MsgTypeDef *modbus_msg, int index, uint8_t *obj_id, char *obj_data, uint8_t *obj_length) {return 0;} void MB_WriteSingleObjectToMessage(char *mbdata, unsigned *ind, MB_DeviceObjectTypeDef *obj) {} void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj) {} -uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg) {return 0;} +uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg) {return 0;} void MB_DeviceInentificationInit(void) {} #endif \ No newline at end of file diff --git a/Src/modbus_diag.c b/Src/modbus_diag.c index 0e67a1d..b0d656d 100644 --- a/Src/modbus_diag.c +++ b/Src/modbus_diag.c @@ -35,6 +35,22 @@ void MB_DiagnosticsInit(void) MB_DIAG.Counters.BusCharacterOverrun = 0; } + +/** + * @brief Получить данные диагностики из сообщения (DATA[1]) + * @param modbus_msg Указатель на структуру сообщения + * @param data Указатель куда положить данные + * @return 1 - успех, 0 - ошибка + */ +int MB_GetDiagnosticResponse(RS_MsgTypeDef *modbus_msg, uint16_t *data) +{ + if(modbus_msg == NULL || data == NULL) + return 0; + + *data = modbus_msg->DATA[1]; + return 1; +} + /** * @brief Выставить бит в регистре диагностике * @param bit_num Номер бита для выставления (1-15, 0 бит нельзя выставить) @@ -71,7 +87,7 @@ int MB_Diagnostics_GetBit(int bit_num) * @param modbus_msg Указатель на структуру сообщения modbus * @return fMessageHandled Статус обработки команды */ -uint8_t MB_Proccess_Diagnostics(RS_MsgTypeDef *modbus_msg) +uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg) { uint16_t sub_function = modbus_msg->DATA[0]; uint16_t request_data = modbus_msg->DATA[1]; @@ -295,9 +311,10 @@ MB_DeviceModeTypeDef MB_GetDeviceMode(void) #else //MODBUS_ENABLE_DIAGNOSTICS void MB_DiagnosticsInit(void) {} +int MB_GetDiagnosticResponse(RS_MsgTypeDef *modbus_msg, uint16_t *data) {return 0;} int MB_Diagnostics_WriteBit(int bit_num, int bit_state) {return 0;} int MB_Diagnostics_GetBit(int bit_num) {return 0;} -uint8_t MB_Proccess_Diagnostics(RS_MsgTypeDef *modbus_msg) {return 0;} +uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg) {return 0;} void MB_Diagnostics_BusMessageCnt(void) {} void MB_Diagnostics_CommunicationErrorCnt(void) {} void MB_Diagnostics_ExceptionErrorCnt(void) {} diff --git a/Src/modbus_holdregs.c b/Src/modbus_holdregs.c index 9d048bc..64760b7 100644 --- a/Src/modbus_holdregs.c +++ b/Src/modbus_holdregs.c @@ -30,7 +30,7 @@ * @return fMessageHandled Статус о результате обработки комманды. * @details Обработка команды Read Holding Registers. */ -uint8_t MB_Proccess_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg) +uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg) { //---------CHECK FOR ERRORS---------- // get origin address for data @@ -58,7 +58,7 @@ uint8_t MB_Proccess_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg) * @return fMessageHandled Статус о результате обработки комманды. * @details Обработка команды Write Single Register. */ -uint8_t MB_Proccess_Write_Single_Reg(RS_MsgTypeDef *modbus_msg) +uint8_t MB_Process_Write_Single_Reg(RS_MsgTypeDef *modbus_msg) { // get origin address for data uint16_t *pHoldRegs; @@ -77,7 +77,7 @@ uint8_t MB_Proccess_Write_Single_Reg(RS_MsgTypeDef *modbus_msg) * @return fMessageHandled Статус о результате обработки комманды. * @details Обработка команды Write Multiple Registers. */ -uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg) +uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg) { //---------CHECK FOR ERRORS---------- if (modbus_msg->Qnt*2 != modbus_msg->ByteCnt) @@ -102,8 +102,8 @@ uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg) #else //MODBUS_ENABLE_HOLDINGS -uint8_t MB_Proccess_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg) {return 0;} -uint8_t MB_Proccess_Write_Single_Reg(RS_MsgTypeDef *modbus_msg) {return 0;} -uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg) {return 0;} +uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg) {return 0;} +uint8_t MB_Process_Write_Single_Reg(RS_MsgTypeDef *modbus_msg) {return 0;} +uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg) {return 0;} #endif \ No newline at end of file diff --git a/Src/modbus_inputregs.c b/Src/modbus_inputregs.c index 2db5695..3bc34c9 100644 --- a/Src/modbus_inputregs.c +++ b/Src/modbus_inputregs.c @@ -20,7 +20,7 @@ * @return fMessageHandled Статус о результате обработки комманды. * @details Обработка команды Read Input Registers. */ -uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) +uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) { //---------CHECK FOR ERRORS---------- // get origin address for data @@ -47,6 +47,6 @@ uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) #else //MODBUS_ENABLE_INPUTS -uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) {return 0;} +uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) {return 0;} #endif \ No newline at end of file diff --git a/Src/modbus_master.c b/Src/modbus_master.c new file mode 100644 index 0000000..90cb7dd --- /dev/null +++ b/Src/modbus_master.c @@ -0,0 +1,519 @@ +/** +************************************************************************** +* @file modbus_master.c +* @brief Модуль для реализации мастера MODBUS. +************************************************************************** +* @details +Файл содержит реализацию функций для работы Modbus в режиме мастера. + +@section Функции и макросы + +- MB_Master_Collect_Message() — Сбор сообщения в режиме мастера +- MB_Master_Parse_Message() — Парс сообщения в режиме мастера +******************************************************************************/ +#include "modbus.h" + +#ifdef MODBUS_ENABLE_MASTER + + +/** + * @brief Получить значение регистра из ответа по его адресу + * @param modbus_msg Указатель на структуру сообщения + * @param reg_addr Адрес регистра, значение которого нужно получить + * @param reg_value Указатель для значения регистра + * @return 1 - успех, 0 - ошибка или reg_addr вне диапазона запроса + */ +int MB_GetRegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value) +{ + if(modbus_msg == NULL || reg_value == NULL) + return 0; + + // Проверяем что reg_addr в пределах запрошенного диапазона + if(reg_addr < modbus_msg->Addr || reg_addr >= modbus_msg->Addr + modbus_msg->Qnt) + return 0; + + // Вычисляем индекс регистра в полученных данных + uint16_t reg_index = reg_addr - modbus_msg->Addr; + + // Проверяем что регистр существует в данных + if(reg_index >= modbus_msg->ByteCnt / 2) + return 0; + + // Получаем значение регистра + *reg_value = modbus_msg->DATA[reg_index]; + + return 1; +} + +/** + * @brief Определить размер модбас запроса (МАСТЕР версия). + * @param hRS Указатель на хендлер RS. + * @param rx_data_size Указатель на переменную для записи кол-ва байт для принятия. + * @return RS_RES Статус о корректности рассчета кол-ва байт для принятия. + * @details Определение сколько байтов надо принять по протоколу. + */ +static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) +{ + RS_StatusTypeDef MB_RES = 0; + int mb_func_size = 0; + + + // Master mode - calculating response size from slave + if (modbus_msg->Func_Code & ERR_VALUES_START) + { + // Error response: [Addr][Func|0x80][ExceptCode][CRC] + mb_func_size = -1; // Only Exception Code + } + else if (modbus_msg->Func_Code == MB_R_DIAGNOSTIC) + { + // Diagnostics response: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO] + mb_func_size = 1; + } + else if (modbus_msg->Func_Code == MB_R_DEVICE_INFO) + { + // Device identifications: variable size, need to read first to determine + mb_func_size = 0; // Will be determined after reading header + } + else + { + switch (modbus_msg->Func_Code & ~ERR_VALUES_START) + { + case 0x01: // Read Coils + case 0x02: // Read Discrete Inputs + case 0x03: // Read Holding Registers + case 0x04: // Read Input Registers + // Response: [ByteCount][Data...] + mb_func_size = modbus_msg->ByteCnt + 2; // ByteCount + variable data + break; + + case 0x05: // Write Single Coil + case 0x06: // Write Single Register + // Echo response: [Addr][Value][CRC] + mb_func_size = 4; // Address(2) + Value(2) + break; + + case 0x0F: // Write Multiple Coils + case 0x10: // Write Multiple Registers + // Echo response: [Addr][Qty][CRC] + mb_func_size = 4; // Address(2) + Quantity(2) + break; + + default: + mb_func_size = 0; + } + } + + mb_func_size = RX_FIRST_PART_SIZE + mb_func_size; // size of whole message + return mb_func_size; +} + +/** + * @brief Сбор сообщения в буфер UART в режиме мастер (фрейм мастера из msg -> uart). + * @param hmodbus Указатель на хендлер RS. + * @param modbus_msg Указатель на структуру сообщения. + * @param modbus_uart_buff Указатель на буффер UART. + * @return RS_RES Статус о результате заполнения буфера. + */ +RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) +{ + int ind = 0; // ind for modbus-uart buffer + + //------INFO ABOUT DATA/MESSAGE------ + //-----------[first bytes]----------- + // set ID of slave device + modbus_uart_buff[ind++] = modbus_msg->MbAddr; + + // set function code + modbus_uart_buff[ind++] = modbus_msg->Func_Code; + + if(modbus_msg->Func_Code < ERR_VALUES_START) // if no error occur + { + // fill modbus header + if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // device identifications request + { + modbus_uart_buff[ind++] = modbus_msg->DevId.MEI_Type; + modbus_uart_buff[ind++] = modbus_msg->DevId.ReadDevId; + modbus_uart_buff[ind++] = modbus_msg->DevId.NextObjId; + } + else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC) + { + // Diagnostics: [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 // classic modbus request + { + // set address + modbus_uart_buff[ind++] = modbus_msg->Addr >> 8; + modbus_uart_buff[ind++] = modbus_msg->Addr & 0xFF; + + // set quantity + modbus_uart_buff[ind++] = modbus_msg->Qnt >> 8; + modbus_uart_buff[ind++] = modbus_msg->Qnt & 0xFF; + + // for write multiple functions + if((modbus_msg->Func_Code == 0x0F) || (modbus_msg->Func_Code == 0x10)) + { + modbus_uart_buff[ind++] = modbus_msg->ByteCnt; + + // write data bytes + uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA; + for(int i = 0; i < modbus_msg->ByteCnt; i++) + { + modbus_uart_buff[ind++] = tmp_data_addr[i]; + } + } + } + } + + if(ind < 0) + return RS_COLLECT_MSG_ERR; + + //---------------CRC---------------- + //---------[last 2 bytes]---------- + uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind); + modbus_msg->MB_CRC = CRC_VALUE; + modbus_uart_buff[ind++] = CRC_VALUE & 0xFF; + modbus_uart_buff[ind++] = CRC_VALUE >> 8; + + hmodbus->RS_Message_Size = ind; + + return RS_OK; +} + +/** + * @brief Парс сообщения в режиме мастер (фрейм слейва из uart -> msg). + * @param hmodbus Указатель на хендлер RS. + * @param modbus_msg Указатель на структуру сообщения. + * @param modbus_uart_buff Указатель на буффер UART. + * @return RS_RES Статус о результате заполнения структуры. + */ +RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) +{ + int ind = 0; // ind for modbus-uart buffer + int expected_size = 0; + + // get ID of slave device + modbus_msg->MbAddr = modbus_uart_buff[ind++]; + + // get function code (check if error response) + modbus_msg->Func_Code = modbus_uart_buff[ind++]; + + if(modbus_msg->Func_Code & ERR_VALUES_START) // error response + { + modbus_msg->Except_Code = modbus_uart_buff[ind++]; + } + else if(modbus_msg->Func_Code < ERR_VALUES_START) // normal response + { + if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // device identifications response + { + modbus_msg->DevId.MEI_Type = modbus_uart_buff[ind++]; + modbus_msg->DevId.ReadDevId = modbus_uart_buff[ind++]; + modbus_msg->DevId.Conformity = modbus_uart_buff[ind++]; + modbus_msg->DevId.MoreFollows = modbus_uart_buff[ind++]; + modbus_msg->DevId.NextObjId = modbus_uart_buff[ind++]; + modbus_msg->DevId.NumbOfObj = modbus_uart_buff[ind++]; + + modbus_msg->ByteCnt = 0; + + // Парсинг объектов идентификации устройства + uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA; + int data_index = 0; + + for(int obj = 0; obj < modbus_msg->DevId.NumbOfObj; obj++) + { + // Читаем ID объекта + uint8_t object_id = modbus_uart_buff[ind++]; + tmp_data_addr[data_index++] = object_id; + + // Читаем длину объекта + uint8_t object_length = modbus_uart_buff[ind++]; + tmp_data_addr[data_index++] = object_length; + + // Читаем данные объекта + for(int i = 0; i < object_length; i++) + { + tmp_data_addr[data_index++] = modbus_uart_buff[ind++]; + } + + modbus_msg->ByteCnt += (2 + object_length); // ID + длина + данные + } + } + else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC) + { + // Diagnostics response: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO] + modbus_msg->DATA[0] = modbus_uart_buff[ind++] << 8; + modbus_msg->DATA[0] |= modbus_uart_buff[ind++]; + modbus_msg->DATA[1] = modbus_uart_buff[ind++] << 8; + modbus_msg->DATA[1] |= modbus_uart_buff[ind++]; + } + else // classic modbus response + { + // get byte count for read functions + if((modbus_msg->Func_Code == 0x01) || (modbus_msg->Func_Code == 0x02) || + (modbus_msg->Func_Code == 0x03) || (modbus_msg->Func_Code == 0x04)) + { + modbus_msg->ByteCnt = modbus_uart_buff[ind++]; + + // read data bytes + uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA; + for(int i = 0; i < modbus_msg->ByteCnt; i++) + { + if(i % 2 == 0) // HI byte + tmp_data_addr[i/2] = (uint16_t)modbus_uart_buff[ind++] << 8; + else // LO byte + tmp_data_addr[i/2] |= modbus_uart_buff[ind++]; + } + } + // for write functions - echo address and quantity + else if((modbus_msg->Func_Code == 0x05) || (modbus_msg->Func_Code == 0x06) || + (modbus_msg->Func_Code == 0x0F) || (modbus_msg->Func_Code == 0x10)) + { + modbus_msg->Addr = modbus_uart_buff[ind++] << 8; + modbus_msg->Addr |= modbus_uart_buff[ind++]; + modbus_msg->Qnt = modbus_uart_buff[ind++] << 8; + modbus_msg->Qnt |= modbus_uart_buff[ind++]; + } + } + } + + + //---------------CRC---------------- + //----------[last 2 bytes]---------- + uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind); + modbus_msg->MB_CRC = modbus_uart_buff[ind++]; + modbus_msg->MB_CRC |= modbus_uart_buff[ind++] << 8; + + if(modbus_msg->MB_CRC != CRC_VALUE) + { + TrackerCnt_Err(hmodbus->rs_err); + return RS_PARSE_MSG_ERR; + } + + return RS_OK; +} + + + + + +/** @brief Сформировать запрос на чтение коилов */ +RS_MsgTypeDef MB_REQUEST_READ_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) +{ + RS_MsgTypeDef msg = {slave_addr, MB_R_COILS, {0}, start_addr, quantity, 0, {0}, 0, 0}; + return msg; +} + +/** @brief Сформировать запрос на чтение дискретных регистров */ +RS_MsgTypeDef MB_REQUEST_READ_DISCRETE_INPUTS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) +{ + RS_MsgTypeDef msg = {slave_addr, MB_R_DISC_IN, {0}, start_addr, quantity, 0, {0}, 0, 0}; + return msg; +} + +/** @brief Сформировать запрос на чтение холдинг регистров */ +RS_MsgTypeDef MB_REQUEST_READ_HOLDING_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) +{ + RS_MsgTypeDef msg = {slave_addr, MB_R_HOLD_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0}; + return msg; +} + +/** @brief Сформировать запрос на чтение инпут регистров */ +RS_MsgTypeDef MB_REQUEST_READ_INPUT_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) +{ + RS_MsgTypeDef msg = {slave_addr, MB_R_IN_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0}; + return msg; +} + +/** @brief Сформировать запрос на запись одного коила */ +RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_COIL(uint8_t slave_addr, uint16_t coil_addr, uint8_t value) +{ + RS_MsgTypeDef msg = {slave_addr, MB_W_COIL, {0}, coil_addr, (value ? 0xFF00 : 0x0000), 0, {0}, 0, 0}; + return msg; +} + +/** @brief Сформировать запрос на запись одного регистра */ +RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_REG(uint8_t slave_addr, uint16_t reg_addr, uint16_t value) +{ + RS_MsgTypeDef msg = {slave_addr, MB_W_HOLD_REG, {0}, reg_addr, value, 0, {0}, 0, 0}; + return msg; +} + +/** @brief Сформировать запрос на запись нескольких регистров */ +RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint8_t *coils_data) +{ + RS_MsgTypeDef msg = {slave_addr, MB_W_COILS, {0}, start_addr, quantity, 0, {0}, 0, 0}; + + // Calculate byte count and prepare data + uint8_t byte_count = (quantity + 7) / 8; + msg.ByteCnt = byte_count; + + // Copy coil data to message DATA array + for(int i = 0; i < byte_count; i++) { + if(i < DATA_SIZE) { + msg.DATA[i] = coils_data[i]; + } + } + + return msg; +} + +/** @brief Сформировать запрос на запись нескольких коилов */ +RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint16_t *regs_data) +{ + RS_MsgTypeDef msg = {slave_addr, MB_W_HOLD_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0}; + + msg.ByteCnt = quantity * 2; // Each register is 2 bytes + + // Copy register data to message DATA array + for(int i = 0; i < quantity && i < DATA_SIZE; i++) { + msg.DATA[i] = regs_data[i]; + } + + return msg; +} + +//---------ДИАГНОСТИЧЕСКИЕ ДАННЫЕ----------- +RS_MsgTypeDef MB_REQUEST_DIAGNOSTIC_QUERY(uint8_t slave_addr, uint16_t sub_function, uint16_t data) +{ + RS_MsgTypeDef msg = {slave_addr, MB_R_DIAGNOSTIC, {0}, 0, 0, 0, {sub_function, data}, 0, 0}; + return msg; +} +RS_MsgTypeDef MB_REQUEST_RETURN_QUERY_DATA(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0000, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_RESTART_COMMUNICATIONS(uint8_t slave_addr, uint16_t data) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0001, data); +} + +RS_MsgTypeDef MB_REQUEST_RETURN_DIAGNOSTIC_REGISTER(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0002, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_FORCE_LISTEN_ONLY_MODE(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0004, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000A, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000B, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_RETURN_BUS_COMMUNICATION_ERROR_COUNT(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000C, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_EXCEPTION_ERROR_COUNT(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000D, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_MESSAGE_COUNT(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000E, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NO_RESPONSE_COUNT(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000F, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NAK_COUNT(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0010, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_BUSY_COUNT(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0011, 0x0000); +} + +RS_MsgTypeDef MB_REQUEST_RETURN_BUS_CHARACTER_OVERRUN_COUNT(uint8_t slave_addr) +{ + return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0012, 0x0000); +} + +//---------ИДЕНТИФИКАТОРЫ МОДБАС----------- +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_BASIC(uint8_t slave_addr) +{ + RS_MsgTypeDef msg = {slave_addr, MB_R_DEVICE_INFO, {0x0E, 0x01, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0}; + return msg; +} + +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_REGULAR(uint8_t slave_addr) +{ + RS_MsgTypeDef msg = {slave_addr, MB_R_DEVICE_INFO, {0x0E, 0x02, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0}; + return msg; +} + +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_EXTENDED(uint8_t slave_addr) +{ + RS_MsgTypeDef msg = {slave_addr, MB_R_DEVICE_INFO, {0x0E, 0x03, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0}; + return msg; +} + +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_SPECIFIC(uint8_t slave_addr, uint8_t object_id) +{ + RS_MsgTypeDef msg = {slave_addr, MB_R_DEVICE_INFO, {0x0E, 0x04, object_id, 0, 0, 0}, 0, 0, 0, {0}, 0, 0}; + return msg; +} + + + + + +#else +RS_MsgTypeDef msg_dummy = {0}; + +int MB_GetRegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value) {return 0;} + +RS_MsgTypeDef MB_REQUEST_READ_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_READ_DISCRETE_INPUTS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_READ_HOLDING_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_READ_INPUT_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_COIL(uint8_t slave_addr, uint16_t coil_addr, uint8_t value) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_REG(uint8_t slave_addr, uint16_t reg_addr, uint16_t value) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint8_t *coils_data) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint16_t *regs_data) {return msg_dummy;} + +//---------ДИАГНОСТИЧЕСКИЕ ДАННЫЕ----------- +RS_MsgTypeDef MB_REQUEST_DIAGNOSTIC_QUERY(uint8_t slave_addr, uint16_t sub_function, uint16_t data) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_QUERY_DATA(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RESTART_COMMUNICATIONS(uint8_t slave_addr, uint16_t data) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_DIAGNOSTIC_REGISTER(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_FORCE_LISTEN_ONLY_MODE(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_BUS_COMMUNICATION_ERROR_COUNT(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_EXCEPTION_ERROR_COUNT(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_MESSAGE_COUNT(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NO_RESPONSE_COUNT(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NAK_COUNT(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_BUSY_COUNT(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_RETURN_BUS_CHARACTER_OVERRUN_COUNT(uint8_t slave_addr) {return msg_dummy;} + +//---------ИДЕНТИФИКАТОРЫ МОДБАС----------- +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_BASIC(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_REGULAR(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_EXTENDED(uint8_t slave_addr) {return msg_dummy;} +RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_SPECIFIC(uint8_t slave_addr, uint8_t object_id) {return msg_dummy;} + +RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) {return RS_ERR;} +RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) {return RS_ERR;} +#endif + + + diff --git a/Src/modbus_slave.c b/Src/modbus_slave.c new file mode 100644 index 0000000..80311ba --- /dev/null +++ b/Src/modbus_slave.c @@ -0,0 +1,437 @@ +/** +************************************************************************** +* @file modbus_slave.c +* @brief Модуль для реализации слейв MODBUS. +************************************************************************** +* @details +Файл содержит реализацию функций для работы Modbus в режиме слейва. + +@section Функции и макросы + +- MB_Slave_Response() — Ответ на запрос +- MB_Slave_Collect_Message() — Сбор сообщения в режиме слейва. +- MB_Slave_Parse_Message() — Парс сообщения в режиме слейва. +******************************************************************************/ +#include "modbus.h" + +#ifdef MODBUS_ENABLE_SLAVE +/** + * @brief Ответ на сообщение в режиме слейва. + * @param hmodbus Указатель на хендлер RS. + * @param modbus_msg Указатель на структуру сообщения. + * @return RS_RES Статус о результате ответа на комманду. + */ +RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) +{ + RS_StatusTypeDef MB_RES = 0; + hmodbus->f.MessageHandled = 0; + hmodbus->f.EchoResponse = 0; + RS_Reset_TX_Flags(hmodbus); // reset flag for correct transmit + + MB_Diagnostics_BusMessageCnt(); + if(hmodbus->ID == 0 || modbus_msg->MbAddr == 0) + { + MB_Diagnostics_SlaveNoResponseCnt(); // <-- Устройство не отвечает на широковещательные сообщения + hmodbus->RS_STATUS = RS_SKIP; + 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) + { + // Read Coils + case MB_R_COILS: + hmodbus->f.MessageHandled = MB_Process_Read_Coils(hmodbus->pMessagePtr); + break; + + // Read Hodling Registers + case MB_R_HOLD_REGS: + hmodbus->f.MessageHandled = MB_Process_Read_Hold_Regs(hmodbus->pMessagePtr); + break; + case MB_R_IN_REGS: + hmodbus->f.MessageHandled = MB_Process_Read_Input_Regs(hmodbus->pMessagePtr); + break; + + + // Write Single Coils + case MB_W_COIL: + hmodbus->f.MessageHandled = MB_Process_Write_Single_Coil(hmodbus->pMessagePtr); + if(hmodbus->f.MessageHandled) + { + hmodbus->f.DataUpdated = 1; + hmodbus->f.EchoResponse = 1; + hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes) + } + break; + + case MB_W_HOLD_REG: + hmodbus->f.MessageHandled = MB_Process_Write_Single_Reg(hmodbus->pMessagePtr); + if(hmodbus->f.MessageHandled) + { + hmodbus->f.DataUpdated = 1; + hmodbus->f.EchoResponse = 1; + hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes) + } + break; + + // Write Multiple Coils + case MB_W_COILS: + hmodbus->f.MessageHandled = MB_Write_Miltuple_Coils(hmodbus->pMessagePtr); + if(hmodbus->f.MessageHandled) + { + hmodbus->f.DataUpdated = 1; + hmodbus->f.EchoResponse = 1; + hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes) + } + break; + + // Write Multiple Registers + case MB_W_HOLD_REGS: + hmodbus->f.MessageHandled = MB_Process_Write_Miltuple_Regs(hmodbus->pMessagePtr); + if(hmodbus->f.MessageHandled) + { + hmodbus->f.DataUpdated = 1; + hmodbus->f.EchoResponse = 1; + hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes) + } + break; + + case MB_R_DEVICE_INFO: + hmodbus->f.MessageHandled = MB_Process_Read_Device_Identifications(hmodbus->pMessagePtr); + break; + + // Добавить в switch-case после других case: + case MB_R_DIAGNOSTIC: + hmodbus->f.MessageHandled = MB_Process_Diagnostics(hmodbus->pMessagePtr); + break; + + /* unknown func code */ + default: + modbus_msg->Except_Code = 0x01; /* set exception code: illegal function */ + } + + + // Проверяем режим устройства - если 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; + } + else + { + TrackerCnt_Ok(hmodbus->rs_err); + } + + + } + + // if we need response - check that transmit isnt busy + if( RS_Is_TX_Busy(hmodbus) ) + RS_Abort(hmodbus, ABORT_TX); // if tx busy - set it free + + // Transmit right there, or sets (fDeferredResponse) to transmit response in main code + if(hmodbus->f.DeferredResponse == 0) + { + MB_RES = RS_Handle_Transmit_Start(hmodbus, modbus_msg); + } + else + { + RS_Handle_Receive_Start(hmodbus, modbus_msg); + hmodbus->f.DeferredResponse = 0; + } + + hmodbus->RS_STATUS = MB_RES; + return MB_RES; +} + + + +/** + * @brief Сбор сообщения в буфер UART в режиме слейв (фрейм слейва из msg -> uart). + * @param hmodbus Указатель на хендлер RS. + * @param modbus_msg Указатель на структуру сообщения. + * @param modbus_uart_buff Указатель на буффер UART. + * @return RS_RES Статус о результате заполнения буфера. + */ +RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) +{ + int ind = 0; // ind for modbus-uart buffer + + if(hmodbus->f.EchoResponse && hmodbus->f.MessageHandled) // if echo response need + ind = hmodbus->RS_Message_Size; + else + { + //------INFO ABOUT DATA/MESSAGE------ + //-----------[first bytes]----------- + // set ID of message/user + modbus_uart_buff[ind++] = modbus_msg->MbAddr; + + // set dat or err response + modbus_uart_buff[ind++] = modbus_msg->Func_Code; + + if (modbus_msg->Func_Code < ERR_VALUES_START) // if no error occur + { + // fill modbus header + if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // devide identifications header + { + modbus_uart_buff[ind++] = modbus_msg->DevId.MEI_Type; + modbus_uart_buff[ind++] = modbus_msg->DevId.ReadDevId; + modbus_uart_buff[ind++] = modbus_msg->DevId.Conformity; + modbus_uart_buff[ind++] = modbus_msg->DevId.MoreFollows; + modbus_uart_buff[ind++] = modbus_msg->DevId.NextObjId; + modbus_uart_buff[ind++] = modbus_msg->DevId.NumbOfObj; + + if (modbus_msg->ByteCnt > DATA_SIZE*2) // if ByteCnt less than DATA_SIZE + { + TrackerCnt_Err(hmodbus->rs_err); + return RS_COLLECT_MSG_ERR; + } + + + //---------------DATA---------------- + //-----------[data bytes]------------ + uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA; + for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data + { // set data + modbus_uart_buff[ind++] = *tmp_data_addr; + tmp_data_addr++; + } + + } + 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 + if (modbus_msg->ByteCnt <= DATA_SIZE*2) // if ByteCnt less than DATA_SIZE + modbus_uart_buff[ind++] = modbus_msg->ByteCnt; + else // otherwise return data_size err + { + TrackerCnt_Err(hmodbus->rs_err); + return RS_COLLECT_MSG_ERR; + } + + //---------------DATA---------------- + //-----------[data bytes]------------ + uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA; + for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data + { // set data + if (i%2 == 0) // HI byte + modbus_uart_buff[ind++] = (*tmp_data_addr)>>8; + else // LO byte + { + modbus_uart_buff[ind++] = *tmp_data_addr; + tmp_data_addr++; + } + } + + } + + } + else // if some error occur + { // send expection code + modbus_uart_buff[ind++] = modbus_msg->Except_Code; + } + } + if(ind < 0) + return RS_COLLECT_MSG_ERR; + + //---------------CRC---------------- + //---------[last 16 bytes]---------- + // calc crc of received data + uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind); + // write crc to message structure and modbus-uart buffer + modbus_msg->MB_CRC = CRC_VALUE; + modbus_uart_buff[ind++] = CRC_VALUE; + modbus_uart_buff[ind++] = CRC_VALUE >> 8; + + hmodbus->RS_Message_Size = ind; + + return RS_OK; // returns ok +} + +/** + * @brief Определить размер модбас запроса (СЛЕЙВ версия). + * @param hRS Указатель на хендлер RS. + * @param rx_data_size Указатель на переменную для записи кол-ва байт для принятия. + * @return RS_RES Статус о корректности рассчета кол-ва байт для принятия. + * @details Определение сколько байтов надо принять по протоколу. + */ +static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) +{ + RS_StatusTypeDef MB_RES = 0; + int mb_func_size = 0; + + if (modbus_msg->Func_Code == MB_R_DIAGNOSTIC) + { + mb_func_size = 1; + } + 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; +} + + +/** + * @brief Парс сообщения в режиме слейв (фрейм мастера из uart -> msg). + * @param hmodbus Указатель на хендлер RS. + * @param modbus_msg Указатель на структуру сообщения. + * @param modbus_uart_buff Указатель на буффер UART. + * @return RS_RES Статус о результате заполнения структуры. + * @details Заполнение структуры сообщения из буффера UART. + */ +RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) +{ + 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; + ind++; + } + else + { + 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; + } + + if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // if it device identifications request + { + modbus_msg->DevId.MEI_Type = modbus_uart_buff[ind++]; + modbus_msg->DevId.ReadDevId = modbus_uart_buff[ind++]; + 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 + modbus_msg->Addr = modbus_uart_buff[ind++] << 8; + modbus_msg->Addr |= modbus_uart_buff[ind++]; + + // get address from CMD + modbus_msg->Qnt = modbus_uart_buff[ind++] << 8; + modbus_msg->Qnt |= modbus_uart_buff[ind++]; + } + + if((hmodbus->pMessagePtr->Func_Code == 0x0F) || (hmodbus->pMessagePtr->Func_Code == 0x10)) + hmodbus->pMessagePtr->ByteCnt = modbus_uart_buff[ind++]; + 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) + { + //check that data size is correct + if (modbus_msg->ByteCnt > DATA_SIZE*2) + { + 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; + for(int i = 0; i < modbus_msg->ByteCnt; i++) + { // set data + if (i%2 == 0) + *tmp_data_addr = ((uint16_t)modbus_uart_buff[ind++] << 8); + else + { + *tmp_data_addr |= modbus_uart_buff[ind++]; + tmp_data_addr++; + } + } + } + + //---------------CRC---------------- + //----------[last 16 bits]---------- + // calc crc of received data + uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind); + // get crc of received data + modbus_msg->MB_CRC = modbus_uart_buff[ind++]; + modbus_msg->MB_CRC |= modbus_uart_buff[ind++] << 8; + // compare crc + if (modbus_msg->MB_CRC != CRC_VALUE) + { + MB_Diagnostics_CommunicationErrorCnt(); + TrackerCnt_Err(hmodbus->rs_err); + modbus_msg->Func_Code |= ERR_VALUES_START; + } + + return RS_OK; +} + +#else // MODBUS_ENABLE_SLAVE +RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) {return RS_ERR;} +RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) {return RS_ERR;} +RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) {return RS_ERR;} +#endif \ No newline at end of file diff --git a/Src/rs_message.c b/Src/rs_message.c index 0bc0c09..d186530 100644 --- a/Src/rs_message.c +++ b/Src/rs_message.c @@ -151,7 +151,7 @@ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) * @param stim Указатель на структуру с настройками таймера. * @param pRS_BufferPtr Указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер. * @return RS_RES Статус о состоянии RS после инициализации. - * @note Инициализация перефирии и структуры для приема-передачи по RS. + * @details Инициализация перефирии и структуры для приема-передачи по RS. */ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr) { @@ -166,9 +166,6 @@ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_H hRS->htim = htim; - if (hRS->sRS_RX_Size_Mode == NULL) - return RS_ERR; - // check that buffer is defined if (hRS->pBufferPtr == NULL) { @@ -190,20 +187,14 @@ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_H - ABORT_RX_TX: Отмена приема и передачи по ЮАРТ, - ABORT_RS: Отмена приема-передачи RS, с очищением всей структуры. * @return RS_RES Статус о состоянии RS после аборта. - * @note Отмена работы UART в целом или отмена приема/передачи RS. - Также очищается хендл hRS. + * @details Отмена работы UART в целом или отмена приема/передачи RS. + Также очищается хендл hRS. */ RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode) { HAL_StatusTypeDef uart_res = 0; - if(hRS->htim) - { - if(hRS->sRS_Timeout) // if timeout setted - HAL_TIM_Base_Stop_IT(hRS->htim); // stop timeout - hRS->htim->Instance->CNT = 0; - __HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE); - } + RS_Timeout_Stop(hRS); if((AbortMode&ABORT_RS) == 0x00) { @@ -241,7 +232,7 @@ RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode) * @param hRS Указатель на хендлер RS. * @param RS_msg Указатель на структуру сообщения. * @return RS_RES Статус о состоянии RS после инициализации приема или окончания общения. - * @note Определяет начинать прием команды/ответа или нет. + * @details Определяет начинать прием команды/ответа или нет. */ RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) { @@ -249,9 +240,15 @@ RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *R switch(hRS->sRS_Mode) { - case RS_SLAVE_ALWAYS_WAIT: // in slave mode with permanent waiting - RS_RES = RS_Receive_IT(hRS, RS_msg); break; // start receiving again - case RS_SLAVE_TIMEOUT_WAIT: // in slave mode with timeout waiting (start receiving cmd by request) + // В режиме мастер + case RS_MASTER_REQUEST: + RS_Timeout_Start(hRS); // сразу запускаем таймаут и начинаем прием + // В режиме слейв + case RS_SLAVE_ALWAYS_WAIT: + RS_RES = RS_Receive_IT(hRS, RS_msg); // Просто запускаем фоновый прием + break; + + case RS_RESERVED: RS_Set_Free(hRS); RS_RES = RS_OK; break; // end RS communication (set RS unbusy) } @@ -267,7 +264,7 @@ RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *R * @param hRS Указатель на хендлер RS. * @param RS_msg Указатель на структуру сообщения. * @return RS_RES Статус о состоянии RS после инициализации передачи. - * @note Определяет отвечать ли на команду или нет. + * @details Определяет отвечать ли на команду или нет. */ RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) { @@ -276,13 +273,14 @@ RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef * switch(hRS->sRS_Mode) { case RS_SLAVE_ALWAYS_WAIT: // in slave mode always response - case RS_SLAVE_TIMEOUT_WAIT: // transmit response + case RS_RESERVED: // transmit response + case RS_MASTER_REQUEST: // transmit response RS_RES = RS_Transmit_IT(hRS, RS_msg); break; } if(RS_RES != RS_OK) { - if(hRS->sRS_Mode < RS_MASTER_START) + if(hRS->sRS_Mode < RS_MASTER_MODE_START) { RS_Handle_Receive_Start(hRS, RS_msg); } @@ -294,10 +292,10 @@ RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef * } /** -* @brief UART TX Callback: коллбек после окончания передачи. + * @brief UART TX Callback: коллбек после окончания передачи. * @param hRS Указатель на хендлер RS. * @return RS_RES Статус о состоянии RS после обработки приема. - * @note Определяет поведение RS после передачи сообщения. + * @details Определяет поведение RS после передачи сообщения. */ RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS) { @@ -316,8 +314,8 @@ RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS) /** * @brief Обработчик прерывания UART. * @param hRS Указатель на хендлер RS. - * @note Обрабатывает ошибки если есть и вызывает RS Коллбеки. - * Добавить вызов этой функции в UARTx_IRQHandler() ВМЕСТО HAL_UART_IRQHandler(). + * @details Обрабатывает ошибки если есть и вызывает RS Коллбеки. + * Добавить вызов этой функции в UARTx_IRQHandler() ВМЕСТО HAL_UART_IRQHandler(). */ void RS_UART_Handler(RS_HandleTypeDef *hRS) { @@ -352,17 +350,22 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS) // Парсим наше сообщение RS_StatusTypeDef parse_res = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr); - // Если сообещине принято корректно - отвечаем на него + // Если сообещине принято корректно if(parse_res == RS_OK) { - if(hRS->htim) - { - // Останавливаем таймаут - if(hRS->sRS_Timeout) - HAL_TIM_Base_Stop_IT(hRS->htim); - } + RS_Timeout_Stop(hRS); + hRS->lastPacketTick = uwTick; - RS_Response(hRS, hRS->pMessagePtr); + if(hRS->sRS_Mode < RS_MASTER_MODE_START) + RS_Response(hRS, hRS->pMessagePtr); // отвечаем на запрос + else + { + if(hRS->pCallback) + { + hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ + RS_Set_Free(hRS); // освобожднаем RS + } + } } else { @@ -383,19 +386,13 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS) /* IF NO ERROR OCCURS */ if(hRS->huart->ErrorCode == 0) { - - if(hRS->htim) + + // if first byte is received and receive is active + if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS)) { - hRS->htim->Instance->CNT = 0; // reset cnt; - /* Start timeout при получении первого байта */ - if(hRS->sRS_Timeout) // if timeout setted - if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS)) // if first byte is received and receive is active - { - hRS->htim->Instance->ARR = hRS->sRS_Timeout; - HAL_TIM_Base_Start_IT(hRS->htim); - RS_Set_RX_Active_Flags(hRS); - } + RS_Timeout_Start(hRS); } + RS_Timeout_Update(hRS); /* RX Callback - теперь НЕ вызываем здесь, ждем IDLE */ @@ -426,8 +423,8 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS) /** * @brief Обработчик прерывания TIM. * @param hRS Указатель на хендлер RS. - * @note Попадание сюда = таймаут и перезапуск RS приема - * Добавить вызов этой функции в TIMx_IRQHandler() ВМЕСТО HAL_TIM_IRQHandler(). + * @details Попадание сюда = таймаут и перезапуск RS приема + * Добавить вызов этой функции в TIMx_IRQHandler() ВМЕСТО HAL_TIM_IRQHandler(). */ void RS_TIM_Handler(RS_HandleTypeDef *hRS) { @@ -442,8 +439,68 @@ void RS_TIM_Handler(RS_HandleTypeDef *hRS) if(hRS->pMessagePtr->MbAddr == hRS->ID) // ошибка если таймаут по нашему сообщению TrackerCnt_Err(hRS->rs_err); - RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); + if(hRS->sRS_Mode == RS_MASTER_REQUEST) { + // Мастер: таймаут ответа -> освобождаем для нового запроса + RS_Set_Free(hRS); + } else { + // Слейв: перезапускаем прием + RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); + } } + +/** + * @brief Запуск таймаута приема. + * @param hRS Указатель на хендлер RS. + * @return RS_RES Статус операции. + * @details Запускает таймер для отсчета времени ожидания следующего байта. + */ +RS_StatusTypeDef RS_Timeout_Start(RS_HandleTypeDef *hRS) +{ + if(hRS->htim) + { + hRS->htim->Instance->CNT = 0; // reset cnt; + if(hRS->sRS_Timeout) // if timeout setted + { + hRS->htim->Instance->ARR = hRS->sRS_Timeout; + HAL_TIM_Base_Start_IT(hRS->htim); + RS_Set_RX_Active_Flags(hRS); + } + } + return RS_OK; +} +/** + * @brief Остановка таймаута приема. + * @param hRS Указатель на хендлер RS. + * @return RS_RES Статус операции. + * @details Останавливает таймер ожидания. + */ +RS_StatusTypeDef RS_Timeout_Stop(RS_HandleTypeDef *hRS) +{ + if(hRS->htim) + { + // Останавливаем таймаут + if(hRS->sRS_Timeout) + HAL_TIM_Base_Stop_IT(hRS->htim); + hRS->htim->Instance->CNT = 0; + __HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE); + } + return RS_OK; +} +/** + * @brief Обновление (сброс) таймаута приема. + * @param hRS Указатель на хендлер RS. + * @return RS_RES Статус операции. + * @details Сбрасывает счетчик таймера в 0. + */ +RS_StatusTypeDef RS_Timeout_Update(RS_HandleTypeDef *hRS) +{ + if(hRS->htim) + { + hRS->htim->Instance->CNT = 0; // reset cnt; + } + return RS_OK; +} + //--------------------CALLBACK/HANDLER FUNCTIONS--------------------- //------------------------------------------------------------------- @@ -456,7 +513,6 @@ void RS_TIM_Handler(RS_HandleTypeDef *hRS) * @param hRS Указатель на хендлер RS. * @param RS_msg Указатель на структуру сообщения. * @return RS_RES Статус о результате ответа на комманду. - * @note Обработка принятой комманды и ответ на неё. */ __weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) { @@ -470,7 +526,6 @@ __weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg * @param RS_msg Указатель на структуру сообщения. * @param msg_uart_buff Указатель на буффер UART. * @return RS_RES Статус о результате заполнения буфера. - * @note Заполнение буффера UART из структуры сообщения. */ __weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff) { @@ -484,7 +539,6 @@ __weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef * @param RS_msg Указатель на структуру сообщения. * @param msg_uart_buff Указатель на буффер UART. * @return RS_RES Статус о результате заполнения структуры. - * @note Заполнение структуры сообщения из буффера UART. */ __weak RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff) { diff --git a/__modbus_config.h b/__modbus_config.h index 1fde03f..caa8f3e 100644 --- a/__modbus_config.h +++ b/__modbus_config.h @@ -25,17 +25,30 @@ // Строковые идентификаторы устройства #define MODBUS_VENDOR_NAME "NIO-12" #define MODBUS_PRODUCT_CODE "" -#define MODBUS_REVISION "Ver. 1.0" +#define MODBUS_REVISION "" #define MODBUS_VENDOR_URL "" #define MODBUS_PRODUCT_NAME "" -#define MODBUS_MODEL_NAME "STM32F103" +#define MODBUS_MODEL_NAME "" #define MODBUS_USER_APPLICATION_NAME "" #define MODBUS_NUMB_OF_USEROBJECTS 0 // Периферия (опционально) #define mb_huart huart1 ///< Удобный дефайн для модбасовского uart -#define mb_htim htim3 ///< Удобный дефайн для модбасовского таймера -///< +#define mb_htim htim2 ///< Удобный дефайн для модбасовского таймера +//#define RS_EnableReceive() ///< Функция изменения направления передачи на ПРИЕМ для RS-485 +//#define RS_EnableTransmit() ///< Функция изменения направления передачи на ПЕРЕДАЧУ для RS-485 + + +// Модули modbus +#define MODBUS_ENABLE_SLAVE ///< Включить обработку МАСТЕР режима +//#define MODBUS_ENABLE_MASTER ///< Включить обработку СЛЕЙВ режима + +#define MODBUS_ENABLE_COILS ///< Включить обработку коилов +#define MODBUS_ENABLE_HOLDINGS ///< Включить обработку регистров хранения +#define MODBUS_ENABLE_INPUTS ///< Включить обработку входных регистров +#define MODBUS_ENABLE_DEVICE_IDENTIFICATORS ///< Включить обработку идентификаторы устройства +#define MODBUS_ENABLE_DIAGNOSTICS ///< Включить обработку диагностики модбас + /** * @brief Поменять комманды 0x03 и 0x04 местами (для LabView терминалки от двигателей)