release 0.3.2 Фиксы:

- Data Access API расширено функциями для чтения/записи регистров
- API для реквестов MB_RespGet.. перенесено в modbus_master
- Мастер: коллбек реквеста вызывается и при таймауте
	- В коллбеке можно понять статус реквеста по hmodbus->RS_STATUS
This commit is contained in:
2025-11-05 16:38:38 +03:00
parent 4939999789
commit 3d106f18ef
20 changed files with 566 additions and 389 deletions

View File

@@ -36,15 +36,26 @@
#include "modbus.h"
MODBUS_FirstInit(&hmodbus1, &huart1, &htim3);
MODBUS_Config(&hmodbus1, 0, 1000, MODBUS_MODE_MASTER); // - если нужны другие настройки, не из modbus_config.h
MODBUS_Config(&hmodbus1, 0, 1000, MODBUS_MODE_MASTER);
// Запрос на 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 содержит ответ от устройства
}
void callback_func(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
// MB_RespGet_... Чтобы достать нужные данные из ответа
if(hmodbus->RS_STATUS == RS_OK)
{
for(int addr = MODBUS_MSG.Addr; addr < MODBUS_MSG.Addr + MODBUS_MSG.Qnt; addr++)
{
uint16_t value;
if(MB_RespGet_RegisterValue(&MODBUS_MSG, addr, &value))
{
read_hold[i] = value;
}
}
}
}
@endverbatim
@@ -82,6 +93,13 @@
#include "modbus_diag.h"
#ifdef MODBUS_ENABLE_MASTER
#define MODBUS_MODE_MASTER 1
#endif
#ifdef MODBUS_ENABLE_SLAVE
#define MODBUS_MODE_SLAVE 0
#endif
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
@@ -99,7 +117,7 @@ HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mo
HAL_StatusTypeDef MODBUS_MasterRequest(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, void (*pClbk)(RS_HandleTypeDef*, RS_MsgTypeDef*));
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/////////////////////////---FUNCTIONS---/////////////////////////////
#endif //__MODBUS_H_

View File

@@ -34,90 +34,11 @@ Coils упакованы в 16-битные слова для эффективн
//--------------------------------------------------
/**
* @addtogroup MODBUS_COILS
* @{
*/
/**
* @brief Макрос для установки указателя на регистр, содержащий запрашиваемый коил
* @param _parr_ - массив коилов.
* @param _coil_ - Номер коила от начала массива _arr_.
* @note Используется вместе с @ref MB_Set_Coil_Mask
@verbatim Пояснение выражений
- (_coil_/16) - индекс регистра, в котором содержится коил по адресу _coil_
Визуальный пример: 30 коил будет в 30/16 = 1 регистре (индексация с 0)
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
|register[0]----| |register[1]----|
|skip this------| |get this-------|
|shift to 14 bit|
@endverbatim
*/
#define MB_Set_Coil_Reg_Ptr(_parr_, _coil_) ((uint16_t *)(_parr_)+((_coil_)/16))
/**
* @brief Макрос для установки маски, чтобы выделить запрашиваемый коил из регистра
* @param _coil_ - Номер коила от начала массива _arr_.
* @note Используется вместе с @ref MB_Set_Coil_Reg_Ptr
@verbatim Пояснение выражений
- (16*(_coil_/16) - сколько коилов нужно пропустить. прим. (16*30/16) - первые 16 коилов находятся вне регистра
- _coil_-(16*(_coil_/16)) - сдвинуть бит на место запрашиваемого коила в регистре
Визуальный пример: 30 коил будет регистре[1], на 14 бите:
register = 30/16 = 1
bit = 30 - (16*30/16) = 30 - 16 = 14
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
|register[0]----| |register[1]----|
|skip this------| |get this-------|
|shift to 14 bit|
@endverbatim
*/
#define MB_Set_Coil_Mask(_coil_) (1 << ( _coil_ - (16*((_coil_)/16)) ))
/** MODBUS_COILS
* @}
*/
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
/**
* @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_RespGet_CoilState(&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_RespGet_CoilState(&MODBUS_MSG, addr, &state))
* {
* printf("Coil %d: %s\n", addr, state ? "ON" : "OFF");
* }
* }
* @endcode
* @{
*/
int MB_RespGet_CoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state);
/** MODBUS_REQ_COILS_API
* @}
*/
/**
@@ -127,7 +48,7 @@ int MB_RespGet_CoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coi
* @{
*/
/** @brief Structure for coils operation */
/** @brief Enum for coils operation */
typedef enum
{
SET_COIL,
@@ -178,7 +99,6 @@ uint16_t MB_Coil_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception);
* @}
*/
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{
@@ -195,8 +115,4 @@ uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg);
*/
/////////////////////////---FUNCTIONS---/////////////////////////////
#endif //__MODBUS_COILS_H_
/** MODBUS_COILS
* @}
*/
#endif //__MODBUS_COILS_H_

View File

@@ -194,6 +194,42 @@ extern RS_MsgTypeDef MODBUS_MSG;
*/
#define MB_Set_Register_Ptr(_parr_, _addr_) ((uint16_t *)(_parr_)+(_addr_))
/**
* @brief Макрос для установки указателя на регистр, содержащий запрашиваемый коил
* @param _parr_ - массив коилов.
* @param _coil_ - Номер коила от начала массива _arr_.
* @note Используется вместе с @ref MB_Set_Coil_Mask
@verbatim Пояснение выражений
- (_coil_/16) - индекс регистра, в котором содержится коил по адресу _coil_
Визуальный пример: 30 коил будет в 30/16 = 1 регистре (индексация с 0)
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
|register[0]----| |register[1]----|
|skip this------| |get this-------|
|shift to 14 bit|
@endverbatim
*/
#define MB_Set_Coil_Reg_Ptr(_parr_, _coil_) ((uint16_t *)(_parr_)+((_coil_)/16))
/**
* @brief Макрос для установки маски, чтобы выделить запрашиваемый коил из регистра
* @param _coil_ - Номер коила от начала массива _arr_.
* @note Используется вместе с @ref MB_Set_Coil_Reg_Ptr
@verbatim Пояснение выражений
- (16*(_coil_/16) - сколько коилов нужно пропустить. прим. (16*30/16) - первые 16 коилов находятся вне регистра
- _coil_-(16*(_coil_/16)) - сдвинуть бит на место запрашиваемого коила в регистре
Визуальный пример: 30 коил будет регистре[1], на 14 бите:
register = 30/16 = 1
bit = 30 - (16*30/16) = 30 - 16 = 14
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
|register[0]----| |register[1]----|
|skip this------| |get this-------|
|shift to 14 bit|
@endverbatim
*/
#define MB_Set_Coil_Mask(_coil_) (1 << ( _coil_ - (16*((_coil_)/16)) ))
/** GENERAL_MODBUS_STUFF
* @}
*/
@@ -233,7 +269,6 @@ extern RS_MsgTypeDef MODBUS_MSG;
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{

View File

@@ -86,52 +86,6 @@ void MB_DeviceInentificationInit(void);
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
/**
* @addtogroup MODBUS_REQ_DEFID_API API for Device Identifications
* @ingroup MODBUS_REQUEST_MSG
* @brief API для чтения идентификторов из ответа в режиме мастер
* @details Примеры использования:
*
* @code
* // Пример 1: Получить VendorName (ID = 0x00)
* uint8_t length;
* char vendor_name[64];
* if(MB_RespGet_ObjectById(&MODBUS_MSG, 0x00, vendor_name, &length))
* {
* // получено
* }
*
* // Пример 2: Перебрать все объекты в сообщении
* uint8_t obj_id, obj_length;
* char obj_data[256];
*
* int obj_count = MB_RespGet_NumberOfObjects(&MODBUS_MSG);
* printf("Total objects: %d\n", obj_count);
*
* for(int i = 0; i < obj_count; i++)
* {
* if(MB_RespGet_ObjectByIndex(&MODBUS_MSG, i, &obj_id, obj_data, &obj_length))
* {
* // получено
* }
* }
* @endcode
* @{
*/
/* Получить количество объектов в сообщении */
int MB_RespGet_NumberOfObjects(RS_MsgTypeDef *modbus_msg);
/* Найти объект по ID в сообщении */
int MB_RespGet_ObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length);
/* Получить объект по индексу в сообщении */
int MB_RespGet_ObjectByIndex(RS_MsgTypeDef *modbus_msg, int index, uint8_t *obj_id, char *obj_data, uint8_t *obj_length);
/** MODBUS_REQ_DEFID_API
* @}
*/
/**
* @addtogroup MODBUS_DEVID
* @{
@@ -145,7 +99,7 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj);
/** MODBUS_DEVID
* @}
*/
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{

View File

@@ -65,29 +65,6 @@ 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_RespGet_Diagnostic(&MODBUS_MSG, &counter_value))
* {
* printf("Counter value: %d\n", counter_value);
* }
* @endcode
* @{
*/
int MB_RespGet_Diagnostic(RS_MsgTypeDef *modbus_msg, uint16_t *data);
/** MODBUS_REQ_DIAG_API
* @}
*/
@@ -121,7 +98,6 @@ void MB_Diagnostics_SlaveBusyCnt(void);
* @}
*/
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{

View File

@@ -23,7 +23,22 @@
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS
* @{
*/
/* Записать регистр хранения по глобальному адресу. */
MB_ExceptionTypeDef MB_Holding_Write_Global(uint16_t Addr, uint16_t WriteVal);
/* Считать регистр хранения по глобальному адресу. */
uint16_t MB_Holding_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception);
/** MODBUS_DATA_ACCESS_FUNCTIONS
* @}
*/
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{

View File

@@ -21,7 +21,21 @@
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS
* @{
*/
/* Записать входной регистр по глобальному адресу. */
MB_ExceptionTypeDef MB_Input_Write_Global(uint16_t Addr, uint16_t WriteVal);
/* Считать входной регистр по глобальному адресу. */
uint16_t MB_Input_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception);
/** MODBUS_DATA_ACCESS_FUNCTIONS
* @}
*/
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS Internal Process Functions
* @ingroup MODBUS_INTERNAL

View File

@@ -14,10 +14,6 @@
#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
@@ -109,6 +105,115 @@ int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint1
* @}
*/
/**
* @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_RespGet_CoilState(&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_RespGet_CoilState(&MODBUS_MSG, addr, &state))
* {
* printf("Coil %d: %s\n", addr, state ? "ON" : "OFF");
* }
* }
* @endcode
* @{
*/
int MB_RespGet_CoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state);
/** MODBUS_REQ_COILS_API
* @}
*/
/**
* @addtogroup MODBUS_REQ_DEFID_API API for Device Identifications
* @ingroup MODBUS_REQUEST_MSG
* @brief API для чтения идентификторов из ответа в режиме мастер
* @details Примеры использования:
*
* @code
* // Пример 1: Получить VendorName (ID = 0x00)
* uint8_t length;
* char vendor_name[64];
* if(MB_RespGet_ObjectById(&MODBUS_MSG, 0x00, vendor_name, &length))
* {
* // получено
* }
*
* // Пример 2: Перебрать все объекты в сообщении
* uint8_t obj_id, obj_length;
* char obj_data[256];
*
* int obj_count = MB_RespGet_NumberOfObjects(&MODBUS_MSG);
* printf("Total objects: %d\n", obj_count);
*
* for(int i = 0; i < obj_count; i++)
* {
* if(MB_RespGet_ObjectByIndex(&MODBUS_MSG, i, &obj_id, obj_data, &obj_length))
* {
* // получено
* }
* }
* @endcode
* @{
*/
/* Получить количество объектов в сообщении */
int MB_RespGet_NumberOfObjects(RS_MsgTypeDef *modbus_msg);
/* Найти объект по ID в сообщении */
int MB_RespGet_ObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length);
/* Получить объект по индексу в сообщении */
int MB_RespGet_ObjectByIndex(RS_MsgTypeDef *modbus_msg, int index, uint8_t *obj_id, char *obj_data, uint8_t *obj_length);
/** MODBUS_REQ_DEFID_API
* @}
*/
/**
* @addtogroup MODBUS_REQ_DIAG_API API for Diagnostics
* @ingroup MODBUS_REQUEST_MSG
* @brief API для чтения диагностической информации из ответа в режиме мастер
* @details Примеры использования:
*
* @code
* // Получить данные диагностики (значение счетчика)
* uint16_t counter_value;
* if(MB_RespGet_Diagnostic(&MODBUS_MSG, &counter_value))
* {
* printf("Counter value: %d\n", counter_value);
* }
* @endcode
* @{
*/
int MB_RespGet_Diagnostic(RS_MsgTypeDef *modbus_msg, uint16_t *data);
/** MODBUS_REQ_DIAG_API
* @}
*/
/**
* @addtogroup MODBUS_MASTER
* @{

View File

@@ -15,9 +15,6 @@
#include "rs_message.h"
#ifdef MODBUS_ENABLE_SLAVE
#define MODBUS_MODE_SLAVE 0
#endif
/**
* @addtogroup MODBUS_SLAVE
* @{

View File

@@ -115,6 +115,10 @@ static int dummy;
#ifndef RS_USER_VARS_NUMB
#define RS_USER_VARS_NUMB 0
#endif
#ifndef local_time
#define local_time() uwTick
#endif
/** @endcond */
@@ -141,9 +145,10 @@ typedef enum // RS_StatusTypeDef
/*0x03*/ RS_ABORTED,
/*0x04*/ RS_BUSY,
/*0x05*/ RS_SKIP,
/*0x06*/ RS_TIMEOUT,
/*0x06*/ RS_COLLECT_MSG_ERR,
/*0x07*/ RS_PARSE_MSG_ERR,
/*0x07*/ RS_COLLECT_MSG_ERR,
/*0x08*/ RS_PARSE_MSG_ERR,
// reserved values
// /*0x00*/ RS_UNKNOWN_ERR = 0x00, ///< reserved for case, if no one error founded (nothing changed response from zero)

View File

@@ -123,12 +123,15 @@ ProjectRoot/
void callback_func(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
// MB_RespGet_... Чтобы достать нужные данные из ответа
for(int addr = MODBUS_MSG.Addr; addr < MODBUS_MSG.Addr + MODBUS_MSG.Qnt; addr++)
if(hmodbus->RS_STATUS == RS_OK)
{
uint16_t value;
if(MB_RespGet_RegisterValue(&MODBUS_MSG, addr, &value))
for(int addr = MODBUS_MSG.Addr; addr < MODBUS_MSG.Addr + MODBUS_MSG.Qnt; addr++)
{
read_hold[i] = value;
uint16_t value;
if(MB_RespGet_RegisterValue(&MODBUS_MSG, addr, &value))
{
read_hold[i] = value;
}
}
}
}

View File

@@ -57,7 +57,7 @@ MB_ExceptionTypeDef MB_Coil_Write_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteV
/**
* @brief Считать коил по глобальному адресу.
* @param Addr Адрес коила.
* @param Exception Указатель на переменную для кода исключения, в случа неудачи при чтении.
* @param Exception Указатель на переменную для кода исключения, в случае неудачи при чтении.
* @return uint16_t Возвращает весь регистр с маской на запрошенном коиле.
*
* @details Позволяет обратиться к любому коилу по его глобальному адрессу.
@@ -86,51 +86,6 @@ uint16_t MB_Coil_Read_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_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->Func_Code != MB_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->DATA[data_index] >> (bit_index+8)) & 0x01;
else
*coil_state = (modbus_msg->DATA[data_index] >> bit_index) & 0x01;
return 1;
}
/**
* @brief Обработать функцию Read Coils (01 - 0x01).
* @param modbus_msg Указатель на структуру собщения modbus.
@@ -284,7 +239,6 @@ uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg)
#else //MODBUS_ENABLE_COILS
int MB_RespGet_CoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state) {return 0;}
MB_ExceptionTypeDef MB_Coil_Write_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal) {return ILLEGAL_FUNCTION;}
uint16_t MB_Coil_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception) {return 0;}
uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg) {return 0;}

View File

@@ -1,7 +1,7 @@
/**
******************************************************************************
* @file modbus_core.c
* @brief Базовая реализация ядра Modbus (заглушка)
* @brief Базовая реализация ядра Modbus
******************************************************************************
* @details
В текущей реализации этот файл служит заглушкой для будущего расширения

View File

@@ -21,132 +21,6 @@
MB_DeviceIdentificationsTypeDef MB_DEVID; ///< Глобальная структура идентификаторов устройства
/**
* @brief Получить количество объектов в сообщении
* @param modbus_msg Указатель на структуру сообщения
* @return int Количество объектов
*/
int MB_RespGet_NumberOfObjects(RS_MsgTypeDef *modbus_msg)
{
if(modbus_msg == NULL)
{
return 0;
}
// Проверяем что ответ связан с диагностикой
if(modbus_msg->Func_Code != MB_R_DEVICE_INFO)
{
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->Func_Code != MB_R_DEVICE_INFO)
{
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_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->Func_Code != MB_R_DEVICE_INFO)
{
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;
}
/**
@@ -674,9 +548,7 @@ void MB_DeviceInentificationInit(void)
#else //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
/* Получить количество объектов в сообщении */
int MB_RespGet_NumberOfObjects(RS_MsgTypeDef *modbus_msg) {return 0;}
int MB_RespGet_ObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length) {return 0;}
int MB_RespGet_ObjectByIndex(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_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg) {return 0;}

View File

@@ -36,29 +36,6 @@ void MB_DiagnosticsInit(void)
}
/**
* @brief Получить данные диагностики из сообщения (DATA[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->Func_Code != MB_R_DIAGNOSTIC)
{
return 0;
}
*data = modbus_msg->DATA[1];
return 1;
}
/**
* @brief Выставить бит в регистре диагностике
* @param bit_num Номер бита для выставления (1-15, 0 бит нельзя выставить)
@@ -319,7 +296,6 @@ MB_DeviceModeTypeDef MB_GetDeviceMode(void)
#else //MODBUS_ENABLE_DIAGNOSTICS
void MB_DiagnosticsInit(void) {}
int MB_RespGet_Diagnostic(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_Process_Diagnostics(RS_MsgTypeDef *modbus_msg) {return 0;}

View File

@@ -24,6 +24,66 @@
#ifdef MODBUS_ENABLE_HOLDINGS
/**
* @brief Записать регистр хранения по глобальному адресу.
* @param Addr Адрес регистра.
* @param WriteVal Число для записи.
* @return ExceptionCode Код исключения если регистра по адресу не существует, и NO_ERRORS если все ок.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
MB_ExceptionTypeDef MB_Holding_Write_Global(uint16_t Addr, uint16_t WriteVal)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception = NO_ERRORS;
uint16_t *pHoldRegs;
//------------WRITE COIL-------------
Exception = MB_DefineRegistersAddress(&pHoldRegs, Addr, 1, RegisterType_Holding);
if(Exception == NO_ERRORS)
{
*(pHoldRegs) = WriteVal;
}
return Exception;
}
/**
* @brief Считать регистр хранения по глобальному адресу.
* @param Addr Адрес регистра.
* @param Exception Указатель на переменную для кода исключения, в случае неудачи при чтении.
* @return uint16_t Возвращает значение регистра.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
uint16_t MB_Holding_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception_tmp = 0;
uint16_t *pHoldRegs;
//------------READ COIL--------------
Exception_tmp = MB_DefineRegistersAddress(&pHoldRegs, Addr, 1, RegisterType_Holding);
if(Exception) // if exception is not given to func fill it
*Exception = Exception_tmp;
if(Exception_tmp == NO_ERRORS)
{
return *(pHoldRegs);
}
else
{
return 0;
}
}
/**
* @brief Обработать функцию Read Holding Registers (03 - 0x03).
* @param modbus_msg Указатель на структуру собщения modbus.
@@ -34,7 +94,7 @@ uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg)
{
//---------CHECK FOR ERRORS----------
// get origin address for data
uint16_t *pHoldRegs;
uint16_t *pHoldRegs;
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Holding); // определение адреса регистров
if(modbus_msg->Except_Code != NO_ERRORS)
return 0;
@@ -102,6 +162,8 @@ uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg)
#else //MODBUS_ENABLE_HOLDINGS
MB_ExceptionTypeDef MB_Holding_Write_Global(uint16_t Addr, uint16_t WriteVal) {return ILLEGAL_FUNCTION;}
uint16_t MB_Holding_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception) {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;}

View File

@@ -14,6 +14,65 @@
#ifdef MODBUS_ENABLE_INPUTS
/**
* @brief Записать входной регистр по глобальному адресу.
* @param Addr Адрес регистра.
* @param WriteVal Число для записи.
* @return ExceptionCode Код исключения если регистра по адресу не существует, и NO_ERRORS если все ок.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
MB_ExceptionTypeDef MB_Input_Write_Global(uint16_t Addr, uint16_t WriteVal)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception = NO_ERRORS;
uint16_t *pInRegs;
//------------WRITE COIL-------------
Exception = MB_DefineRegistersAddress(&pInRegs, Addr, 1, RegisterType_Input);
if(Exception == NO_ERRORS)
{
*(pInRegs) = WriteVal;
}
return Exception;
}
/**
* @brief Считать входной регистр по глобальному адресу.
* @param Addr Адрес регистра.
* @param Exception Указатель на переменную для кода исключения, в случае неудачи при чтении.
* @return uint16_t Возвращает значение регистра.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
uint16_t MB_Input_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception_tmp = 0;
uint16_t *pInRegs;
//------------READ COIL--------------
Exception_tmp = MB_DefineRegistersAddress(&pInRegs, Addr, 1, RegisterType_Input);
if(Exception) // if exception is not given to func fill it
*Exception = Exception_tmp;
if(Exception_tmp == NO_ERRORS)
{
return *(pInRegs);
}
else
{
return 0;
}
}
/**
* @brief Обработать функцию Read Input Registers (04 - 0x04).
* @param modbus_msg Указатель на структуру собщения modbus.
@@ -47,6 +106,8 @@ uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg)
#else //MODBUS_ENABLE_INPUTS
MB_ExceptionTypeDef MB_Input_Write_Global(uint16_t Addr, uint16_t WriteVal) {return ILLEGAL_FUNCTION;}
uint16_t MB_Input_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception) {return 0;}
uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) {return 0;}
#endif

View File

@@ -53,6 +53,203 @@ int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint1
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->Func_Code != MB_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->DATA[data_index] >> (bit_index+8)) & 0x01;
else
*coil_state = (modbus_msg->DATA[data_index] >> bit_index) & 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->Func_Code != MB_R_DEVICE_INFO)
{
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->Func_Code != MB_R_DEVICE_INFO)
{
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_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->Func_Code != MB_R_DEVICE_INFO)
{
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;
}
/**
* @brief Получить данные диагностики из сообщения (DATA[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->Func_Code != MB_R_DIAGNOSTIC)
{
return 0;
}
*data = modbus_msg->DATA[1];
return 1;
}
/**
* @brief Определить размер модбас запроса (МАСТЕР версия).
* @param hRS Указатель на хендлер RS.
@@ -487,6 +684,11 @@ RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_SPECIFIC(uint8_t slave_addr, uint8_t obj
RS_MsgTypeDef msg_dummy = {0};
int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value) {return 0;}
int MB_RespGet_CoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state) {return 0;}
int MB_RespGet_NumberOfObjects(RS_MsgTypeDef *modbus_msg) {return 0;}
int MB_RespGet_ObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length) {return 0;}
int MB_RespGet_ObjectByIndex(RS_MsgTypeDef *modbus_msg, int index, uint8_t *obj_id, char *obj_data, uint8_t *obj_length) {return 0;}
int MB_RespGet_Diagnostic(RS_MsgTypeDef *modbus_msg, uint16_t *data) {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;}

View File

@@ -348,16 +348,18 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
RS_Set_RX_End(hRS);
// Парсим наше сообщение
RS_StatusTypeDef parse_res = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
hRS->RS_STATUS = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
// Если сообещине принято корректно
if(parse_res == RS_OK)
if(hRS->RS_STATUS == RS_OK)
{
RS_Timeout_Stop(hRS);
hRS->lastPacketTick = uwTick;
hRS->lastPacketTick = local_time();
if(hRS->sRS_Mode < RS_MASTER_MODE_START)
{
RS_Response(hRS, hRS->pMessagePtr); // отвечаем на запрос
}
else
{
if(hRS->pCallback)
@@ -435,12 +437,20 @@ void RS_TIM_Handler(RS_HandleTypeDef *hRS)
HAL_TIM_IRQHandler(hRS->htim);
RS_Abort(hRS, ABORT_RS);
if(hRS->pMessagePtr->MbAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
TrackerCnt_Err(hRS->rs_err);
hRS->RS_STATUS = RS_TIMEOUT;
if(hRS->sRS_Mode < RS_MASTER_MODE_START)
if(hRS->pMessagePtr->MbAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
TrackerCnt_Err(hRS->rs_err);
if(hRS->sRS_Mode == RS_MASTER_REQUEST) {
// Мастер: таймаут ответа -> освобождаем для нового запроса
if(hRS->sRS_Mode >= RS_MASTER_MODE_START)
{ // Мастер: коллбек и освобождение для нового запроса
if(hRS->pCallback)
{
hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
}
RS_Set_Free(hRS);
} else {
// Слейв: перезапускаем прием

View File

@@ -9,9 +9,11 @@
******************************************************************************
* @details
Файл содержит настройки для работы Modbus:
- Идентификатор устройства и таймауты
- Подключение библиотек контроллера
- ID устройства и таймауты
- Строковые идентификаторы (Vendor, Product, Revision)
- Настройки периферии (UART, TIMER)
- Подключение модулей Modbus
- Опциональные функции (переключение команд 0x03/0x04)
******************************************************************************/
#ifndef _MODBUS_CONFIG_H_