756 lines
26 KiB
C
756 lines
26 KiB
C
/**
|
||
*******************************************************************************
|
||
* @file modbus_master.c
|
||
* @brief Модуль для реализации мастера MODBUS.
|
||
*******************************************************************************
|
||
* @details
|
||
Файл содержит реализацию функций для работы Modbus в режиме мастера.
|
||
|
||
@section mast Функции и макросы
|
||
- MB_RespGet_RegisterAll() — Считать все регистра из ответа
|
||
- MB_RespGet_RegisterValue() — Считать один регистр из ответа
|
||
- MB_RespGet_CoilAll() — Считать все коилы из ответа
|
||
- MB_RespGet_CoilState() — Считать один коил из ответа
|
||
- MB_RespGet_NumberOfObjects() — Считать количество принятых объектов идентификатора
|
||
- MB_RespGet_ObjectById() — Считать объект идентификатора по
|
||
его ID
|
||
- MB_RespGet_ObjectByIndex() — Считать объект идентификатора по
|
||
порядковому номеру в сообщении
|
||
- MB_RespGet_Diagnostic() — Считать запрошенный диагностический счетчик
|
||
|
||
- MB_Master_Collect_Message() — Сбор сообщения в режиме мастера
|
||
- MB_Master_Parse_Message() — Парс сообщения в режиме мастера
|
||
******************************************************************************/
|
||
#include "modbus.h"
|
||
|
||
#ifdef MODBUS_ENABLE_MASTER
|
||
|
||
|
||
//-------------------------------------------------------------------
|
||
//-----------------------------FOR USER------------------------------
|
||
/**
|
||
* @brief Получить значение ВСЕХ регистров в ответе
|
||
* @param modbus_msg Указатель на структуру сообщения
|
||
* @param reg_addr Адрес регистра, значение которого нужно получить
|
||
* @param reg_arr Указатель для массив для сохранения значений регистров
|
||
* @return количество считанных регистров, 0 - ошибка
|
||
*/
|
||
int MB_RespGet_RegisterAll(RS_MsgTypeDef *modbus_msg, uint16_t *reg_arr)
|
||
{
|
||
if(modbus_msg == NULL || reg_arr == NULL)
|
||
return 0;
|
||
int read_cnt = 0;
|
||
int i = 0;
|
||
for(int addr = modbus_msg->Addr; addr < modbus_msg->Addr + modbus_msg->Qnt; addr++)
|
||
{
|
||
if(MB_RespGet_RegisterValue(modbus_msg, addr, ®_arr[i]))
|
||
{
|
||
read_cnt++;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
return read_cnt;
|
||
}
|
||
|
||
/**
|
||
* @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->FuncCode != FC_R_DISC_IN) &&
|
||
(modbus_msg->FuncCode != FC_R_HOLD_REGS) &&
|
||
(modbus_msg->FuncCode != FC_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->MbData[reg_index];
|
||
|
||
return 1;
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @brief Получить состояние ВСЕХ coil в ответе
|
||
* @param modbus_msg Указатель на структуру сообщения
|
||
* @param coil_arr Указатель для массив доя сохранения состояний coil (1 - ON, 0 - OFF)
|
||
* @return 1 - успех, 0 - ошибка или coil_addr вне диапазона запроса
|
||
*/
|
||
int MB_RespGet_CoilAll(RS_MsgTypeDef *modbus_msg, int *coil_arr)
|
||
{
|
||
if(modbus_msg == NULL || coil_arr == NULL)
|
||
return 0;
|
||
|
||
int read_cnt = 0;
|
||
int i = 0;
|
||
for(int addr = modbus_msg->Addr; addr < modbus_msg->Addr + modbus_msg->Qnt; addr++)
|
||
{
|
||
if(MB_RespGet_CoilState(modbus_msg, addr, &coil_arr[i]))
|
||
{
|
||
read_cnt++;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/**
|
||
* @brief Получить состояние coil в ответе по его адресу
|
||
* @param modbus_msg Указатель на структуру сообщения
|
||
* @param coil_addr Адрес coil, состояние которого нужно получить
|
||
* @param coil_state Указатель для состояния coil (1 - ON, 0 - OFF)
|
||
* @return 1 - успех, 0 - ошибка или coil_addr вне диапазона запроса
|
||
*/
|
||
int MB_RespGet_CoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state)
|
||
{
|
||
if(modbus_msg == NULL || coil_state == NULL)
|
||
return 0;
|
||
|
||
// Проверяем что ответ связан с коилами
|
||
if(modbus_msg->FuncCode != FC_R_COILS)
|
||
{
|
||
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 data_index = coil_index / 16;
|
||
uint8_t bit_index = coil_index % 16;
|
||
|
||
// Проверяем что байт существует в данных
|
||
if(byte_index >= modbus_msg->ByteCnt)
|
||
return 0;
|
||
|
||
// Получаем байт и проверяем бит
|
||
if(bit_index < 8)
|
||
*coil_state = (modbus_msg->MbData[data_index] >> (bit_index+8)) & 0x01;
|
||
else
|
||
*coil_state = ((modbus_msg->MbData[data_index]&0xFF) >> (bit_index-8)) & 0x01;
|
||
|
||
|
||
return 1;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Получить количество объектов в сообщении
|
||
* @param modbus_msg Указатель на структуру сообщения
|
||
* @return int Количество объектов
|
||
*/
|
||
int MB_RespGet_NumberOfObjects(RS_MsgTypeDef *modbus_msg)
|
||
{
|
||
if(modbus_msg == NULL)
|
||
{
|
||
return 0;
|
||
}
|
||
// Проверяем что ответ связан с диагностикой
|
||
if(modbus_msg->FuncCode != FC_R_DEVICE_ID)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
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_RespGet_ObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length)
|
||
{
|
||
if((modbus_msg == NULL) || (obj_data == NULL))
|
||
return 0;
|
||
|
||
// Проверяем что ответ связан с диагностикой
|
||
if(modbus_msg->FuncCode != FC_R_DEVICE_ID)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
uint8_t *data = (uint8_t*)modbus_msg->MbData;
|
||
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_RespGet_ObjectByIndex(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(modbus_msg->FuncCode != FC_R_DEVICE_ID)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if(index >= modbus_msg->DevId.NumbOfObj)
|
||
return 0;
|
||
|
||
uint8_t *data = (uint8_t*)modbus_msg->MbData;
|
||
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;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Получить данные диагностики из сообщения (MbData[1])
|
||
* @param modbus_msg Указатель на структуру сообщения
|
||
* @param data Указатель куда положить данные
|
||
* @return 1 - успех, 0 - ошибка
|
||
*/
|
||
int MB_RespGet_Diagnostic(RS_MsgTypeDef *modbus_msg, uint16_t *data)
|
||
{
|
||
if(modbus_msg == NULL || data == NULL)
|
||
return 0;
|
||
|
||
// Проверяем что ответ связан с диагностикой
|
||
if(modbus_msg->FuncCode != FC_R_DIAGNOSTICS)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
*data = modbus_msg->MbData[1];
|
||
return 1;
|
||
}
|
||
|
||
|
||
|
||
|
||
//-------------------------------------------------------------------
|
||
//-----------------------------INTERNAL------------------------------
|
||
/**
|
||
* @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->FuncCode & FC_ERR_VALUES_START)
|
||
{
|
||
// Error response: [Addr][Func|0x80][ExceptCode][CRC]
|
||
mb_func_size = -1; // Only Exception Code
|
||
}
|
||
else if (modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
|
||
{
|
||
// Diagnostics response: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
|
||
mb_func_size = 1;
|
||
}
|
||
else if (modbus_msg->FuncCode == FC_R_DEVICE_ID)
|
||
{
|
||
// Device identifications: variable size, need to read first to determine
|
||
mb_func_size = 0; // Will be determined after reading header
|
||
}
|
||
else
|
||
{
|
||
switch (modbus_msg->FuncCode & ~FC_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 = RS_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->DeviceAddr;
|
||
|
||
// set function code
|
||
modbus_uart_buff[ind++] = modbus_msg->FuncCode;
|
||
|
||
if(modbus_msg->FuncCode < FC_ERR_VALUES_START) // if no error occur
|
||
{
|
||
// fill modbus header
|
||
if(0) {}
|
||
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
|
||
else if(modbus_msg->FuncCode == FC_R_DEVICE_ID) // 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;
|
||
}
|
||
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
|
||
#ifdef MODBUS_ENABLE_DIAGNOSTICS
|
||
else if(modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
|
||
{
|
||
// Diagnostics: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
|
||
modbus_uart_buff[ind++] = modbus_msg->MbData[0] >> 8; // Sub-function HI
|
||
modbus_uart_buff[ind++] = modbus_msg->MbData[0] & 0xFF; // Sub-function LO
|
||
modbus_uart_buff[ind++] = modbus_msg->MbData[1] >> 8; // Data HI
|
||
modbus_uart_buff[ind++] = modbus_msg->MbData[1] & 0xFF; // Data LO
|
||
}
|
||
#endif //MODBUS_ENABLE_DIAGNOSTICS
|
||
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->FuncCode == 0x0F) || (modbus_msg->FuncCode == 0x10))
|
||
{
|
||
modbus_uart_buff[ind++] = modbus_msg->ByteCnt;
|
||
|
||
// write data bytes
|
||
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->MbData;
|
||
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->MbCRC = 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->DeviceAddr = modbus_uart_buff[ind++];
|
||
|
||
// get function code (check if error response)
|
||
modbus_msg->FuncCode = modbus_uart_buff[ind++];
|
||
|
||
if(modbus_msg->FuncCode & FC_ERR_VALUES_START) // error response
|
||
{
|
||
modbus_msg->Except_Code = modbus_uart_buff[ind++];
|
||
}
|
||
else if(modbus_msg->FuncCode < FC_ERR_VALUES_START) // normal response
|
||
{
|
||
if(0) {}
|
||
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
|
||
else if(modbus_msg->FuncCode == FC_R_DEVICE_ID) // 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->MbData;
|
||
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 + длина + данные
|
||
}
|
||
}
|
||
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
|
||
#ifdef MODBUS_ENABLE_DIAGNOSTICS
|
||
else if(modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
|
||
{
|
||
// Diagnostics response: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
|
||
modbus_msg->MbData[0] = modbus_uart_buff[ind++] << 8;
|
||
modbus_msg->MbData[0] |= modbus_uart_buff[ind++];
|
||
modbus_msg->MbData[1] = modbus_uart_buff[ind++] << 8;
|
||
modbus_msg->MbData[1] |= modbus_uart_buff[ind++];
|
||
}
|
||
#endif //MODBUS_ENABLE_DIAGNOSTICS
|
||
else // classic modbus response
|
||
{
|
||
// get byte count for read functions
|
||
if((modbus_msg->FuncCode == 0x01) || (modbus_msg->FuncCode == 0x02) ||
|
||
(modbus_msg->FuncCode == 0x03) || (modbus_msg->FuncCode == 0x04))
|
||
{
|
||
modbus_msg->ByteCnt = modbus_uart_buff[ind++];
|
||
|
||
// read data bytes
|
||
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->MbData;
|
||
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->FuncCode == 0x05) || (modbus_msg->FuncCode == 0x06) ||
|
||
(modbus_msg->FuncCode == 0x0F) || (modbus_msg->FuncCode == 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->MbCRC = modbus_uart_buff[ind++];
|
||
modbus_msg->MbCRC |= modbus_uart_buff[ind++] << 8;
|
||
|
||
if(modbus_msg->MbCRC != 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, FC_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, FC_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, FC_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, FC_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, FC_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, FC_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, FC_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 MbData array
|
||
for(int i = 0; i < byte_count; i++) {
|
||
if(i < MbData_size) {
|
||
msg.MbData[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, FC_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 MbData array
|
||
for(int i = 0; i < quantity && i < MbData_size; i++) {
|
||
msg.MbData[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, FC_R_DIAGNOSTICS, {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, FC_R_DEVICE_ID, {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, FC_R_DEVICE_ID, {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, FC_R_DEVICE_ID, {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, FC_R_DEVICE_ID, {0x0E, 0x04, object_id, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
|
||
return msg;
|
||
}
|
||
|
||
#endif //MODBUS_ENABLE_MASTER
|