/** ************************************************************************** * @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_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value) { if(modbus_msg == NULL || reg_value == NULL) return 0; // Проверяем что ответ связан с регистрами if((modbus_msg->Func_Code != MB_R_DISC_IN) && (modbus_msg->Func_Code != MB_R_HOLD_REGS) && (modbus_msg->Func_Code != MB_R_IN_REGS)) { 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_RespGet_RegisterValue(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