8 Commits

Author SHA1 Message Date
Razvalyaev
7d40b019fb release 0.51
Бета добавление новых кодов исключения

Улучшение механики ответа с задержкой, когда время ответа определяется пользователем, а не в прерывании сразу
2026-02-20 16:39:15 +03:00
ca8fa259c8 Обновить README.md 2026-02-20 11:26:15 +03:00
36db3964ea Обновить README.md 2026-02-20 10:48:10 +03:00
Razvalyaev
a880f46a56 release 0.5 (*API CHANGED)
- чуть переделаны некоторые дефайны для универсализации
- смена направления сделана через указател на функцию, а не глобальный дефайн
- добавлен бета осцилограф модбас
- некоторый рефакторинг
2026-02-20 10:41:31 +03:00
Razvalyaev
3aa279736d Минор фикс rs_message 2025-11-07 22:38:40 +03:00
Razvalyaev
cba13802b1 Добавление совместимости с старым API 2025-11-06 22:36:21 +03:00
Razvalyaev
c648a605f5 release 0.4 (*API CHANGED)
*Не совсем апи, но поменялись enum в modbus_core.h:
- MB_ExceptionTypeDef
- MB_FunctonTypeDef

Необходимо обновить modbus_data.c:
NO_ERRORS 		->	NO_ERRORS
ILLEGAL_FUNCTION	->	ET_ILLEGAL_FUNCTION
ILLEGAL_DATA_ADDRESS 	-> 	ET_ILLEGAL_DATA_ADDRESS
ILLEGAL_DATA_ADDRESS	->	ET_ILLEGAL_DATA_ADDRESS

- множественные правки докумнтации
- множественный рефакторинг
2025-11-06 21:33:08 +03:00
Razvalyaev
3d106f18ef release 0.3.2 Фиксы:
- Data Access API расширено функциями для чтения/записи регистров
- API для реквестов MB_RespGet.. перенесено в modbus_master
- Мастер: коллбек реквеста вызывается и при таймауте
	- В коллбеке можно понять статус реквеста по hmodbus->RS_STATUS
2025-11-05 16:38:38 +03:00
28 changed files with 2198 additions and 1318 deletions

168
Inc/__modbus_compat.h Normal file
View File

@@ -0,0 +1,168 @@
/**
*******************************************************************************
* @file __modbus_compat.h
* @brief Модуль для совместимости библиотеки MODBUS.
*******************************************************************************
* @details Файл содержит API старых функций, а также заглушки для отключенных модулей:
******************************************************************************/
#ifndef __MODBUS_COMPAT_H_
#define __MODBUS_COMPAT_H_
#include "modbus_core.h"
#define CREATE_DEPRECATED_ALIAS(old_name, new_name, type) \
__attribute__((deprecated("Use " #new_name " instead"))) \
static const type old_name = new_name;
// Для функций
#define CREATE_DEPRECATED_FUNCTION(old_name, new_name, return_type, ...) \
__attribute__((deprecated("Use " #new_name " instead"))) \
return_type old_name(__VA_ARGS__) { \
return new_name(__VA_ARGS__); \
}
/** @addtogroup MODBUS_LEGACY_API Legacy API
* @ingroup MODBUS_FUNCTIONS
* @brief Старые API функций, сохранённые для обратной совместимости.
* @note Не используйте эти функции в новом коде. Они будут удалены в будущих версиях.
* Вместо них используйте актуальные API, приведенные в втором столбце.
* @{
*/
// Устаревшие enum //Старый замените на // Новый
// | // |
// v // v
CREATE_DEPRECATED_ALIAS(NO_ERRORS, ET_NO_ERRORS, MB_ExceptionTypeDef);
CREATE_DEPRECATED_ALIAS(ILLEGAL_FUNCTION, ET_ILLEGAL_FUNCTION, MB_ExceptionTypeDef);
CREATE_DEPRECATED_ALIAS(ILLEGAL_DATA_ADDRESS, ET_ILLEGAL_DATA_ADDRESS, MB_ExceptionTypeDef);
CREATE_DEPRECATED_ALIAS(ILLEGAL_DATA_VALUE, ET_ILLEGAL_DATA_VALUE, MB_ExceptionTypeDef);
CREATE_DEPRECATED_ALIAS(SLAVE_DEVICE_FAILURE, ET_SLAVE_DEVICE_FAILURE, MB_ExceptionTypeDef);
CREATE_DEPRECATED_ALIAS(MB_R_COILS, FC_R_COILS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_R_DISC_IN, FC_R_DISC_IN, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_R_HOLD_REGS, FC_R_HOLD_REGS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_R_IN_REGS, FC_R_IN_REGS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_W_COIL, FC_W_COIL, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_W_HOLD_REG, FC_W_HOLD_REG, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_W_COILS, FC_W_COILS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_W_HOLD_REGS, FC_W_HOLD_REGS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_R_DIAGNOSTICS, FC_R_DIAGNOSTICS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_R_DEVICE_ID, FC_R_DEVICE_ID, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_R_COILS, FC_ERR_R_COILS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_R_DISC_IN, FC_ERR_R_DISC_IN, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_R_IN_REGS, FC_ERR_R_IN_REGS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_R_HOLD_REGS, FC_ERR_R_HOLD_REGS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_W_COIL, FC_ERR_W_COIL, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_W_HOLD_REG, FC_ERR_W_HOLD_REG, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_W_COILS, FC_ERR_W_COILS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_W_HOLD_REGS, FC_ERR_W_HOLD_REGS, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_R_DIAGNOSTIC, FC_ERR_R_DIAGNOSTIC, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_ERR_R_DEVICE_INFO, FC_ERR_R_DEVICE_INFO, MB_FunctonTypeDef);
CREATE_DEPRECATED_ALIAS(MB_BASIC_IDENTIFICATIONS, RID_BASIC_IDENTIFICATIONS, MB_ReadDevId);
CREATE_DEPRECATED_ALIAS(MB_REGULAR_IDENTIFICATIONS, RID_REGULAR_IDENTIFICATIONS, MB_ReadDevId);
CREATE_DEPRECATED_ALIAS(MB_EXTENDED_IDENTIFICATIONS, RID_EXTENDED_IDENTIFICATIONS, MB_ReadDevId);
CREATE_DEPRECATED_ALIAS(MB_SPEDIFIC_IDENTIFICATIONS, RID_SPEDIFIC_IDENTIFICATIONS, MB_ReadDevId);
#define MbAddr DeviceAddr
/** MODBUS_LEGACY_API
* @}
*/
/** @cond Заглушки отключенных модулей */
#ifndef MODBUS_ENABLE_COILS
#define MB_Coil_Write_Global(Addr, WriteVal) ET_ILLEGAL_FUNCTION
#define MB_Coil_Read_Global(Addr, Exception) 0
#define MB_Process_Read_Coils(modbus_msg) 0
#define MB_Process_Write_Single_Coil(modbus_msg) 0
#define MB_Process_Write_Miltuple_Coils(modbus_msg) 0
#endif
#ifndef MODBUS_ENABLE_HOLDINGS
#define MB_Holding_Write_Global(Addr, WriteVal) ET_ILLEGAL_FUNCTION
#define MB_Holding_Read_Global(Addr, Exception) 0
#define MB_Process_Read_Hold_Regs(modbus_msg) 0
#define MB_Process_Write_Single_Reg(modbus_msg) 0
#define MB_Process_Write_Miltuple_Regs(modbus_msg) 0
#endif
#ifndef MODBUS_ENABLE_INPUTS
#define MB_Input_Write_Global(Addr, WriteVal) ET_ILLEGAL_FUNCTION
#define MB_Input_Read_Global(Addr, Exception) 0
#define MB_Process_Read_Input_Regs(modbus_msg) 0
#endif
#ifndef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
#define MB_WriteSingleObjectToMessage(mbdata, ind, obj)
#define MB_WriteObjectsToMessage(modbus_msg, maxidofobj)
#define MB_Process_Read_Device_Identifications(modbus_msg) 0
#define MB_DeviceInentificationInit()
#endif
#ifndef MODBUS_ENABLE_DIAGNOSTICS
#define MB_DiagnosticsInit()
#define MB_Diagnostics_WriteBit(bit_num, bit_state) 0
#define MB_Diagnostics_GetBit(bit_num) 0
#define MB_Process_Diagnostics(modbus_msg) 0
#define MB_Diagnostics_BusMessageCnt()
#define MB_Diagnostics_CommunicationErrorCnt()
#define MB_Diagnostics_ExceptionErrorCnt()
#define MB_Diagnostics_CharacterOverrunCnt()
#define MB_Diagnostics_SlaveMessageCnt()
#define MB_Diagnostics_SlaveNoResponseCnt()
#define MB_Diagnostics_SlaveNAKCnt()
#define MB_Diagnostics_SlaveBusyCnt()
#define MB_GetDeviceMode() MODBUS_NORMAL_MODE
#endif
#ifndef MODBUS_ENABLE_MASTER
#define MB_RespGet_RegisterValue(modbus_msg, reg_addr, reg_value) 0
#define MB_RespGet_CoilState(modbus_msg, coil_addr, coil_state) 0
#define MB_RespGet_NumberOfObjects(modbus_msg) 0
#define MB_RespGet_ObjectById(modbus_msg, obj_id, obj_data, obj_length) 0
#define MB_RespGet_ObjectByIndex(modbus_msg, index, obj_id, obj_data, obj_length) 0
#define MB_RespGet_Diagnostic(modbus_msg, data) 0
#define MB_REQUEST_READ_COILS(slave_addr, start_addr, quantity) {0}
#define MB_REQUEST_READ_DISCRETE_INPUTS(slave_addr, start_addr, quantity) {0}
#define MB_REQUEST_READ_HOLDING_REGS(slave_addr, start_addr, quantity) {0}
#define MB_REQUEST_READ_INPUT_REGS(slave_addr, start_addr, quantity) {0}
#define MB_REQUEST_WRITE_SINGLE_COIL(slave_addr, coil_addr, value) {0}
#define MB_REQUEST_WRITE_SINGLE_REG(slave_addr, reg_addr, value) {0}
#define MB_REQUEST_WRITE_MULTIPLE_COILS(slave_addr, start_addr, quantity, coils_data) {0}
#define MB_REQUEST_WRITE_MULTIPLE_REGS(slave_addr, start_addr, quantity, regs_data) {0}
#define MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, sub_function, data) {0}
#define MB_REQUEST_RETURN_QUERY_DATA(slave_addr) {0}
#define MB_REQUEST_RESTART_COMMUNICATIONS(slave_addr, data) {0}
#define MB_REQUEST_RETURN_DIAGNOSTIC_REGISTER(slave_addr) {0}
#define MB_REQUEST_FORCE_LISTEN_ONLY_MODE(slave_addr) {0}
#define MB_REQUEST_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER(slave_addr) {0}
#define MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(slave_addr) {0}
#define MB_REQUEST_RETURN_BUS_COMMUNICATION_ERROR_COUNT(slave_addr) {0}
#define MB_REQUEST_RETURN_SLAVE_EXCEPTION_ERROR_COUNT(slave_addr) {0}
#define MB_REQUEST_RETURN_SLAVE_MESSAGE_COUNT(slave_addr) {0}
#define MB_REQUEST_RETURN_SLAVE_NO_RESPONSE_COUNT(slave_addr) {0}
#define MB_REQUEST_RETURN_SLAVE_NAK_COUNT(slave_addr) {0}
#define MB_REQUEST_RETURN_SLAVE_BUSY_COUNT(slave_addr) {0}
#define MB_REQUEST_RETURN_BUS_CHARACTER_OVERRUN_COUNT(slave_addr) {0}
#define MB_REQUEST_READ_DEVICE_ID_BASIC(slave_addr) {0}
#define MB_REQUEST_READ_DEVICE_ID_REGULAR(slave_addr) {0}
#define MB_REQUEST_READ_DEVICE_ID_EXTENDED(slave_addr) {0}
#define MB_REQUEST_READ_DEVICE_ID_SPECIFIC(slave_addr, object_id) {0}
#define MB_Master_Collect_Message(hmodbus, modbus_msg, modbus_uart_buff) RS_ERR
#define MB_Master_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff) RS_ERR
#endif
#ifndef MODBUS_ENABLE_SLAVE
#define MB_Slave_Response(hmodbus, modbus_msg) RS_ERR
#define MB_Slave_Collect_Message(hmodbus, modbus_msg, modbus_uart_buff) RS_ERR
#define MB_Slave_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff) RS_ERR
#endif
/** @endcond */
#endif //__MODBUS_COMPAT_H_

View File

@@ -1,20 +1,22 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus.h
* @brief Главный заголовочный файл Modbus библиотеки
******************************************************************************
*******************************************************************************
@addtogroup MODBUS Modbus tools
******************************************************************************
@brief Библиотека реализующая протокол Modbus
*******************************************************************************
@addtogroup MODBUS_FUNCTIONS Main API for Modbus Library
@ingroup MODBUS
@brief Публичные функции библиотеки
@{
******************************************************************************
*******************************************************************************
* @details
Объединяющий файл для подключения всей функциональности Modbus.
Подключает все необходимые модули:
@section init Инструкция по подключению
@section Start Инструкция по подключению
Для корректной работы надо:
- Подключить обработчики RS_UART_Handler(), RS_TIM_Handler(), в соответствубщие
низкоуровневые прерывания UART_IRQHandler, TIM_IRQHandler вместо HAL'овского обработчика
@@ -23,32 +25,51 @@
- Инициализировать хендл мобдас. По умолчанию глобально создается hmodbus1
- После для запуска Modbus:
@verbatim
@code
//----------------Слейв модбас----------------//
#include "modbus.h"
MODBUS_FirstInit(&hmodbus1, &huart1, &htim3);
MODBUS_Config(&hmodbus1, 1, 1000, MODBUS_MODE_SLAVE);
MODBUS_FirstInit(&hmodbus1, &huart1, &htim3, NULL); // NULL, если управление RE/DE не нужно
MODBUS_Config(&hmodbus1, MODBUS_DEVICE_ID, MODBUS_TIMEOUT, MODBUS_MODE_SLAVE);
MODBUS_SlaveStart(&hmodbus1, NULL);
@endverbatim
@verbatim
@endcode
@code
//----------------Мастер модбас----------------//
#include "modbus.h"
MODBUS_FirstInit(&hmodbus1, &huart1, &htim3);
MODBUS_Config(&hmodbus1, 0, 1000, MODBUS_MODE_MASTER); // - если нужны другие настройки, не из modbus_config.h
MODBUS_FirstInit(&hmodbus1, &huart1, &htim3, NULL); // NULL, если управление RE/DE не нужно
MODBUS_Config(&hmodbus1, 0, MODBUS_TIMEOUT, MODBUS_MODE_MASTER);
// Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук
// При получении ответа вызовется функция callback_func()
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 содержит ответ от устройства
// 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;
}
}
}
else // Ответ получен с ошибкой или не получен вовсе
{
}
}
@endverbatim
@endcode
@section Подключаемые модули:
@section modules Подключаемые модули:
- rs_message.h - работа с uart
- modbus_core.h - базовые определения
- modbus_coils.h - работа с дискретными выходами
@@ -58,7 +79,7 @@
- modbus_diag.h - диагностика modbus
@section Структура данных Modbus
@section data Структура данных Modbus
#### Holding/Input Registers:
- Регистры — 16-битные слова. Доступ к регистрам осуществляется через указатель.
@@ -73,15 +94,40 @@
#define __MODBUS_H_
#include "rs_message.h"
#ifdef MODBUS_ENABLE_MASTER
#include "modbus_master.h"
#endif
#ifdef MODBUS_ENABLE_SLAVE
#include "modbus_slave.h"
#endif
#ifdef MODBUS_ENABLE_COILS
#include "modbus_coils.h"
#endif
#ifdef MODBUS_ENABLE_HOLDINGS
#include "modbus_holdregs.h"
#endif
#ifdef MODBUS_ENABLE_INPUTS
#include "modbus_inputregs.h"
#endif
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
#include "modbus_devid.h"
#endif
#ifdef MODBUS_ENABLE_DIAGNOSTICS
#include "modbus_diag.h"
#endif
#ifdef MODBUS_ENABLE_OSCIL
#include "modbus_oscil.h"
#endif
#include "modbus_data.h"
#ifdef MODBUS_ENABLE_MASTER
#define MODBUS_MODE_MASTER 1 ///< Псевдо-enum: Режим мастер
#endif
#ifdef MODBUS_ENABLE_SLAVE
#define MODBUS_MODE_SLAVE 0 ///< Псевдо-enum: Режим слейв
#endif
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
@@ -90,7 +136,7 @@
//----------------FUNCTIONS FOR USER----------------
/* Инициализация периферии модбас. */
HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim);
HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, void (*pSetDirection)(int Tx));
/* Программная конфигурация модбас. */
HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master);
/* Запуск слейв устройства */
@@ -99,11 +145,12 @@ 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_
/** MODBUS_FUNCTIONS
* @}
*/
*/

View File

@@ -1,23 +1,27 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_coils.h
* @brief Работа с коилами Modbus
******************************************************************************
*******************************************************************************
@addtogroup MODBUS_COILS Coils Tools
@ingroup MODBUS_INTERNAL
******************************************************************************
@brief Функции для работы с коилами
*******************************************************************************
* @details
Модуль для доступа к coils внутри программы:
- Функции для доступа к coils по глобальным адресам
- Макросы для доступа к coils по локальным адресам
Модуль предоставляет функции и макросы для работы с битовыми данными:
- Чтение coils (0x01) Упаковка битов в байты
- Запись одиночного coil (0x05) Установка/сброс бита
- Запись множественных coils (0x0F) - распаковка байтов в биты
- Макросы для локального доступа к coils
@section Организация битовых данных:
@section cbits Организация битовых данных:
Coils упакованы в 16-битные слова для эффективного использования памяти.
Биты нумеруются от младшего к старшему внутри каждого слова.
@section Адресация:
@section caddr Адресация:
- Глобальная - абсолютный адрес в пространстве Modbus
- Локальная - относительный адрес внутри массива coils
- Макросы автоматически вычисляют смещения и маски
@@ -26,6 +30,7 @@ Coils упакованы в 16-битные слова для эффективн
#ifndef __MODBUS_COILS_H_
#define __MODBUS_COILS_H_
#include "modbus_core.h"
#ifdef MODBUS_ENABLE_COILS
/////////////////////////////////////////////////////////////////////
////////////////////---MODBUS FUNCTION DEFINES---////////////////////
@@ -34,100 +39,21 @@ 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
* @}
*/
/**
* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS API for Data Access
* @ingroup MODBUS_FUNCTIONS
* @brief Функции для доступа к данным модбас
* @brief API для доступа к данным модбас внутри программы
* @{
*/
/** @brief Structure for coils operation */
/** @brief Enum for coils operation */
typedef enum
{
SET_COIL,
@@ -178,10 +104,9 @@ uint16_t MB_Coil_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception);
* @}
*/
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{
* @{
*/
/* Обработать функцию Read Coils (01 - 0x01) */
uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg);
@@ -194,9 +119,5 @@ uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg);
* @}
*/
/////////////////////////---FUNCTIONS---/////////////////////////////
#endif //MODBUS_ENABLE_COILS
#endif //__MODBUS_COILS_H_
/** MODBUS_COILS
* @}
*/

View File

@@ -1,12 +1,13 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_core.h
* @brief Ядро Modbus протокола - определения и структуры
******************************************************************************
*******************************************************************************
@addtogroup MODBUS_INTERNAL Modbus Internal Tools
@ingroup MODBUS
@brief Внутренние штуки библиотеки
@{
******************************************************************************
*******************************************************************************
* @details
Базовые определения для реализации Modbus RTU устройства:
- Структуры сообщений Modbus
@@ -14,7 +15,7 @@
Константы размеров полей
Вспомогательные макросы
@section Структура сообщения:
@section msg Структура сообщения:
[ADDR][FUNC][DATA...][CRC]
- Адрес: 1 байт
- Функция: 1 байт
@@ -26,132 +27,147 @@
#define __MODBUS_CORE_H_
#include "modbus_config.h"
#include "modbus_data.h"
#include "__crc_algs.h"
/**
* @addtogroup MODBUS_MESSAGE_DEFINES Modbus Message Tools
* @ingroup MODBUS
* @brief Определения протокола модбас
@{
* @{
*/
/////////////////////////////////////////////////////////////////////
////////////////////---MODBUS MESSAGE DEFINES---/////////////////////
//-------------DEFINES FOR STRUCTURE----------------
/* defines for structure of modbus message */
#define MbAddr_SIZE 1 ///< size of (MbAddr)
#define Func_Code_SIZE 1 ///< size of (Func_Code)
#define Addr_SIZE 2 ///< size of (Addr)
#define Qnt_SIZE 2 ///< size of (Qnt)
#define ByteCnt_SIZE 1 ///< size of (ByteCnt)
#define DATA_SIZE 125 ///< maximum number of data: DWORD (NOT MESSAGE SIZE)
#define CRC_SIZE 2 ///< size of (MB_CRC) in bytes
#define MbTransactionID_size 2 ///< size of (Transaction ID)
#define MbProtocolID_size 2 ///< size of (Protocol ID)
#define MbPDULength_size 2 ///< size of (PDU Length)
#define MbDeviceAddr_size 1 ///< size of (Slave Addr)
#define MbFuncCode_size 1 ///< size of (Function Code)
#define MbAddr_size 2 ///< size of (Address)
#define MbQnt_size 2 ///< size of (Quantity)
#define MbByteCnt_size 1 ///< size of (Byte Count)
#define MbData_size 125 ///< maximum number of data: DWORD (NOT MESSAGE SIZE)
#define MbCRC_size 2 ///< size of (MbCRC) in bytes
/** @brief Size of whole message */
#define INFO_SIZE_MAX (MbAddr_SIZE+Func_Code_SIZE+Addr_SIZE+Qnt_SIZE+ByteCnt_SIZE)
#ifndef MODBUS_PROTOCOL_TCP
/** @brief Size of whole RTU message */
#define RS_INFO_SIZE_MAX (MbDeviceAddr_size+MbFuncCode_size+MbAddr_size+MbQnt_size+MbByteCnt_size)
#else
/** @brief Size of whole TCP message */
#define RS_INFO_SIZE_MAX (MbTransactionID_size+MbProtocolID_size+MbPDULength_size+MbDeviceAddr_size+MbFuncCode_size+MbAddr_size+MbQnt_size)
#endif
/** @brief Size of first part of message that will be received
first receive info part of message, than defines size of rest message*/
#define RX_FIRST_PART_SIZE INFO_SIZE_MAX
#define RS_RX_FIRST_PART_SIZE RS_INFO_SIZE_MAX
/** @brief Size of buffer: max size of whole message */
#define MSG_SIZE_MAX (INFO_SIZE_MAX + DATA_SIZE*2 + CRC_SIZE) // max possible size of message
#define RS_MSG_SIZE_MAX (RS_INFO_SIZE_MAX + MbData_size*2 + MbCRC_size) // max possible size of message
/** @brief Structure for modbus exception codes */
/**
* @brief Enum for modbus exception codes
* @details Prefix ET for Error Type
*/
typedef enum //MB_ExceptionTypeDef
{
// reading
NO_ERRORS = 0x00, ///< no errors
ILLEGAL_FUNCTION = 0x01, ///< Принятый код функции не может быть обработан
ILLEGAL_DATA_ADDRESS = 0x02, ///< Адрес данных, указанный в запросе, недоступен
ILLEGAL_DATA_VALUE = 0x03, ///< Значение, содержащееся в поле данных запроса, является недопустимой величиной
SLAVE_DEVICE_FAILURE = 0x04, ///< Невосстанавливаемая ошибка имела место, пока ведомое устройство пыталось выполнить затребованное действие
// ACKNOWLEDGE = 0x05, ///< idk
// SLAVE_DEVICE_BUSY = 0x06, ///< idk
// MEMORY_PARITY_ERROR = 0x08, ///< idk
/* Регулярные коды ошибок, которые определены конкретной программой */
ET_NO_ERRORS = 0x00, ///< no errors
ET_ILLEGAL_FUNCTION = 0x01, ///< Принятый код функции не может быть обработан
ET_ILLEGAL_DATA_ADDRESS = 0x02, ///< Адрес данных, указанный в запросе, недоступен
ET_ILLEGAL_DATA_VALUE = 0x03, ///< Значение, содержащееся в поле данных запроса, является недопустимой величиной
ET_SLAVE_DEVICE_FAILURE = 0x04, ///< Невосстанавливаемая ошибка имела место, пока ведомое устройство пыталось выполнить затребованное действие
/* Специальные коды ошибок, которые определены конкретной программой */
ET_ACKNOWLEDGE = 0x05, ///< Устройство приняло запрос, но на обработку требуется время. Нужно чтобы не было Timeout ошибки
ET_SLAVE_DEVICE_BUSY = 0x06, ///< Устройство занято обработкой комманды
ET_MEMORY_PARITY_ERROR = 0x08, ///< Ошибка внешней памяти
}MB_ExceptionTypeDef;
#define ERR_VALUES_START 0x80U ///< from this value starts error func codes
/** @brief Structure for modbus func codes */
#define FC_ERR_VALUES_START 0x80U ///< from this value starts error func codes
/**
* @brief Enum for modbus func codes
* @details Prefix FC for Function Code
*/
typedef enum //MB_FunctonTypeDef
{
/* COMMANDS */
// reading
MB_R_COILS = 0x01, ///< Чтение битовых ячеек
MB_R_DISC_IN = 0x02, ///< Чтение дискретных входов
FC_R_COILS = 0x01, ///< Чтение битовых ячеек
FC_R_DISC_IN = 0x02, ///< Чтение дискретных входов
#ifndef MODBUS_SWITCH_COMMAND_R_IN_REGS_AND_R_HOLD_REGS
MB_R_HOLD_REGS = 0x03, ///< Чтение входных регистров
MB_R_IN_REGS = 0x04, ///< Чтение регистров хранения
FC_R_HOLD_REGS = 0x03, ///< Чтение входных регистров
FC_R_IN_REGS = 0x04, ///< Чтение регистров хранения
#else
MB_R_HOLD_REGS = 0x04, ///< Чтение входных регистров
MB_R_IN_REGS = 0x03, ///< Чтение регистров хранения
FC_R_HOLD_REGS = 0x04, ///< Чтение входных регистров
FC_R_IN_REGS = 0x03, ///< Чтение регистров хранения
#endif
// writting
MB_W_COIL = 0x05, ///< Запись битовой ячейки
MB_W_HOLD_REG = 0x06, ///< Запись одиночного регистра
MB_W_COILS = 0x0F, ///< Запись нескольких битовых ячеек
MB_W_HOLD_REGS = 0x10, ///< Запись нескольких регистров
FC_W_COIL = 0x05, ///< Запись битовой ячейки
FC_W_HOLD_REG = 0x06, ///< Запись одиночного регистра
FC_W_COILS = 0x0F, ///< Запись нескольких битовых ячеек
FC_W_HOLD_REGS = 0x10, ///< Запись нескольких регистров
MB_R_DIAGNOSTIC = 0x08, ///< Чтение диагностической информации устройства
MB_R_DEVICE_INFO = 0x2B, ///< Чтение информации об устройстве
FC_R_DIAGNOSTICS = 0x08, ///< Чтение диагностической информации устройства
FC_R_DEVICE_ID = 0x2B, ///< Чтение информации об устройстве
/* ERRORS */
// error reading
MB_ERR_R_COILS = MB_R_COILS + ERR_VALUES_START, ///< Ошибка чтения битовых ячеек
MB_ERR_R_DISC_IN = MB_R_DISC_IN + ERR_VALUES_START, ///< Ошибка чтения дискретных входов
MB_ERR_R_IN_REGS = MB_R_IN_REGS + ERR_VALUES_START, ///< Ошибка чтения регистров хранения
MB_ERR_R_HOLD_REGS = MB_R_HOLD_REGS + ERR_VALUES_START, ///< Ошибка чтения входных регистров
FC_ERR_R_COILS = FC_R_COILS + FC_ERR_VALUES_START, ///< Ошибка чтения битовых ячеек
FC_ERR_R_DISC_IN = FC_R_DISC_IN + FC_ERR_VALUES_START, ///< Ошибка чтения дискретных входов
FC_ERR_R_IN_REGS = FC_R_IN_REGS + FC_ERR_VALUES_START, ///< Ошибка чтения регистров хранения
FC_ERR_R_HOLD_REGS = FC_R_HOLD_REGS + FC_ERR_VALUES_START, ///< Ошибка чтения входных регистров
// error writting
MB_ERR_W_COIL = MB_W_COIL + ERR_VALUES_START, ///< Ошибка записи битовой ячейки
MB_ERR_W_HOLD_REG = MB_W_HOLD_REG + ERR_VALUES_START, ///< Ошибка записи одиночного регистра
MB_ERR_W_COILS = MB_W_COILS + ERR_VALUES_START, ///< Ошибка записи нескольких битовых ячеек
MB_ERR_W_HOLD_REGS = MB_W_HOLD_REGS + ERR_VALUES_START, ///< Ошибка записи нескольких регистров
FC_ERR_W_COIL = FC_W_COIL + FC_ERR_VALUES_START, ///< Ошибка записи битовой ячейки
FC_ERR_W_HOLD_REG = FC_W_HOLD_REG + FC_ERR_VALUES_START, ///< Ошибка записи одиночного регистра
FC_ERR_W_COILS = FC_W_COILS + FC_ERR_VALUES_START, ///< Ошибка записи нескольких битовых ячеек
FC_ERR_W_HOLD_REGS = FC_W_HOLD_REGS + FC_ERR_VALUES_START, ///< Ошибка записи нескольких регистров
MB_ERR_R_DIAGNOSTIC = MB_R_DIAGNOSTIC + ERR_VALUES_START, ///< Ошибка чтения диагностической информации устройства
MB_ERR_R_DEVICE_INFO = MB_R_DEVICE_INFO + ERR_VALUES_START, ///< Ошибка чтения информации об устройстве
FC_ERR_R_DIAGNOSTIC = FC_R_DIAGNOSTICS + FC_ERR_VALUES_START, ///< Ошибка чтения диагностической информации устройства
FC_ERR_R_DEVICE_INFO = FC_R_DEVICE_ID + FC_ERR_VALUES_START, ///< Ошибка чтения информации об устройстве
}MB_FunctonTypeDef;
/** @brief Structure for MEI func codes */
/**
* @brief Enum for MEI func codes
*/
typedef enum //MB_FunctonTypeDef
{
MEI_DEVICE_IDENTIFICATIONS = 0x0E,
}MB_MEITypeDef;
/** @brief Structure for comformity */
/**
* @brief Enum for Read Device Id codes
* @details Prefix RID for Read ID
*/
typedef enum //MB_FunctonTypeDef
{
MB_BASIC_IDENTIFICATIONS = 0x01, /*!< @brief Basic Device Identifications.
RID_BASIC_IDENTIFICATIONS = 0x01, /*!< @brief Basic Device Identifications.
@details All objects of this category are mandatory:
VendorName,Product code, and revision number */
VendorName, Product code, and revision number */
MB_REGULAR_IDENTIFICATIONS = 0x02, /*!< @brief Regular Device Identifications.
RID_REGULAR_IDENTIFICATIONS = 0x02, /*!< @brief Regular Device Identifications.
@details The device provides additional and optional
identifications and description data objects */
MB_EXTENDED_IDENTIFICATIONS = 0x03, /*!< @brief Extended Device Identifications.
RID_EXTENDED_IDENTIFICATIONS = 0x03, /*!< @brief Extended Device Identifications.
@details The device provides additional and optional
identifications and description private data about the physical
device itself. All of these data are device dependent. */
MB_SPEDIFIC_IDENTIFICATIONS = 0x04, /*!< @brief Specific Device Identifications.
RID_SPEDIFIC_IDENTIFICATIONS = 0x04, /*!< @brief Specific Device Identifications.
@details The device provides one specific identifications object. */
/* ERRORS */
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;
}MB_ReadDevId;
/** @brief Structure for decive identifications message type */
/** @brief Structure for device identifications message type */
typedef struct
{
MB_MEITypeDef MEI_Type; ///< MEI Type assigned number for Device Identifications Interface
MB_ConformityTypeDef ReadDevId;
MB_ConformityTypeDef Conformity;
MB_ReadDevId ReadDevId;
uint8_t Conformity; ///< Identification conformity level of the device and type of supported access @ref MODBUS_DEVICE_CONFORMITY
uint8_t MoreFollows;
uint8_t NextObjId;
uint8_t NumbOfObj;
@@ -161,17 +177,23 @@ typedef struct
/** @brief Structure for modbus messsage */
typedef struct // RS_MsgTypeDef
{
uint8_t MbAddr; ///< Modbus Slave Address
MB_FunctonTypeDef Func_Code; ///< Modbus Function Code
#ifdef MODBUS_PROTOCOL_TCP
uint16_t TransactionID; ///< Modbus TCP: ID Transaction
uint16_t ProtocolID; ///< Modbus TCP: ID Protocol
uint16_t PDULength; ///< Modbus TCP: PDU Length
#endif
uint8_t DeviceAddr; ///< Modbus Slave Address
MB_FunctonTypeDef FuncCode; ///< Modbus Function Code
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
uint16_t DATA[DATA_SIZE]; ///< Modbus Data
uint16_t MbData[MbData_size]; ///< Modbus Data
MB_ExceptionTypeDef Except_Code; ///< Exception Code for the command
uint16_t MB_CRC; ///< Modbus CRC
uint16_t MbCRC; ///< Modbus CRC
}RS_MsgTypeDef;
//--------------------------------------------------
extern RS_MsgTypeDef MODBUS_MSG;
@@ -194,6 +216,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
@code Пояснение выражений
- (_coil_/16) - индекс регистра, в котором содержится коил по адресу _coil_
Визуальный пример: 30 коил будет в 30/16 = 1 регистре (индексация с 0)
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
|register[0]----| |register[1]----|
|skip this------| |get this-------|
|shift to 14 bit|
@endcode
*/
#define MB_Set_Coil_Reg_Ptr(_parr_, _coil_) ((uint16_t *)(_parr_)+((_coil_)/16))
/**
* @brief Макрос для установки маски, чтобы выделить запрашиваемый коил из регистра
* @param _coil_ - Номер коила от начала массива _arr_.
* @note Используется вместе с @ref MB_Set_Coil_Reg_Ptr
@code Пояснение выражений
- (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|
@endcode
*/
#define MB_Set_Coil_Mask(_coil_) (1 << ( _coil_ - (16*((_coil_)/16)) ))
/** GENERAL_MODBUS_STUFF
* @}
*/
@@ -233,10 +291,9 @@ extern RS_MsgTypeDef MODBUS_MSG;
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{
* @{
*/
/* Реализация этих функций лежит в modbus_data.c */
@@ -244,14 +301,17 @@ extern RS_MsgTypeDef MODBUS_MSG;
/* Check is address valid for certain array */
MB_ExceptionTypeDef MB_Check_Address_For_Arr(uint16_t Addr, uint16_t Qnt, uint16_t R_ARR_ADDR, uint16_t R_ARR_NUMB);
/* Define Address Origin for Input/Holding Registers */
MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, uint16_t Qnt, uint8_t RegisterType);
MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, uint16_t Qnt, uint8_t RegisterType, uint8_t WriteFlag);
/* Define Address Origin for coils */
MB_ExceptionTypeDef MB_DefineCoilsAddress(uint16_t **pCoils, uint16_t Addr, uint16_t Qnt, uint16_t *start_shift, uint8_t WriteFlag);
/** MODBUS_CMD_PROCESS_FUNCTIONS
* @}
*/
/////////////////////////---FUNCTIONS---/////////////////////////////
#include "__modbus_compat.h"
#endif //__MODBUS_CORE_H_
/** MODBUS_INTERNAL
* @}
*/
*/

View File

@@ -1,18 +1,19 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_devid.h
* @brief Идентификаторы устройства Modbus
******************************************************************************
*******************************************************************************
@addtogroup MODBUS_DEVID Device Identifications Tools
@ingroup MODBUS_INTERNAL
******************************************************************************
@brief Функции для работы с идентификаторами устройства
*******************************************************************************
* @details
Модуль реализации функции Read Device Identifications (0x2B):
- Базовая идентификация (Vendor, Product, Revision)
- Расширенная идентификация (URL, Model, User fields)
- Поддержка потоковой передачи больших объектов
@section Объекты идентификации:
@section devobj Объекты идентификации:
- VendorName, ProductCode, Revision - обязательные
- VendorUrl, ProductName, ModelName - опциональные
- User objects - пользовательские поля
@@ -21,6 +22,7 @@
#ifndef __MODBUS_DEVID_H_
#define __MODBUS_DEVID_H_
#include "modbus_core.h"
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
/////////////////////////////////////////////////////////////////////
@@ -31,6 +33,12 @@
* @{
*/
#if MODBUS_NUMB_OF_USEROBJECTS > 0
#define MODBUS_DEVICE_CONFORMITY 0x83
#else
#define MODBUS_DEVICE_CONFORMITY 0x82
#endif
/** @brief Структура для объекта (идентификатора устройства модбас) */
typedef struct
{
@@ -86,52 +94,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,10 +107,10 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj);
/** MODBUS_DEVID
* @}
*/
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{
* @{
*/
/* Обработать функцию Read Device Identifications (43/14 - 0x2B/0E) */
@@ -158,4 +120,6 @@ uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg);
*/
/////////////////////////---FUNCTIONS---/////////////////////////////
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
#endif //__MODBUS_DEVID_H_

View File

@@ -1,21 +1,23 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_diag.h
* @brief Диагностика устройства Modbus
******************************************************************************
*******************************************************************************
@addtogroup MODBUS_DIAG Diagnostics Tools
@ingroup MODBUS_INTERNAL
******************************************************************************
@brief Функции для работы с диагностикой
*******************************************************************************
* @details
Модуль реализации Diagnostics (Serial Line only) (0x08):
- Полная поддержка всех подфункций диагностики
- Возможность выставить/сбросить любой бит в диагностическом регистре
- Сбор статистики работы устройства
- Управление режимами работы
****************************************** ************************************/
******************************************************************************/
#ifndef __MODBUS_DIAG_H_
#define __MODBUS_DIAG_H_
#include "modbus_core.h"
#ifdef MODBUS_ENABLE_DIAGNOSTICS
/////////////////////////////////////////////////////////////////////
/////////////////---DEVICE DIAGNOSTICS DEFINES---////////////////////
@@ -65,29 +67,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,10 +100,9 @@ void MB_Diagnostics_SlaveBusyCnt(void);
* @}
*/
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{
* @{
*/
/* Обработка команды диагностики (0x08) */
@@ -139,8 +117,10 @@ uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg);
/////////////////////////---FUNCTIONS---/////////////////////////////
#endif //MODBUS_ENABLE_DIAGNOSTICS
#endif //__MODBUS_DIAG_H_
/** MODBUS_DIAG
* @}
*/
*/

View File

@@ -1,32 +1,52 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_holdregs.h
* @brief Работа с регистрами хранения Modbus
******************************************************************************
@addtogroup MODBUS_INS Input Register Tools
*******************************************************************************
@addtogroup MODBUS_HOLD Holding Registers Tools
@ingroup MODBUS_INTERNAL
******************************************************************************
@brief Функции для работы с регистрами хранения
*******************************************************************************
* @details
Модуль для доступа к регистрам внутри программы:
- Функции для доступа к регистрам хранения по глобальным адресам
Модуль обработки команд для регистров хранения (Holding Registers):
- Чтение множества регистров (0x03)
- Запись одиночного регистра (0x06)
- Запись множества регистров (0x10)
@section Регистры хранения:
@section hold Регистры хранения:
- Read/Write доступ
- 16-битные значения (uint16_t)
******************************************************************************/
#ifndef __MODBUS_HOLDREGS_H_
#define __MODBUS_HOLDREGS_H_
#include "modbus_core.h"
#ifdef MODBUS_ENABLE_HOLDINGS
/////////////////////////////////////////////////////////////////////
/////////////////////////---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
@{
* @{
*/
/* Обработать функцию Read Holding Registers (03 - 0x03) */
uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg);
@@ -40,4 +60,5 @@ uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg);
*/
/////////////////////////---FUNCTIONS---/////////////////////////////
#endif //MODBUS_ENABLE_HOLDINGS
#endif //__MODBUS_HOLDREGS_H_

View File

@@ -1,32 +1,51 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_inputregs.h
* @brief Работа с входными регистрами Modbus
******************************************************************************
@addtogroup MODBUS_HOLD Holding Registers Tools
*******************************************************************************
@addtogroup MODBUS_INS Input Register Tools
@ingroup MODBUS_INTERNAL
******************************************************************************
@brief Функции для работы с входными регистрами
*******************************************************************************
* @details
Модуль для доступа к регистрам внутри программы:
- Функции для доступа к входным регистрам по глобальным адресам
Модуль обработки команд для входных регистров (Input Registers):
- Чтение множества регистров (0x04)
@section Входные регистры:
@section in Входные регистры:
- Read-Only доступ
- 16-битные значения
******************************************************************************/
#ifndef __MODBUS_INPUTREGS_H_
#define __MODBUS_INPUTREGS_H_
#include "modbus_core.h"
#ifdef MODBUS_ENABLE_INPUTS
/////////////////////////////////////////////////////////////////////
/////////////////////////---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
* @brief Функции обработки запросов модбас
@{
* @{
*/
/* Обработать функцию Read Input Registers (04 - 0x04) */
uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg);
@@ -36,4 +55,5 @@ uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg);
*/
/////////////////////////---FUNCTIONS---/////////////////////////////
#endif //MODBUS_ENABLE_INPUTS
#endif //__MODBUS_INPUTREGS_H_

View File

@@ -1,27 +1,24 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_master.h
* @brief Главный заголовочный файл Modbus библиотеки
******************************************************************************
*******************************************************************************
@addtogroup MODBUS_MASTER Modbus master funtions
@ingroup MODBUS_CMD_PROCESS_FUNCTIONS
******************************************************************************
@brief Функции для работы в режиме Master
*******************************************************************************
* @details
Модуль реализации обработки UART сообщение в режиме мастер
Модуль реализации Modbus в режиме мастер
******************************************************************************/
#ifndef __MODBUS_MASTER_H_
#define __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 Макросы для создания запросов в режиме мастер
* @brief API для формирования фрейма-запроса в режиме мастер
* @details Примеры использования:
* @code
* // Чтение 10 holding registers начиная с адреса 0
@@ -84,24 +81,20 @@ RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_SPECIFIC(uint8_t slave_addr, uint8_t obj
* @code
* // Пример: Запросили 10 регистров с адреса 100, хотим получить значение регистра 105
* uint16_t reg_value;
* if(MB_RespGet_RegisterValue(&MODBUS_MSG, 105, &reg_value))
* if(MB_RespGet_RegisterValue(modbus_msg, 105, &reg_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_RespGet_RegisterValue(&MODBUS_MSG, addr, &value))
* {
* printf("Register %d: %d\n", addr, value);
* }
* }
* uint16_t reg_value[125];
* MB_RespGet_RegisterAll(modbus_msg)
* @endcode
* @{
*/
/* Получить значение ВСЕХ регистров в ответе */
int MB_RespGet_RegisterAll(RS_MsgTypeDef *modbus_msg, uint16_t *reg_arr);
/* Получить значение регистра в ответе по его адресу */
int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value);
@@ -109,6 +102,118 @@ 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
* @{
*/
/* Получить состояние ВСЕХ coil в ответе */
int MB_RespGet_CoilAll(RS_MsgTypeDef *modbus_msg, int *coil_arr);
/* Получить состояние coil в ответе по его адресу */
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
* @{
@@ -123,4 +228,5 @@ RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDe
* @}
*/
#endif //MODBUS_ENABLE_MASTER
#endif //__MODBUS_MASTER_H_

111
Inc/modbus_oscil.h Normal file
View File

@@ -0,0 +1,111 @@
/**
*******************************************************************************
* @file modbus_oscil.h
* @brief Заголовочный файл модуля осциллографа через MODBUS.
*******************************************************************************
* @addtogroup MODBUS_OSCIL Modbus Oscilloscope
* @ingroup MODBUS
* @brief Модуль для сбора и хранения данных осциллографа
* @details
* Реализует кольцевой буфер для хранения данных с нескольких каналов
* с возможностью чтения через Modbus. Данные 8-битные, упаковываются
* по 2 сэмпла в 16-битный регистр Modbus.
*
* Карта Modbus регистров (адрес относительный):
* | Адрес | Биты | Назначение | Диапазон | Описание |
* |--------|------|------------|----------|----------|
* | 0x0000 | 0-3 | channels | 1-16 | Количество каналов |
* | | 4-7 | reserved | 0 | Зарезервировано |
* | | 8-15 | buffer_size| 1-125 | Размер буфера в регистрах |
* | 0x0001 | 0-7 | tail | 0-124 | Начало новых данных |
* | | 8-15 | head | 0-124 | Конец новых данных |
* | 0x0002...| 0-15| data | - | Буфер данных |
* @{
******************************************************************************/
#ifndef __MODBUS_OSCIL_H_
#define __MODBUS_OSCIL_H_
#include "modbus_core.h"
#ifdef MODBUS_ENABLE_OSCIL
/**
* @brief Структура конфигурации осциллографа
*/
typedef struct
{
/* Адрес 0 */
uint16_t Overrun:1; ///< Overrun
uint16_t NumbOfChannels:4; ///< Количество каналов (1-16)
uint16_t BufferSize:7; ///< Размер буфера в регистрах (1-125)
uint16_t SampleTime; ///< Адрес 1: Время между сэмплами в мкс/мс
uint16_t reserved[2]; ///< Резерв
uint32_t LastTick; ///< Адрес 4-5: Время последнего добавления (uint32_t)
} MB_ConfigTypeDef;
/**
* @brief Структура указателей буфера
*/
typedef struct
{
uint16_t Head:8; ///< Указатель на последний записанный байт
uint16_t Tail:8; ///< Указатель на начало непрочитанных данных (байты)
} MB_PreambleTypeDef;
/**
* @brief Основная структура осциллографа
*/
typedef struct
{
MB_ConfigTypeDef Config; /*!< @brief Отн. Адрес 0-6: Конфигурация */
uint16_t User[4]; /*!< @brief Отн. Адрес 6-9: Пользовательские регистры */
MB_PreambleTypeDef Preamble; /*!< @brief Отн. Адрес 10: Указатели head и tail буфера */
uint8_t Data[MbData_size*2-1]; ///< /*!< @brief Отн. Адрес 11-131: Буфер данных (в байтах) */
} MB_OscilTypeDef;
/**
* @addtogroup MODBUS_OSCIL_API API for Oscilloscope
* @ingroup MODBUS_OSCIL
* @brief API для работы с буфером осциллографа
* @details Примеры использования:
* @code
* MB_OscilTypeDef oscil;
*
* // Инициализация осциллографа
* MB_Oscil_Init(&oscil, 120, 4, 1000);
*
* // В цикле сбора данных
* uint8_t ch_data[4] = {adc1, adc2, adc3, adc4};
* MB_Oscil_Add(&oscil, ch_data);
*
* // После чтения данных через Modbus
* MB_Oscil_UpdateTail(&oscil);
* @endcode
* @{
*/
/* Инициализация структуры осциллографа */
HAL_StatusTypeDef MB_Oscil_Init(MB_OscilTypeDef *oscil, uint8_t BufferSize, uint8_t NumbOfChannels, uint16_t SampleTime);
/* Добавление данных всех каналов в буфер */
HAL_StatusTypeDef MB_Oscil_Add(MB_OscilTypeDef *oscil, uint8_t *Data);
/* Обновление указателя Tail */
HAL_StatusTypeDef MB_Oscil_UpdateTail(MB_OscilTypeDef *oscil);
/** MODBUS_OSCIL_API
* @}
*/
#endif //MODBUS_ENABLE_OSCIL
#endif //__MODBUS_OSCIL_H_
/** MODBUS_OSCIL
* @}
*/

View File

@@ -1,28 +1,30 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_slave.h
* @brief Главный заголовочный файл Modbus библиотеки
******************************************************************************
*******************************************************************************
@addtogroup MODBUS_SLAVE Modbus slave funtions
@ingroup MODBUS_CMD_PROCESS_FUNCTIONS
******************************************************************************
@brief Функции для работы в режиме Slave
*******************************************************************************
* @details
Модуль реализации обработки UART сообщение в режиме слейв
Модуль реализации Modbus в режиме слейв
******************************************************************************/
#ifndef __MODBUS_SLAVE_H_
#define __MODBUS_SLAVE_H_
#define __MODBUS_SLAVE_H_
#include "rs_message.h"
#ifdef MODBUS_ENABLE_SLAVE
#define MODBUS_MODE_SLAVE 0
#endif
/**
* @addtogroup MODBUS_SLAVE
* @{
*/
/* Ответить позже, не в прерывании */
HAL_StatusTypeDef MB_Slave_ResponseLater(RS_HandleTypeDef *hmodbus, uint8_t ResponseCode);
/* Ответить на запрос */
HAL_StatusTypeDef MB_Slave_SendResponse(RS_HandleTypeDef *hmodbus, uint8_t ResponseCode, MB_ExceptionTypeDef error);
/* Ответ на сообщение в режиме слейва */
RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg);
/* Сбор сообщения в буфер UART в режиме слейв (фрейм слейва из msg -> uart) */
@@ -33,4 +35,7 @@ RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef
/** MODBUS_SLAVE
* @}
*/
#endif //MODBUS_ENABLE_SLAVE
#endif //__MODBUS_SLAVE_H_

View File

@@ -1,30 +1,30 @@
/**
******************************************************************************
*******************************************************************************
* @file rs_message.h
* @brief Библиотека обмена сообщениями по RS-интерфейсу
******************************************************************************
*******************************************************************************
@defgroup RS_TOOLS RS Tools
@brief Всякое для работы по UART/RS
@{
******************************************************************************
*******************************************************************************
* @details
Универсальная библиотека для работы с последовательными протоколами (Modbus, Custom)
через UART в режиме прерываний с поддержкой таймаутов.
@section Основные возможности:
@section posibility Основные возможности:
- Прием/передача в прерываниях
- Обработка IDLE линии для определения конца фрейма
- Таймауты приема через TIM
- Гибкая настройка размера сообщений
@section Использование:
@section usage Использование:
1. Определить структуру сообщения и размеры буфера
2. Реализовать weak-функции обработки сообщений
3. Добавить вызовы RS_UART_Handler/RS_TIM_Handler в прерывания
4. Инициализировать через RS_Init() и запустить прием RS_Receive_IT()
@section Особенности:
- Буфер: RS_Buffer[MSG_SIZE_MAX] Общий для приема/передачи
@section features Особенности:
- Буфер: RS_Buffer[RS_MSG_SIZE_MAX] Общий для приема/передачи
- Состояния: отслеживается через флаги в RS_HandleTypeDef
- Таймауты: контролируют максимальное время ожидания фрейма
******************************************************************************/
@@ -36,8 +36,8 @@
/////////////////////////////////////////////////////////////////////
////////////////////////////---DEFINES---////////////////////////////
/* Check that all defines required by RS are defined */
#ifndef MSG_SIZE_MAX
#error Define MSG_SIZE_MAX (Maximum size of message). This is necessary to create buffer for UART.
#ifndef RS_MSG_SIZE_MAX
#error Define RS_MSG_SIZE_MAX (Maximum size of message). This is necessary to create buffer for UART.
#endif
/**
@@ -45,7 +45,7 @@
*/
/* Clear message-uart buffer */
#define RS_Clear_Buff(_buff_) for(int i=0; i<MSG_SIZE_MAX;i++) _buff_[i] = NULL
#define RS_Clear_Buff(_buff_) for(int i=0; i<RS_MSG_SIZE_MAX;i++) _buff_[i] = NULL
/* Set/Reset flags */
#define RS_Set_Free(_hRS_) _hRS_->f.RS_Busy = 0
@@ -68,7 +68,7 @@
#define RS_Set_TX_End(_hRS_) RS_Reset_TX_Flags(_hRS_); RS_Set_TX_End_Flag(_hRS_)
/* Clear all RS stuff */
#define RS_Clear_All(_hRS_) RS_Clear_Buff(_hRS_->pBufferPtr); RS_Reset_RX_Flags(_hRS_); RS_Reset_TX_Flags(_hRS_);
#define RS_Clear_All(_hRS_) RS_Clear_Buff(_hRS_->BufferPtr); RS_Reset_RX_Flags(_hRS_); RS_Reset_TX_Flags(_hRS_);
//#define MB_Is_RX_Busy(_hRS_) ((_hRS_->huart->gState&HAL_USART_STATE_BUSY_RX) == HAL_USART_STATE_BUSY_RX)
//#define MB_Is_TX_Busy(_hRS_) ((_hRS_->huart->gState&HAL_USART_STATE_BUSY_RX) == HAL_USART_STATE_BUSY_TX)
@@ -76,48 +76,83 @@
#define RS_Is_TX_Busy(_hRS_) (_hRS_->f.TX_Busy == 1)
#ifndef printf_rs_err
#define printf_rs_err(...)
#endif
#ifndef printf_rs
#define printf_rs(...)
#endif
#ifndef __MYLIBS_CONFIG_H_
// дефайны из mylibs include
static int dummy;
#define TrackerTypeDef(num_user_vars) void *
#define num_of_usercnts(_user_) 0
#define assert_tracecnt(_cntstruct_, _uservarnumb_) 0
#define if_assert_usertracker(_cntstruct_, _uservarnumb_) if(0)
#define tern_assert_usertracker(_cntstruct_, _uservarnumb_) 0
#define TrackerGet_Ok(_cntstruct_) dummy
#define TrackerGet_Err(_cntstruct_) dummy
#define TrackerGet_Warn(_cntstruct_) dummy
#define TrackerGet_User(_cntstruct_, _uservarnumb_) dummy
#define TrackerCnt_Ok(_cntstruct_)
#define TrackerCnt_Err(_cntstruct_)
#define TrackerCnt_Warn(_cntstruct_)
#define TrackerCnt_User(_cntstruct_, _uservarnumb_)
#define TrackerWrite_User(_cntstruct_, _uservarnumb_, _val_)
#define TrackerClear_All(_cntstruct_)
#define TrackerClear_Ok(_cntstruct_)
#define TrackerClear_Err(_cntstruct_)
#define TrackerClear_Warn(_cntstruct_)
#define TrackerClear_User(_cntstruct_)
#define TrackerClear_UserAll(_cntstruct_)
#else
#include "mylibs_include.h"
#endif
#ifndef RS_USER_VARS_NUMB
#define RS_USER_VARS_NUMB 0
#endif
#ifndef local_time
#define local_time() uwTick
#endif
/** @endcond */
/**
* @addtogroup RS_DEBUG Tools for debug RS/UART/TIM
* @ingroup RS_TOOLS
* @brief Дефайны для отладки периферии
* @{
*/
#ifndef RS_USER_VARS_NUMB
#define RS_USER_VARS_NUMB 0 ///< Количество переменных в @ref TrackerTypeDef
#endif
#ifndef TrackerTypeDef
/**
* @brief Тип структуры для счетчиков-переменных
* @param num_user_vars Есть возмоность добавления num_user_vars количества пользовательскиъх переменных
*/
#define TrackerTypeDef(num_user_vars) void *
#endif
#ifndef TrackerCnt_Ok
/** @brief Инкрементировать переменную - успешных событий */
#define TrackerCnt_Ok(_cntstruct_)
#endif
#ifndef TrackerCnt_Err
/** @brief Инкрементировать переменную - ошибок */
#define TrackerCnt_Err(_cntstruct_)
#endif
#ifndef TrackerCnt_Warn
/** @brief Инкрементировать переменную - предупреждений */
#define TrackerCnt_Warn(_cntstruct_)
#endif
#ifndef printf_rs
/** @brief Printf обычных событий RS/UART/TIM */
#define printf_rs(...)
#endif
#ifndef printf_rs_err
/** @brief Printf ошибок RS/UART/TIM */
#define printf_rs_err(...)
#endif
#ifndef RS_TIM_Handler_ENTER
/** @brief Действия при заходе в прерывания таймера */
#define RS_TIM_Handler_ENTER()
#endif
#ifndef RS_TIM_Handler_EXIT
/** @brief Действия при выходе из прерывания таймера */
#define RS_TIM_Handler_EXIT()
#endif
#ifndef RS_UART_Handler_ENTER
/** @brief Действия при заходе в прерывания UART */
#define RS_UART_Handler_ENTER()
#endif
#ifndef RS_UART_Handler_EXIT
/** @brief Действия при выходе из прерывания UART */
#define RS_UART_Handler_EXIT()
#endif
/** RS_TOOLS
* @}
*/
// направление передачи rs485
#ifndef RS_EnableReceive
#define RS_EnableReceive() ///< Функция изменения направления передачи на ПРИЕМ для RS-485
@@ -141,9 +176,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)
@@ -183,11 +219,13 @@ typedef struct
unsigned TX_Done:1; ///< 1 - Передача закончена, 0 - Передача еще в процессе или не инициализирована
// Выставление следующие флагов определяет пользователь
unsigned RX_Continue:1; ///< 0 - Продолжить принимать, 0 - Начать прием сначала
unsigned RX_Continue:1; ///< 1 - Продолжить принимать, 0 - Начать прием сначала
unsigned MessageHandled:1; ///< 1 - Обработка запроса успешна, 0 - Обработка запроса в процессе или ошибка
unsigned EchoResponse:1; ///< 1 - Ответить эхом, 0 - Ответить своим сообщением
unsigned DeferredResponse:1; ///< 1 - Не начинать передачу в IT, 0 - Ответить в прерывании
unsigned DataUpdated:1; ///< 1 - Данные были обновлены
unsigned DataUpdated:1; ///< 1 - Данные были обновлены:
unsigned DeviceBusy:1; ///< 1 - Устройство занято (важно! не rs а именно устрройство)
unsigned DeferredResponse:8; ///< >0 - Не начинать передачу в IT, 0 - Ответить в прерывании
}RS_FlagsTypeDef;
@@ -201,7 +239,7 @@ typedef struct // RS_HandleTypeDef
/* MESSAGE */
uint8_t ID; ///< ID хендла
RS_MsgTypeDef *pMessagePtr; ///< Указатель на структуру протокола
uint8_t *pBufferPtr; ///< Указатеь на буфер UART
uint8_t BufferPtr[RS_MSG_SIZE_MAX]; ///< Указатеь на буфер UART
int32_t RS_Message_Size; ///< size of whole message, not only data
/* HANDLERS and SETTINGS */
@@ -209,6 +247,7 @@ typedef struct // RS_HandleTypeDef
TIM_HandleTypeDef *htim; ///< Хендл TIM
RS_ModeTypeDef sRS_Mode; ///< Настройка: слейв/мастер @ref RS_ModeTypeDef
uint16_t sRS_Timeout; ///< Настройка: Таймаут в тиках таймера
void (*pSetDirection)(int Tx); ///< Указатель на функцию: смена направления
void (*pCallback)(void*, void*); ///< Указатель на коллбек: принят ответ в режиме мастер
/* FLAGS */
@@ -252,7 +291,7 @@ RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
/* Инициалазация структуры @ref RS_HandleTypeDef */
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr);
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, void (*pSetDirection)(int Tx));
/* Отменить прием/передачу RS/UART */
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode);

423
README.md
View File

@@ -6,40 +6,45 @@
*Note: Файлы начинающиеся с `__` и которых **нет** в этом дереве являются **внутренними/непротестированными/недокументированными***
```
Modbus/ Иерархия модулей:
│ inc/ modbus
│ ├── modbus.h # Главный заголовочный файл modbus_slave
│ ├── modbus_core.h # Базовые определения и структуры modbus_master
│ ├── modbus_coils.h # Работа с дискретными выходами ├── modbus_coils
│ ├── modbus_holdregs.h # Работа с регистрами хранения ├── modbus_inputregs
│ ├── modbus_inputregs.h # Работа с входными регистрами ├── modbus_inputregs
│ ├── modbus_devid.h # Идентификация устройства ├── modbus_devid
│ ├── rs_message.h # Драйвер обмена по RS/UART ├── modbus_diag
├── src/ └── rs_message
│ ├── modbus.c # Основная логика Modbus │
│ ├── modbus_slave.c # Основная логика Slave Modbus └── modbus_core (единое ядро)
── modbus_master.c # Основная логика Master Modbus ├── modbus_config
│ ├── modbus_coils.c # Реализация работы с coils ├── modbus_data
│ ├── modbus_holdregs.c # Реализация регистров хранения └── __crc_algs
│ ├── modbus_inputregs.c # Реализация входных регистров
│ ├── modbus_devid.c # Реализация идентификации устройства
│ ├── modbus_data.c # Функции доступа к данным
── rs_message.c # Реализация драйвера RS
├── __modbus_config.h # Конфигурация Modbus (надо заменить)
├── __modbus_data.h # Структуры данных (надо заменить)
── __modbus_data.c # Функции доступа (надо заменить)
Modbus/ Иерархия модулей:
│ inc/ modbus
│ ├── modbus.h # Главный заголовочный файл modbus_slave
│ ├── modbus_core.h # Базовые определения и структуры modbus_master
│ ├── modbus_coils.h # Работа с дискретными коилами ├── modbus_coils
│ ├── modbus_holdregs.h # Работа с регистрами хранения ├── modbus_inputregs
│ ├── modbus_inputregs.h # Работа с входными регистрами ├── modbus_inputregs
│ ├── modbus_devid.h # Идентификаторы устройства ├── modbus_devid
│ ├── modbus_master.h # Заголовочный файл Master режима ├── modbus_diag
│ ├── modbus_slave.h # Заголовочный файл Slave режима ├── modbus_oscil
│ ├── modbus_diag.h # Диагностика Modbus └── rs_message
│ ├── modbus_oscil.h # Осциллографирование данных │
── rs_message.h # Драйвер обмена по RS/UART └── modbus_core (единое ядро)
├── src/ ├── modbus_config
│ ├── modbus.c # Основная логика Modbus ├── modbus_data
│ ├── modbus_slave.c # Основная логика Slave Modbus └── __crc_algs
│ ├── modbus_master.c # Основная логика Master Modbus
│ ├── modbus_coils.c # Реализация работы с coils
── modbus_holdregs.c # Реализация регистров хранения
├── modbus_inputregs.c # Реализация входных регистров
├── modbus_devid.c # Реализация идентификации устройства
│ ├── modbus_diag.c # Диагностические функции и счетчики ошибок
│ ├── modbus_oscil.c # Сбор и хранение осциллограмм
│ └── rs_message.c # Реализация драйвера RS
├── __modbus_config.h # Конфигурация Modbus (надо заменить)
├── __modbus_data.h # Структуры данных (надо заменить)
└── __modbus_data.c # Функции доступа (надо заменить)
```
## Инструкция по подключению
1. **Склонируйте субмодуль** в ваш проект:
### 1. **Склонируйте субмодуль** в ваш проект:
```bash
git submodule add https://git.arktika.cyou/set506/STM32_Modbus path/to/Modbus
git submodule update --init --recursive
```
2. **Скопируйте файлы конфигурации** в отдельную папку в вашем проекте (вне субмодуля) и удалите `__` из имени файлов:
### 2. **Скопируйте файлы конфигурации** в отдельную папку в вашем проекте (вне субмодуля) и удалите `__` из имени файлов:
```
ProjectRoot/
@@ -47,216 +52,230 @@ ProjectRoot/
│ ├── modbus_config.h # скопировать из __modbus_config.h
│ ├── modbus_data.h # скопировать из __modbus_data.h
│ └── modbus_data.c # скопировать из __modbus_data.c
└── Modbus/ # Субмодуль
└── Modbus/ # Субмодуль
```
3. **Настройте конфигурацию** под ваш проект:
3.1. Настройка периферии
### 3. **Настройте конфигурацию** под ваш проект:
#### 3.1. Настройка периферии
- **UART**: Настройте в режиме Asynchronous, нужная скорость (9600, 19200, etc), 8N1
- **TIM**: Настройте таймер для генерации прерываний (например, 1ms tick)
- **Включите прерывания** для UART и TIM
- **UART**: Настройте в режиме Asynchronous, нужная скорость (9600, 19200, etc), 8N1
- **TIM**: Настройте таймер для генерации прерываний (например, 1ms tick)
- **Включите прерывания** для UART и TIM
3.2. Подключение обработчиков прерываний
#### 3.2. Подключение обработчиков прерываний
Подключите обработчики прерываний **UART** и **TIM** в свои IRQ обработчики ***вместо*** HAL-обработчиков:
Подключите обработчики прерываний **UART** и **TIM** в свои IRQ обработчики ***вместо*** HAL-обработчиков:
```c
#include "modbus.h"
```c
#include "modbus.h"
void USARTx_IRQHandler(void)
{
RS_UART_Handler(&hmodbus1);
return;
HAL_UART_IRQHandler(&huart);
}
void USARTx_IRQHandler(void)
{
RS_UART_Handler(&hmodbus1);
return;
HAL_UART_IRQHandler(&huart);
}
void TIMx_IRQHandler(void)
{
RS_TIM_Handler(&hmodbus1);
return;
HAL_TIM_IRQHandler(&htim);
}
```
#### 3.3. В `modbus_config.h` укажите параметры устройства
void TIMx_IRQHandler(void)
{
RS_TIM_Handler(&hmodbus1);
return;
HAL_TIM_IRQHandler(&htim);
}
```
#### 3.4. Инициализация в коде
3.3. В `modbus_config.h` укажите параметры устройства
Чтобы настроить Slave-режим `main()` после инициализации HAL:
3.4. Инициализация в коде
Чтобы настроить Slave-режим `main()` после инициализации HAL:
```c
#include "modbus.h"
```c
#include "modbus.h"
// TxEnable: 1 - передача, 0 - прием
void SetTxDirectionFunc(int TxEnable)
{
HAL_GPIO_WritePin(SCIDE1_GPIO_Port, SCIDE1_Pin, !TxEnable);
}
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, MODBUS_DEVICE_ID, MODBUS_TIMEOUT, MODBUS_MODE_SLAVE);
// Запуск приема Modbus
MODBUS_SlaveStart(&hmodbus1, NULL);
while (1)
{
// Основной цикл
}
}
```
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, &SetTxDirectionFunc);
MODBUS_Config(&hmodbus1, MODBUS_DEVICE_ID, MODBUS_TIMEOUT, MODBUS_MODE_SLAVE);
// Запуск приема Modbus
MODBUS_SlaveStart(&hmodbus1, NULL);
while (1)
{
// Основной цикл
}
}
```
Чтобы настроить Master-режим `main()` после инициализации HAL:
Чтобы настроить Master-режим `main()` после инициализации HAL:
```c
#include "modbus.h"
```c
#include "modbus.h"
// Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук
RS_MsgTypeDef read_hold_cmd = MB_MASTER_READ_HOLDING_REGS(1, 0, 10);
// коллбек, вызовется при получении ответа от слейва
read_hold[10];
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++)
{
uint16_t value;
if(MB_RespGet_RegisterValue(&MODBUS_MSG, addr, &value))
{
read_hold[i] = value;
}
}
}
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, MODBUS_TIMEOUT, MODBUS_MODE_MASTER);
// Запрос по Modbus
MODBUS_MasterRequest(&hmodbus1, &read_hold_cmd, &callback_func);
// Инициализация Modbus
MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim, &SetTxDirectionFunc);
MODBUS_Config(&hmodbus1, 0, MODBUS_TIMEOUT, MODBUS_MODE_MASTER);
}
```
3.5. Настройка карты данных
// Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук
// При получении ответа вызовется функция callback_func()
RS_MsgTypeDef msg = MB_REQUEST_READ_HOLDING_REGS(1, 0, 10);
MODBUS_MasterRequest(&hmodbus1, &msg, &callback_func);
В `modbus_data.h` настройте регистры и coils под ваше устройство:
// коллбек, вызовется при получении ответа от слейва
read_hold[10];
void callback_func(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
// MB_RespGet_... Чтобы достать нужные данные из ответа
**Input Registers (только чтение)**
```c
typedef struct
{
uint16_t Temperature; // Адрес 0
uint16_t Humidity; // Адрес 1
uint16_t Pressure; // Адрес 2
uint16_t Voltage; // Адрес 3
} MB_DataInRegsTypeDef;
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;
}
}
}
else // Ответ получен с ошибкой или не получен вовсе
{
}
}
#define R_INPUT_ADDR 0 // Начальный адрес Input регистров
#define R_INPUT_QNT 4 // Количество Input регистров
```
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, MODBUS_TIMEOUT, MODBUS_MODE_MASTER);
// Запрос по Modbus
MODBUS_MasterRequest(&hmodbus1, &read_hold_cmd, &callback_func);
**Holding Registers (чтение/запись)**
```c
typedef struct
{
uint16_t SetpointTemp; // Адрес 0
uint16_t SetpointHumidity; // Адрес 1
uint16_t ControlMode; // Адрес 2
} MB_DataHoldRegsTypeDef;
}
```
#### 3.5. Настройка карты данных (только для режима Slave)
#define R_HOLDING_ADDR 0 // Начальный адрес Holding регистров
#define R_HOLDING_QNT 3 // Количество Holding регистров
```
В `modbus_data.h` настройте регистры и coils под ваше устройство:
**Coils (1-битные)**
```c
typedef struct
{
unsigned Relay1 : 1; // Адрес 0
unsigned Relay2 : 1; // Адрес 1
unsigned Pump : 1; // Адрес 2
unsigned Heater : 1; // Адрес 3
unsigned reserved : 12; // Резерв (выравнивание до 16 бит)
} MB_DataCoilsTypeDef;
**Input Registers (только чтение)**
```c
typedef struct
{
uint16_t Temperature; // Адрес 0
uint16_t Humidity; // Адрес 1
uint16_t Pressure; // Адрес 2
uint16_t Voltage; // Адрес 3
} MB_DataInRegsTypeDef;
#define C_COILS_ADDR 0 // Начальный адрес Coils
#define C_COILS_QNT 4 // Количество Coils
```
#define R_INPUT_ADDR 0 // Начальный адрес Input регистров
#define R_INPUT_QNT 4 // Количество Input регистров
```
**Holding Registers (чтение/запись)**
```c
typedef struct
{
uint16_t SetpointTemp; // Адрес 0
uint16_t SetpointHumidity; // Адрес 1
uint16_t ControlMode; // Адрес 2
} MB_DataHoldRegsTypeDef;
3.6. Доступ к данным в коде
#define R_HOLDING_ADDR 0 // Начальный адрес Holding регистров
#define R_HOLDING_QNT 3 // Количество Holding регистров
```
**Coils (1-битные)**
```c
typedef struct
{
unsigned Relay1 : 1; // Адрес 0
unsigned Relay2 : 1; // Адрес 1
unsigned Pump : 1; // Адрес 2
unsigned Heater : 1; // Адрес 3
unsigned reserved : 12; // Резерв (выравнивание до 16 бит)
} MB_DataCoilsTypeDef;
#define C_COILS_ADDR 0 // Начальный адрес Coils
#define C_COILS_QNT 4 // Количество Coils
```
#### 3.6. Доступ к данным в коде
В режиме слейва есть дефайны для удобного выставления Коилов. На случай если они не упакованы в битовые поля
```c
// Чтение входных регистров
uint16_t temp = MB_DATA.InRegs.Temperature;
В режиме **слейва** есть дефайны для удобного выставления Коилов. На случай если они не упакованы в битовые поля
```c
// Чтение входных регистров
uint16_t temp = MB_DATA.InRegs.Temperature;
// Запись в регистры хранения
MB_DATA.HoldRegs.SetpointTemp = 2500;
// Запись в регистры хранения
MB_DATA.HoldRegs.SetpointTemp = 2500;
// Управление coils
MB_Coil_Set_Local(&MB_DATA.Coils, 0); // Включить Relay1
MB_Coil_Reset_Local(&MB_DATA.Coils, 1); // Выключить Relay2
// Управление coils
MB_Coil_Set_Local(&MB_DATA.Coils, 0); // Включить 0 бит в Coils
MB_Coil_Reset_Local(&MB_DATA.Coils, 1); // Выключить 1 бит в Coils
// Чтение coil
if (MB_Coil_Read_Local(&MB_DATA.Coils, 2)) {
// Pump включен
}
```
// Чтение coil
if (MB_Coil_Read_Local(&MB_DATA.Coils, 2)) {
// Pump включен
}
```
В режиме **мастера** есть функции для получения информации из ответа `MB_RespGet_...()`
```c
// Чтение регистров: Получить запрошенные регистры
uint16_t value;
if(MB_RespGet_RegisterValue(&MODBUS_MSG, 105, &reg_value))
{
printf("Register 105 value: %d\n", reg_value);
}
// Чтение коилов: Получить запрошенные коилы
int state;
if(MB_RespGet_CoilState(&MODBUS_MSG, 25, &coil_state))
{
printf("Coil 25 state: %s\n", coil_state ? "ON" : "OFF");
}
// Чтение диагностики: Получить запрошенныую диагностику
uint16_t counter_value;
if(MB_RespGet_DiagnosticResponse(&MODBUS_MSG, &counter_value))
{
printf("Counter value: %d\n", counter_value);
}
// Чтение идентификаторов: Получить запрошенные идентификаторы
uint8_t length;
char vendor_name[64];
if(MB_RespGet_ObjectById(&MODBUS_MSG, 0x00, vendor_name, &length))
{
printf("Vendor Name: %s (length: %d)\n", vendor_name, length);
}
В режиме мастера есть функции для получения информации из ответа `MB_RespGet_...()`
```c
// Чтение регистров: Получить запрошенные регистры
uint16_t value;
if(MB_RespGet_RegisterValue(&MODBUS_MSG, 105, &reg_value))
{
printf("Register 105 value: %d\n", reg_value);
}
// Чтение коилов: Получить запрошенные коилы
int state;
if(MB_RespGet_CoilState(&MODBUS_MSG, 25, &coil_state))
{
printf("Coil 25 state: %s\n", coil_state ? "ON" : "OFF");
}
// Чтение диагностики: Получить запрошенныую диагностику
uint16_t counter_value;
if(MB_RespGet_DiagnosticResponse(&MODBUS_MSG, &counter_value))
{
printf("Counter value: %d\n", counter_value);
}
// Чтение идентификаторов: Получить запрошенные идентификаторы
uint8_t length;
char vendor_name[64];
if(MB_RespGet_ObjectById(&MODBUS_MSG, 0x00, vendor_name, &length))
{
printf("Vendor Name: %s (length: %d)\n", vendor_name, length);
}
uint8_t obj_id, obj_length;
char obj_data[64];
if(MB_RespGet_ObjectByIndex(&MODBUS_MSG, 0x00, &obj_id, obj_data, &obj_length))
{
printf("First object - ID: 0x%02X, Data: %s\n", obj_id, obj_data);
}
```
uint8_t obj_id, obj_length;
char obj_data[64];
if(MB_RespGet_ObjectByIndex(&MODBUS_MSG, 0x00, &obj_id, obj_data, &obj_length))
{
printf("First object - ID: 0x%02X, Data: %s\n", obj_id, obj_data);
}
```
5. **Обновление библиотеки**:
### 5. **Обновление библиотеки**:
После обновления субмодуля из Git, исходные файлы библиотеки будут обновлены, и ваши конфиги вне субмодуля не перезапишутся:
```bash

7
Src/__modbus_compat.c Normal file
View File

@@ -0,0 +1,7 @@
/**
*******************************************************************************
* @file __modbus_compat.c
* @brief Модуль для совместимости библиотеки MODBUS.
*******************************************************************************
******************************************************************************/
#include "modbus.h"

View File

@@ -1,12 +1,12 @@
/**
**************************************************************************
* @file modbus.c
* @brief Модуль для реализации MODBUS.
**************************************************************************
*******************************************************************************
* @file modbus.c
* @brief Модуль для реализации MODBUS.
*******************************************************************************
* @details
Файл содержит реализацию функций работы с Modbus.
@section Функции и макросы
@section mbapi Функции и макросы
### Инициализация:
- MODBUS_FirstInit() — Инициализация Modbus (подключение UART, TIM)
@@ -24,8 +24,6 @@
RS_HandleTypeDef hmodbus1; ///< Default Handle for Modbus
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);
@@ -36,10 +34,11 @@ static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_
* @param hmodbus Указатель на хендлер RS
* @param huart Указатель на хендлер UART
* @param htim Указатель на хендлер TIM
* @param pSetDirection Указатель на функцию для смены направления RS485
* @details Подключает хендлы периферии к hmodbus
* Конфигурация выставляется по умолчанию из modbus_config.h
*/
HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim)
HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, void (*pSetDirection)(int Tx))
{
if((hmodbus == NULL) || (huart == NULL))
{
@@ -53,9 +52,10 @@ HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
// INIT
hmodbus->RS_STATUS = RS_Init(hmodbus, huart, htim, 0);
hmodbus->RS_STATUS = RS_Init(hmodbus, huart, htim, pSetDirection);
RS_EnableReceive();
if(hmodbus->pSetDirection)
hmodbus->pSetDirection(0);
if(hmodbus->RS_STATUS == RS_OK)
return HAL_OK;
@@ -164,9 +164,15 @@ HAL_StatusTypeDef MODBUS_MasterRequest(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef
return HAL_ERROR;
}
//-------------------------------------------------------------------
//-----------------------------INTERNAL------------------------------
/**
* @brief Дефолтный коллбек для мастера.
* @param hmodbus Указатель на хендлер RS
* @param modbus_msg Указатель на структуру сообщения
* @details В этот коллбек попадут все запросы, с NULL-коллбеком
*/
static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
__NOP();
@@ -206,4 +212,4 @@ RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modb
{
return MB_Master_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff);
}
}
}

View File

@@ -1,14 +1,22 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_coils.c
* @brief Реализация работы с коилами Modbus
******************************************************************************
*******************************************************************************
* @details
Модуль для доступа к coils внутри программы:
- Функции для доступа к coils по глобальным адресам
- Макросы для доступа к coils по локальным адресам
Модуль обработки команд для coils (битовых данных):
- Чтение coils (0x01) - упаковка битов в байты для передачи
- Запись одиночного coil (0x05) - установка/сброс бита
- Запись множественных coils (0x0F) - распаковка битов из байтов
@section cvalid Валидация данных:
- Проверка соответствия количества байт и регистров
- Валидация адресов через MB_DefineRegistersAddress()
- Обработка исключений при некорректных запросах
******************************************************************************/
#include "modbus_coils.h"
@@ -18,7 +26,7 @@
* @brief Выставить/сбросить коил по глобальному адресу.
* @param Addr Адрес коила.
* @param WriteVal Что записать в коил: 0 или 1.
* @return ExceptionCode Код исключения если коила по адресу не существует, и NO_ERRORS если все ок.
* @return ExceptionCode Код исключения если коила по адресу не существует, и ET_NO_ERRORS если все ок.
*
* @details Позволяет обратиться к любому коилу по его глобальному адрессу.
Вне зависимости от того как коилы размещены в памяти.
@@ -26,13 +34,13 @@
MB_ExceptionTypeDef MB_Coil_Write_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception = NO_ERRORS;
MB_ExceptionTypeDef Exception = ET_NO_ERRORS;
uint16_t *coils;
uint16_t start_shift = 0; // shift in coils register
//------------WRITE COIL-------------
Exception = MB_DefineCoilsAddress(&coils, Addr, 1, &start_shift, 1);
if(Exception == NO_ERRORS)
if(Exception == ET_NO_ERRORS)
{
switch(WriteVal)
{
@@ -57,7 +65,7 @@ MB_ExceptionTypeDef MB_Coil_Write_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteV
/**
* @brief Считать коил по глобальному адресу.
* @param Addr Адрес коила.
* @param Exception Указатель на переменную для кода исключения, в случа неудачи при чтении.
* @param Exception Указатель на переменную для кода исключения, в случае неудачи при чтении.
* @return uint16_t Возвращает весь регистр с маской на запрошенном коиле.
*
* @details Позволяет обратиться к любому коилу по его глобальному адрессу.
@@ -75,7 +83,7 @@ uint16_t MB_Coil_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
//------------READ COIL--------------
*Exception = MB_DefineCoilsAddress(&coils, Addr, 1, &start_shift, 0);
if(*Exception == NO_ERRORS)
if(*Exception == ET_NO_ERRORS)
{
return ((*coils)&(1<<start_shift));
}
@@ -86,51 +94,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.
@@ -144,7 +107,7 @@ uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg)
uint16_t start_shift = 0; // shift in coils register
modbus_msg->Except_Code = MB_DefineCoilsAddress(&coils, modbus_msg->Addr, modbus_msg->Qnt, &start_shift, 0);
if(modbus_msg->Except_Code != NO_ERRORS)
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//-----------READING COIL------------
@@ -172,14 +135,14 @@ uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg)
shift = 0; // set shift to zero for the next step
//-----------READ COILS--------------
modbus_msg->DATA[ind] = (*(coils+ind)&mask_for_coils) >> start_shift;
modbus_msg->MbData[ind] = (*(coils+ind)&mask_for_coils) >> start_shift;
if(ind > 0)
modbus_msg->DATA[ind-1] |= ((*(coils+ind)&mask_for_coils) << 16) >> start_shift;
modbus_msg->MbData[ind-1] |= ((*(coils+ind)&mask_for_coils) << 16) >> start_shift;
}
// т.к. DATA 16-битная, для 8-битной передачи, надо поменять местами верхний и нижний байты
// т.к. MbData 16-битная, для 8-битной передачи, надо поменять местами верхний и нижний байты
for(; ind >= 0; --ind)
modbus_msg->DATA[ind] = ByteSwap16(modbus_msg->DATA[ind]);
modbus_msg->MbData[ind] = ByteSwap16(modbus_msg->MbData[ind]);
return 1;
}
@@ -195,14 +158,14 @@ uint8_t MB_Process_Write_Single_Coil(RS_MsgTypeDef *modbus_msg)
//---------CHECK FOR ERRORS----------
if ((modbus_msg->Qnt != 0x0000) && (modbus_msg->Qnt != 0xFF00))
{
modbus_msg->Except_Code = ILLEGAL_DATA_VALUE;
modbus_msg->Except_Code = ET_ILLEGAL_DATA_VALUE;
return 0;
}
// define position of coil
uint16_t *coils;
uint16_t start_shift = 0; // shift in coils register
modbus_msg->Except_Code = MB_DefineCoilsAddress(&coils, modbus_msg->Addr, 0, &start_shift, 1);
if(modbus_msg->Except_Code != NO_ERRORS)
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
@@ -226,14 +189,14 @@ uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg)
//---------CHECK FOR ERRORS----------
if (modbus_msg->ByteCnt != Divide_Up(modbus_msg->Qnt, 8))
{ // if quantity too large OR if quantity and bytes count arent match
modbus_msg->Except_Code = ILLEGAL_DATA_VALUE;
modbus_msg->Except_Code = ET_ILLEGAL_DATA_VALUE;
return 0;
}
// define position of coil
uint16_t *coils; // pointer to coils
uint16_t start_shift = 0; // shift in coils register
modbus_msg->Except_Code = MB_DefineCoilsAddress(&coils, modbus_msg->Addr, modbus_msg->Qnt, &start_shift, 1);
if(modbus_msg->Except_Code != NO_ERRORS)
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//----------WRITTING COILS-----------
@@ -263,10 +226,10 @@ uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg)
// get current coils
temp_reg = *(coils+ind);
// set coils
setted_coils = ByteSwap16(modbus_msg->DATA[ind]) << start_shift;
setted_coils = ByteSwap16(modbus_msg->MbData[ind]) << start_shift;
if(ind > 0)
{
setted_coils |= ((ByteSwap16(modbus_msg->DATA[ind-1]) << start_shift) >> 16);
setted_coils |= ((ByteSwap16(modbus_msg->MbData[ind-1]) << start_shift) >> 16);
}
// write coils
@@ -281,14 +244,5 @@ uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg)
return 1;
}
#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;}
uint8_t MB_Process_Write_Single_Coil(RS_MsgTypeDef *modbus_msg) {return 0;}
uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg) {return 0;}
#endif
#endif //MODBUS_ENABLE_COILS

View File

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

View File

@@ -1,15 +1,15 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_devid.c
* @brief Реализация идентификаторов устройства Modbus
******************************************************************************
*******************************************************************************
* @details
Модуль обработки запросов идентификации устройства через MEI-тип 0x0E:
- Формирование иерархии объектов идентификации
- Поддержка потоковой передачи при большом количестве объектов
- Автоматический расчет MoreFollows флагов
@section Потоковая передача:
@section stream Потоковая передача:
При большом количестве объектов идентификация разбивается на несколько
сообщений с установкой флага MoreFollows и указанием NextObjId для
продолжения чтения в следующем запросе.
@@ -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;
}
/**
@@ -173,18 +47,24 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj)
{
MB_DeviceObjectTypeDef *obj = (MB_DeviceObjectTypeDef *)&MB_DEVID;
unsigned objidtmp = modbus_msg->DevId.NextObjId;
modbus_msg->Except_Code = ET_NO_ERRORS;
/* Define number of object in one message */
unsigned lastobjid = 0;
for(int i = 0; i < DATA_SIZE*2;)
for(int i = 0; i < MbData_size*2;)
{
/* Если объект за пределами допутимого - выходим из цикла */
if(objidtmp >= 0xFF + MODBUS_NUMB_OF_USEROBJECTS)
break;
i += 2;
i += obj[objidtmp].length;
/* Если все еще помещается в массив переходим на следующий объект */
if(i < DATA_SIZE*2)
if(i < MbData_size*2)
{
objidtmp++;
}
/* Если объекты для записи закончились - выходим из цикла*/
if(objidtmp > maxidofobj)
break;
@@ -193,7 +73,7 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj)
/* Fill message with objects data */
char *mbdata = (char *)&modbus_msg->DATA;
char *mbdata = (char *)&modbus_msg->MbData;
unsigned ind = 0;
unsigned objid = modbus_msg->DevId.NextObjId;
for(; objid <= lastobjid; objid++)
@@ -202,15 +82,23 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj)
MB_WriteSingleObjectToMessage(mbdata, &ind, &obj[objid]);
}
objid--;
modbus_msg->ByteCnt = ind;
modbus_msg->DevId.NextObjId = lastobjid+1;
if(objid == maxidofobj)
if(modbus_msg->ByteCnt != 0)
{
modbus_msg->DevId.MoreFollows = 0;
modbus_msg->ByteCnt = ind;
modbus_msg->DevId.NextObjId = lastobjid+1;
if(objid == maxidofobj)
{
modbus_msg->DevId.MoreFollows = 0;
}
else
{
modbus_msg->DevId.MoreFollows = 0xFF;
}
}
else
{
modbus_msg->DevId.MoreFollows = 0xFF;
modbus_msg->Except_Code = ET_ILLEGAL_DATA_VALUE;
}
}
@@ -223,9 +111,11 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj)
*/
uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg)
{
modbus_msg->DevId.Conformity = MODBUS_DEVICE_CONFORMITY;
switch(modbus_msg->DevId.ReadDevId)
{
case MB_BASIC_IDENTIFICATIONS:
case RID_BASIC_IDENTIFICATIONS:
if (modbus_msg->DevId.NextObjId == 0)
{
modbus_msg->DevId.NextObjId = 0;
@@ -235,7 +125,7 @@ uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg)
modbus_msg->DevId.NumbOfObj = 3;
break;
case MB_REGULAR_IDENTIFICATIONS:
case RID_REGULAR_IDENTIFICATIONS:
if (modbus_msg->DevId.NextObjId == 0)
{
modbus_msg->DevId.NextObjId = 3;
@@ -245,29 +135,38 @@ uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg)
modbus_msg->DevId.NumbOfObj = 4;
break;
case MB_EXTENDED_IDENTIFICATIONS:
case RID_EXTENDED_IDENTIFICATIONS:
if(MODBUS_NUMB_OF_USEROBJECTS <= 0 || MODBUS_NUMB_OF_USEROBJECTS > 128)
{
return 0;
modbus_msg->Except_Code = ET_ILLEGAL_DATA_VALUE;
break;
}
if (modbus_msg->DevId.NextObjId == 0)
{
modbus_msg->DevId.NextObjId = 0x80;
}
MB_WriteObjectsToMessage(modbus_msg, 0x80+MODBUS_NUMB_OF_USEROBJECTS);
modbus_msg->DevId.NumbOfObj = MODBUS_NUMB_OF_USEROBJECTS;
break;
case MB_SPEDIFIC_IDENTIFICATIONS:
case RID_SPEDIFIC_IDENTIFICATIONS:
MB_WriteObjectsToMessage(modbus_msg, modbus_msg->DevId.NextObjId);
modbus_msg->DevId.NumbOfObj = 1;
break;
default:
return 0;
}
return 1;
if(modbus_msg->Except_Code != ET_NO_ERRORS)
{
return 0;
}
else
{
return 1;
}
}
@@ -284,402 +183,391 @@ void MB_DeviceInentificationInit(void)
MB_ObjectInit(&MB_DEVID.ProductName, MODBUS_PRODUCT_NAME);
MB_ObjectInit(&MB_DEVID.ModelName, MODBUS_MODEL_NAME);
#ifdef MODBUS_USEROBJECT_0_NAME
MB_ObjectInit(&MB_DEVID.User[0], MODBUS_USEROBJECT_0_NAME);
#if defined(MODBUS_USEROBJECT_0_NAME) && MODBUS_NUMB_OF_USEROBJECTS>0
MB_ObjectInit(&MB_DEVID.User[0], MODBUS_USEROBJECT_0_NAME);
#endif
#ifdef MODBUS_USEROBJECT_1_NAME
MB_ObjectInit(&MB_DEVID.User[1], MODBUS_USEROBJECT_1_NAME);
#if defined(MODBUS_USEROBJECT_1_NAME) && MODBUS_NUMB_OF_USEROBJECTS>1
MB_ObjectInit(&MB_DEVID.User[1], MODBUS_USEROBJECT_1_NAME);
#endif
#ifdef MODBUS_USEROBJECT_2_NAME
MB_ObjectInit(&MB_DEVID.User[2], MODBUS_USEROBJECT_2_NAME);
#if defined(MODBUS_USEROBJECT_2_NAME) && MODBUS_NUMB_OF_USEROBJECTS>2
MB_ObjectInit(&MB_DEVID.User[2], MODBUS_USEROBJECT_2_NAME);
#endif
#ifdef MODBUS_USEROBJECT_3_NAME
MB_ObjectInit(&MB_DEVID.User[3], MODBUS_USEROBJECT_3_NAME);
#if defined(MODBUS_USEROBJECT_3_NAME) && MODBUS_NUMB_OF_USEROBJECTS>3
MB_ObjectInit(&MB_DEVID.User[3], MODBUS_USEROBJECT_3_NAME);
#endif
#ifdef MODBUS_USEROBJECT_4_NAME
MB_ObjectInit(&MB_DEVID.User[4], MODBUS_USEROBJECT_4_NAME);
#if defined(MODBUS_USEROBJECT_4_NAME) && MODBUS_NUMB_OF_USEROBJECTS>4
MB_ObjectInit(&MB_DEVID.User[4], MODBUS_USEROBJECT_4_NAME);
#endif
#ifdef MODBUS_USEROBJECT_5_NAME
MB_ObjectInit(&MB_DEVID.User[5], MODBUS_USEROBJECT_5_NAME);
#if defined(MODBUS_USEROBJECT_5_NAME) && MODBUS_NUMB_OF_USEROBJECTS>5
MB_ObjectInit(&MB_DEVID.User[5], MODBUS_USEROBJECT_5_NAME);
#endif
#ifdef MODBUS_USEROBJECT_6_NAME
MB_ObjectInit(&MB_DEVID.User[6], MODBUS_USEROBJECT_6_NAME);
#if defined(MODBUS_USEROBJECT_6_NAME) && MODBUS_NUMB_OF_USEROBJECTS>6
MB_ObjectInit(&MB_DEVID.User[6], MODBUS_USEROBJECT_6_NAME);
#endif
#ifdef MODBUS_USEROBJECT_7_NAME
MB_ObjectInit(&MB_DEVID.User[7], MODBUS_USEROBJECT_7_NAME);
#if defined(MODBUS_USEROBJECT_7_NAME) && MODBUS_NUMB_OF_USEROBJECTS>7
MB_ObjectInit(&MB_DEVID.User[7], MODBUS_USEROBJECT_7_NAME);
#endif
#ifdef MODBUS_USEROBJECT_8_NAME
MB_ObjectInit(&MB_DEVID.User[8], MODBUS_USEROBJECT_8_NAME);
#if defined(MODBUS_USEROBJECT_8_NAME) && MODBUS_NUMB_OF_USEROBJECTS>8
MB_ObjectInit(&MB_DEVID.User[8], MODBUS_USEROBJECT_8_NAME);
#endif
#ifdef MODBUS_USEROBJECT_9_NAME
MB_ObjectInit(&MB_DEVID.User[9], MODBUS_USEROBJECT_9_NAME);
#if defined(MODBUS_USEROBJECT_9_NAME) && MODBUS_NUMB_OF_USEROBJECTS>9
MB_ObjectInit(&MB_DEVID.User[9], MODBUS_USEROBJECT_9_NAME);
#endif
#ifdef MODBUS_USEROBJECT_10_NAME
MB_ObjectInit(&MB_DEVID.User[10], MODBUS_USEROBJECT_10_NAME);
#if defined(MODBUS_USEROBJECT_10_NAME) && MODBUS_NUMB_OF_USEROBJECTS>10
MB_ObjectInit(&MB_DEVID.User[10], MODBUS_USEROBJECT_10_NAME);
#endif
#ifdef MODBUS_USEROBJECT_11_NAME
MB_ObjectInit(&MB_DEVID.User[11], MODBUS_USEROBJECT_11_NAME);
#if defined(MODBUS_USEROBJECT_11_NAME) && MODBUS_NUMB_OF_USEROBJECTS>11
MB_ObjectInit(&MB_DEVID.User[11], MODBUS_USEROBJECT_11_NAME);
#endif
#ifdef MODBUS_USEROBJECT_12_NAME
MB_ObjectInit(&MB_DEVID.User[12], MODBUS_USEROBJECT_12_NAME);
#if defined(MODBUS_USEROBJECT_12_NAME) && MODBUS_NUMB_OF_USEROBJECTS>12
MB_ObjectInit(&MB_DEVID.User[12], MODBUS_USEROBJECT_12_NAME);
#endif
#ifdef MODBUS_USEROBJECT_13_NAME
#if defined(MODBUS_USEROBJECT_13_NAME) && MODBUS_NUMB_OF_USEROBJECTS>13
MB_ObjectInit(&MB_DEVID.User[13], MODBUS_USEROBJECT_13_NAME);
#endif
#ifdef MODBUS_USEROBJECT_14_NAME
#if defined(MODBUS_USEROBJECT_14_NAME) && MODBUS_NUMB_OF_USEROBJECTS>14
MB_ObjectInit(&MB_DEVID.User[14], MODBUS_USEROBJECT_14_NAME);
#endif
#ifdef MODBUS_USEROBJECT_15_NAME
#if defined(MODBUS_USEROBJECT_15_NAME) && MODBUS_NUMB_OF_USEROBJECTS>15
MB_ObjectInit(&MB_DEVID.User[15], MODBUS_USEROBJECT_15_NAME);
#endif
#ifdef MODBUS_USEROBJECT_16_NAME
#if defined(MODBUS_USEROBJECT_16_NAME) && MODBUS_NUMB_OF_USEROBJECTS>16
MB_ObjectInit(&MB_DEVID.User[16], MODBUS_USEROBJECT_16_NAME);
#endif
#ifdef MODBUS_USEROBJECT_17_NAME
#if defined(MODBUS_USEROBJECT_17_NAME) && MODBUS_NUMB_OF_USEROBJECTS>17
MB_ObjectInit(&MB_DEVID.User[17], MODBUS_USEROBJECT_17_NAME);
#endif
#ifdef MODBUS_USEROBJECT_18_NAME
#if defined(MODBUS_USEROBJECT_18_NAME) && MODBUS_NUMB_OF_USEROBJECTS>18
MB_ObjectInit(&MB_DEVID.User[18], MODBUS_USEROBJECT_18_NAME);
#endif
#ifdef MODBUS_USEROBJECT_19_NAME
#if defined(MODBUS_USEROBJECT_19_NAME) && MODBUS_NUMB_OF_USEROBJECTS>19
MB_ObjectInit(&MB_DEVID.User[19], MODBUS_USEROBJECT_19_NAME);
#endif
#ifdef MODBUS_USEROBJECT_20_NAME
#if defined(MODBUS_USEROBJECT_20_NAME) && MODBUS_NUMB_OF_USEROBJECTS>20
MB_ObjectInit(&MB_DEVID.User[20], MODBUS_USEROBJECT_20_NAME);
#endif
#ifdef MODBUS_USEROBJECT_21_NAME
#if defined(MODBUS_USEROBJECT_21_NAME) && MODBUS_NUMB_OF_USEROBJECTS>21
MB_ObjectInit(&MB_DEVID.User[21], MODBUS_USEROBJECT_21_NAME);
#endif
#ifdef MODBUS_USEROBJECT_22_NAME
#if defined(MODBUS_USEROBJECT_22_NAME) && MODBUS_NUMB_OF_USEROBJECTS>22
MB_ObjectInit(&MB_DEVID.User[22], MODBUS_USEROBJECT_22_NAME);
#endif
#ifdef MODBUS_USEROBJECT_23_NAME
#if defined(MODBUS_USEROBJECT_23_NAME) && MODBUS_NUMB_OF_USEROBJECTS>23
MB_ObjectInit(&MB_DEVID.User[23], MODBUS_USEROBJECT_23_NAME);
#endif
#ifdef MODBUS_USEROBJECT_24_NAME
#if defined(MODBUS_USEROBJECT_24_NAME) && MODBUS_NUMB_OF_USEROBJECTS>24
MB_ObjectInit(&MB_DEVID.User[24], MODBUS_USEROBJECT_24_NAME);
#endif
#ifdef MODBUS_USEROBJECT_25_NAME
#if defined(MODBUS_USEROBJECT_25_NAME) && MODBUS_NUMB_OF_USEROBJECTS>25
MB_ObjectInit(&MB_DEVID.User[25], MODBUS_USEROBJECT_25_NAME);
#endif
#ifdef MODBUS_USEROBJECT_26_NAME
#if defined(MODBUS_USEROBJECT_26_NAME) && MODBUS_NUMB_OF_USEROBJECTS>26
MB_ObjectInit(&MB_DEVID.User[26], MODBUS_USEROBJECT_26_NAME);
#endif
#ifdef MODBUS_USEROBJECT_27_NAME
#if defined(MODBUS_USEROBJECT_27_NAME) && MODBUS_NUMB_OF_USEROBJECTS>27
MB_ObjectInit(&MB_DEVID.User[27], MODBUS_USEROBJECT_27_NAME);
#endif
#ifdef MODBUS_USEROBJECT_28_NAME
#if defined(MODBUS_USEROBJECT_28_NAME) && MODBUS_NUMB_OF_USEROBJECTS>28
MB_ObjectInit(&MB_DEVID.User[28], MODBUS_USEROBJECT_28_NAME);
#endif
#ifdef MODBUS_USEROBJECT_29_NAME
#if defined(MODBUS_USEROBJECT_29_NAME) && MODBUS_NUMB_OF_USEROBJECTS>29
MB_ObjectInit(&MB_DEVID.User[29], MODBUS_USEROBJECT_29_NAME);
#endif
#ifdef MODBUS_USEROBJECT_30_NAME
#if defined(MODBUS_USEROBJECT_30_NAME) && MODBUS_NUMB_OF_USEROBJECTS>30
MB_ObjectInit(&MB_DEVID.User[30], MODBUS_USEROBJECT_30_NAME);
#endif
#ifdef MODBUS_USEROBJECT_31_NAME
#if defined(MODBUS_USEROBJECT_31_NAME) && MODBUS_NUMB_OF_USEROBJECTS>31
MB_ObjectInit(&MB_DEVID.User[31], MODBUS_USEROBJECT_31_NAME);
#endif
#ifdef MODBUS_USEROBJECT_32_NAME
#if defined(MODBUS_USEROBJECT_32_NAME) && MODBUS_NUMB_OF_USEROBJECTS>32
MB_ObjectInit(&MB_DEVID.User[32], MODBUS_USEROBJECT_32_NAME);
#endif
#ifdef MODBUS_USEROBJECT_33_NAME
#if defined(MODBUS_USEROBJECT_33_NAME) && MODBUS_NUMB_OF_USEROBJECTS>33
MB_ObjectInit(&MB_DEVID.User[33], MODBUS_USEROBJECT_33_NAME);
#endif
#ifdef MODBUS_USEROBJECT_34_NAME
#if defined(MODBUS_USEROBJECT_34_NAME) && MODBUS_NUMB_OF_USEROBJECTS>34
MB_ObjectInit(&MB_DEVID.User[34], MODBUS_USEROBJECT_34_NAME);
#endif
#ifdef MODBUS_USEROBJECT_35_NAME
#if defined(MODBUS_USEROBJECT_35_NAME) && MODBUS_NUMB_OF_USEROBJECTS>35
MB_ObjectInit(&MB_DEVID.User[35], MODBUS_USEROBJECT_35_NAME);
#endif
#ifdef MODBUS_USEROBJECT_36_NAME
#if defined(MODBUS_USEROBJECT_36_NAME) && MODBUS_NUMB_OF_USEROBJECTS>36
MB_ObjectInit(&MB_DEVID.User[36], MODBUS_USEROBJECT_36_NAME);
#endif
#ifdef MODBUS_USEROBJECT_37_NAME
#if defined(MODBUS_USEROBJECT_37_NAME) && MODBUS_NUMB_OF_USEROBJECTS>37
MB_ObjectInit(&MB_DEVID.User[37], MODBUS_USEROBJECT_37_NAME);
#endif
#ifdef MODBUS_USEROBJECT_38_NAME
#if defined(MODBUS_USEROBJECT_38_NAME) && MODBUS_NUMB_OF_USEROBJECTS>38
MB_ObjectInit(&MB_DEVID.User[38], MODBUS_USEROBJECT_38_NAME);
#endif
#ifdef MODBUS_USEROBJECT_39_NAME
#if defined(MODBUS_USEROBJECT_39_NAME) && MODBUS_NUMB_OF_USEROBJECTS>39
MB_ObjectInit(&MB_DEVID.User[39], MODBUS_USEROBJECT_39_NAME);
#endif
#ifdef MODBUS_USEROBJECT_40_NAME
#if defined(MODBUS_USEROBJECT_40_NAME) && MODBUS_NUMB_OF_USEROBJECTS>40
MB_ObjectInit(&MB_DEVID.User[40], MODBUS_USEROBJECT_40_NAME);
#endif
#ifdef MODBUS_USEROBJECT_41_NAME
#if defined(MODBUS_USEROBJECT_41_NAME) && MODBUS_NUMB_OF_USEROBJECTS>41
MB_ObjectInit(&MB_DEVID.User[41], MODBUS_USEROBJECT_41_NAME);
#endif
#ifdef MODBUS_USEROBJECT_42_NAME
#if defined(MODBUS_USEROBJECT_42_NAME) && MODBUS_NUMB_OF_USEROBJECTS>42
MB_ObjectInit(&MB_DEVID.User[42], MODBUS_USEROBJECT_42_NAME);
#endif
#ifdef MODBUS_USEROBJECT_43_NAME
#if defined(MODBUS_USEROBJECT_43_NAME) && MODBUS_NUMB_OF_USEROBJECTS>43
MB_ObjectInit(&MB_DEVID.User[43], MODBUS_USEROBJECT_43_NAME);
#endif
#ifdef MODBUS_USEROBJECT_44_NAME
#if defined(MODBUS_USEROBJECT_44_NAME) && MODBUS_NUMB_OF_USEROBJECTS>44
MB_ObjectInit(&MB_DEVID.User[44], MODBUS_USEROBJECT_44_NAME);
#endif
#ifdef MODBUS_USEROBJECT_45_NAME
#if defined(MODBUS_USEROBJECT_45_NAME) && MODBUS_NUMB_OF_USEROBJECTS>45
MB_ObjectInit(&MB_DEVID.User[45], MODBUS_USEROBJECT_45_NAME);
#endif
#ifdef MODBUS_USEROBJECT_46_NAME
#if defined(MODBUS_USEROBJECT_46_NAME) && MODBUS_NUMB_OF_USEROBJECTS>46
MB_ObjectInit(&MB_DEVID.User[46], MODBUS_USEROBJECT_46_NAME);
#endif
#ifdef MODBUS_USEROBJECT_47_NAME
#if defined(MODBUS_USEROBJECT_47_NAME) && MODBUS_NUMB_OF_USEROBJECTS>47
MB_ObjectInit(&MB_DEVID.User[47], MODBUS_USEROBJECT_47_NAME);
#endif
#ifdef MODBUS_USEROBJECT_48_NAME
#if defined(MODBUS_USEROBJECT_48_NAME) && MODBUS_NUMB_OF_USEROBJECTS>48
MB_ObjectInit(&MB_DEVID.User[48], MODBUS_USEROBJECT_48_NAME);
#endif
#ifdef MODBUS_USEROBJECT_49_NAME
#if defined(MODBUS_USEROBJECT_49_NAME) && MODBUS_NUMB_OF_USEROBJECTS>49
MB_ObjectInit(&MB_DEVID.User[49], MODBUS_USEROBJECT_49_NAME);
#endif
#ifdef MODBUS_USEROBJECT_50_NAME
#if defined(MODBUS_USEROBJECT_50_NAME) && MODBUS_NUMB_OF_USEROBJECTS>50
MB_ObjectInit(&MB_DEVID.User[50], MODBUS_USEROBJECT_50_NAME);
#endif
#ifdef MODBUS_USEROBJECT_51_NAME
#if defined(MODBUS_USEROBJECT_51_NAME) && MODBUS_NUMB_OF_USEROBJECTS>51
MB_ObjectInit(&MB_DEVID.User[51], MODBUS_USEROBJECT_51_NAME);
#endif
#ifdef MODBUS_USEROBJECT_52_NAME
#if defined(MODBUS_USEROBJECT_52_NAME) && MODBUS_NUMB_OF_USEROBJECTS>52
MB_ObjectInit(&MB_DEVID.User[52], MODBUS_USEROBJECT_52_NAME);
#endif
#ifdef MODBUS_USEROBJECT_53_NAME
#if defined(MODBUS_USEROBJECT_53_NAME) && MODBUS_NUMB_OF_USEROBJECTS>53
MB_ObjectInit(&MB_DEVID.User[53], MODBUS_USEROBJECT_53_NAME);
#endif
#ifdef MODBUS_USEROBJECT_54_NAME
#if defined(MODBUS_USEROBJECT_54_NAME) && MODBUS_NUMB_OF_USEROBJECTS>54
MB_ObjectInit(&MB_DEVID.User[54], MODBUS_USEROBJECT_54_NAME);
#endif
#ifdef MODBUS_USEROBJECT_55_NAME
#if defined(MODBUS_USEROBJECT_55_NAME) && MODBUS_NUMB_OF_USEROBJECTS>55
MB_ObjectInit(&MB_DEVID.User[55], MODBUS_USEROBJECT_55_NAME);
#endif
#ifdef MODBUS_USEROBJECT_56_NAME
#if defined(MODBUS_USEROBJECT_56_NAME) && MODBUS_NUMB_OF_USEROBJECTS>56
MB_ObjectInit(&MB_DEVID.User[56], MODBUS_USEROBJECT_56_NAME);
#endif
#ifdef MODBUS_USEROBJECT_57_NAME
#if defined(MODBUS_USEROBJECT_57_NAME) && MODBUS_NUMB_OF_USEROBJECTS>57
MB_ObjectInit(&MB_DEVID.User[57], MODBUS_USEROBJECT_57_NAME);
#endif
#ifdef MODBUS_USEROBJECT_58_NAME
#if defined(MODBUS_USEROBJECT_58_NAME) && MODBUS_NUMB_OF_USEROBJECTS>58
MB_ObjectInit(&MB_DEVID.User[58], MODBUS_USEROBJECT_58_NAME);
#endif
#ifdef MODBUS_USEROBJECT_59_NAME
#if defined(MODBUS_USEROBJECT_59_NAME) && MODBUS_NUMB_OF_USEROBJECTS>59
MB_ObjectInit(&MB_DEVID.User[59], MODBUS_USEROBJECT_59_NAME);
#endif
#ifdef MODBUS_USEROBJECT_60_NAME
#if defined(MODBUS_USEROBJECT_60_NAME) && MODBUS_NUMB_OF_USEROBJECTS>60
MB_ObjectInit(&MB_DEVID.User[60], MODBUS_USEROBJECT_60_NAME);
#endif
#ifdef MODBUS_USEROBJECT_61_NAME
#if defined(MODBUS_USEROBJECT_61_NAME) && MODBUS_NUMB_OF_USEROBJECTS>61
MB_ObjectInit(&MB_DEVID.User[61], MODBUS_USEROBJECT_61_NAME);
#endif
#ifdef MODBUS_USEROBJECT_62_NAME
#if defined(MODBUS_USEROBJECT_62_NAME) && MODBUS_NUMB_OF_USEROBJECTS>62
MB_ObjectInit(&MB_DEVID.User[62], MODBUS_USEROBJECT_62_NAME);
#endif
#ifdef MODBUS_USEROBJECT_63_NAME
#if defined(MODBUS_USEROBJECT_63_NAME) && MODBUS_NUMB_OF_USEROBJECTS>63
MB_ObjectInit(&MB_DEVID.User[63], MODBUS_USEROBJECT_63_NAME);
#endif
#ifdef MODBUS_USEROBJECT_64_NAME
#if defined(MODBUS_USEROBJECT_64_NAME) && MODBUS_NUMB_OF_USEROBJECTS>64
MB_ObjectInit(&MB_DEVID.User[64], MODBUS_USEROBJECT_64_NAME);
#endif
#ifdef MODBUS_USEROBJECT_65_NAME
#if defined(MODBUS_USEROBJECT_65_NAME) && MODBUS_NUMB_OF_USEROBJECTS>65
MB_ObjectInit(&MB_DEVID.User[65], MODBUS_USEROBJECT_65_NAME);
#endif
#ifdef MODBUS_USEROBJECT_66_NAME
#if defined(MODBUS_USEROBJECT_66_NAME) && MODBUS_NUMB_OF_USEROBJECTS>66
MB_ObjectInit(&MB_DEVID.User[66], MODBUS_USEROBJECT_66_NAME);
#endif
#ifdef MODBUS_USEROBJECT_67_NAME
#if defined(MODBUS_USEROBJECT_67_NAME) && MODBUS_NUMB_OF_USEROBJECTS>67
MB_ObjectInit(&MB_DEVID.User[67], MODBUS_USEROBJECT_67_NAME);
#endif
#ifdef MODBUS_USEROBJECT_68_NAME
#if defined(MODBUS_USEROBJECT_68_NAME) && MODBUS_NUMB_OF_USEROBJECTS>68
MB_ObjectInit(&MB_DEVID.User[68], MODBUS_USEROBJECT_68_NAME);
#endif
#ifdef MODBUS_USEROBJECT_69_NAME
#if defined(MODBUS_USEROBJECT_69_NAME) && MODBUS_NUMB_OF_USEROBJECTS>69
MB_ObjectInit(&MB_DEVID.User[69], MODBUS_USEROBJECT_69_NAME);
#endif
#ifdef MODBUS_USEROBJECT_70_NAME
#if defined(MODBUS_USEROBJECT_70_NAME) && MODBUS_NUMB_OF_USEROBJECTS>70
MB_ObjectInit(&MB_DEVID.User[70], MODBUS_USEROBJECT_70_NAME);
#endif
#ifdef MODBUS_USEROBJECT_71_NAME
#if defined(MODBUS_USEROBJECT_71_NAME) && MODBUS_NUMB_OF_USEROBJECTS>71
MB_ObjectInit(&MB_DEVID.User[71], MODBUS_USEROBJECT_71_NAME);
#endif
#ifdef MODBUS_USEROBJECT_72_NAME
#if defined(MODBUS_USEROBJECT_72_NAME) && MODBUS_NUMB_OF_USEROBJECTS>72
MB_ObjectInit(&MB_DEVID.User[72], MODBUS_USEROBJECT_72_NAME);
#endif
#ifdef MODBUS_USEROBJECT_73_NAME
#if defined(MODBUS_USEROBJECT_73_NAME) && MODBUS_NUMB_OF_USEROBJECTS>73
MB_ObjectInit(&MB_DEVID.User[73], MODBUS_USEROBJECT_73_NAME);
#endif
#ifdef MODBUS_USEROBJECT_74_NAME
#if defined(MODBUS_USEROBJECT_74_NAME) && MODBUS_NUMB_OF_USEROBJECTS>74
MB_ObjectInit(&MB_DEVID.User[74], MODBUS_USEROBJECT_74_NAME);
#endif
#ifdef MODBUS_USEROBJECT_75_NAME
#if defined(MODBUS_USEROBJECT_75_NAME) && MODBUS_NUMB_OF_USEROBJECTS>75
MB_ObjectInit(&MB_DEVID.User[75], MODBUS_USEROBJECT_75_NAME);
#endif
#ifdef MODBUS_USEROBJECT_76_NAME
#if defined(MODBUS_USEROBJECT_76_NAME) && MODBUS_NUMB_OF_USEROBJECTS>76
MB_ObjectInit(&MB_DEVID.User[76], MODBUS_USEROBJECT_76_NAME);
#endif
#ifdef MODBUS_USEROBJECT_77_NAME
#if defined(MODBUS_USEROBJECT_77_NAME) && MODBUS_NUMB_OF_USEROBJECTS>77
MB_ObjectInit(&MB_DEVID.User[77], MODBUS_USEROBJECT_77_NAME);
#endif
#ifdef MODBUS_USEROBJECT_78_NAME
#if defined(MODBUS_USEROBJECT_78_NAME) && MODBUS_NUMB_OF_USEROBJECTS>78
MB_ObjectInit(&MB_DEVID.User[78], MODBUS_USEROBJECT_78_NAME);
#endif
#ifdef MODBUS_USEROBJECT_79_NAME
#if defined(MODBUS_USEROBJECT_79_NAME) && MODBUS_NUMB_OF_USEROBJECTS>79
MB_ObjectInit(&MB_DEVID.User[79], MODBUS_USEROBJECT_79_NAME);
#endif
#ifdef MODBUS_USEROBJECT_80_NAME
#if defined(MODBUS_USEROBJECT_80_NAME) && MODBUS_NUMB_OF_USEROBJECTS>80
MB_ObjectInit(&MB_DEVID.User[80], MODBUS_USEROBJECT_80_NAME);
#endif
#ifdef MODBUS_USEROBJECT_81_NAME
#if defined(MODBUS_USEROBJECT_81_NAME) && MODBUS_NUMB_OF_USEROBJECTS>81
MB_ObjectInit(&MB_DEVID.User[81], MODBUS_USEROBJECT_81_NAME);
#endif
#ifdef MODBUS_USEROBJECT_82_NAME
#if defined(MODBUS_USEROBJECT_82_NAME) && MODBUS_NUMB_OF_USEROBJECTS>82
MB_ObjectInit(&MB_DEVID.User[82], MODBUS_USEROBJECT_82_NAME);
#endif
#ifdef MODBUS_USEROBJECT_83_NAME
#if defined(MODBUS_USEROBJECT_83_NAME) && MODBUS_NUMB_OF_USEROBJECTS>83
MB_ObjectInit(&MB_DEVID.User[83], MODBUS_USEROBJECT_83_NAME);
#endif
#ifdef MODBUS_USEROBJECT_84_NAME
#if defined(MODBUS_USEROBJECT_84_NAME) && MODBUS_NUMB_OF_USEROBJECTS>84
MB_ObjectInit(&MB_DEVID.User[84], MODBUS_USEROBJECT_84_NAME);
#endif
#ifdef MODBUS_USEROBJECT_85_NAME
#if defined(MODBUS_USEROBJECT_85_NAME) && MODBUS_NUMB_OF_USEROBJECTS>85
MB_ObjectInit(&MB_DEVID.User[85], MODBUS_USEROBJECT_85_NAME);
#endif
#ifdef MODBUS_USEROBJECT_86_NAME
#if defined(MODBUS_USEROBJECT_86_NAME) && MODBUS_NUMB_OF_USEROBJECTS>86
MB_ObjectInit(&MB_DEVID.User[86], MODBUS_USEROBJECT_86_NAME);
#endif
#ifdef MODBUS_USEROBJECT_87_NAME
#if defined(MODBUS_USEROBJECT_87_NAME) && MODBUS_NUMB_OF_USEROBJECTS>87
MB_ObjectInit(&MB_DEVID.User[87], MODBUS_USEROBJECT_87_NAME);
#endif
#ifdef MODBUS_USEROBJECT_88_NAME
#if defined(MODBUS_USEROBJECT_88_NAME) && MODBUS_NUMB_OF_USEROBJECTS>88
MB_ObjectInit(&MB_DEVID.User[88], MODBUS_USEROBJECT_88_NAME);
#endif
#ifdef MODBUS_USEROBJECT_89_NAME
#if defined(MODBUS_USEROBJECT_89_NAME) && MODBUS_NUMB_OF_USEROBJECTS>89
MB_ObjectInit(&MB_DEVID.User[89], MODBUS_USEROBJECT_89_NAME);
#endif
#ifdef MODBUS_USEROBJECT_90_NAME
#if defined(MODBUS_USEROBJECT_90_NAME) && MODBUS_NUMB_OF_USEROBJECTS>90
MB_ObjectInit(&MB_DEVID.User[90], MODBUS_USEROBJECT_90_NAME);
#endif
#ifdef MODBUS_USEROBJECT_91_NAME
#if defined(MODBUS_USEROBJECT_91_NAME) && MODBUS_NUMB_OF_USEROBJECTS>91
MB_ObjectInit(&MB_DEVID.User[91], MODBUS_USEROBJECT_91_NAME);
#endif
#ifdef MODBUS_USEROBJECT_92_NAME
#if defined(MODBUS_USEROBJECT_92_NAME) && MODBUS_NUMB_OF_USEROBJECTS>92
MB_ObjectInit(&MB_DEVID.User[92], MODBUS_USEROBJECT_92_NAME);
#endif
#ifdef MODBUS_USEROBJECT_93_NAME
#if defined(MODBUS_USEROBJECT_93_NAME) && MODBUS_NUMB_OF_USEROBJECTS>93
MB_ObjectInit(&MB_DEVID.User[93], MODBUS_USEROBJECT_93_NAME);
#endif
#ifdef MODBUS_USEROBJECT_94_NAME
#if defined(MODBUS_USEROBJECT_94_NAME) && MODBUS_NUMB_OF_USEROBJECTS>94
MB_ObjectInit(&MB_DEVID.User[94], MODBUS_USEROBJECT_94_NAME);
#endif
#ifdef MODBUS_USEROBJECT_95_NAME
#if defined(MODBUS_USEROBJECT_95_NAME) && MODBUS_NUMB_OF_USEROBJECTS>95
MB_ObjectInit(&MB_DEVID.User[95], MODBUS_USEROBJECT_95_NAME);
#endif
#ifdef MODBUS_USEROBJECT_96_NAME
#if defined(MODBUS_USEROBJECT_96_NAME) && MODBUS_NUMB_OF_USEROBJECTS>96
MB_ObjectInit(&MB_DEVID.User[96], MODBUS_USEROBJECT_96_NAME);
#endif
#ifdef MODBUS_USEROBJECT_97_NAME
#if defined(MODBUS_USEROBJECT_97_NAME) && MODBUS_NUMB_OF_USEROBJECTS>97
MB_ObjectInit(&MB_DEVID.User[97], MODBUS_USEROBJECT_97_NAME);
#endif
#ifdef MODBUS_USEROBJECT_98_NAME
#if defined(MODBUS_USEROBJECT_98_NAME) && MODBUS_NUMB_OF_USEROBJECTS>98
MB_ObjectInit(&MB_DEVID.User[98], MODBUS_USEROBJECT_98_NAME);
#endif
#ifdef MODBUS_USEROBJECT_99_NAME
#if defined(MODBUS_USEROBJECT_99_NAME) && MODBUS_NUMB_OF_USEROBJECTS>99
MB_ObjectInit(&MB_DEVID.User[99], MODBUS_USEROBJECT_99_NAME);
#endif
#ifdef MODBUS_USEROBJECT_100_NAME
#if defined(MODBUS_USEROBJECT_100_NAME) && MODBUS_NUMB_OF_USEROBJECTS>100
MB_ObjectInit(&MB_DEVID.User[100], MODBUS_USEROBJECT_100_NAME);
#endif
#ifdef MODBUS_USEROBJECT_101_NAME
#if defined(MODBUS_USEROBJECT_101_NAME) && MODBUS_NUMB_OF_USEROBJECTS>101
MB_ObjectInit(&MB_DEVID.User[101], MODBUS_USEROBJECT_101_NAME);
#endif
#ifdef MODBUS_USEROBJECT_102_NAME
#if defined(MODBUS_USEROBJECT_102_NAME) && MODBUS_NUMB_OF_USEROBJECTS>102
MB_ObjectInit(&MB_DEVID.User[102], MODBUS_USEROBJECT_102_NAME);
#endif
#ifdef MODBUS_USEROBJECT_103_NAME
#if defined(MODBUS_USEROBJECT_103_NAME) && MODBUS_NUMB_OF_USEROBJECTS>103
MB_ObjectInit(&MB_DEVID.User[103], MODBUS_USEROBJECT_103_NAME);
#endif
#ifdef MODBUS_USEROBJECT_104_NAME
#if defined(MODBUS_USEROBJECT_104_NAME) && MODBUS_NUMB_OF_USEROBJECTS>104
MB_ObjectInit(&MB_DEVID.User[104], MODBUS_USEROBJECT_104_NAME);
#endif
#ifdef MODBUS_USEROBJECT_105_NAME
#if defined(MODBUS_USEROBJECT_105_NAME) && MODBUS_NUMB_OF_USEROBJECTS>105
MB_ObjectInit(&MB_DEVID.User[105], MODBUS_USEROBJECT_105_NAME);
#endif
#ifdef MODBUS_USEROBJECT_106_NAME
#if defined(MODBUS_USEROBJECT_106_NAME) && MODBUS_NUMB_OF_USEROBJECTS>106
MB_ObjectInit(&MB_DEVID.User[106], MODBUS_USEROBJECT_106_NAME);
#endif
#ifdef MODBUS_USEROBJECT_107_NAME
#if defined(MODBUS_USEROBJECT_107_NAME) && MODBUS_NUMB_OF_USEROBJECTS>107
MB_ObjectInit(&MB_DEVID.User[107], MODBUS_USEROBJECT_107_NAME);
#endif
#ifdef MODBUS_USEROBJECT_108_NAME
#if defined(MODBUS_USEROBJECT_108_NAME) && MODBUS_NUMB_OF_USEROBJECTS>108
MB_ObjectInit(&MB_DEVID.User[108], MODBUS_USEROBJECT_108_NAME);
#endif
#ifdef MODBUS_USEROBJECT_109_NAME
#if defined(MODBUS_USEROBJECT_109_NAME) && MODBUS_NUMB_OF_USEROBJECTS>109
MB_ObjectInit(&MB_DEVID.User[109], MODBUS_USEROBJECT_109_NAME);
#endif
#ifdef MODBUS_USEROBJECT_110_NAME
#if defined(MODBUS_USEROBJECT_110_NAME) && MODBUS_NUMB_OF_USEROBJECTS>110
MB_ObjectInit(&MB_DEVID.User[110], MODBUS_USEROBJECT_110_NAME);
#endif
#ifdef MODBUS_USEROBJECT_111_NAME
#if defined(MODBUS_USEROBJECT_111_NAME) && MODBUS_NUMB_OF_USEROBJECTS>111
MB_ObjectInit(&MB_DEVID.User[111], MODBUS_USEROBJECT_111_NAME);
#endif
#ifdef MODBUS_USEROBJECT_112_NAME
#if defined(MODBUS_USEROBJECT_112_NAME) && MODBUS_NUMB_OF_USEROBJECTS>112
MB_ObjectInit(&MB_DEVID.User[112], MODBUS_USEROBJECT_112_NAME);
#endif
#ifdef MODBUS_USEROBJECT_113_NAME
#if defined(MODBUS_USEROBJECT_113_NAME) && MODBUS_NUMB_OF_USEROBJECTS>113
MB_ObjectInit(&MB_DEVID.User[113], MODBUS_USEROBJECT_113_NAME);
#endif
#ifdef MODBUS_USEROBJECT_114_NAME
#if defined(MODBUS_USEROBJECT_114_NAME) && MODBUS_NUMB_OF_USEROBJECTS>114
MB_ObjectInit(&MB_DEVID.User[114], MODBUS_USEROBJECT_114_NAME);
#endif
#ifdef MODBUS_USEROBJECT_115_NAME
#if defined(MODBUS_USEROBJECT_115_NAME) && MODBUS_NUMB_OF_USEROBJECTS>115
MB_ObjectInit(&MB_DEVID.User[115], MODBUS_USEROBJECT_115_NAME);
#endif
#ifdef MODBUS_USEROBJECT_116_NAME
#if defined(MODBUS_USEROBJECT_116_NAME) && MODBUS_NUMB_OF_USEROBJECTS>116
MB_ObjectInit(&MB_DEVID.User[116], MODBUS_USEROBJECT_116_NAME);
#endif
#ifdef MODBUS_USEROBJECT_117_NAME
#if defined(MODBUS_USEROBJECT_117_NAME) && MODBUS_NUMB_OF_USEROBJECTS>117
MB_ObjectInit(&MB_DEVID.User[117], MODBUS_USEROBJECT_117_NAME);
#endif
#ifdef MODBUS_USEROBJECT_118_NAME
#if defined(MODBUS_USEROBJECT_118_NAME) && MODBUS_NUMB_OF_USEROBJECTS>118
MB_ObjectInit(&MB_DEVID.User[118], MODBUS_USEROBJECT_118_NAME);
#endif
#ifdef MODBUS_USEROBJECT_119_NAME
#if defined(MODBUS_USEROBJECT_119_NAME) && MODBUS_NUMB_OF_USEROBJECTS>119
MB_ObjectInit(&MB_DEVID.User[119], MODBUS_USEROBJECT_119_NAME);
#endif
#ifdef MODBUS_USEROBJECT_120_NAME
#if defined(MODBUS_USEROBJECT_120_NAME) && MODBUS_NUMB_OF_USEROBJECTS>120
MB_ObjectInit(&MB_DEVID.User[120], MODBUS_USEROBJECT_120_NAME);
#endif
#ifdef MODBUS_USEROBJECT_121_NAME
#if defined(MODBUS_USEROBJECT_121_NAME) && MODBUS_NUMB_OF_USEROBJECTS>121
MB_ObjectInit(&MB_DEVID.User[121], MODBUS_USEROBJECT_121_NAME);
#endif
#ifdef MODBUS_USEROBJECT_122_NAME
#if defined(MODBUS_USEROBJECT_122_NAME) && MODBUS_NUMB_OF_USEROBJECTS>122
MB_ObjectInit(&MB_DEVID.User[122], MODBUS_USEROBJECT_122_NAME);
#endif
#ifdef MODBUS_USEROBJECT_123_NAME
#if defined(MODBUS_USEROBJECT_123_NAME) && MODBUS_NUMB_OF_USEROBJECTS>123
MB_ObjectInit(&MB_DEVID.User[123], MODBUS_USEROBJECT_123_NAME);
#endif
#ifdef MODBUS_USEROBJECT_124_NAME
#if defined(MODBUS_USEROBJECT_124_NAME) && MODBUS_NUMB_OF_USEROBJECTS>124
MB_ObjectInit(&MB_DEVID.User[124], MODBUS_USEROBJECT_124_NAME);
#endif
#ifdef MODBUS_USEROBJECT_125_NAME
#if defined(MODBUS_USEROBJECT_125_NAME) && MODBUS_NUMB_OF_USEROBJECTS>125
MB_ObjectInit(&MB_DEVID.User[125], MODBUS_USEROBJECT_125_NAME);
#endif
#ifdef MODBUS_USEROBJECT_126_NAME
#if defined(MODBUS_USEROBJECT_126_NAME) && MODBUS_NUMB_OF_USEROBJECTS>126
MB_ObjectInit(&MB_DEVID.User[126], MODBUS_USEROBJECT_126_NAME);
#endif
#ifdef MODBUS_USEROBJECT_127_NAME
#if defined(MODBUS_USEROBJECT_127_NAME) && MODBUS_NUMB_OF_USEROBJECTS>127
MB_ObjectInit(&MB_DEVID.User[127], MODBUS_USEROBJECT_127_NAME);
#endif
}
#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;}
void MB_DeviceInentificationInit(void) {}
#endif
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS

View File

@@ -1,8 +1,8 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_diag.c
* @brief Реализация диагностики устройства Modbus
******************************************************************************
*******************************************************************************
* @details
Модуль обработки запросов диагностической информации (0x08):
- Полная поддержка всех подфункций диагностики согласно спецификации Modbus
@@ -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 бит нельзя выставить)
@@ -97,8 +74,8 @@ int MB_Diagnostics_GetBit(int bit_num)
*/
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];
uint16_t sub_function = modbus_msg->MbData[0];
uint16_t request_data = modbus_msg->MbData[1];
// Если устройство в режиме Listen Only, отвечаем только на sub-function 0x01
if (MB_DIAG.DeviceMode == MODBUS_LISTEN_ONLY_MODE && sub_function != 0x0001)
@@ -110,8 +87,8 @@ uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg)
{
case 0x0000: // Return Query Data
// Эхо-ответ с теми же данными
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = request_data;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = request_data;
modbus_msg->ByteCnt = 4;
break;
@@ -137,21 +114,21 @@ uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg)
MB_DIAG.Counters.BusCharacterOverrun = 0;
}
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = request_data;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = request_data;
modbus_msg->ByteCnt = 4;
break;
case 0x0002: // Return Diagnostic Register
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = MB_DIAG.DiagnosticRegister;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.DiagnosticRegister;
modbus_msg->ByteCnt = 4;
break;
case 0x0003: // Change ASCII Input Delimiter
// В RTU режиме не поддерживается
modbus_msg->Func_Code |= ERR_VALUES_START;
modbus_msg->Except_Code = ILLEGAL_FUNCTION;
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
modbus_msg->Except_Code = ET_ILLEGAL_FUNCTION;
return 0;
case 0x0004: // Force Listen Only Mode
@@ -161,56 +138,56 @@ uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg)
case 0x000A: // Clear Counters and Diagnostic Register
MB_DiagnosticsInit(); // Полный сброс
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = 0;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = 0;
modbus_msg->ByteCnt = 4;
break;
case 0x000B: // Return Bus Message Count
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = MB_DIAG.Counters.BusMessage;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.BusMessage;
modbus_msg->ByteCnt = 4;
break;
case 0x000C: // Return Bus Communication Error Count
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = MB_DIAG.Counters.BusCommunicationErr;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.BusCommunicationErr;
modbus_msg->ByteCnt = 4;
break;
case 0x000D: // Return Bus Exception Error Count
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = MB_DIAG.Counters.BusExceptionErr;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.BusExceptionErr;
modbus_msg->ByteCnt = 4;
break;
case 0x000E: // Return Server Message Count
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = MB_DIAG.Counters.SlaveMessage;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveMessage;
modbus_msg->ByteCnt = 4;
break;
case 0x000F: // Return Slave No Response Count
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = MB_DIAG.Counters.SlaveNoResponse;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveNoResponse;
modbus_msg->ByteCnt = 4;
break;
case 0x0010: // Return Slave NAK Count
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = MB_DIAG.Counters.SlaveNAK;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveNAK;
modbus_msg->ByteCnt = 4;
break;
case 0x0011: // Return Slave Busy Count
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = MB_DIAG.Counters.SlaveBusy;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveBusy;
modbus_msg->ByteCnt = 4;
break;
case 0x0012: // Return Bus Character Overrun Count
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = MB_DIAG.Counters.BusCharacterOverrun;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.BusCharacterOverrun;
modbus_msg->ByteCnt = 4;
break;
@@ -218,14 +195,14 @@ uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg)
MB_DIAG.Counters.BusCharacterOverrun = 0;
// Сбрасываем флаг переполнения в DiagnosticRegister
MB_DIAG.DiagnosticRegister &= ~(1<<0);
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = 0;
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = 0;
modbus_msg->ByteCnt = 4;
break;
default:
modbus_msg->Func_Code |= ERR_VALUES_START;
modbus_msg->Except_Code = ILLEGAL_FUNCTION;
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
modbus_msg->Except_Code = ET_ILLEGAL_FUNCTION;
return 0;
}
@@ -316,21 +293,4 @@ MB_DeviceModeTypeDef MB_GetDeviceMode(void)
return MB_DIAG.DeviceMode;
}
#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;}
void MB_Diagnostics_BusMessageCnt(void) {}
void MB_Diagnostics_CommunicationErrorCnt(void) {}
void MB_Diagnostics_ExceptionErrorCnt(void) {}
void MB_Diagnostics_CharacterOverrunCnt(void) {}
void MB_Diagnostics_SlaveMessageCnt(void) {}
void MB_Diagnostics_SlaveNoResponseCnt(void) {}
void MB_Diagnostics_SlaveNAKCnt(void) {}
void MB_Diagnostics_SlaveBusyCnt(void) {}
MB_DeviceModeTypeDef MB_GetDeviceMode(void) {return MODBUS_NORMAL_MODE;}
#endif
#endif //MODBUS_ENABLE_DIAGNOSTICS

View File

@@ -1,22 +1,21 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_holdregs.c
* @brief Реализация работы с регистрами хранения Modbus
******************************************************************************
*******************************************************************************
* @details
Модуль для доступа к регистрам внутри программы:
- Функции для доступа к регистрам хранения по глобальным адресам
Модуль обработки команд для holding registers (регистров хранения):
- Чтение множественных регистров (0x03) - копирование данных в буфер ответа
- Запись одиночного регистра (0x06) - прямая запись значения
- Запись множественных регистров (0x10) - пакетная запись из буфера
@section Валидация данных:
@section hvalid Валидация данных:
- Проверка соответствия количества байт и регистров
Валидация адресов через MB_DefineRegistersAddress()
- Валидация адресов через MB_DefineRegistersAddress()
- Обработка исключений при некорректных запросах
@section Echo-ответы:
При успешной записи формируется echo-ответ с теми же данными,
что были в запросе (для функций 0x05, 0x06, 0x0F, 0x10).
******************************************************************************/
#include "modbus_inputregs.h"
@@ -24,6 +23,66 @@
#ifdef MODBUS_ENABLE_HOLDINGS
/**
* @brief Записать регистр хранения по глобальному адресу.
* @param Addr Адрес регистра.
* @param WriteVal Число для записи.
* @return ExceptionCode Код исключения если регистра по адресу не существует, и ET_NO_ERRORS если все ок.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
MB_ExceptionTypeDef MB_Holding_Write_Global(uint16_t Addr, uint16_t WriteVal)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception = ET_NO_ERRORS;
uint16_t *pHoldRegs;
//------------WRITE COIL-------------
Exception = MB_DefineRegistersAddress(&pHoldRegs, Addr, 1, RegisterType_Holding, 1);
if(Exception == ET_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, 0);
if(Exception) // if exception is not given to func fill it
*Exception = Exception_tmp;
if(Exception_tmp == ET_NO_ERRORS)
{
return *(pHoldRegs);
}
else
{
return 0;
}
}
/**
* @brief Обработать функцию Read Holding Registers (03 - 0x03).
* @param modbus_msg Указатель на структуру собщения modbus.
@@ -34,9 +93,9 @@ uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg)
{
//---------CHECK FOR ERRORS----------
// get origin address for data
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)
uint16_t *pHoldRegs;
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Holding, 0); // определение адреса регистров
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
@@ -47,8 +106,8 @@ uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg)
int i;
for (i = 0; i<modbus_msg->Qnt; i++)
{
modbus_msg->DATA[i] = *(pHoldRegs++);
}
modbus_msg->MbData[i] = *(pHoldRegs++);
}
return 1;
}
@@ -62,8 +121,8 @@ uint8_t MB_Process_Write_Single_Reg(RS_MsgTypeDef *modbus_msg)
{
// get origin address for data
uint16_t *pHoldRegs;
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, 1, RegisterType_Holding); // определение адреса регистров
if(modbus_msg->Except_Code != NO_ERRORS)
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, 1, RegisterType_Holding, 1); // определение адреса регистров
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//-----------WRITTING REG------------
@@ -87,23 +146,16 @@ uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg)
}
// get origin address for data
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)
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Holding, 1); // определение адреса регистров
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//-----------WRITTING REGS-----------
for (int i = 0; i<modbus_msg->Qnt; i++)
{
*(pHoldRegs++) = modbus_msg->DATA[i];
*(pHoldRegs++) = modbus_msg->MbData[i];
}
return 1;
}
#else //MODBUS_ENABLE_HOLDINGS
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
#endif //MODBUS_ENABLE_HOLDINGS

View File

@@ -1,19 +1,83 @@
/**
******************************************************************************
*******************************************************************************
* @file modbus_inputregs.c
* @brief Реализация работы с входными регистрами Modbus
******************************************************************************
*******************************************************************************
* @details
Модуль для доступа к регистрам внутри программы:
- Функции для доступа к входным регистрам по глобальным адресам
Модуль обработки команды чтения input registers (0x04):
- Чтение множественных входных регистров
Копирование данных из структур устройства в буфер ответа
- Поддержка знаковых и беззнаковых значений
@section ivalid Валидация данных:
- Проверка соответствия количества байт и регистров
- Валидация адресов через MB_DefineRegistersAddress()
- Обработка исключений при некорректных запросах
******************************************************************************/
#include "modbus_inputregs.h"
#ifdef MODBUS_ENABLE_INPUTS
/**
* @brief Записать входной регистр по глобальному адресу.
* @param Addr Адрес регистра.
* @param WriteVal Число для записи.
* @return ExceptionCode Код исключения если регистра по адресу не существует, и ET_NO_ERRORS если все ок.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
MB_ExceptionTypeDef MB_Input_Write_Global(uint16_t Addr, uint16_t WriteVal)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception = ET_NO_ERRORS;
uint16_t *pInRegs;
//------------WRITE COIL-------------
Exception = MB_DefineRegistersAddress(&pInRegs, Addr, 1, RegisterType_Input, 1);
if(Exception == ET_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, 0);
if(Exception) // if exception is not given to func fill it
*Exception = Exception_tmp;
if(Exception_tmp == ET_NO_ERRORS)
{
return *(pInRegs);
}
else
{
return 0;
}
}
/**
* @brief Обработать функцию Read Input Registers (04 - 0x04).
* @param modbus_msg Указатель на структуру собщения modbus.
@@ -25,8 +89,8 @@ uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg)
//---------CHECK FOR ERRORS----------
// get origin address for data
uint16_t *pInRegs;
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pInRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Input); // определение адреса регистров
if(modbus_msg->Except_Code != NO_ERRORS)
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pInRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Input, 0); // определение адреса регистров
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
@@ -38,15 +102,11 @@ uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg)
for (i = 0; i<modbus_msg->Qnt; i++)
{
if(*((int16_t *)pInRegs) > 0)
modbus_msg->DATA[i] = (*pInRegs++);
modbus_msg->MbData[i] = (*pInRegs++);
else
modbus_msg->DATA[i] = (*pInRegs++);
modbus_msg->MbData[i] = (*pInRegs++);
}
return 1;
}
#else //MODBUS_ENABLE_INPUTS
uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) {return 0;}
#endif
#endif //MODBUS_ENABLE_INPUTS

View File

@@ -1,12 +1,22 @@
/**
**************************************************************************
* @file modbus_master.c
* @brief Модуль для реализации мастера MODBUS.
**************************************************************************
*******************************************************************************
* @file modbus_master.c
* @brief Модуль для реализации мастера MODBUS.
*******************************************************************************
* @details
Файл содержит реализацию функций для работы Modbus в режиме мастера.
@section Функции и макросы
@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() — Парс сообщения в режиме мастера
@@ -16,8 +26,35 @@
#ifdef MODBUS_ENABLE_MASTER
//-------------------------------------------------------------------
//-----------------------------FOR USER------------------------------
/**
* @brief Получить значение регистра из ответа по его адресу
* @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, &reg_arr[i]))
{
read_cnt++;
}
i++;
}
return read_cnt;
}
/**
* @brief Получить значение регистра в ответе по его адресу
* @param modbus_msg Указатель на структуру сообщения
* @param reg_addr Адрес регистра, значение которого нужно получить
* @param reg_value Указатель для значения регистра
@@ -29,9 +66,9 @@ int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint1
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))
if((modbus_msg->FuncCode != FC_R_DISC_IN) &&
(modbus_msg->FuncCode != FC_R_HOLD_REGS) &&
(modbus_msg->FuncCode != FC_R_IN_REGS))
{
return 0;
}
@@ -48,11 +85,237 @@ int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint1
return 0;
// Получаем значение регистра
*reg_value = modbus_msg->DATA[reg_index];
*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.
@@ -67,24 +330,24 @@ static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *
// Master mode - calculating response size from slave
if (modbus_msg->Func_Code & ERR_VALUES_START)
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->Func_Code == MB_R_DIAGNOSTIC)
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->Func_Code == MB_R_DEVICE_INFO)
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->Func_Code & ~ERR_VALUES_START)
switch (modbus_msg->FuncCode & ~FC_ERR_VALUES_START)
{
case 0x01: // Read Coils
case 0x02: // Read Discrete Inputs
@@ -111,7 +374,7 @@ static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *
}
}
mb_func_size = RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
mb_func_size = RS_RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
return mb_func_size;
}
@@ -129,28 +392,33 @@ RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgType
//------INFO ABOUT DATA/MESSAGE------
//-----------[first bytes]-----------
// set ID of slave device
modbus_uart_buff[ind++] = modbus_msg->MbAddr;
modbus_uart_buff[ind++] = modbus_msg->DeviceAddr;
// set function code
modbus_uart_buff[ind++] = modbus_msg->Func_Code;
modbus_uart_buff[ind++] = modbus_msg->FuncCode;
if(modbus_msg->Func_Code < ERR_VALUES_START) // if no error occur
if(modbus_msg->FuncCode < FC_ERR_VALUES_START) // if no error occur
{
// fill modbus header
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // device identifications request
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;
}
else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
#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->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
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
@@ -162,12 +430,12 @@ RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgType
modbus_uart_buff[ind++] = modbus_msg->Qnt & 0xFF;
// for write multiple functions
if((modbus_msg->Func_Code == 0x0F) || (modbus_msg->Func_Code == 0x10))
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->DATA;
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];
@@ -182,7 +450,7 @@ RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgType
//---------------CRC----------------
//---------[last 2 bytes]----------
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
modbus_msg->MB_CRC = CRC_VALUE;
modbus_msg->MbCRC = CRC_VALUE;
modbus_uart_buff[ind++] = CRC_VALUE & 0xFF;
modbus_uart_buff[ind++] = CRC_VALUE >> 8;
@@ -204,18 +472,20 @@ RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDe
int expected_size = 0;
// get ID of slave device
modbus_msg->MbAddr = modbus_uart_buff[ind++];
modbus_msg->DeviceAddr = modbus_uart_buff[ind++];
// get function code (check if error response)
modbus_msg->Func_Code = modbus_uart_buff[ind++];
modbus_msg->FuncCode = modbus_uart_buff[ind++];
if(modbus_msg->Func_Code & ERR_VALUES_START) // error response
if(modbus_msg->FuncCode & FC_ERR_VALUES_START) // error response
{
modbus_msg->Except_Code = modbus_uart_buff[ind++];
}
else if(modbus_msg->Func_Code < ERR_VALUES_START) // normal response
else if(modbus_msg->FuncCode < FC_ERR_VALUES_START) // normal response
{
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // device identifications 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++];
@@ -227,7 +497,7 @@ RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDe
modbus_msg->ByteCnt = 0;
// Парсинг объектов идентификации устройства
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA;
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->MbData;
int data_index = 0;
for(int obj = 0; obj < modbus_msg->DevId.NumbOfObj; obj++)
@@ -249,24 +519,27 @@ RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDe
modbus_msg->ByteCnt += (2 + object_length); // ID + длина + данные
}
}
else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
#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->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++];
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->Func_Code == 0x01) || (modbus_msg->Func_Code == 0x02) ||
(modbus_msg->Func_Code == 0x03) || (modbus_msg->Func_Code == 0x04))
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->DATA;
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
@@ -276,8 +549,8 @@ RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDe
}
}
// 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))
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++];
@@ -291,10 +564,10 @@ RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDe
//---------------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;
modbus_msg->MbCRC = modbus_uart_buff[ind++];
modbus_msg->MbCRC |= modbus_uart_buff[ind++] << 8;
if(modbus_msg->MB_CRC != CRC_VALUE)
if(modbus_msg->MbCRC != CRC_VALUE)
{
TrackerCnt_Err(hmodbus->rs_err);
return RS_PARSE_MSG_ERR;
@@ -310,58 +583,58 @@ RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDe
/** @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};
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, MB_R_DISC_IN, {0}, start_addr, quantity, 0, {0}, 0, 0};
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, MB_R_HOLD_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0};
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, MB_R_IN_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0};
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, MB_W_COIL, {0}, coil_addr, (value ? 0xFF00 : 0x0000), 0, {0}, 0, 0};
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, MB_W_HOLD_REG, {0}, reg_addr, value, 0, {0}, 0, 0};
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, MB_W_COILS, {0}, start_addr, quantity, 0, {0}, 0, 0};
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 DATA array
// Copy coil data to message MbData array
for(int i = 0; i < byte_count; i++) {
if(i < DATA_SIZE) {
msg.DATA[i] = coils_data[i];
if(i < MbData_size) {
msg.MbData[i] = coils_data[i];
}
}
@@ -371,13 +644,13 @@ RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_COILS(uint8_t slave_addr, uint16_t start
/** @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};
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 DATA array
for(int i = 0; i < quantity && i < DATA_SIZE; i++) {
msg.DATA[i] = regs_data[i];
// 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;
@@ -386,7 +659,7 @@ RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_REGS(uint8_t slave_addr, uint16_t start_
//---------ДИАГНОСТИЧЕСКИЕ ДАННЫЕ-----------
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};
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)
@@ -457,71 +730,26 @@ 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 msg = {slave_addr, MB_R_DEVICE_INFO, {0x0E, 0x01, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
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, MB_R_DEVICE_INFO, {0x0E, 0x02, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
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, MB_R_DEVICE_INFO, {0x0E, 0x03, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
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, MB_R_DEVICE_INFO, {0x0E, 0x04, object_id, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
RS_MsgTypeDef msg = {slave_addr, FC_R_DEVICE_ID, {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
#endif //MODBUS_ENABLE_MASTER

145
Src/modbus_oscil.c Normal file
View File

@@ -0,0 +1,145 @@
/**
*******************************************************************************
* @file modbus_oscil.c
* @brief Модуль для реализации осциллографа через MODBUS.
*******************************************************************************
* @details
* Файл содержит реализацию функций для работы с буфером осциллографа.
*
* @section oscil_functions Функции
* - MB_Oscil_Init() — Инициализация структуры осциллографа
* - MB_Oscil_Add() — Добавление данных в буфер
* - MB_Oscil_UpdateTail() — Обновление указателя Tail
******************************************************************************/
#include "modbus_oscil.h"
#ifdef MODBUS_ENABLE_OSCIL
/**
* @brief Инициализация структуры осциллографа
* @param oscil Указатель на структуру осциллографа
* @param BufferSize Размер буфера в Modbus регистрах (max 125)
* @param NumbOfChannels Количество каналов (должно быть четным)
* @param SampleTime Период дискретизации
* @return HAL Status
*/
HAL_StatusTypeDef MB_Oscil_Init(MB_OscilTypeDef *oscil, uint8_t BufferSize, uint8_t NumbOfChannels, uint16_t SampleTime)
{
if(!oscil || (BufferSize >= MbData_size) || (NumbOfChannels > 15) || !SampleTime)
return HAL_ERROR;
if(NumbOfChannels % 2 != 0)
return HAL_ERROR;
oscil->Config.BufferSize = BufferSize;
oscil->Config.NumbOfChannels = NumbOfChannels;
oscil->Config.SampleTime = SampleTime;
oscil->Config.LastTick = 0;
oscil->Preamble.Head = 0;
oscil->Preamble.Tail = 0;
oscil->Config.Overrun = 0;
return HAL_OK;
}
/**
* @brief Инициализация структуры осциллографа
* @param oscil Указатель на структуру осциллографа
* @param BufferSize Размер буфера в Modbus регистрах (max 125)
* @param NumbOfChannels Количество каналов (должно быть четным)
* @param SampleTime Период дискретизации
* @return HAL Status
*/
HAL_StatusTypeDef MB_Oscil_Set_SampleTime(MB_OscilTypeDef *oscil, uint16_t SampleTime)
{
if(!oscil || !SampleTime)
return HAL_ERROR;
oscil->Config.SampleTime = SampleTime;
return HAL_OK;
}
/**
* @brief Добавление данных всех каналов в буфер
* @param oscil Указатель на структуру осциллографа
* @param Data Массив с данными каналов (длина = NumbOfChannels)
* @return HAL Status
*/
HAL_StatusTypeDef MB_Oscil_Add(MB_OscilTypeDef *oscil, uint8_t *Data)
{
if(!oscil || !Data)
return HAL_ERROR;
if(local_time() - oscil->Config.LastTick < oscil->Config.SampleTime)
return HAL_BUSY;
oscil->Config.LastTick = local_time();
uint8_t head_max = oscil->Config.BufferSize * 2;
uint8_t start_pos = oscil->Preamble.Head;
uint8_t channels = oscil->Config.NumbOfChannels;
uint8_t tail = oscil->Preamble.Tail;
// Пишем данные
for(uint8_t i = 0; i < channels; i++)
{
uint8_t write_pos = (start_pos + i) % head_max;
oscil->Data[write_pos] = Data[i];
}
// Новое положение Head
uint8_t new_head = (start_pos + channels) % head_max;
// Проверяем, есть ли непрочитанные данные (Tail != Head)
// if(tail != start_pos) {
// Если есть непрочитанные данные, проверяем не перетерли ли мы их
if(start_pos < new_head) {
// Линейный случай
if(tail > start_pos && tail <= new_head) {
oscil->Preamble.Tail = (new_head + 1) % head_max;
oscil->Config.Overrun = 1;
}
} else if(start_pos > new_head) {
// Кольцевой случай (было переполнение)
if(tail > start_pos || tail <= new_head) {
oscil->Preamble.Tail = (new_head + 1) % head_max;
oscil->Config.Overrun = 1;
}
}
// }
oscil->Preamble.Head = new_head;
return HAL_OK;
}
/**
* @brief Обновление указателя Tail
* @param oscil Указатель на структуру осциллографа
* @return HAL Status
*
* @details Вызывается после того, как данные были прочитаны через Modbus.
* Tail устанавливается на следующий байт за Head.
*/
HAL_StatusTypeDef MB_Oscil_UpdateTail(MB_OscilTypeDef *oscil)
{
if(!oscil)
return HAL_ERROR;
// Сдвигаем Tail на следующий байт за Head
uint8_t head_max = oscil->Config.BufferSize * 2;
oscil->Preamble.Tail = oscil->Preamble.Head;
if(oscil->Preamble.Tail >= head_max)
oscil->Preamble.Tail = 0;
oscil->Config.Overrun = 0;
return HAL_OK;
}
#endif //MODBUS_ENABLE_OSCIL

View File

@@ -1,12 +1,14 @@
/**
**************************************************************************
* @file modbus_slave.c
* @brief Модуль для реализации слейв MODBUS.
**************************************************************************
*******************************************************************************
* @file modbus_slave.c
* @brief Модуль для реализации слейв MODBUS.
*******************************************************************************
* @details
Файл содержит реализацию функций для работы Modbus в режиме слейва.
@section Функции и макросы
@section slave Функции и макросы
- MB_Slave_ResponseLater() — Выставить флаг на ответ вне прерывания
- MB_Slave_SendResponse() — Ответить на запрос
- MB_Slave_Response() — Ответ на запрос
- MB_Slave_Collect_Message() — Сбор сообщения в режиме слейва.
@@ -15,6 +17,66 @@
#include "modbus.h"
#ifdef MODBUS_ENABLE_SLAVE
/**
* @brief Ответить позже, не в прерывании.
* @param hmodbus Указатель на хендлер RS.
* @param ResponseCode Код запроса на который надо ответить.
* @return HAL Status.
* @details Отменяет ответ в прерывании на запрос.
Используется вместе с @ref MB_Slave_SendResponse
*/
HAL_StatusTypeDef MB_Slave_ResponseLater(RS_HandleTypeDef *hmodbus, uint8_t ResponseCode)
{
hmodbus->f.DeferredResponse = ResponseCode;
return HAL_OK;
}
/**
* @brief Ответить на запрос.
* @param hmodbus Указатель на хендлер RS.
* @param ResponseCode Код запроса на который надо ответить.
* @param error Тип ошибки или её отсутствия в ответе.
* @return HAL Status.
* @details Ответить на запрос вне прерывания.
Используется вместе с @ref MB_Slave_ResponseLater
*/
HAL_StatusTypeDef MB_Slave_SendResponse(RS_HandleTypeDef *hmodbus, uint8_t ResponseCode, MB_ExceptionTypeDef error)
{
RS_StatusTypeDef MB_RES = 0;
if(hmodbus->f.DeferredResponse == ResponseCode)
{
hmodbus->f.DeferredResponse = 0;
switch(error)
{
case ET_NO_ERRORS:
break;
case ET_ACKNOWLEDGE:
MB_Diagnostics_SlaveNAKCnt();
break;
case ET_SLAVE_DEVICE_BUSY:
MB_Diagnostics_SlaveBusyCnt();
break;
default:
break;
}
if(error != ET_NO_ERRORS)
{
MB_Diagnostics_ExceptionErrorCnt();
TrackerCnt_Warn(hmodbus->rs_err);
hmodbus->pMessagePtr->FuncCode |= FC_ERR_VALUES_START;
hmodbus->pMessagePtr->Except_Code = error;
}
hmodbus->RS_STATUS = RS_Handle_Transmit_Start(hmodbus, hmodbus->pMessagePtr);
}
return HAL_OK;
}
/**
* @brief Ответ на сообщение в режиме слейва.
* @param hmodbus Указатель на хендлер RS.
@@ -29,7 +91,7 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod
RS_Reset_TX_Flags(hmodbus); // reset flag for correct transmit
MB_Diagnostics_BusMessageCnt();
if(hmodbus->ID == 0 || modbus_msg->MbAddr == 0)
if(hmodbus->ID == 0 || modbus_msg->DeviceAddr == 0)
{
MB_Diagnostics_SlaveNoResponseCnt(); // <-- Устройство не отвечает на широковещательные сообщения
hmodbus->RS_STATUS = RS_SKIP;
@@ -37,26 +99,31 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod
}
MB_Diagnostics_SlaveMessageCnt();
if(modbus_msg->Func_Code < ERR_VALUES_START)// if no errors after parsing
if(modbus_msg->FuncCode < FC_ERR_VALUES_START)// if no errors after parsing
{
switch (modbus_msg->Func_Code)
switch (modbus_msg->FuncCode)
{
// Read Coils
case MB_R_COILS:
case FC_R_COILS:
hmodbus->f.MessageHandled = MB_Process_Read_Coils(hmodbus->pMessagePtr);
break;
// Read Hodling Registers
case MB_R_HOLD_REGS:
case FC_R_HOLD_REGS:
hmodbus->f.MessageHandled = MB_Process_Read_Hold_Regs(hmodbus->pMessagePtr);
#ifdef MODBUS_ENABLE_OSCIL
int is_oscil_data_register = ((hmodbus->pMessagePtr->Addr >= R_HOLDING_OSCIL_ADDR+4) && (hmodbus->pMessagePtr->Addr + hmodbus->pMessagePtr->Qnt <= (R_HOLDING_OSCIL_ADDR+5+(sizeof(MB_OscilTypeDef)/2))));
if(is_oscil_data_register)
MB_Oscil_UpdateTail(&MB_INTERNAL.oscil);
#endif
break;
case MB_R_IN_REGS:
case FC_R_IN_REGS:
hmodbus->f.MessageHandled = MB_Process_Read_Input_Regs(hmodbus->pMessagePtr);
break;
// Write Single Coils
case MB_W_COIL:
case FC_W_COIL:
hmodbus->f.MessageHandled = MB_Process_Write_Single_Coil(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
@@ -66,7 +133,7 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod
}
break;
case MB_W_HOLD_REG:
case FC_W_HOLD_REG:
hmodbus->f.MessageHandled = MB_Process_Write_Single_Reg(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
@@ -77,7 +144,7 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod
break;
// Write Multiple Coils
case MB_W_COILS:
case FC_W_COILS:
hmodbus->f.MessageHandled = MB_Process_Write_Miltuple_Coils(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
@@ -88,7 +155,7 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod
break;
// Write Multiple Registers
case MB_W_HOLD_REGS:
case FC_W_HOLD_REGS:
hmodbus->f.MessageHandled = MB_Process_Write_Miltuple_Regs(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
@@ -98,20 +165,19 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod
}
break;
case MB_R_DEVICE_INFO:
case FC_R_DEVICE_ID:
hmodbus->f.MessageHandled = MB_Process_Read_Device_Identifications(hmodbus->pMessagePtr);
break;
// Добавить в switch-case после других case:
case MB_R_DIAGNOSTIC:
case FC_R_DIAGNOSTICS:
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)
@@ -126,7 +192,7 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod
{
MB_Diagnostics_ExceptionErrorCnt();
TrackerCnt_Warn(hmodbus->rs_err);
modbus_msg->Func_Code |= ERR_VALUES_START;
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
}
else
{
@@ -147,8 +213,8 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod
}
else
{
RS_Handle_Receive_Start(hmodbus, modbus_msg);
hmodbus->f.DeferredResponse = 0;
// RS_Handle_Receive_Start(hmodbus, modbus_msg);
// hmodbus->f.DeferredResponse = 0;
}
hmodbus->RS_STATUS = MB_RES;
@@ -168,22 +234,34 @@ RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeD
{
int ind = 0; // ind for modbus-uart buffer
if(hmodbus->f.EchoResponse && hmodbus->f.MessageHandled) // if echo response need
if(hmodbus->f.EchoResponse && (modbus_msg->Except_Code == ET_NO_ERRORS)) // if echo response need
ind = hmodbus->RS_Message_Size;
else
{
//------INFO ABOUT DATA/MESSAGE------
#ifdef MODBUS_PROTOCOL_TCP
modbus_uart_buff[ind++] = modbus_msg->TransactionID >> 8;
modbus_uart_buff[ind++] = modbus_msg->TransactionID& 0xFF;
modbus_uart_buff[ind++] = modbus_msg->ProtocolID >> 8;
modbus_uart_buff[ind++] = modbus_msg->ProtocolID& 0xFF;
modbus_uart_buff[ind++] = modbus_msg->PDULength >> 8;
modbus_uart_buff[ind++] = modbus_msg->PDULength& 0xFF;
#endif
//-----------[first bytes]-----------
// set ID of message/user
modbus_uart_buff[ind++] = modbus_msg->MbAddr;
modbus_uart_buff[ind++] = modbus_msg->DeviceAddr;
// set dat or err response
modbus_uart_buff[ind++] = modbus_msg->Func_Code;
modbus_uart_buff[ind++] = modbus_msg->FuncCode;
if (modbus_msg->Func_Code < ERR_VALUES_START) // if no error occur
if (modbus_msg->FuncCode < FC_ERR_VALUES_START) // if no error occur
{
// fill modbus header
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // devide identifications header
if(0) {}
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
else if(modbus_msg->FuncCode == FC_R_DEVICE_ID) // devide identifications header
{
modbus_uart_buff[ind++] = modbus_msg->DevId.MEI_Type;
modbus_uart_buff[ind++] = modbus_msg->DevId.ReadDevId;
@@ -192,7 +270,7 @@ RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeD
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
if (modbus_msg->ByteCnt > MbData_size*2) // if ByteCnt less than MbData_size
{
TrackerCnt_Err(hmodbus->rs_err);
return RS_COLLECT_MSG_ERR;
@@ -201,7 +279,7 @@ RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeD
//---------------DATA----------------
//-----------[data bytes]------------
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA;
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->MbData;
for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data
{ // set data
modbus_uart_buff[ind++] = *tmp_data_addr;
@@ -209,18 +287,21 @@ RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeD
}
}
else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
#ifdef MODBUS_ENABLE_DIAGNOSTICS
else if(modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
{
// 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
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 // modbus data header
{
// set size of received data
if (modbus_msg->ByteCnt <= DATA_SIZE*2) // if ByteCnt less than DATA_SIZE
if (modbus_msg->ByteCnt <= MbData_size*2) // if ByteCnt less than MbData_size
modbus_uart_buff[ind++] = modbus_msg->ByteCnt;
else // otherwise return data_size err
{
@@ -230,7 +311,7 @@ RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeD
//---------------DATA----------------
//-----------[data bytes]------------
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA;
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->MbData;
for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data
{ // set data
if (i%2 == 0) // HI byte
@@ -255,12 +336,14 @@ RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeD
//---------------CRC----------------
//---------[last 16 bytes]----------
#ifndef MODBUS_PROTOCOL_TCP
// 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_msg->MbCRC = CRC_VALUE;
modbus_uart_buff[ind++] = CRC_VALUE;
modbus_uart_buff[ind++] = CRC_VALUE >> 8;
#endif
hmodbus->RS_Message_Size = ind;
@@ -279,15 +362,15 @@ static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *
RS_StatusTypeDef MB_RES = 0;
int mb_func_size = 0;
if (modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
if (modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
{
mb_func_size = 1;
}
else if(modbus_msg->Func_Code == MB_R_DEVICE_INFO)
else if(modbus_msg->FuncCode == FC_R_DEVICE_ID)
{
mb_func_size = 0;
}
else if ((modbus_msg->Func_Code & ~ERR_VALUES_START) < 0x0F)
else if ((modbus_msg->FuncCode & ~FC_ERR_VALUES_START) < 0x0F)
{
mb_func_size = 1;
}
@@ -297,7 +380,7 @@ static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *
}
mb_func_size = RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
mb_func_size = RS_RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
return mb_func_size;
}
@@ -317,46 +400,61 @@ RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef
hmodbus->f.RX_Continue = 0;
int expected_size = 0;
//-----INFO ABOUT DATA/MESSAGE-------
#ifdef MODBUS_PROTOCOL_TCP
modbus_msg->TransactionID =modbus_uart_buff[ind++]<<8;
modbus_msg->TransactionID |=modbus_uart_buff[ind++];
modbus_msg->ProtocolID =modbus_uart_buff[ind++]<<8;
modbus_msg->ProtocolID |=modbus_uart_buff[ind++];
modbus_msg->PDULength =modbus_uart_buff[ind++]<<8;
modbus_msg->PDULength |=modbus_uart_buff[ind++];
#endif
//-----------[first bits]------------
// get ID of message/user
if(modbus_uart_buff[ind] != hmodbus->ID)
{
modbus_msg->MbAddr = 0;
modbus_msg->DeviceAddr = 0;
ind++;
}
else
{
modbus_msg->MbAddr = modbus_uart_buff[ind++];
modbus_msg->DeviceAddr = modbus_uart_buff[ind++];
}
// get func code
modbus_msg->Func_Code = modbus_uart_buff[ind++];
if(modbus_msg->Func_Code & ERR_VALUES_START) // явная херня
modbus_msg->FuncCode = modbus_uart_buff[ind++];
if(modbus_msg->FuncCode & FC_ERR_VALUES_START) // явная херня
{
MB_Diagnostics_SlaveNAKCnt();
modbus_msg->MbAddr = 0;
modbus_msg->DeviceAddr = 0;
return RS_SKIP;
}
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // if it device identifications request
if(0) {}
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
else if(modbus_msg->FuncCode == FC_R_DEVICE_ID) // 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)
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
#ifdef MODBUS_ENABLE_DIAGNOSTICS
else if(modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
{
// Diagnostics: читаем 4 байта в DATA[0] и DATA[1]
// Diagnostics: читаем 4 байта в MbData[0] и MbData[1]
// Sub-function
modbus_msg->DATA[0] = modbus_uart_buff[ind++] << 8;
modbus_msg->DATA[0] |= modbus_uart_buff[ind++];
modbus_msg->MbData[0] = modbus_uart_buff[ind++] << 8;
modbus_msg->MbData[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->MbData[1] = modbus_uart_buff[ind++] << 8;
modbus_msg->MbData[1] |= modbus_uart_buff[ind++];
modbus_msg->Addr = 0; // не использует Addr
modbus_msg->Qnt = 0; // не использует Qnt
}
#endif //MODBUS_ENABLE_DIAGNOSTICS
else // if its classic modbus request
{
// get address from CMD
@@ -368,7 +466,7 @@ RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef
modbus_msg->Qnt |= modbus_uart_buff[ind++];
}
if((hmodbus->pMessagePtr->Func_Code == 0x0F) || (hmodbus->pMessagePtr->Func_Code == 0x10))
if((hmodbus->pMessagePtr->FuncCode == 0x0F) || (hmodbus->pMessagePtr->FuncCode == 0x10))
hmodbus->pMessagePtr->ByteCnt = modbus_uart_buff[ind++];
else
hmodbus->pMessagePtr->ByteCnt = 0;
@@ -392,14 +490,14 @@ RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef
if (modbus_msg->ByteCnt != 0)
{
//check that data size is correct
if (modbus_msg->ByteCnt > DATA_SIZE*2)
if (modbus_msg->ByteCnt > MbData_size*2)
{
TrackerCnt_Err(hmodbus->rs_err);
modbus_msg->Func_Code |= ERR_VALUES_START;
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
MB_Diagnostics_CommunicationErrorCnt();
return RS_PARSE_MSG_ERR;
}
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA;
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->MbData;
for(int i = 0; i < modbus_msg->ByteCnt; i++)
{ // set data
if (i%2 == 0)
@@ -414,24 +512,22 @@ RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef
//---------------CRC----------------
//----------[last 16 bits]----------
#ifndef MODBUS_PROTOCOL_TCP
// 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;
modbus_msg->MbCRC = modbus_uart_buff[ind++];
modbus_msg->MbCRC |= modbus_uart_buff[ind++] << 8;
// compare crc
if (modbus_msg->MB_CRC != CRC_VALUE)
if (modbus_msg->MbCRC != CRC_VALUE)
{
MB_Diagnostics_CommunicationErrorCnt();
TrackerCnt_Err(hmodbus->rs_err);
modbus_msg->Func_Code |= ERR_VALUES_START;
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
}
#endif
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
#endif //MODBUS_ENABLE_SLAVE

View File

@@ -1,8 +1,8 @@
/**
******************************************************************************
*******************************************************************************
* @file rs_message.c
* @brief Реализация протоколов обмена по RS/UART
******************************************************************************
*******************************************************************************
* @details
Модуль реализует асинхронный обмен сообщениями через UART с использованием:
- Прерываний по приему/передаче
@@ -10,23 +10,21 @@
- Таймаутов через таймер
- Двухстадийного приема (заголовок + данные)
@section Архитектура:
@section arch Архитектура:
В режиме слейв:
- Инициализация приема с сообщения с максимальным размером MSG_SIZE_MAX
- Инициализация приема с сообщения с максимальным размером RS_MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного сообщения
В режиме мастер (пока не реализовано):
- Отправка запроса и переход в режим приема сообщения с максимальным размером MSG_SIZE_MAX
- Отправка запроса и переход в режим приема сообщения с максимальным размером RS_MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного ответа
@section Необходимые обработчики:
@section ithandler Необходимые обработчики:
- RS_UART_Handler() в UARTx_IRQHandler вместо HAL_UART_IRQHandler()
- RS_TIM_Handler() в TIMx_IRQHandler вместо HAL_TIM_IRQHandler()
******************************************************************************/
#include "rs_message.h"
#include "modbus_diag.h"
uint8_t RS_Buffer[MSG_SIZE_MAX]; // uart buffer
extern void RS_UART_Init(void);
extern void RS_UART_DeInit(UART_HandleTypeDef *huart);
extern void RS_TIM_Init(void);
@@ -52,7 +50,8 @@ RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
//-----------INITIALIZE RECEIVE-------------
// if all OK: start receiving
RS_EnableReceive();
if(hRS->pSetDirection)
hRS->pSetDirection(0);
RS_Set_Busy(hRS); // set RS busy
RS_Set_RX_Flags(hRS); // initialize flags for receive
hRS->pMessagePtr = RS_msg; // set pointer to message structire for filling it from UARTHandler fucntions
@@ -61,7 +60,7 @@ RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
// start receiving
__HAL_UART_ENABLE_IT(hRS->huart, UART_IT_IDLE);
uart_res = HAL_UART_Receive_IT(hRS->huart, &hRS->pBufferPtr[hRS->RS_Message_Size], MSG_SIZE_MAX); // receive until ByteCnt+1 byte,
uart_res = HAL_UART_Receive_IT(hRS->huart, &hRS->BufferPtr[hRS->RS_Message_Size], RS_MSG_SIZE_MAX); // receive until ByteCnt+1 byte,
// then in Callback restart receive for rest bytes
// if receive isnt started - abort RS
@@ -101,7 +100,7 @@ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
//------------COLLECT MESSAGE---------------
RS_RES = RS_Collect_Message(hRS, RS_msg, hRS->pBufferPtr);
RS_RES = RS_Collect_Message(hRS, RS_msg, hRS->BufferPtr);
if (RS_RES != RS_OK) // if message isnt collect - stop RS and return error in RS_RES
{// need collect message status, so doesnt write abort to RS_RES
RS_Abort(hRS, ABORT_RS);
@@ -111,7 +110,8 @@ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
//----------INITIALIZE TRANSMIT-------------
RS_EnableTransmit();
if(hRS->pSetDirection)
hRS->pSetDirection(1);
RS_Set_Busy(hRS); // set RS busy
RS_Set_TX_Flags(hRS); // initialize flags for transmit IT
@@ -123,7 +123,7 @@ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
return RS_ERR;
}
// if all OK: start transmitting
uart_res = HAL_UART_Transmit_IT(hRS->huart, hRS->pBufferPtr, hRS->RS_Message_Size);
uart_res = HAL_UART_Transmit_IT(hRS->huart, hRS->BufferPtr, hRS->RS_Message_Size);
// if transmit isnt started - abort RS
if(uart_res != HAL_OK)
{
@@ -153,7 +153,7 @@ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
* @return RS_RES Статус о состоянии RS после инициализации.
* @details Инициализация перефирии и структуры для приема-передачи по RS.
*/
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr)
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, void (*pSetDirection)(int Tx))
{
// check that hRS is defined
if (hRS == NULL)
@@ -166,13 +166,7 @@ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_H
hRS->htim = htim;
// check that buffer is defined
if (hRS->pBufferPtr == NULL)
{
hRS->pBufferPtr = RS_Buffer; // if no - set default
}
else
hRS->pBufferPtr = pRS_BufferPtr; // if yes - set by user
hRS->pSetDirection = pSetDirection;
return RS_OK;
}
@@ -323,6 +317,7 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
{
return;
}
RS_UART_Handler_ENTER();
//-------------CHECK IDLE FLAG FIRST-------------
/* Проверяем флаг IDLE в первую очередь - это гарантирует обработку только после idle */
if(__HAL_UART_GET_FLAG(hRS->huart, UART_FLAG_IDLE) && __HAL_UART_GET_IT_SOURCE(hRS->huart, UART_IT_IDLE))
@@ -348,22 +343,24 @@ 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->BufferPtr);
// Если сообещине принято корректно
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
{
RS_Set_Free(hRS); // освобожднаем RS
if(hRS->pCallback)
{
hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
RS_Set_Free(hRS); // освобожднаем RS
}
}
}
@@ -417,6 +414,7 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
// later, maybe, will be added specific handlers for err
}
RS_UART_Handler_EXIT();
}
@@ -432,20 +430,30 @@ void RS_TIM_Handler(RS_HandleTypeDef *hRS)
{
return;
}
RS_TIM_Handler_ENTER();
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->DeviceAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
TrackerCnt_Err(hRS->rs_err);
if(hRS->sRS_Mode == RS_MASTER_REQUEST) {
// Мастер: таймаут ответа -> освобождаем для нового запроса
if(hRS->sRS_Mode >= RS_MASTER_MODE_START)
{ // Мастер: коллбек и освобождение для нового запроса
RS_Set_Free(hRS);
if(hRS->pCallback)
{
hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
}
} else {
// Слейв: перезапускаем прием
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
}
RS_TIM_Handler_EXIT();
}
/**

View File

@@ -5,13 +5,16 @@
******************************************************************************
@addtogroup MODBUS_CONFIGS Modbus configs
@ingroup MODBUS
@brief Конфигурация библиотеки
@{
******************************************************************************
* @details
Файл содержит настройки для работы Modbus:
- Идентификатор устройства и таймауты
- Подключение библиотек контроллера
- ID устройства и таймауты
- Строковые идентификаторы (Vendor, Product, Revision)
- Настройки периферии (UART, TIMER)
- Подключение модулей Modbus
- Опциональные функции (переключение команд 0x03/0x04)
******************************************************************************/
#ifndef _MODBUS_CONFIG_H_
@@ -30,13 +33,13 @@
#define MODBUS_PRODUCT_NAME ""
#define MODBUS_MODEL_NAME ""
#define MODBUS_USER_APPLICATION_NAME ""
#define MODBUS_NUMB_OF_USEROBJECTS 0
#define MODBUS_NUMB_OF_USEROBJECTS 0 ///< Количество пользовательских объектов
#define MODBUS_USEROBJECT_0_NAME "" ///< Строка пользовательского идентификатора 0. По аналогии можно определить строки до <=128 USEROBJECT
// Периферия (опционально)
#define mb_huart huart1 ///< Удобный дефайн для модбасовского uart
#define mb_htim htim3 ///< Удобный дефайн для модбасовского таймера
//#define RS_EnableReceive() ///< Функция изменения направления передачи на ПРИЕМ для RS-485
//#define RS_EnableTransmit() ///< Функция изменения направления передачи на ПЕРЕДАЧУ для RS-485
// Модули modbus
@@ -50,8 +53,11 @@
#define MODBUS_ENABLE_DIAGNOSTICS ///< Включить обработку диагностики модбас
#define MODBUS_ENABLE_OSCIL ///< Включить осциллограф Modbus
//#define MODBUS_PROTOCOL_TCP ///< Включить TCP-протокол, иначе - RTU
/**
* @brief Поменять комманды 0x03 и 0x04 местами (для LabView терминалки от двигателей)
* @brief Поменять комманды 0x03 и 0x04 местами
* @details Терминалка от двигателей использует для чтения регистров комманду R_HOLD_REGS вместо R_IN_REGS
* Поэтому чтобы считывать Input Regs - надо поменять их местами.
*/
@@ -62,4 +68,4 @@
#endif //_MODBUS_CONFIG_H_
#endif //_MODBUS_CONFIG_H_

View File

@@ -22,6 +22,8 @@
#include "modbus_inputregs.h"
#include "modbus_devid.h"
/* DEFINE DATA FOR MODBUS */
MB_DataStructureTypeDef MB_DATA = { 0 }; ///< Coils & Registers
/**
* @brief Check is address valid for certain array.
@@ -29,11 +31,11 @@
* @param Qnt Количество запрашиваемых элементов.
* @param R_ARR_ADDR Начальный адресс массива R_ARR.
* @param R_ARR_NUMB Количество элементов в массиве R_ARR.
* @return ExceptionCode - ILLEGAL DATA ADRESS если адресс недействителен, и NO_ERRORS если все ок.
* @return ExceptionCode - ET_ILLEGAL_DATA_ADRESS если адресс недействителен, и ET_NO_ERRORS если все ок.
*
* @details Позволяет определить, принадлежит ли адресс Addr массиву R_ARR:
* Если адресс Addr находится в диапазоне адрессов массива R_ARR, то возвращаем NO_ERROR.
* Если адресс Addr находится за пределами адрессов массива R_ARR - ILLEGAL_DATA_ADDRESSю.
* Если адресс Addr находится за пределами адрессов массива R_ARR - ET_ILLEGAL_DATA_ADDRESSю.
*/
MB_ExceptionTypeDef MB_Check_Address_For_Arr(uint16_t Addr, uint16_t Qnt, uint16_t R_ARR_ADDR, uint16_t R_ARR_NUMB)
{
@@ -43,14 +45,14 @@ MB_ExceptionTypeDef MB_Check_Address_For_Arr(uint16_t Addr, uint16_t Qnt, uint16
// if quantity too big return error
if ((Addr - R_ARR_ADDR) + Qnt > R_ARR_NUMB)
{
return ILLEGAL_DATA_ADDRESS; // return exception code
return ET_ILLEGAL_DATA_ADDRESS; // return exception code
}
// if all ok - return no errors
return NO_ERRORS;
return ET_NO_ERRORS;
}
// if address isnt from this array return error
else
return ILLEGAL_DATA_ADDRESS; // return exception code
return ET_ILLEGAL_DATA_ADDRESS; // return exception code
}
/**
* @brief Define Address Origin for Input/Holding Registers
@@ -58,51 +60,58 @@ MB_ExceptionTypeDef MB_Check_Address_For_Arr(uint16_t Addr, uint16_t Qnt, uint16
* @param Addr Адрес начального регистра.
* @param Qnt Количество запрашиваемых регистров.
* @param WriteFlag Флаг регистр нужны для чтения или записи.
* @return ExceptionCode Код исключения если есть, и NO_ERRORS если нет.
* @return ExceptionCode Код исключения если есть, и ET_NO_ERRORS если нет.
*
* @details Определение адреса начального регистра.
* @note WriteFlag пока не используется.
*/
MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, uint16_t Qnt, uint8_t RegisterType)
MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, uint16_t Qnt, uint8_t RegisterType, uint8_t WriteFlag)
{
/* check quantity error */
if (Qnt > DATA_SIZE)
if (Qnt > MbData_size)
{
return ILLEGAL_DATA_VALUE; // return exception code
return ET_ILLEGAL_DATA_VALUE; // return exception code
}
if(RegisterType == RegisterType_Holding)
{
// Default holding registers
if(MB_Check_Address_For_Arr(Addr, Qnt, R_HOLDING_ADDR, R_HOLDING_QNT) == NO_ERRORS)
if(MB_Check_Address_For_Arr(Addr, Qnt, R_HOLDING_ADDR, R_HOLDING_QNT) == ET_NO_ERRORS)
{
*pRegs = MB_Set_Register_Ptr(&MB_DATA.HoldRegs, Addr - R_HOLDING_ADDR); // указатель на выбранный по Addr регистр
}
#if defined(MODBUS_ENABLE_OSCIL) && defined(R_HOLDING_OSCIL_ADDR) && defined(R_HOLDING_OSCIL_QNT)
else if(MB_Check_Address_For_Arr(Addr, Qnt, R_HOLDING_OSCIL_ADDR, R_HOLDING_OSCIL_QNT) == ET_NO_ERRORS)
{
local_addr = Addr - R_HOLDING_OSCIL_ADDR;
*pRegs = MB_Set_Register_Ptr(&MB_INTERNAL.oscil , local_addr); // указатель на выбранный по Addr регистр
}
#endif
// if address doesnt match any array - return illegal data address response
else
{
return ILLEGAL_DATA_ADDRESS;
return ET_ILLEGAL_DATA_ADDRESS;
}
}
else if(RegisterType == RegisterType_Input)
{
// Default input registers
if(MB_Check_Address_For_Arr(Addr, Qnt, R_INPUT_ADDR, R_INPUT_QNT) == NO_ERRORS)
if(MB_Check_Address_For_Arr(Addr, Qnt, R_INPUT_ADDR, R_INPUT_QNT) == ET_NO_ERRORS)
{
*pRegs = MB_Set_Register_Ptr(&MB_DATA.InRegs, Addr - R_INPUT_ADDR); // указатель на выбранный по Addr регистр
}
// if address doesnt match any array - return illegal data address response
else
{
return ILLEGAL_DATA_ADDRESS;
return ET_ILLEGAL_DATA_ADDRESS;
}
}
else
{
return ILLEGAL_FUNCTION;
return ET_ILLEGAL_FUNCTION;
}
// if found requeried array return no err
return NO_ERRORS; // return no errors
return ET_NO_ERRORS; // return no errors
}
/**
* @brief Define Address Origin for coils
@@ -111,7 +120,7 @@ MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, u
* @param Qnt Количество запрашиваемых коилов.
* @param start_shift Указатель на переменную содержащую сдвиг внутри регистра для начального коила.
* @param WriteFlag Флаг коилы нужны для чтения или записи.
* @return ExceptionCode Код исключения если есть, и NO_ERRORS если нет.
* @return ExceptionCode Код исключения если есть, и ET_NO_ERRORS если нет.
*
* @details Определение адреса начального регистра запрашиваемых коилов.
* @note WriteFlag используется для определния регистров GPIO: ODR или IDR.
@@ -121,21 +130,21 @@ MB_ExceptionTypeDef MB_DefineCoilsAddress(uint16_t **pCoils, uint16_t Addr, uint
/* check quantity error */
if (Qnt > 2000)
{
return ILLEGAL_DATA_VALUE; // return exception code
return ET_ILLEGAL_DATA_VALUE; // return exception code
}
// Default coils
if(MB_Check_Address_For_Arr(Addr, Qnt, C_COILS_ADDR, C_COILS_QNT) == NO_ERRORS)
if(MB_Check_Address_For_Arr(Addr, Qnt, C_COILS_ADDR, C_COILS_QNT) == ET_NO_ERRORS)
{
*pCoils = MB_Set_Coil_Reg_Ptr(&MB_DATA.Coils, Addr - C_COILS_ADDR); // указатель на выбранный по Addr массив коилов
}
// if address doesnt match any array - return illegal data address response
else
{
return ILLEGAL_DATA_ADDRESS;
return ET_ILLEGAL_DATA_ADDRESS;
}
*start_shift = Addr % 16; // set shift to requested coil
// if found requeried array return no err
return NO_ERRORS; // return no errors
}
return ET_NO_ERRORS; // return no errors
}

View File

@@ -3,7 +3,7 @@
* @file modbus_data.h
* @brief Определения структур данных Modbus устройства
******************************************************************************
@defgroup MODBUS_DATA Modbus Data Tools
@defgroup MODBUS_DATA Modbus Registers Map
@ingroup MODBUS
@brief Определение карты регистров и коилов
******************************************************************************
@@ -13,7 +13,7 @@
- Input Registers (R/O) - входные регистры
- Coils (R/W) - дискретные выходы
@section Базовая настройка под устройство:
@section datinit Базовая настройка под устройство:
1. Настроить диапазоны адресов
- @ref R_INPUT_ADDR и @ref R_INPUT_QNT для входных регистров
- @ref R_HOLDING_ADDR и @ref R_HOLDING_QNT для регистров хранения
@@ -24,7 +24,7 @@
- @ref MB_DataCoilsTypeDef
@section Расширенная настройка под устройство:
@section datexpert Расширенная настройка под устройство:
1. Добавить новый массив с нужными данными.
2. Добавить дефайны для определения его начального адреса и количества элементов
3. Добавить проверку адресов в MB_DefineRegistersAddress/MB_DefineCoilsAddress.
@@ -36,14 +36,14 @@
uint16_t user_regs[16];
//...
else if(MB_Check_Address_For_Arr(Addr, Qnt, R_USER_ADDR, R_USER_QNT) == NO_ERRORS)
else if(MB_Check_Address_For_Arr(Addr, Qnt, R_USER_ADDR, R_USER_QNT) == ET_NO_ERRORS)
{
*pRegs = MB_Set_Register_Ptr(&user_regs, Addr-R_USER_ADDR); // ВАЖНО!
// -R_USER_ADDR нужен чтобы взять адрес относительно начала массива
}
else
{
return ILLEGAL_DATA_ADDRESS;
return ET_ILLEGAL_DATA_ADDRESS;
}
@endcode
******************************************************************************/
@@ -75,11 +75,11 @@
* @addtogroup MODBUS_DATA_RERISTERS_DEFINES Registers structures
* @ingroup MODBUS_DATA
* @brief Стуруктура регистров (входных и хранения)
@verbatim
@code
Для массивов регистров:
R_<NAME_ARRAY>_ADDR - модбас адресс первого регистра в массиве
R_<NAME_ARRAY>_QNT - количество регистров в массиве
@endverbatim
@endcode
* @{
*/
@@ -111,12 +111,12 @@ typedef struct //MB_DataInRegsTypeDef
* @addtogroup MODBUS_DATA_COILS_DEFINES Coils Structure
* @ingroup MODBUS_DATA
* @brief Структура коилов
@verbatim
@code
Структура дефайна
Для массивов коилов:
C_<NAME_ARRAY>_ADDR - модбас адресс первого коила в массиве
C_<NAME_ARRAY>_QNT - количество коилов в массиве (минимум 16)
@endverbatim
@endcode
* @{
*/
@@ -156,4 +156,4 @@ extern MB_DataStructureTypeDef MB_DATA;
#endif //_MODBUS_DATA_H_
/////////////////////////////////////////////////////////////
///////////////////////TEMP/OUTDATE/OTHER////////////////////
///////////////////////TEMP/OUTDATE/OTHER////////////////////