5 Commits

Author SHA1 Message Date
98e9e22752 Обновить README.md 2025-11-05 11:06:04 +03:00
Razvalyaev
c5c265ba6c doxygen update 2025-11-05 00:00:30 +03:00
Razvalyaev
b530a60de2 Доработки и рефакторинг 2025-11-04 23:12:25 +03:00
Razvalyaev
5c70316df7 Обновление примера + доработки мастера 2025-11-04 22:42:03 +03:00
Razvalyaev
fbc424e507 Подтягивание всех изменений с релиза в ветку 2025-11-04 22:02:28 +03:00
28 changed files with 6687 additions and 1035 deletions

View File

@@ -42,19 +42,19 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places. # title of most generated pages and in a few other places.
# The default value is: My Project. # The default value is: My Project.
PROJECT_NAME = MyLibs PROJECT_NAME = Modbus
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 1.0 PROJECT_NUMBER = 0.3
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewers a # for a project that appears at the top of each page and should give viewers a
# quick idea about the purpose of the project. Keep the description short. # quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "Расширенные библиотеки для STM32" PROJECT_BRIEF = "Библиотека Modbus для STM"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included # With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55 # in the documentation. The maximum height of the logo should not exceed 55
@@ -273,7 +273,7 @@ SEPARATE_MEMBER_PAGES = NO
# uses this value to replace tabs by spaces in code fragments. # uses this value to replace tabs by spaces in code fragments.
# Minimum value: 1, maximum value: 16, default value: 4. # Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 4 TAB_SIZE = 2
# This tag can be used to specify a number of aliases that act as commands in # This tag can be used to specify a number of aliases that act as commands in
# the documentation. An alias has the form: # the documentation. An alias has the form:
@@ -991,7 +991,9 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = E:\.WORK\STM32\STM32_Modbus\Modbus INPUT = ../ \
../Modbus/Inc \
../Modbus/Src
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses # that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses
@@ -1086,7 +1088,7 @@ FILE_PATTERNS = *.c \
# be searched for input files as well. # be searched for input files as well.
# The default value is: NO. # The default value is: NO.
RECURSIVE = YES RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should be # The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a # excluded from the INPUT source files. This way you can easily exclude a
@@ -1206,7 +1208,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub # (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the Doxygen output. # and want to reuse the introduction page also for the Doxygen output.
USE_MDFILE_AS_MAINPAGE = USE_MDFILE_AS_MAINPAGE = ..\README.md
# If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub- # If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub-
# directories of the project's root, is used as the documentation for that sub- # directories of the project's root, is used as the documentation for that sub-
@@ -1215,7 +1217,7 @@ USE_MDFILE_AS_MAINPAGE =
# command in order to be used as directory documentation. # command in order to be used as directory documentation.
# The default value is: YES. # The default value is: YES.
IMPLICIT_DIR_DOCS = YES IMPLICIT_DIR_DOCS = NO
# The Fortran standard specifies that for fixed formatted Fortran code all # The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common # characters from position 72 are to be considered as comment. A common
@@ -2498,7 +2500,7 @@ MACRO_EXPANSION = YES
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES, the include files in the # If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found. # INCLUDE_PATH will be searched if a #include is found.
@@ -2513,7 +2515,8 @@ SEARCH_INCLUDES = YES
# RECURSIVE has no effect here. # RECURSIVE has no effect here.
# This tag requires that the tag SEARCH_INCLUDES is set to YES. # This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH = INCLUDE_PATH = ../Modbus/Inc \
../
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the # patterns (like *.h and *.hpp) to filter out the header-files in the
@@ -2531,11 +2534,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator. # recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = RTT_TRACE_ENABLE \ PREDEFINED =
GPIO_TRACE_ENABLE \
SERIAL_TRACE_ENABLE \
HAL_MODULE_ENABLED \
TRACKERS_ENABLE
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The # tag can be used to specify a list of macro names that should be expanded. The

View File

@@ -5,7 +5,7 @@
****************************************************************************** ******************************************************************************
@addtogroup MODBUS Modbus tools @addtogroup MODBUS Modbus tools
****************************************************************************** ******************************************************************************
@addtogroup MODBUS_FUNCTIONS Modbus library funtions @addtogroup MODBUS_FUNCTIONS Main API for Modbus Library
@ingroup MODBUS @ingroup MODBUS
@{ @{
****************************************************************************** ******************************************************************************
@@ -14,41 +14,51 @@
Подключает все необходимые модули: Подключает все необходимые модули:
@section Инструкция по подключению @section init Инструкция по подключению
Для корректной работы надо: Для корректной работы надо:
- Подключить обработчики RS_UART_Handler(), RS_TIM_Handler(), в соответствубщие - Подключить обработчики RS_UART_Handler(), RS_TIM_Handler(), в соответствубщие
низкоуровневые прерывания UART_IRQHandler, TIM_IRQHandler. Вместо HAL'овского обработчика низкоуровневые прерывания UART_IRQHandler, TIM_IRQHandler вместо HAL'овского обработчика
В modbus_config.h настроить дефайны для нужной работы UART - В modbus_config.h настроить дефайны для нужной работы UART
- Инициализировать хендл мобдас. По умолчанию глобально создается hmodbus1, но можно сделать свой - Инициализировать хендл мобдас. По умолчанию глобально создается hmodbus1
После для запуска Modbus: - После для запуска Modbus:
@verbatim @verbatim
//----------------Прием модбас----------------// //----------------Слейв модбас----------------//
#include "modbus.h" #include "modbus.h"
MODBUS_SetupHardware(&hmodbus1, &huart1, &htim3); MODBUS_FirstInit(&hmodbus1, &huart1, &htim3);
MODBUS_SlaveStart(&hmodbus1, NULL); MODBUS_Config(&hmodbus1, 1, 1000, MODBUS_MODE_SLAVE);
// или если нужно переключится на другой MODBUS_SlaveStart(&hmodbus1, NULL);
@endverbatim @endverbatim
@verbatim
//----------------Мастер модбас----------------//
#include "modbus.h"
MODBUS_FirstInit(&hmodbus1, &huart1, &htim3);
MODBUS_Config(&hmodbus1, 0, 1000, MODBUS_MODE_MASTER); // - если нужны другие настройки, не из modbus_config.h
// Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук
RS_MsgTypeDef msg = MB_REQUEST_READ_HOLDING_REGS(1, 0, 10);
MODBUS_MasterRequest(&hmodbus1, &msg, &callback_func);
void callback_func(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
// modbus_msg содержит ответ от устройства
}
@endverbatim
@section Подключаемые модули: @section Подключаемые модули:
- rs_message.h - работа с uart
- modbus_core.h - базовые определения - modbus_core.h - базовые определения
- modbus_coils.h - работа с дискретными выходами - modbus_coils.h - работа с дискретными выходами
- modbus_holdregs.h - работа с регистрами хранения - modbus_holdregs.h - работа с регистрами хранения
- modbus_inputregs.h - работа с входными регистрами - modbus_inputregs.h - работа с входными регистрами
- modbus_devid.h - идентификация устройства - modbus_devid.h - идентификация устройства
- __crc_algs.h - алгоритмы CRC - modbus_diag.h - диагностика modbus
@section Использование в проекте:
1. Настроить modbus_config.h под устройство
2. Определить структуры данных в modbus_data.h
3. Подключить этот файл в rs_message.h
4. Вызвать MODBUS_FirstInit() и RS_Receive_IT()
@section Структура данных Modbus @section Структура данных Modbus
#### Holding/Input Registers: #### Holding/Input Registers:
- Регистры — 16-битные слова. Доступ к регистрам осуществляется через указатель. - Регистры — 16-битные слова. Доступ к регистрам осуществляется через указатель.
@@ -62,12 +72,14 @@ MODBUS_SlaveStart(&hmodbus1, NULL);
#ifndef __MODBUS_H_ #ifndef __MODBUS_H_
#define __MODBUS_H_ #define __MODBUS_H_
#include "__crc_algs.h"
#include "rs_message.h" #include "rs_message.h"
#include "modbus_master.h"
#include "modbus_slave.h"
#include "modbus_coils.h" #include "modbus_coils.h"
#include "modbus_holdregs.h" #include "modbus_holdregs.h"
#include "modbus_inputregs.h" #include "modbus_inputregs.h"
#include "modbus_devid.h" #include "modbus_devid.h"
#include "modbus_diag.h"
@@ -76,20 +88,16 @@ MODBUS_SlaveStart(&hmodbus1, NULL);
//----------------FUNCTIONS FOR USER---------------- //----------------FUNCTIONS FOR USER----------------
/**
* @addtogroup MODBUS_INIT_FUNCTIONS Functions for Init
* @ingroup MODBUS_FUNCTIONS
* @brief Функции для инициализации
@{
*/
/* Инициализация периферии модбас. */
void MODBUS_SetupHardware(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim);
/* Программная конфигурация модбас. */
void MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master);
/** MODBUS_INIT_FUNCTIONS /* Инициализация периферии модбас. */
* @} HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim);
*/ /* Программная конфигурация модбас. */
HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master);
/* Запуск слейв устройства */
HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg);
/* Реквест мастера модбас */
HAL_StatusTypeDef MODBUS_MasterRequest(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, void (*pClbk)(RS_HandleTypeDef*, RS_MsgTypeDef*));
//---------PROCESS MODBUS COMMAND FUNCTIONS--------- //---------PROCESS MODBUS COMMAND FUNCTIONS---------
/////////////////////////---FUNCTIONS---///////////////////////////// /////////////////////////---FUNCTIONS---/////////////////////////////

View File

@@ -5,7 +5,6 @@
****************************************************************************** ******************************************************************************
@addtogroup MODBUS_COILS Coils Tools @addtogroup MODBUS_COILS Coils Tools
@ingroup MODBUS_INTERNAL @ingroup MODBUS_INTERNAL
@{
****************************************************************************** ******************************************************************************
* @details * @details
Модуль предоставляет функции и макросы для работы с битовыми данными: Модуль предоставляет функции и макросы для работы с битовыми данными:
@@ -14,11 +13,11 @@
- Запись множественных coils (0x0F) - распаковка байтов в биты - Запись множественных coils (0x0F) - распаковка байтов в биты
- Макросы для локального доступа к coils - Макросы для локального доступа к coils
@section Организация битовых данных: @section Организация битовых данных:
Coils упакованы в 16-битные слова для эффективного использования памяти. Coils упакованы в 16-битные слова для эффективного использования памяти.
Биты нумеруются от младшего к старшему внутри каждого слова. Биты нумеруются от младшего к старшему внутри каждого слова.
@section Адресация: @section Адресация:
- Глобальная - абсолютный адрес в пространстве Modbus - Глобальная - абсолютный адрес в пространстве Modbus
- Локальная - относительный адрес внутри массива coils - Локальная - относительный адрес внутри массива coils
- Макросы автоматически вычисляют смещения и маски - Макросы автоматически вычисляют смещения и маски
@@ -31,24 +30,24 @@ Coils упакованы в 16-битные слова для эффективн
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
////////////////////---MODBUS FUNCTION DEFINES---//////////////////// ////////////////////---MODBUS FUNCTION DEFINES---////////////////////
/** @brief Structure for coils operation */
typedef enum
{
SET_COIL,
RESET_COIL,
TOOGLE_COIL,
}MB_CoilsOpTypeDef;
//-------------------------------------------------- //--------------------------------------------------
/** /**
* @brief Macros to set pointer to a certain register that contains certain coil * @addtogroup MODBUS_COILS
* @param _parr_ - массив коилов. * @{
* @param _coil_ - Номер коила от начала массива _arr_. */
/**
* @brief Макрос для установки указателя на регистр, содержащий запрашиваемый коил
* @param _parr_ - массив коилов.
* @param _coil_ - Номер коила от начала массива _arr_.
* @note Используется вместе с @ref MB_Set_Coil_Mask * @note Используется вместе с @ref MB_Set_Coil_Mask
@verbatim Пояснение выражений @verbatim Пояснение выражений
(_coil_/16) - get index (address shift) of register that contain certain coil - (_coil_/16) - индекс регистра, в котором содержится коил по адресу _coil_
Visual explanation: 30th coil in coils registers array
Визуальный пример: 30 коил будет в 30/16 = 1 регистре (индексация с 0)
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
|register[0]----| |register[1]----| |register[0]----| |register[1]----|
|skip this------| |get this-------| |skip this------| |get this-------|
@@ -57,15 +56,17 @@ typedef enum
*/ */
#define MB_Set_Coil_Reg_Ptr(_parr_, _coil_) ((uint16_t *)(_parr_)+((_coil_)/16)) #define MB_Set_Coil_Reg_Ptr(_parr_, _coil_) ((uint16_t *)(_parr_)+((_coil_)/16))
/** /**
* @brief Macros to set mask to a certain bit in coils register * @brief Макрос для установки маски, чтобы выделить запрашиваемый коил из регистра
* @param _coil_ - Номер коила от начала массива _arr_. * @param _coil_ - Номер коила от начала массива _arr_.
* @note Используется вместе с @ref MB_Set_Coil_Reg_Ptr * @note Используется вместе с @ref MB_Set_Coil_Reg_Ptr
@verbatim Пояснение выражений @verbatim Пояснение выражений
(16*(_coil_/16) - how many coils we need to skip. e.g. (16*30/16) - skip 16 coils from first register - (16*(_coil_/16) - сколько коилов нужно пропустить. прим. (16*30/16) - первые 16 коилов находятся вне регистра
_coil_-(16*(_coil_/16)) - shift to certain coil in certain register - _coil_-(16*(_coil_/16)) - сдвинуть бит на место запрашиваемого коила в регистре
e.g. Coil(30) gets in register[1] (30/16 = 1) coil №14 (30 - (16*30/16) = 30 - 16 = 14)
Визуальный пример: 30 коил будет регистре[1], на 14 бите:
register = 30/16 = 1
bit = 30 - (16*30/16) = 30 - 16 = 14
Visual explanation: 30th coil in coils registers array
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx
|register[0]----| |register[1]----| |register[0]----| |register[1]----|
|skip this------| |get this-------| |skip this------| |get this-------|
@@ -74,55 +75,104 @@ typedef enum
*/ */
#define MB_Set_Coil_Mask(_coil_) (1 << ( _coil_ - (16*((_coil_)/16)) )) #define MB_Set_Coil_Mask(_coil_) (1 << ( _coil_ - (16*((_coil_)/16)) ))
/** MODBUS_COILS
* @}
*/
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---///////////////////////////// /////////////////////////---FUNCTIONS---/////////////////////////////
/**
* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS Modbus Data Access
* @ingroup MODBUS_FUNCTIONS
* @brief Функции для доступа к данным модбас (коилы)
@{
*/
/** /**
* @brief Read Coil at its local address. * @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 Structure for coils operation */
typedef enum
{
SET_COIL,
RESET_COIL,
TOOGLE_COIL,
}MB_CoilsOpTypeDef;
/**
* @brief Считать коил по локальному адресу.
* @param _parr_ - массив коилов. * @param _parr_ - массив коилов.
* @param _coil_ - Номер коила от начала массива _arr_. * @param _coil_ - Номер коила от начала массива _arr_.
* @return uint16_t Возвращает запрошенный коил на 0м бите. * @return uint16_t Возвращает запрошенный коил на 0м бите.
* *
* @details Позволяет обратиться к коилу по адресу относительно _arr_. * @details Позволяет обратиться к коилу по адресу относительно _arr_.
*/ */
#define MB_Read_Coil_Local(_parr_, _coil_) (( *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) & MB_Set_Coil_Mask(_coil_) ) >> (_coil_)) #define MB_Coil_Read_Local(_parr_, _coil_) (( *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) & MB_Set_Coil_Mask(_coil_) ) >> (_coil_))
/** /**
* @brief Set Coil at its local address. * @brief Выставить коил по локальному адресу.
* @param _parr_ Указатель на массив коилов. * @param _parr_ Указатель на массив коилов.
* @param _coil_ - Номер коила от начала массива _arr_. * @param _coil_ - Номер коила от начала массива _arr_.
* *
* @details Позволяет обратиться к коилу по адресу относительно _arr_. * @details Позволяет обратиться к коилу по адресу относительно _arr_.
*/ */
#define MB_Set_Coil_Local(_parr_, _coil_) *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) |= MB_Set_Coil_Mask(_coil_) #define MB_Coil_Set_Local(_parr_, _coil_) *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) |= MB_Set_Coil_Mask(_coil_)
/** /**
* @brief Reset Coil at its local address. * @brief Сбросить коил по локальному адресу.
* @param _parr_ Указатель на массив коилов. * @param _parr_ Указатель на массив коилов.
* @param _coil_ - Номер коила от начала массива _arr_. * @param _coil_ - Номер коила от начала массива _arr_.
* *
* @details Позволяет обратиться к коилу по адресу относительно _arr_. * @details Позволяет обратиться к коилу по адресу относительно _arr_.
*/ */
#define MB_Reset_Coil_Local(_parr_, _coil_) *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) &= ~(MB_Set_Coil_Mask(_coil_)) #define MB_Coil_Reset_Local(_parr_, _coil_) *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) &= ~(MB_Set_Coil_Mask(_coil_))
/** /**
* @brief Set Coil at its local address. * @brief Переключить состояние коила по локальному адресу.
* @param _parr_ Указатель на массив коилов. * @param _parr_ Указатель на массив коилов.
* @param _coil_ - Номер коила от начала массива _arr_. * @param _coil_ - Номер коила от начала массива _arr_.
* *
* @details Позволяет обратиться к коилу по адресу относительно _arr_. * @details Позволяет обратиться к коилу по адресу относительно _arr_.
*/ */
#define MB_Toogle_Coil_Local(_parr_, _coil_) *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) ^= MB_Set_Coil_Mask(_coil_) #define MB_Coil_Toogle_Local(_parr_, _coil_) *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) ^= MB_Set_Coil_Mask(_coil_)
/* Set or Reset Coil at its global address */ /* Выставить/сбросить коил по глобальному адресу */
MB_ExceptionTypeDef MB_Write_Coil_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal); MB_ExceptionTypeDef MB_Coil_Write_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal);
/* Read Coil at its global address */ /* Считать коил по глобальному адресу */
uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception); uint16_t MB_Coil_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception);
/** MODBUS_DATA_ACCESS_FUNCTIONS /** MODBUS_DATA_ACCESS_FUNCTIONS
* @} * @}
@@ -133,12 +183,12 @@ uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception);
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS * @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{ @{
*/ */
/* Proccess command Read Coils (01 - 0x01) */ /* Обработать функцию Read Coils (01 - 0x01) */
uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg); uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg);
/* Proccess command Write Single Coils (05 - 0x05) */ /* Обработать функцию Write Single Coils (05 - 0x05) */
uint8_t MB_Proccess_Write_Single_Coil(RS_MsgTypeDef *modbus_msg); uint8_t MB_Process_Write_Single_Coil(RS_MsgTypeDef *modbus_msg);
/* Proccess command Write Multiple Coils (15 - 0x0F) */ /* Обработать функцию Write Multiple Coils (15 - 0x0F) */
uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg); uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg);
/** MODBUS_CMD_PROCESS_FUNCTIONS /** MODBUS_CMD_PROCESS_FUNCTIONS
* @} * @}

View File

@@ -19,23 +19,36 @@
#include "stm32f1xx_hal.h" #include "stm32f1xx_hal.h"
// Общие параметры // Общие параметры
#define MODBUS_DEVICE_ID 1 ///< девайс текущего устройства #define MODBUS_DEVICE_ID 1 ///< Адрес устройства в сети Modbus
#define MODBUS_TIMEOUT 5000 ///< максимальнйы тайтаут MB в тиках таймера #define MODBUS_TIMEOUT 5000 ///< Таймаут в тиках таймера
// Строковые идентификаторы устройства // Строковые идентификаторы устройства
#define MODBUS_VENDOR_NAME "NIO-12" #define MODBUS_VENDOR_NAME "NIO-12"
#define MODBUS_PRODUCT_CODE "" #define MODBUS_PRODUCT_CODE ""
#define MODBUS_REVISION "Ver. 1.0" #define MODBUS_REVISION ""
#define MODBUS_VENDOR_URL "" #define MODBUS_VENDOR_URL ""
#define MODBUS_PRODUCT_NAME "" #define MODBUS_PRODUCT_NAME ""
#define MODBUS_MODEL_NAME "STM32F103" #define MODBUS_MODEL_NAME ""
#define MODBUS_USER_APPLICATION_NAME "" #define MODBUS_USER_APPLICATION_NAME ""
#define MODBUS_NUMB_OF_USEROBJECTS 0 #define MODBUS_NUMB_OF_USEROBJECTS 0
// Периферия (опционально) // Периферия (опционально)
#define mb_huart huart1 ///< Удобный дефайн для модбасовского uart #define mb_huart huart1 ///< Удобный дефайн для модбасовского uart
#define mb_htim htim3 ///< Удобный дефайн для модбасовского таймера #define mb_htim htim3 ///< Удобный дефайн для модбасовского таймера
///< //#define RS_EnableReceive() ///< Функция изменения направления передачи на ПРИЕМ для RS-485
//#define RS_EnableTransmit() ///< Функция изменения направления передачи на ПЕРЕДАЧУ для RS-485
// Модули modbus
#define MODBUS_ENABLE_SLAVE ///< Включить обработку СЛЕЙВ режима
#define MODBUS_ENABLE_MASTER ///< Включить обработку МАСТЕР режима
#define MODBUS_ENABLE_COILS ///< Включить обработку коилов
#define MODBUS_ENABLE_HOLDINGS ///< Включить обработку регистров хранения
#define MODBUS_ENABLE_INPUTS ///< Включить обработку входных регистров
#define MODBUS_ENABLE_DEVICE_IDENTIFICATIONS ///< Включить обработку идентификаторы устройства
#define MODBUS_ENABLE_DIAGNOSTICS ///< Включить обработку диагностики модбас
/** /**
* @brief Поменять комманды 0x03 и 0x04 местами (для LabView терминалки от двигателей) * @brief Поменять комманды 0x03 и 0x04 местами (для LabView терминалки от двигателей)

View File

@@ -14,7 +14,7 @@
Константы размеров полей Константы размеров полей
Вспомогательные макросы Вспомогательные макросы
@section Структура сообщения: @section Структура сообщения:
[ADDR][FUNC][DATA...][CRC] [ADDR][FUNC][DATA...][CRC]
- Адрес: 1 байт - Адрес: 1 байт
- Функция: 1 байт - Функция: 1 байт
@@ -27,6 +27,7 @@
#include "modbus_config.h" #include "modbus_config.h"
#include "modbus_data.h" #include "modbus_data.h"
#include "__crc_algs.h"
/** /**
* @addtogroup MODBUS_MESSAGE_DEFINES Modbus Message Tools * @addtogroup MODBUS_MESSAGE_DEFINES Modbus Message Tools
@@ -93,8 +94,8 @@ typedef enum //MB_FunctonTypeDef
MB_W_COILS = 0x0F, ///< Запись нескольких битовых ячеек MB_W_COILS = 0x0F, ///< Запись нескольких битовых ячеек
MB_W_HOLD_REGS = 0x10, ///< Запись нескольких регистров MB_W_HOLD_REGS = 0x10, ///< Запись нескольких регистров
MB_R_DIAGNOSTIC = 0x08, ///< Чтение диагностической информации устройства MB_R_DIAGNOSTIC = 0x08, ///< Чтение диагностической информации устройства
MB_R_DEVICE_INFO = 0x2B, ///< Чтение информации об устройстве MB_R_DEVICE_INFO = 0x2B, ///< Чтение информации об устройстве
/* ERRORS */ /* ERRORS */
// error reading // error reading
@@ -116,39 +117,39 @@ typedef enum //MB_FunctonTypeDef
/** @brief Structure for MEI func codes */ /** @brief Structure for MEI func codes */
typedef enum //MB_FunctonTypeDef typedef enum //MB_FunctonTypeDef
{ {
MEI_DEVICE_IDENTIFICATION = 0x0E, MEI_DEVICE_IDENTIFICATIONS = 0x0E,
}MB_MEITypeDef; }MB_MEITypeDef;
/** @brief Structure for comformity */ /** @brief Structure for comformity */
typedef enum //MB_FunctonTypeDef typedef enum //MB_FunctonTypeDef
{ {
MB_BASIC_IDENTIFICATION = 0x01, /*!< @brief Basic Device Identification. MB_BASIC_IDENTIFICATIONS = 0x01, /*!< @brief Basic Device Identifications.
@details All objects of this category are mandatory: @details All objects of this category are mandatory:
VendorName,Product code, and revision number */ VendorName,Product code, and revision number */
MB_REGULAR_IDENTIFICATION = 0x02, /*!< @brief Regular Device Identification. MB_REGULAR_IDENTIFICATIONS = 0x02, /*!< @brief Regular Device Identifications.
@details The device provides additional and optional @details The device provides additional and optional
identification and description data objects */ identifications and description data objects */
MB_EXTENDED_IDENTIFICATION = 0x03, /*!< @brief Extended Device Identification. MB_EXTENDED_IDENTIFICATIONS = 0x03, /*!< @brief Extended Device Identifications.
@details The device provides additional and optional @details The device provides additional and optional
identification and description private data about the physical identifications and description private data about the physical
device itself. All of these data are device dependent. */ device itself. All of these data are device dependent. */
MB_SPEDIFIC_IDENTIFICATION = 0x04, /*!< @brief Specific Device Identification. MB_SPEDIFIC_IDENTIFICATIONS = 0x04, /*!< @brief Specific Device Identifications.
@details The device provides one specific identification object. */ @details The device provides one specific identifications object. */
/* ERRORS */ /* ERRORS */
MB_ERR_BASIC_IDENTIFICATION = MB_BASIC_IDENTIFICATION + ERR_VALUES_START, MB_ERR_BASIC_IDENTIFICATIONS = MB_BASIC_IDENTIFICATIONS + ERR_VALUES_START,
MB_ERR_REGULAR_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START, MB_ERR_REGULAR_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START,
MB_ERR_EXTENDED_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START, MB_ERR_EXTENDED_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START,
MB_ERR_SPEDIFIC_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START, MB_ERR_SPEDIFIC_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START,
}MB_ConformityTypeDef; }MB_ConformityTypeDef;
/** @brief Structure for decive identification message type */ /** @brief Structure for decive identifications message type */
typedef struct typedef struct
{ {
MB_MEITypeDef MEI_Type; ///< MEI Type assigned number for Device Identification Interface MB_MEITypeDef MEI_Type; ///< MEI Type assigned number for Device Identifications Interface
MB_ConformityTypeDef ReadDevId; MB_ConformityTypeDef ReadDevId;
MB_ConformityTypeDef Conformity; MB_ConformityTypeDef Conformity;
uint8_t MoreFollows; uint8_t MoreFollows;
@@ -162,7 +163,7 @@ typedef struct // RS_MsgTypeDef
{ {
uint8_t MbAddr; ///< Modbus Slave Address uint8_t MbAddr; ///< Modbus Slave Address
MB_FunctonTypeDef Func_Code; ///< Modbus Function Code MB_FunctonTypeDef Func_Code; ///< Modbus Function Code
MB_DevIdMsgTypeDef DevId; ///< Read Device Identification Header struct MB_DevIdMsgTypeDef DevId; ///< Read Device Identifications Header struct
uint16_t Addr; ///< Modbus Address of data uint16_t Addr; ///< Modbus Address of data
uint16_t Qnt; ///< Quantity of modbus data uint16_t Qnt; ///< Quantity of modbus data
uint8_t ByteCnt; ///< Quantity of bytes of data in message to transmit/receive uint8_t ByteCnt; ///< Quantity of bytes of data in message to transmit/receive

View File

@@ -13,7 +13,7 @@
- Input Registers (R/O) - входные регистры - Input Registers (R/O) - входные регистры
- Coils (R/W) - дискретные выходы - Coils (R/W) - дискретные выходы
@section Базовая настройка под устройство: @section Базовая настройка под устройство:
1. Настроить диапазоны адресов 1. Настроить диапазоны адресов
- @ref R_INPUT_ADDR и @ref R_INPUT_QNT для входных регистров - @ref R_INPUT_ADDR и @ref R_INPUT_QNT для входных регистров
- @ref R_HOLDING_ADDR и @ref R_HOLDING_QNT для регистров хранения - @ref R_HOLDING_ADDR и @ref R_HOLDING_QNT для регистров хранения
@@ -24,7 +24,7 @@
- @ref MB_DataCoilsTypeDef - @ref MB_DataCoilsTypeDef
@section Расширенная настройка под устройство: @section Расширенная настройка под устройство:
1. Добавить новый массив с нужными данными. 1. Добавить новый массив с нужными данными.
2. Добавить дефайны для определения его начального адреса и количества элементов 2. Добавить дефайны для определения его начального адреса и количества элементов
3. Добавить проверку адресов в MB_DefineRegistersAddress/MB_DefineCoilsAddress. 3. Добавить проверку адресов в MB_DefineRegistersAddress/MB_DefineCoilsAddress.

View File

@@ -1,19 +1,18 @@
/** /**
****************************************************************************** ******************************************************************************
* @file modbus_devid.h * @file modbus_devid.h
* @brief Идентификация устройства Modbus * @brief Идентификаторы устройства Modbus
****************************************************************************** ******************************************************************************
@addtogroup MODBUS_DEVID Device Identificators Tools @addtogroup MODBUS_DEVID Device Identifications Tools
@ingroup MODBUS_INTERNAL @ingroup MODBUS_INTERNAL
@{
****************************************************************************** ******************************************************************************
* @details * @details
Модуль реализации функции Read Device Identification (0x2B): Модуль реализации функции Read Device Identifications (0x2B):
- Базовая идентификация (Vendor, Product, Revision) - Базовая идентификация (Vendor, Product, Revision)
- Расширенная идентификация (URL, Model, User fields) - Расширенная идентификация (URL, Model, User fields)
- Поддержка потоковой передачи больших объектов - Поддержка потоковой передачи больших объектов
@section Объекты идентификации: @section Объекты идентификации:
- VendorName, ProductCode, Revision - обязательные - VendorName, ProductCode, Revision - обязательные
- VendorUrl, ProductName, ModelName - опциональные - VendorUrl, ProductName, ModelName - опциональные
- User objects - пользовательские поля - User objects - пользовательские поля
@@ -27,6 +26,11 @@
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
///////////////---DEVICE IDENTIVICATIONS DEFINES---////////////////// ///////////////---DEVICE IDENTIVICATIONS DEFINES---//////////////////
/**
* @addtogroup MODBUS_DEVID
* @{
*/
/** @brief Структура для объекта (идентификатора устройства модбас) */ /** @brief Структура для объекта (идентификатора устройства модбас) */
typedef struct typedef struct
{ {
@@ -48,35 +52,11 @@ typedef struct
MB_DeviceObjectTypeDef Reserved[0x79]; MB_DeviceObjectTypeDef Reserved[0x79];
MB_DeviceObjectTypeDef User[MODBUS_NUMB_OF_USEROBJECTS]; MB_DeviceObjectTypeDef User[MODBUS_NUMB_OF_USEROBJECTS];
}MB_DeviceIdentificationTypeDef; }MB_DeviceIdentificationsTypeDef;
extern MB_DeviceIdentificationTypeDef MB_DEVID; extern MB_DeviceIdentificationsTypeDef MB_DEVID;
void MB_DeviceInentificationInit(void); void MB_DeviceInentificationInit(void);
///////////////---DEVICE IDENTIVICATIONS DEFINES---////////////////// ///////////////---DEVICE IDENTIVICATIONS DEFINES---//////////////////
/////////////////////////////////////////////////////////////////////
/////////////////---DEVICE DIAGNOSTICS DEFINES---////////////////////
/** @brief Структура со диагностической информацией устройства модбас */
typedef struct
{
uint16_t DiagnosticRegister;
struct
{
uint16_t BusMessage;
uint16_t BusCommunicationErr;
uint16_t BusExceptionErr;
uint16_t SlaveMessage;
uint16_t SlaveNoResponse;
uint16_t SlaveNAK;
uint16_t SlaveBusy;
uint16_t BusCharacterOverrun;
}Counters;
}MB_DiagnosticsInfoTypeDef;
extern MB_DiagnosticsInfoTypeDef MB_DINFO;
/////////////////---DEVICE DIAGNOSTICS DEFINES---////////////////////
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
////////////////////---MODBUS FUNCTION DEFINES---//////////////////// ////////////////////---MODBUS FUNCTION DEFINES---////////////////////
@@ -85,15 +65,20 @@ extern MB_DiagnosticsInfoTypeDef MB_DINFO;
* @brief Инициализация объектов * @brief Инициализация объектов
* @details С помозью этого дефайна инициализируются объекты в @ref MB_DeviceInentificationInit * @details С помозью этого дефайна инициализируются объекты в @ref MB_DeviceInentificationInit
*/ */
#define MB_ObjectInit(_p_obj_, _userstring_) (_p_obj_)->length = sizeof(_userstring_);\ #define MB_ObjectInit(_p_obj_, _userstring_) \
(_p_obj_)->name = _userstring_; (_p_obj_)->length = sizeof(_userstring_);\
/** (_p_obj_)->name = _userstring_;
/**
* @brief Инициализация пользовательских объектов * @brief Инициализация пользовательских объектов
* @details С помозью этого дефайна инициализируются пользовательские объекты в MB_DeviceInentificationInit * @details С помозью этого дефайна инициализируются пользовательские объекты в MB_DeviceInentificationInit
*/ */
#define MB_UserObjectInit(_pinfostruct_, _user_numb_) MB_ObjectInit(&(_pinfostruct_)->User[_user_numb_], MODBUS_USEROBJECT##_user_numb_##_NAME) #define MB_UserObjectInit(_pinfostruct_, _user_numb_) MB_ObjectInit(&(_pinfostruct_)->User[_user_numb_], MODBUS_USEROBJECT##_user_numb_##_NAME)
/** MODBUS_DEVID
* @}
*/
////////////////////---MODBUS MESSAGE DEFINES---///////////////////// ////////////////////---MODBUS MESSAGE DEFINES---/////////////////////
@@ -101,25 +86,76 @@ extern MB_DiagnosticsInfoTypeDef MB_DINFO;
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---///////////////////////////// /////////////////////////---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
* @{
*/
/* Записать Один Объект Идентификатора в массив данных */
void MB_WriteSingleObjectToMessage(char *mbdata, unsigned *ind, MB_DeviceObjectTypeDef *obj);
/* Записать Массив Объектов Идентификатора в массив данных */
void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj);
/** MODBUS_DEVID
* @}
*/
//---------PROCESS MODBUS COMMAND FUNCTIONS--------- //---------PROCESS MODBUS COMMAND FUNCTIONS---------
/** /**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS * @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{ @{
*/ */
/* Write Object of Device Identification to MessageData */ /* Обработать функцию Read Device Identifications (43/14 - 0x2B/0E) */
void MB_WriteSingleObjectToMessage(char *mbdata, unsigned *ind, MB_DeviceObjectTypeDef *obj); uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg);
/* Write Object of Device Identification to MessageData */
void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj);
/* Proccess command Read Device Identification (43/14 - 0x2B/0E) */
uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg);
/** MODBUS_CMD_PROCESS_FUNCTIONS /** MODBUS_CMD_PROCESS_FUNCTIONS
* @} * @}
*/ */
/////////////////////////---FUNCTIONS---///////////////////////////// /////////////////////////---FUNCTIONS---/////////////////////////////
#endif //__MODBUS_DEVID_H_ #endif //__MODBUS_DEVID_H_
/** MODBUS_DEVID
* @}
*/

146
Modbus/Inc/modbus_diag.h Normal file
View File

@@ -0,0 +1,146 @@
/**
******************************************************************************
* @file modbus_diag.h
* @brief Диагностика устройства Modbus
******************************************************************************
@addtogroup MODBUS_DIAG Diagnostics Tools
@ingroup MODBUS_INTERNAL
******************************************************************************
* @details
Модуль реализации Diagnostics (Serial Line only) (0x08):
- Полная поддержка всех подфункций диагностики
- Возможность выставить/сбросить любой бит в диагностическом регистре
- Сбор статистики работы устройства
- Управление режимами работы
****************************************** ************************************/
#ifndef __MODBUS_DIAG_H_
#define __MODBUS_DIAG_H_
#include "modbus_core.h"
/////////////////////////////////////////////////////////////////////
/////////////////---DEVICE DIAGNOSTICS DEFINES---////////////////////
/**
* @addtogroup MODBUS_DIAG
* @{
*/
/** @brief Режимы работы устройства */
typedef enum
{
MODBUS_NORMAL_MODE = 0,
MODBUS_LISTEN_ONLY_MODE = 1
} MB_DeviceModeTypeDef;
/** @brief Структура со диагностической информацией устройства модбас */
typedef struct
{
uint16_t DiagnosticRegister; ///< Регистр диагностики. 0 бит - overrun. Остальное заполняется пользователем
MB_DeviceModeTypeDef DeviceMode;///< Режим устройства - NORMAL/LISTEN_ONLY
struct
{
uint16_t BusMessage; ///< Все принятые фреймы modbus на линии (с всех адресов)
uint16_t BusCommunicationErr; ///< Ошибки при приеме фрейма modbus
uint16_t BusExceptionErr; ///< Ошибки при обработке фрейма modbus
uint16_t SlaveMessage; ///< Принятые сообщения (только запросы на адрес данного устройства)
uint16_t SlaveNoResponse; ///< Счетчик сколько мы раз не ответили на запрос
uint16_t SlaveNAK; ///< Счетчик аномальной ошибки при обработке фрейма
uint16_t SlaveBusy; ///< Счетчик принятых запросов когда устройство занято. Здесь не работает: из-за архитектуры отследить невозможно
uint16_t BusCharacterOverrun; ///< Overrun Error
} Counters;
} MB_DiagnosticsInfoTypeDef;
extern MB_DiagnosticsInfoTypeDef MB_DIAG;
/////////////////---DEVICE DIAGNOSTICS DEFINES---////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
/* Инициализация диагностических счетчиков */
void MB_DiagnosticsInit(void);
/** MODBUS_DIAG
* @}
*/
/**
* @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_DATA_ACCESS_FUNCTIONS
@{
*/
/* Выставить бит в регистре диагностике */
int MB_Diagnostics_WriteBit(int bit_num, int bit_state);
/*ь Прочитать состояние бита диагностического регистра */
int MB_Diagnostics_GetBit(int bit_num);
/* Получение текущего режима устройства */
MB_DeviceModeTypeDef MB_GetDeviceMode(void);
/* Функции для обновления счетчиков диагностики */
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);
/** MODBUS_CMD_PROCESS_FUNCTIONS
* @}
*/
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{
*/
/* Обработка команды диагностики (0x08) */
uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg);
/** MODBUS_CMD_PROCESS_FUNCTIONS
* @}
*/
/////////////////////////---FUNCTIONS---/////////////////////////////
#endif //__MODBUS_DIAG_H_
/** MODBUS_DIAG
* @}
*/

View File

@@ -5,7 +5,6 @@
****************************************************************************** ******************************************************************************
@addtogroup MODBUS_INS Input Register Tools @addtogroup MODBUS_INS Input Register Tools
@ingroup MODBUS_INTERNAL @ingroup MODBUS_INTERNAL
@{
****************************************************************************** ******************************************************************************
* @details * @details
Модуль обработки команд для регистров хранения (Holding Registers): Модуль обработки команд для регистров хранения (Holding Registers):
@@ -13,7 +12,7 @@
- Запись одиночного регистра (0x06) - Запись одиночного регистра (0x06)
- Запись множества регистров (0x10) - Запись множества регистров (0x10)
@section Регистры хранения: @section Регистры хранения:
- Read/Write доступ - Read/Write доступ
- 16-битные значения (uint16_t) - 16-битные значения (uint16_t)
******************************************************************************/ ******************************************************************************/
@@ -29,12 +28,12 @@
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS * @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS
@{ @{
*/ */
/* Proccess command Read Holding Registers (03 - 0x03) */ /* Обработать функцию Read Holding Registers (03 - 0x03) */
uint8_t MB_Proccess_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg); uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg);
/* Proccess command Write Single Coils (06 - 0x06) */ /* Обработать функцию Write Single Coils (06 - 0x06) */
uint8_t MB_Proccess_Write_Single_Reg(RS_MsgTypeDef *modbus_msg); uint8_t MB_Process_Write_Single_Reg(RS_MsgTypeDef *modbus_msg);
/* Proccess command Write Multiple Register (16 - 0x10) */ /* Обработать функцию Write Multiple Register (16 - 0x10) */
uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg); uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg);
/** MODBUS_CMD_PROCESS_FUNCTIONS /** MODBUS_CMD_PROCESS_FUNCTIONS
* @} * @}
@@ -42,7 +41,3 @@ uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg);
/////////////////////////---FUNCTIONS---///////////////////////////// /////////////////////////---FUNCTIONS---/////////////////////////////
#endif //__MODBUS_HOLDREGS_H_ #endif //__MODBUS_HOLDREGS_H_
/** MODBUS_INS
* @}
*/

View File

@@ -5,13 +5,12 @@
****************************************************************************** ******************************************************************************
@addtogroup MODBUS_HOLD Holding Registers Tools @addtogroup MODBUS_HOLD Holding Registers Tools
@ingroup MODBUS_INTERNAL @ingroup MODBUS_INTERNAL
@{
****************************************************************************** ******************************************************************************
* @details * @details
Модуль обработки команд для входных регистров (Input Registers): Модуль обработки команд для входных регистров (Input Registers):
- Чтение множества регистров (0x04) - Чтение множества регистров (0x04)
@section Входные регистры: @section Входные регистры:
- Read-Only доступ - Read-Only доступ
- 16-битные значения - 16-битные значения
******************************************************************************/ ******************************************************************************/
@@ -24,13 +23,13 @@
//---------PROCESS MODBUS COMMAND FUNCTIONS--------- //---------PROCESS MODBUS COMMAND FUNCTIONS---------
/** /**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS Proccess Functions * @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS Internal Process Functions
* @ingroup MODBUS_FUNCTIONS * @ingroup MODBUS_INTERNAL
* @brief Функции обработки запросов модбас * @brief Функции обработки запросов модбас
@{ @{
*/ */
/* Proccess command Read Input Registers (04 - 0x04) */ /* Обработать функцию Read Input Registers (04 - 0x04) */
uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg); uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg);
/** MODBUS_CMD_PROCESS_FUNCTIONS /** MODBUS_CMD_PROCESS_FUNCTIONS
* @} * @}
@@ -38,6 +37,3 @@ uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg);
/////////////////////////---FUNCTIONS---///////////////////////////// /////////////////////////---FUNCTIONS---/////////////////////////////
#endif //__MODBUS_INPUTREGS_H_ #endif //__MODBUS_INPUTREGS_H_
/** MODBUS_HOLD
* @}
*/

126
Modbus/Inc/modbus_master.h Normal file
View File

@@ -0,0 +1,126 @@
/**
******************************************************************************
* @file modbus_master.h
* @brief Главный заголовочный файл Modbus библиотеки
******************************************************************************
@addtogroup MODBUS_MASTER Modbus master funtions
@ingroup MODBUS_CMD_PROCESS_FUNCTIONS
******************************************************************************
* @details
Модуль реализации обработки UART сообщение в режиме мастер
******************************************************************************/
#ifndef __MODBUS_MASTER_H_
#define __MODBUS_MASTER_H_
#include "rs_message.h"
#ifdef MODBUS_ENABLE_MASTER
#define MODBUS_MODE_MASTER 1
#endif
/**
* @addtogroup MODBUS_REQUEST_MSG API for Master Requests
* @ingroup MODBUS_FUNCTIONS
* @brief Макросы для создания запросов в режиме мастер
* @details Примеры использования:
* @code
* // Чтение 10 holding registers начиная с адреса 0
* RS_MsgTypeDef read_msg = MB_REQUEST_READ_HOLDING_REGS(1, 0, 10);
*
* // Запись одного coil
* RS_MsgTypeDef write_coil_msg = MB_REQUEST_WRITE_SINGLE_COIL(1, 5, 1);
*
* // Диагностический запрос
* RS_MsgTypeDef diag_msg = MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(1);
*
* // Идентификация устройства
* RS_MsgTypeDef dev_id_msg = MB_REQUEST_READ_DEVICE_ID_BASIC(1);
* @endcode
* @{
*/
//---------КЛАССИЧЕСКИЕ ДАННЫЕ-----------
RS_MsgTypeDef MB_REQUEST_READ_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity);
RS_MsgTypeDef MB_REQUEST_READ_DISCRETE_INPUTS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity);
RS_MsgTypeDef MB_REQUEST_READ_HOLDING_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity);
RS_MsgTypeDef MB_REQUEST_READ_INPUT_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity);
RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_COIL(uint8_t slave_addr, uint16_t coil_addr, uint8_t value);
RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_REG(uint8_t slave_addr, uint16_t reg_addr, uint16_t value);
RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint8_t *coils_data);
RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint16_t *regs_data);
//---------ДИАГНОСТИЧЕСКИЕ ДАННЫЕ-----------
RS_MsgTypeDef MB_REQUEST_DIAGNOSTIC_QUERY(uint8_t slave_addr, uint16_t sub_function, uint16_t data);
RS_MsgTypeDef MB_REQUEST_RETURN_QUERY_DATA(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_RESTART_COMMUNICATIONS(uint8_t slave_addr, uint16_t data);
RS_MsgTypeDef MB_REQUEST_RETURN_DIAGNOSTIC_REGISTER(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_FORCE_LISTEN_ONLY_MODE(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_COMMUNICATION_ERROR_COUNT(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_EXCEPTION_ERROR_COUNT(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_MESSAGE_COUNT(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NO_RESPONSE_COUNT(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NAK_COUNT(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_BUSY_COUNT(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_CHARACTER_OVERRUN_COUNT(uint8_t slave_addr);
//---------ИДЕНТИФИКАТОРЫ МОДБАС-----------
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_BASIC(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_REGULAR(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_EXTENDED(uint8_t slave_addr);
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_SPECIFIC(uint8_t slave_addr, uint8_t object_id);
/** MODBUS_REQUEST_MSG
* @}
*/
/**
* @addtogroup MODBUS_REGS_API API for Registers
* @ingroup MODBUS_REQUEST_MSG
* @brief API для чтения регистров из ответа в режиме мастер
* @details Примеры использования:
*
* @code
* // Пример: Запросили 10 регистров с адреса 100, хотим получить значение регистра 105
* uint16_t reg_value;
* if(MB_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);
* }
* }
* @endcode
* @{
*/
int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value);
/** MODBUS_REQ_REGS_API
* @}
*/
/**
* @addtogroup MODBUS_MASTER
* @{
*/
/* Сбор сообщения в буфер UART в режиме мастер (фрейм мастера из msg -> uart) */
RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff);
/* Парс сообщения в режиме мастер (фрейм слейва из uart -> msg) */
RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff);
/** MODBUS_MASTER
* @}
*/
#endif //__MODBUS_MASTER_H_

36
Modbus/Inc/modbus_slave.h Normal file
View File

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

View File

@@ -11,19 +11,19 @@
Универсальная библиотека для работы с последовательными протоколами (Modbus, Custom) Универсальная библиотека для работы с последовательными протоколами (Modbus, Custom)
через UART в режиме прерываний с поддержкой таймаутов. через UART в режиме прерываний с поддержкой таймаутов.
@section Основные возможности: @section Основные возможности:
- Прием/передача в прерываниях - Прием/передача в прерываниях
- Обработка IDLE линии для определения конца фрейма - Обработка IDLE линии для определения конца фрейма
- Таймауты приема через TIM - Таймауты приема через TIM
- Гибкая настройка размера сообщений - Гибкая настройка размера сообщений
@section Использование: @section Использование:
1. Определить структуру сообщения и размеры буфера 1. Определить структуру сообщения и размеры буфера
2. Реализовать weak-функции обработки сообщений 2. Реализовать weak-функции обработки сообщений
3. Добавить вызовы RS_UART_Handler/RS_TIM_Handler в прерывания 3. Добавить вызовы RS_UART_Handler/RS_TIM_Handler в прерывания
4. Инициализировать через RS_Init() и запустить прием RS_Receive_IT() 4. Инициализировать через RS_Init() и запустить прием RS_Receive_IT()
@section Особенности: @section Особенности:
- Буфер: RS_Buffer[MSG_SIZE_MAX] Общий для приема/передачи - Буфер: RS_Buffer[MSG_SIZE_MAX] Общий для приема/передачи
- Состояния: отслеживается через флаги в RS_HandleTypeDef - Состояния: отслеживается через флаги в RS_HandleTypeDef
- Таймауты: контролируют максимальное время ожидания фрейма - Таймауты: контролируют максимальное время ожидания фрейма
@@ -40,10 +40,6 @@
#error Define MSG_SIZE_MAX (Maximum size of message). This is necessary to create buffer for UART. #error Define MSG_SIZE_MAX (Maximum size of message). This is necessary to create buffer for UART.
#endif #endif
#ifndef RX_FIRST_PART_SIZE
#error Define RX_FIRST_PART_SIZE (Size of first part of message). This is necessary to receive the first part of the message, from which determine the size of the remaining part of the message.
#endif
/** /**
* @cond Заглушки и внутренний недокументированный стаф * @cond Заглушки и внутренний недокументированный стаф
*/ */
@@ -55,14 +51,14 @@
#define RS_Set_Free(_hRS_) _hRS_->f.RS_Busy = 0 #define RS_Set_Free(_hRS_) _hRS_->f.RS_Busy = 0
#define RS_Set_Busy(_hRS_) _hRS_->f.RS_Busy = 1 #define RS_Set_Busy(_hRS_) _hRS_->f.RS_Busy = 1
#define RS_Set_RX_Flags(_hRS_) _hRS_->f.RX_Busy = 1; _hRS_->f.RX_Done = 0; _hRS_->f.RX_Half = 0 #define RS_Set_RX_Flags(_hRS_) _hRS_->f.RX_Busy = 1; _hRS_->f.RX_Done = 0;
#define RS_Set_RX_Active_Flags(_hRS_) _hRS_->f.RX_Ongoing = 1 #define RS_Set_RX_Active_Flags(_hRS_) _hRS_->f.RX_Ongoing = 1
#define RS_Set_TX_Flags(_hRS_) _hRS_->f.TX_Busy = 1; _hRS_->f.TX_Done = 0 #define RS_Set_TX_Flags(_hRS_) _hRS_->f.TX_Busy = 1; _hRS_->f.TX_Done = 0
#define RS_Reset_RX_Active_Flags(_hRS_) _hRS_->f.RX_Ongoing = 0 #define RS_Reset_RX_Active_Flags(_hRS_) _hRS_->f.RX_Ongoing = 0; _hRS_->f.RX_Continue = 0;
#define RS_Reset_RX_Flags(_hRS_) RS_Reset_RX_Active_Flags(_hRS_); _hRS_->f.RX_Busy = 0; _hRS_->f.RX_Done = 0; _hRS_->f.RX_Half = 0 #define RS_Reset_RX_Flags(_hRS_) RS_Reset_RX_Active_Flags(_hRS_); _hRS_->f.RX_Busy = 0; _hRS_->f.RX_Done = 0;
#define RS_Reset_TX_Flags(_hRS_) _hRS_->f.TX_Busy = 0; _hRS_->f.TX_Done = 0 #define RS_Reset_TX_Flags(_hRS_) _hRS_->f.TX_Busy = 0; _hRS_->f.TX_Done = 0
#define RS_Set_RX_End_Flag(_hRS_) _hRS_->f.RX_Done = 1; #define RS_Set_RX_End_Flag(_hRS_) _hRS_->f.RX_Done = 1;
@@ -79,15 +75,16 @@
#define RS_Is_RX_Busy(_hRS_) (_hRS_->f.RX_Busy == 1) #define RS_Is_RX_Busy(_hRS_) (_hRS_->f.RX_Busy == 1)
#define RS_Is_TX_Busy(_hRS_) (_hRS_->f.TX_Busy == 1) #define RS_Is_TX_Busy(_hRS_) (_hRS_->f.TX_Busy == 1)
// направление передачи rs485
#ifndef RS_EnableReceive #ifndef printf_rs_err
#define RS_EnableReceive() #define printf_rs_err(...)
#endif
#ifndef RS_EnableTransmit
#define RS_EnableTransmit()
#endif #endif
#ifndef __MYLIBS_INCLUDE_H_ #ifndef printf_rs
#define printf_rs(...)
#endif
#ifndef __MYLIBS_CONFIG_H_
// дефайны из mylibs include // дефайны из mylibs include
static int dummy; static int dummy;
#define TrackerTypeDef(num_user_vars) void * #define TrackerTypeDef(num_user_vars) void *
@@ -110,20 +107,24 @@ static int dummy;
#define TrackerClear_Warn(_cntstruct_) #define TrackerClear_Warn(_cntstruct_)
#define TrackerClear_User(_cntstruct_) #define TrackerClear_User(_cntstruct_)
#define TrackerClear_UserAll(_cntstruct_) #define TrackerClear_UserAll(_cntstruct_)
#else
#ifndef printf_rs_err #include "mylibs_include.h"
#define printf_rs_err(...)
#endif #endif
#ifndef printf_rs
#define printf_rs(...)
#endif
#endif
#ifndef RS_USER_VARS_NUMB #ifndef RS_USER_VARS_NUMB
#define RS_USER_VARS_NUMB 0 #define RS_USER_VARS_NUMB 0
#endif #endif
/** @endcond */ /** @endcond */
// направление передачи rs485
#ifndef RS_EnableReceive
#define RS_EnableReceive() ///< Функция изменения направления передачи на ПРИЕМ для RS-485
#endif
#ifndef RS_EnableTransmit
#define RS_EnableTransmit() ///< Функция изменения направления передачи на ПЕРЕДАЧУ для RS-485
#endif
////////////////////////////---DEFINES---//////////////////////////// ////////////////////////////---DEFINES---////////////////////////////
@@ -149,61 +150,44 @@ typedef enum // RS_StatusTypeDef
}RS_StatusTypeDef; }RS_StatusTypeDef;
#define RS_MASTER_START 0x3 #define RS_MASTER_MODE_START 0x3 ///< Начало режимов мастера (до него - режим слейв)
/** @brief Enums for RS Modes */ /** @brief Enums for RS Modes */
typedef enum // RS_ModeTypeDef typedef enum // RS_ModeTypeDef
{ {
RS_SLAVE_ALWAYS_WAIT = 0x01, ///< Slave mode with infinity waiting RS_SLAVE_ALWAYS_WAIT = 0x01, ///< Слейв в постоянном ожидании
RS_SLAVE_TIMEOUT_WAIT = 0x02, ///< Slave mode with waiting with timeout RS_RESERVED = 0x02, ///< резерв
// RS_MASTER = 0x03, ///< Master mode RS_MASTER_REQUEST = 0x03, ///< Мастер с ручным запросом
//RS_MASTER_POLLING = 0x04, ///< Мастер с опросом в фоновом режиме
}RS_ModeTypeDef; }RS_ModeTypeDef;
/** @brief Enums for RS UART Modes */
typedef enum // RS_ITModeTypeDef
{
BLCK_MODE = 0x00, ///< Blocking mode
IT_MODE = 0x01, ///< Interrupt mode
}RS_ITModeTypeDef;
/** @brief Enums for Abort modes */ /** @brief Enums for Abort modes */
typedef enum // RS_AbortTypeDef typedef enum // RS_AbortTypeDef
{ {
ABORT_TX = 0x01, ///< Abort transmit ABORT_TX = 0x01, ///< Отменить передачу
ABORT_RX = 0x02, ///< Abort receive ABORT_RX = 0x02, ///< Отменить прием
ABORT_RX_TX = 0x03, ///< Abort receive and transmit ABORT_RX_TX = 0x03, ///< Отменить прием и передачу
ABORT_RS = 0x04, ///< Abort uart and reset RS structure ABORT_RS = 0x04, ///< Отменить любую работу UART в целом
}RS_AbortTypeDef; }RS_AbortTypeDef;
/** @brief Enums for RX Size modes */
typedef enum // RS_RXSizeTypeDef
{
RS_RX_Size_Const = 0x01, ///< size of receiving message is constant
RS_RX_Size_NotConst = 0x02, ///< size of receiving message isnt constant
}RS_RXSizeTypeDef;
//-----------STRUCTURE FOR HANDLE RS------------ //-----------STRUCTURE FOR HANDLE RS------------
/** @brief Struct for flags RS */ /** @brief Struct for flags RS */
typedef struct typedef struct
{ {
unsigned RX_Half:1; ///< flag: 0 - receiving msg before ByteCnt, 0 - receiving msg after ByteCnt unsigned RS_Busy:1; ///< 1 - RS занят, 0 - RS свободен
unsigned RX_Ongoing:1; ///< 1 - Прием данных в активном состоянии, 0 - Ожидаем начало приема данных
unsigned RS_Busy:1; ///< flag: 1 - RS is busy, 0 - RS isnt busy unsigned RX_Busy:1; ///< 1 - Режим приема активен, 0 - Прием не активен
unsigned RX_Ongoing:1; ///< flag: 1 - receiving data right now, 0 - waiting for receiving data unsigned TX_Busy:1; ///< 1 - Режим передачи активен, 0 - Прием не активен
unsigned RX_Busy:1; ///< flag: 1 - receiving is active, 0 - receiving isnt active unsigned RX_Done:1; ///< 1 - Прием закончен, 0 - Прием еще в процессе или не инициализирован
unsigned TX_Busy:1; ///< flag: 1 - transmiting is active, 0 - transmiting isnt active unsigned TX_Done:1; ///< 1 - Передача закончена, 0 - Передача еще в процессе или не инициализирована
unsigned RX_Done:1; ///< flag: 1 - receiving is done, 0 - receiving isnt done // Выставление следующие флагов определяет пользователь
unsigned TX_Done:1; ///< flag: 1 - transmiting is done, 0 - transmiting isnt done unsigned RX_Continue:1; ///< 0 - Продолжить принимать, 0 - Начать прием сначала
unsigned MessageHandled:1; ///< 1 - Обработка запроса успешна, 0 - Обработка запроса в процессе или ошибка
// setted by user unsigned EchoResponse:1; ///< 1 - Ответить эхом, 0 - Ответить своим сообщением
unsigned RX_Continue:1; ///< flag: 0 - continue receiving, 0 - start receiving from ind = 0 unsigned DeferredResponse:1; ///< 1 - Не начинать передачу в IT, 0 - Ответить в прерывании
unsigned MessageHandled:1; ///< flag: 1 - RS command is handled, 0 - RS command isnt handled yet unsigned DataUpdated:1; ///< 1 - Данные были обновлены
unsigned EchoResponse:1; ///< flag: 1 - response with received msg, 0 - response with own msg
unsigned DeferredResponse:1; ///< flag: 1 - response not in interrupt, 0 - response in interrupt
unsigned DataUpdated:1; ///< flag: 1 - Received command to write colis/resg
unsigned ReInit_UART:1; ///< flag: 1 - need to reinitialize uart, 0 - nothing
}RS_FlagsTypeDef; }RS_FlagsTypeDef;
@@ -215,25 +199,24 @@ typedef struct
typedef struct // RS_HandleTypeDef typedef struct // RS_HandleTypeDef
{ {
/* MESSAGE */ /* MESSAGE */
uint8_t ID; ///< ID of RS "channel" uint8_t ID; ///< ID хендла
RS_MsgTypeDef *pMessagePtr; ///< pointer to message struct RS_MsgTypeDef *pMessagePtr; ///< Указатель на структуру протокола
uint8_t *pBufferPtr; ///< pointer to message buffer uint8_t *pBufferPtr; ///< Указатеь на буфер UART
int32_t RS_Message_Size; ///< size of whole message, not only data int32_t RS_Message_Size; ///< size of whole message, not only data
/* HANDLERS and SETTINGS */ /* HANDLERS and SETTINGS */
UART_HandleTypeDef *huart; ///< handler for used uart UART_HandleTypeDef *huart; ///< Хендл UART
TIM_HandleTypeDef *htim; ///< handler for used tim TIM_HandleTypeDef *htim; ///< Хендл TIM
RS_ModeTypeDef sRS_Mode; ///< setting: slave or master @ref RS_ModeTypeDef RS_ModeTypeDef sRS_Mode; ///< Настройка: слейв/мастер @ref RS_ModeTypeDef
RS_ITModeTypeDef sRS_IT_Mode; ///< setting: 1 - IT mode, 0 - Blocking mode uint16_t sRS_Timeout; ///< Настройка: Таймаут в тиках таймера
uint16_t sRS_Timeout; ///< setting: timeout in ms void (*pCallback)(void*, void*); ///< Указатель на коллбек: принят ответ в режиме мастер
RS_RXSizeTypeDef sRS_RX_Size_Mode; ///< setting: 1 - not const, 0 - const
/* FLAGS */ /* FLAGS */
RS_FlagsTypeDef f; ///< These flags for controling receive/transmit RS_FlagsTypeDef f; ///< Флаги для контроля приема/передачи
/* RS STATUS */ /* RS STATUS */
uint32_t lastPacketTick; uint32_t lastPacketTick; ///< Время последнего принятого пакета
RS_StatusTypeDef RS_STATUS; ///< RS status RS_StatusTypeDef RS_STATUS; ///< Статус RS
TrackerTypeDef(RS_USER_VARS_NUMB) rs_err; TrackerTypeDef(RS_USER_VARS_NUMB) rs_err;
}RS_HandleTypeDef; }RS_HandleTypeDef;
@@ -247,47 +230,53 @@ extern RS_HandleTypeDef hmodbus1;
///////////////////////////---FUNCTIONS---/////////////////////////// ///////////////////////////---FUNCTIONS---///////////////////////////
//----------------FUNCTIONS FOR PROCESSING MESSAGE------------------- //----------------FUNCTIONS FOR PROCESSING MESSAGE-------------------
/*--------------------Defined by users purposes--------------------*/ /*--------------------Defined by users purposes--------------------*/
/* Respond accord to received message */ /* Пользовательская функция для ответа на запрос по UART */
RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
/* Collect message in buffer to transmit it */ /* Пользовательская функция для обработки принятого ответа по UART */
__weak RS_StatusTypeDef RS_Response_Callback(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
/* Пользовательская функция для сбора сообщения в буфер UART */
RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff); RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff);
/* Parse message from buffer to process it */ /* Пользовательская функция для парса сообщения из буфера UART */
RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff); RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff);
//-------------------------GENERAL FUNCTIONS------------------------- //-------------------------GENERAL FUNCTIONS-------------------------
/*-----------------Should be called from main code-----------------*/ /*-----------------Should be called from main code-----------------*/
/* Start receive IT */ /* Начать прием по прерываниям */
RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
/* Start transmit IT */ /* Начать передачу по прерываниям */
RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
/* Initialize UART and handle RS stucture */ /* Инициалазация структуры @ref RS_HandleTypeDef */
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr); RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr);
/* ReInitialize UART and RS receive */ /* Отменить прием/передачу RS/UART */
HAL_StatusTypeDef RS_ReInit_UART(RS_HandleTypeDef *hRS, UART_HandleTypeDef *suart);
/* Abort RS/UART */
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode); RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode);
//-------------------------GENERAL FUNCTIONS------------------------- //-------------------------GENERAL FUNCTIONS-------------------------
//------------------------------------------------------------------- //-------------------------------------------------------------------
//--------------------CALLBACK/HANDLER FUNCTIONS--------------------- //--------------------CALLBACK/HANDLER FUNCTIONS---------------------
/* Handle for starting receive */ /* Обработчик для начала приема */
RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
/* Handle for starting transmit */ /* Обработчик для начала передачи */
RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg); RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg);
/* UART TX Callback: define behaviour after transmiting message */ /* UART TX Callback: коллбек после окончания передачи */
RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS); RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS);
/* Handler for UART */ /* Обработчик прерывания UART */
void RS_UART_Handler(RS_HandleTypeDef *hRS); void RS_UART_Handler(RS_HandleTypeDef *hRS);
/* Handler for TIM */ /* Обработчик прерывания TIM */
void RS_TIM_Handler(RS_HandleTypeDef *hRS); void RS_TIM_Handler(RS_HandleTypeDef *hRS);
/* Запуск таймаута приема. */
RS_StatusTypeDef RS_Timeout_Start(RS_HandleTypeDef *hRS);
/* Остановка таймаута приема. */
RS_StatusTypeDef RS_Timeout_Stop(RS_HandleTypeDef *hRS);
/* Обновление (сброс) таймаута приема. */
RS_StatusTypeDef RS_Timeout_Update(RS_HandleTypeDef *hRS);
//--------------------CALLBACK/HANDLER FUNCTIONS--------------------- //--------------------CALLBACK/HANDLER FUNCTIONS---------------------
///////////////////////////---FUNCTIONS---/////////////////////////// ///////////////////////////---FUNCTIONS---///////////////////////////

View File

@@ -6,61 +6,61 @@
* @details * @details
Файл содержит реализацию функций работы с Modbus. Файл содержит реализацию функций работы с Modbus.
@section Функции и макросы @section Функции и макросы
### Инициализация: ### Инициализация:
- MODBUS_SetupHardware() — Инициализация модуля Modbus. - MODBUS_FirstInit() — Инициализация Modbus (подключение UART, TIM)
- MODBUS_Config() — Инициализация модуля Modbus. - MODBUS_Config() — Конфигурацмя Modbus (ID, Timeout).
- MODBUS_SlaveStart() — Запуск Modbus как Slave.
### Функции для Modbus - MODBUS_MasterRequest() — Отправить запрос в MODBUS как Master.
- MB_Slave_Response()
- MB_Slave_Collect_Message()
- MB_Slave_Parse_Message()
- MB_Master_Collect_Message()
- MB_Master_Parse_Message()
### Функции для работы с RS (UART): ### Функции для работы с RS (UART):
- RS_Parse_Message() / RS_Collect_Message() — Парсинг и сборка сообщения. - RS_Parse_Message() / RS_Collect_Message() — Парсинг и сборка сообщения.
- RS_Response() — Отправка ответа. - RS_Response() — Отправка ответа.
******************************************************************************/ ******************************************************************************/
#include "modbus.h" #include "modbus.h"
/* MODBUS HANDLES */ /* MODBUS HANDLES */
RS_HandleTypeDef hmodbus1; RS_HandleTypeDef hmodbus1; ///< Default Handle for Modbus
RS_MsgTypeDef MODBUS_MSG; RS_MsgTypeDef MODBUS_MSG; ///< Default Message Struct for Modbus
/* DEFINE REGISTERS/COILS */ /* DEFINE DATA FOR MODBUS */
MB_DeviceIdentificationTypeDef MB_DEVID; MB_DataStructureTypeDef MB_DATA = {0};; ///< Coils & Registers
MB_DataStructureTypeDef MB_DATA = {0};;
static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg);
//------------------------------------------------------------------- //-------------------------------------------------------------------
//-----------------------------FOR USER------------------------------ //-----------------------------FOR USER------------------------------
/** /**
* @brief Инициализация периферии модбас. * @brief Инициализация периферии модбас.
* @param hmodbus Указатель на хендлер RS * @param hmodbus Указатель на хендлер RS
* @param huart Указатель на хендлер UART
* @param htim Указатель на хендлер TIM
* @details Подключает хендлы периферии к hmodbus * @details Подключает хендлы периферии к hmodbus
* Конфигурация выставляется по умолчанию из modbus_config.h * Конфигурация выставляется по умолчанию из modbus_config.h
*/ */
void MODBUS_SetupHardware(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim) HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim)
{ {
if((hmodbus == NULL) || (huart == NULL)) if((hmodbus == NULL) || (huart == NULL))
{ {
return; return HAL_ERROR;
} }
MB_DeviceInentificationInit(); MB_DeviceInentificationInit();
//-----------SETUP MODBUS------------- //-----------SETUP MODBUS-------------
// set up modbus: MB_RX_Size_NotConst and Timeout enable // set up modbus: MB_RX_Size_NotConst and Timeout enable
hmodbus1.ID = MODBUS_DEVICE_ID; hmodbus->ID = MODBUS_DEVICE_ID;
hmodbus1.sRS_Timeout = MODBUS_TIMEOUT; hmodbus->sRS_Timeout = MODBUS_TIMEOUT;
hmodbus1.sRS_Mode = RS_SLAVE_ALWAYS_WAIT; hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
hmodbus1.sRS_RX_Size_Mode = RS_RX_Size_NotConst;
// INIT // INIT
hmodbus1.RS_STATUS = RS_Init(hmodbus, huart, htim, 0); hmodbus->RS_STATUS = RS_Init(hmodbus, huart, htim, 0);
RS_EnableReceive(); RS_EnableReceive();
if(hmodbus->RS_STATUS == RS_OK)
return HAL_OK;
else
return HAL_ERROR;
} }
/** /**
* @brief Программная конфигурация модбас. * @brief Программная конфигурация модбас.
@@ -69,460 +69,115 @@ void MODBUS_SetupHardware(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart,
* @param master Режим мастер (пока не сделан) * @param master Режим мастер (пока не сделан)
* @details Конфигурирует ID, таймаут и режим hmodbus * @details Конфигурирует ID, таймаут и режим hmodbus
*/ */
void MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master) HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master)
{ {
if(hmodbus == NULL) if(hmodbus == NULL)
{ {
return; return HAL_ERROR;
} }
//-----------SETUP MODBUS------------- if(!master)
// set up modbus: MB_RX_Size_NotConst and Timeout enable {
hmodbus->ID = ID; if((ID < 1) || (ID > 247))
{
return HAL_ERROR;
}
hmodbus->ID = ID;
}
else
hmodbus->ID = 0;
hmodbus->sRS_Timeout = Timeout; hmodbus->sRS_Timeout = Timeout;
if(master) if(master)
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT; hmodbus->sRS_Mode = RS_MASTER_REQUEST;
else else
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT; hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
hmodbus->sRS_RX_Size_Mode = RS_RX_Size_NotConst;
return HAL_OK;
} }
/** /**
* @brief Запуск слейв модбас. * @brief Запуск слейв модбас.
* @param hmodbus Указатель на хендлер RS. * @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения. * @param modbus_msg Указатель на структуру сообщения.
(NULL чтобы использовать дефолтную)
* @details Конфигурирует ID, таймаут и режим hmodbus * @details Конфигурирует ID, таймаут и режим hmodbus
*/ */
void MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{ {
if(hmodbus == NULL) if(hmodbus == NULL)
{ {
return; return HAL_ERROR;
} }
if(hmodbus->sRS_Mode >= RS_MASTER_MODE_START)
{
return HAL_ERROR;
}
MB_DiagnosticsInit();
if(modbus_msg) if(modbus_msg)
RS_Receive_IT(hmodbus, modbus_msg); hmodbus->RS_STATUS = RS_Receive_IT(hmodbus, modbus_msg);
else else
RS_Receive_IT(hmodbus, &MODBUS_MSG); hmodbus->RS_STATUS = RS_Receive_IT(hmodbus, &MODBUS_MSG);
if(hmodbus->RS_STATUS == RS_OK)
return HAL_OK;
else
return HAL_ERROR;
} }
/** /**
* @brief Ответ на сообщение в режиме слейва. * @brief Реквест мастера модбас.
* @param hmodbus Указатель на хендлер RS. * @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения. * @param modbus_msg Указатель на структуру сообщения
* @return RS_RES Статус о результате ответа на комманду. * @details Конфигурирует ID, таймаут и режим hmodbus
*/ */
static RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) HAL_StatusTypeDef MODBUS_MasterRequest(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, void (*pClbk)(RS_HandleTypeDef*, RS_MsgTypeDef*))
{ {
RS_StatusTypeDef MB_RES = 0; if(hmodbus == NULL)
hmodbus->f.MessageHandled = 0;
hmodbus->f.EchoResponse = 0;
RS_Reset_TX_Flags(hmodbus); // reset flag for correct transmit
if(hmodbus->ID == 0)
{ {
hmodbus->RS_STATUS = RS_SKIP; return HAL_ERROR;
return MB_RES; }
if(modbus_msg == NULL)
{
return HAL_ERROR;
}
if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
{
return HAL_ERROR;
} }
if(modbus_msg->Func_Code < ERR_VALUES_START)// if no errors after parsing if(hmodbus->f.RS_Busy)
{ return HAL_BUSY;
switch (modbus_msg->Func_Code)
{
// Read Coils
case MB_R_COILS:
hmodbus->f.MessageHandled = MB_Proccess_Read_Coils(hmodbus->pMessagePtr);
break;
// Read Hodling Registers if(pClbk) // если задан используем пользовательский коллбек
case MB_R_HOLD_REGS: hmodbus->pCallback = (void (*)(void*, void*))(pClbk);
hmodbus->f.MessageHandled = MB_Proccess_Read_Hold_Regs(hmodbus->pMessagePtr); else // иначе дефолтный
break; hmodbus->pCallback = (void (*)(void*, void*))(&MB_DefaultCallback);
case MB_R_IN_REGS:
hmodbus->f.MessageHandled = MB_Proccess_Read_Input_Regs(hmodbus->pMessagePtr);
break;
hmodbus->RS_STATUS = RS_Transmit_IT(hmodbus, modbus_msg);
// Write Single Coils if(hmodbus->RS_STATUS == RS_OK)
case MB_W_COIL: return HAL_OK;
hmodbus->f.MessageHandled = MB_Proccess_Write_Single_Coil(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
hmodbus->f.DataUpdated = 1;
hmodbus->f.EchoResponse = 1;
hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes)
}
break;
case MB_W_HOLD_REG:
hmodbus->f.MessageHandled = MB_Proccess_Write_Single_Reg(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
hmodbus->f.DataUpdated = 1;
hmodbus->f.EchoResponse = 1;
hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes)
}
break;
// Write Multiple Coils
case MB_W_COILS:
hmodbus->f.MessageHandled = MB_Write_Miltuple_Coils(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
hmodbus->f.DataUpdated = 1;
hmodbus->f.EchoResponse = 1;
hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes)
}
break;
// Write Multiple Registers
case MB_W_HOLD_REGS:
hmodbus->f.MessageHandled = MB_Proccess_Write_Miltuple_Regs(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
hmodbus->f.DataUpdated = 1;
hmodbus->f.EchoResponse = 1;
hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes)
}
break;
case MB_R_DEVICE_INFO:
hmodbus->f.MessageHandled = MB_Proccess_Read_Device_Identification(hmodbus->pMessagePtr);
break;
/* unknown func code */
default: modbus_msg->Except_Code = 0x01; /* set exception code: illegal function */
}
if(hmodbus->f.MessageHandled == 0)
{
TrackerCnt_Warn(hmodbus->rs_err);
modbus_msg->Func_Code |= ERR_VALUES_START;
}
else
{
TrackerCnt_Ok(hmodbus->rs_err);
}
}
// if we need response - check that transmit isnt busy
if( RS_Is_TX_Busy(hmodbus) )
RS_Abort(hmodbus, ABORT_TX); // if tx busy - set it free
// Transmit right there, or sets (fDeferredResponse) to transmit response in main code
if(hmodbus->f.DeferredResponse == 0)
{
MB_RES = RS_Handle_Transmit_Start(hmodbus, modbus_msg);
}
else else
{ return HAL_ERROR;
RS_Handle_Receive_Start(hmodbus, modbus_msg);
hmodbus->f.DeferredResponse = 0;
}
hmodbus->RS_STATUS = MB_RES;
return MB_RES;
} }
/**
* @brief Сбор сообщения в буфер UART в режиме слейв. static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения буфера.
*/
static RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{ {
int ind = 0; // ind for modbus-uart buffer __NOP();
return;
if(hmodbus->f.EchoResponse && hmodbus->f.MessageHandled) // if echo response need
ind = hmodbus->RS_Message_Size;
else
{
//------INFO ABOUT DATA/MESSAGE------
//-----------[first bytes]-----------
// set ID of message/user
modbus_uart_buff[ind++] = modbus_msg->MbAddr;
// set dat or err response
modbus_uart_buff[ind++] = modbus_msg->Func_Code;
if (modbus_msg->Func_Code < ERR_VALUES_START) // if no error occur
{
// fill modbus header
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // devide identification header
{
modbus_uart_buff[ind++] = modbus_msg->DevId.MEI_Type;
modbus_uart_buff[ind++] = modbus_msg->DevId.ReadDevId;
modbus_uart_buff[ind++] = modbus_msg->DevId.Conformity;
modbus_uart_buff[ind++] = modbus_msg->DevId.MoreFollows;
modbus_uart_buff[ind++] = modbus_msg->DevId.NextObjId;
modbus_uart_buff[ind++] = modbus_msg->DevId.NumbOfObj;
if (modbus_msg->ByteCnt > DATA_SIZE*2) // if ByteCnt less than DATA_SIZE
{
TrackerCnt_Err(hmodbus->rs_err);
return RS_COLLECT_MSG_ERR;
}
//---------------DATA----------------
//-----------[data bytes]------------
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA;
for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data
{ // set data
modbus_uart_buff[ind++] = *tmp_data_addr;
tmp_data_addr++;
}
}
else // modbus data header
{
// set size of received data
if (modbus_msg->ByteCnt <= DATA_SIZE*2) // if ByteCnt less than DATA_SIZE
modbus_uart_buff[ind++] = modbus_msg->ByteCnt;
else // otherwise return data_size err
{
TrackerCnt_Err(hmodbus->rs_err);
return RS_COLLECT_MSG_ERR;
}
//---------------DATA----------------
//-----------[data bytes]------------
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA;
for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data
{ // set data
if (i%2 == 0) // HI byte
modbus_uart_buff[ind++] = (*tmp_data_addr)>>8;
else // LO byte
{
modbus_uart_buff[ind++] = *tmp_data_addr;
tmp_data_addr++;
}
}
}
}
else // if some error occur
{ // send expection code
modbus_uart_buff[ind++] = modbus_msg->Except_Code;
}
}
if(ind < 0)
return RS_COLLECT_MSG_ERR;
//---------------CRC----------------
//---------[last 16 bytes]----------
// calc crc of received data
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
// write crc to message structure and modbus-uart buffer
modbus_msg->MB_CRC = CRC_VALUE;
modbus_uart_buff[ind++] = CRC_VALUE;
modbus_uart_buff[ind++] = CRC_VALUE >> 8;
hmodbus->RS_Message_Size = ind;
return RS_OK; // returns ok
} }
/**
* @brief Определить размер модбас запроса.
* @param hRS Указатель на хендлер RS.
* @param rx_data_size Указатель на переменную для записи кол-ва байт для принятия.
* @return RS_RES Статус о корректности рассчета кол-ва байт для принятия.
* @details Определение сколько байтов надо принять по протоколу.
*/
static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
RS_StatusTypeDef MB_RES = 0;
int mb_func_size = 0;
if ((modbus_msg->Func_Code & ~ERR_VALUES_START) < 0x0F)
{
modbus_msg->ByteCnt = 0;
mb_func_size = 1;
}
else
{
modbus_msg->ByteCnt = hmodbus->pBufferPtr[RX_FIRST_PART_SIZE-1]; // get numb of data in command
// +1 because that defines is size, not ind.
mb_func_size = modbus_msg->ByteCnt + 2;
}
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO)
{
mb_func_size = 0;
}
mb_func_size = RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
return mb_func_size;
}
/**
* @brief Парс сообщения в режиме слейв.
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения структуры.
* @details Заполнение структуры сообщения из буффера UART.
*/
static RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
uint32_t check_empty_buff;
int ind = 0; // ind for modbus-uart buffer
hmodbus->f.RX_Continue = 0;
//-----INFO ABOUT DATA/MESSAGE-------
//-----------[first bits]------------
// get ID of message/user
if(modbus_uart_buff[ind] != hmodbus->ID)
{
modbus_msg->MbAddr = 0;
return RS_SKIP;
}
modbus_msg->MbAddr = modbus_uart_buff[ind++];
// get func code
modbus_msg->Func_Code = modbus_uart_buff[ind++];
if(modbus_msg->Func_Code & ERR_VALUES_START) // явная херня
{
modbus_msg->MbAddr = 0;
return RS_SKIP;
}
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // if it device identification request
{
modbus_msg->DevId.MEI_Type = modbus_uart_buff[ind++];
modbus_msg->DevId.ReadDevId = modbus_uart_buff[ind++];
modbus_msg->DevId.NextObjId = modbus_uart_buff[ind++];
modbus_msg->ByteCnt = 0;
}
else // if its classic modbus request
{
// get address from CMD
modbus_msg->Addr = modbus_uart_buff[ind++] << 8;
modbus_msg->Addr |= modbus_uart_buff[ind++];
// get address from CMD
modbus_msg->Qnt = modbus_uart_buff[ind++] << 8;
modbus_msg->Qnt |= modbus_uart_buff[ind++];
}
if((hmodbus->pMessagePtr->Func_Code == 0x0F) || (hmodbus->pMessagePtr->Func_Code == 0x10))
hmodbus->pMessagePtr->ByteCnt = modbus_uart_buff[ind++];
else
hmodbus->pMessagePtr->ByteCnt = 0;
//---------------DATA----------------
// (optional)
if (modbus_msg->ByteCnt != 0)
{
//check that data size is correct
if (modbus_msg->ByteCnt > DATA_SIZE*2)
{
TrackerCnt_Err(hmodbus->rs_err);
modbus_msg->Func_Code |= ERR_VALUES_START;
return RS_PARSE_MSG_ERR;
}
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA;
for(int i = 0; i < modbus_msg->ByteCnt; i++)
{ // set data
if (i%2 == 0)
*tmp_data_addr = ((uint16_t)modbus_uart_buff[ind++] << 8);
else
{
*tmp_data_addr |= modbus_uart_buff[ind++];
tmp_data_addr++;
}
}
}
//---------------CRC----------------
//----------[last 16 bits]----------
// calc crc of received data
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
// get crc of received data
modbus_msg->MB_CRC = modbus_uart_buff[ind++];
modbus_msg->MB_CRC |= modbus_uart_buff[ind++] << 8;
// compare crc
if (modbus_msg->MB_CRC != CRC_VALUE)
{
TrackerCnt_Err(hmodbus->rs_err);
modbus_msg->Func_Code |= ERR_VALUES_START;
}
// hmodbus->MB_RESPONSE = MB_CRC_ERR; // set func code - error about wrong crc
// check is buffer empty
check_empty_buff = 0;
for(int i=0; i<ind;i++)
check_empty_buff += modbus_uart_buff[i];
// if(check_empty_buff == 0)
// hmodbus->MB_RESPONSE = MB_EMPTY_MSG; //
// если размер меньше ожидаемого - продолжаем принимать
if(hmodbus->RS_Message_Size < MB_Define_Size_of_Function(hmodbus, modbus_msg))
{
hmodbus->f.RX_Continue = 1;
return RS_SKIP;
}
// если больше Ошибка
else if (hmodbus->RS_Message_Size > MB_Define_Size_of_Function(hmodbus, modbus_msg))
{
return RS_PARSE_MSG_ERR;
}
return RS_OK;
}
/**
* @brief Сбор сообщения в буфер UART в режиме мастер.
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения буфера.
*/
static RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
return RS_PARSE_MSG_ERR;
}
/**
* @brief Парс сообщения в режиме мастер.
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения структуры.
*/
static RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
return RS_PARSE_MSG_ERR;
}
/* Реализация функций из rs_message.c для протокола */ /* Реализация функций из rs_message.c для протокола */
RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{ {
if(hmodbus->sRS_Mode >= RS_MASTER_START) if(hmodbus->sRS_Mode >= RS_MASTER_MODE_START)
{ {
return RS_ERR; return RS_ERR;
} }
@@ -531,7 +186,7 @@ RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_ms
RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{ {
if(hmodbus->sRS_Mode < RS_MASTER_START) if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
{ {
return MB_Slave_Collect_Message(hmodbus, modbus_msg, modbus_uart_buff); return MB_Slave_Collect_Message(hmodbus, modbus_msg, modbus_uart_buff);
} }
@@ -543,7 +198,7 @@ RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mo
RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{ {
if(hmodbus->sRS_Mode < RS_MASTER_START) if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
{ {
return MB_Slave_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff); return MB_Slave_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff);
} }

View File

@@ -12,10 +12,10 @@
******************************************************************************/ ******************************************************************************/
#include "modbus_coils.h" #include "modbus_coils.h"
#ifdef MODBUS_ENABLE_COILS
/** /**
* @brief Set or Reset Coil at its global address. * @brief Выставить/сбросить коил по глобальному адресу.
* @param Addr Адрес коила. * @param Addr Адрес коила.
* @param WriteVal Что записать в коил: 0 или 1. * @param WriteVal Что записать в коил: 0 или 1.
* @return ExceptionCode Код исключения если коила по адресу не существует, и NO_ERRORS если все ок. * @return ExceptionCode Код исключения если коила по адресу не существует, и NO_ERRORS если все ок.
@@ -23,7 +23,7 @@
* @details Позволяет обратиться к любому коилу по его глобальному адрессу. * @details Позволяет обратиться к любому коилу по его глобальному адрессу.
Вне зависимости от того как коилы размещены в памяти. Вне зависимости от того как коилы размещены в памяти.
*/ */
MB_ExceptionTypeDef MB_Write_Coil_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal) MB_ExceptionTypeDef MB_Coil_Write_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal)
{ {
//---------CHECK FOR ERRORS---------- //---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception = NO_ERRORS; MB_ExceptionTypeDef Exception = NO_ERRORS;
@@ -55,7 +55,7 @@ MB_ExceptionTypeDef MB_Write_Coil_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteV
/** /**
* @brief Read Coil at its global address. * @brief Считать коил по глобальному адресу.
* @param Addr Адрес коила. * @param Addr Адрес коила.
* @param Exception Указатель на переменную для кода исключения, в случа неудачи при чтении. * @param Exception Указатель на переменную для кода исключения, в случа неудачи при чтении.
* @return uint16_t Возвращает весь регистр с маской на запрошенном коиле. * @return uint16_t Возвращает весь регистр с маской на запрошенном коиле.
@@ -63,7 +63,7 @@ MB_ExceptionTypeDef MB_Write_Coil_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteV
* @details Позволяет обратиться к любому коилу по его глобальному адрессу. * @details Позволяет обратиться к любому коилу по его глобальному адрессу.
Вне зависимости от того как коилы размещены в памяти. Вне зависимости от того как коилы размещены в памяти.
*/ */
uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception) uint16_t MB_Coil_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
{ {
//---------CHECK FOR ERRORS---------- //---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception_tmp; MB_ExceptionTypeDef Exception_tmp;
@@ -86,13 +86,58 @@ uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
} }
/** /**
* @brief Proccess command Read Coils (01 - 0x01). * @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. * @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды. * @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Read Coils. * @details Обработка команды Read Coils.
*/ */
uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg) uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg)
{ {
//---------CHECK FOR ERRORS---------- //---------CHECK FOR ERRORS----------
uint16_t *coils; uint16_t *coils;
@@ -140,12 +185,12 @@ uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg)
} }
/** /**
* @brief Proccess command Write Single Coils (05 - 0x05). * @brief Обработать функцию Write Single Coils (05 - 0x05).
* @param modbus_msg Указатель на структуру собщения modbus. * @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды. * @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Single Coils. * @details Обработка команды Write Single Coils.
*/ */
uint8_t MB_Proccess_Write_Single_Coil(RS_MsgTypeDef *modbus_msg) uint8_t MB_Process_Write_Single_Coil(RS_MsgTypeDef *modbus_msg)
{ {
//---------CHECK FOR ERRORS---------- //---------CHECK FOR ERRORS----------
if ((modbus_msg->Qnt != 0x0000) && (modbus_msg->Qnt != 0xFF00)) if ((modbus_msg->Qnt != 0x0000) && (modbus_msg->Qnt != 0xFF00))
@@ -171,12 +216,12 @@ uint8_t MB_Proccess_Write_Single_Coil(RS_MsgTypeDef *modbus_msg)
} }
/** /**
* @brief Proccess command Write Multiple Coils (15 - 0x0F). * @brief Обработать функцию Write Multiple Coils (15 - 0x0F).
* @param modbus_msg Указатель на структуру собщения modbus. * @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды. * @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Multiple Coils. * @details Обработка команды Write Multiple Coils.
*/ */
uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg) uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg)
{ {
//---------CHECK FOR ERRORS---------- //---------CHECK FOR ERRORS----------
if (modbus_msg->ByteCnt != Divide_Up(modbus_msg->Qnt, 8)) if (modbus_msg->ByteCnt != Divide_Up(modbus_msg->Qnt, 8))
@@ -236,3 +281,14 @@ uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg)
return 1; 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

View File

@@ -9,7 +9,7 @@
- Определение указателей на реальные данные в памяти - Определение указателей на реальные данные в памяти
- Поддержка пользовательских массивов регистров и coils - Поддержка пользовательских массивов регистров и coils
@section Валидация адресов: @section Валидация адресов:
- MB_Check_Address_For_Arr() - проверка принадлежности адреса массиву - MB_Check_Address_For_Arr() - проверка принадлежности адреса массиву
- MB_DefineRegistersAddress() - получение указателя на регистры - MB_DefineRegistersAddress() - получение указателя на регистры
- MB_DefineCoilsAddress() - получение указателя на coils - MB_DefineCoilsAddress() - получение указателя на coils
@@ -23,7 +23,6 @@
#include "modbus_devid.h" #include "modbus_devid.h"
/** /**
* @brief Check is address valid for certain array. * @brief Check is address valid for certain array.
* @param Addr Начальный адресс. * @param Addr Начальный адресс.
@@ -77,7 +76,7 @@ MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, u
// Default holding registers // 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) == NO_ERRORS)
{ {
*pRegs = MB_Set_Register_Ptr(&MB_DATA.HoldRegs, Addr); // указатель на выбранный по Addr регистр *pRegs = MB_Set_Register_Ptr(&MB_DATA.HoldRegs, Addr - R_HOLDING_ADDR); // указатель на выбранный по Addr регистр
} }
// if address doesnt match any array - return illegal data address response // if address doesnt match any array - return illegal data address response
else else
@@ -90,7 +89,7 @@ MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, u
// Default input registers // 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) == NO_ERRORS)
{ {
*pRegs = MB_Set_Register_Ptr(&MB_DATA.InRegs, Addr); // указатель на выбранный по Addr регистр *pRegs = MB_Set_Register_Ptr(&MB_DATA.InRegs, Addr - R_INPUT_ADDR); // указатель на выбранный по Addr регистр
} }
// if address doesnt match any array - return illegal data address response // if address doesnt match any array - return illegal data address response
else else
@@ -128,7 +127,7 @@ MB_ExceptionTypeDef MB_DefineCoilsAddress(uint16_t **pCoils, uint16_t Addr, uint
// Default coils // 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) == NO_ERRORS)
{ {
*pCoils = MB_Set_Coil_Reg_Ptr(&MB_DATA.Coils, Addr); // указатель на выбранный по Addr массив коилов *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 // if address doesnt match any array - return illegal data address response
else else

View File

@@ -1,7 +1,7 @@
/** /**
****************************************************************************** ******************************************************************************
* @file modbus_devid.c * @file modbus_devid.c
* @brief Реализация идентификации устройства Modbus * @brief Реализация идентификаторов устройства Modbus
****************************************************************************** ******************************************************************************
* @details * @details
Модуль обработки запросов идентификации устройства через MEI-тип 0x0E: Модуль обработки запросов идентификации устройства через MEI-тип 0x0E:
@@ -9,20 +9,151 @@
- Поддержка потоковой передачи при большом количестве объектов - Поддержка потоковой передачи при большом количестве объектов
- Автоматический расчет MoreFollows флагов - Автоматический расчет MoreFollows флагов
@section Потоковая передача: @section Потоковая передача:
При большом количестве объектов идентификация разбивается на несколько При большом количестве объектов идентификация разбивается на несколько
сообщений с установкой флага MoreFollows и указанием NextObjId для сообщений с установкой флага MoreFollows и указанием NextObjId для
продолжения чтения в следующем запросе. продолжения чтения в следующем запросе.
******************************************************************************/ ******************************************************************************/
#include "modbus_devid.h" #include "modbus_devid.h"
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
MB_DeviceIdentificationsTypeDef MB_DEVID; ///< Глобальная структура идентификаторов устройства
/** /**
* @brief Write Object of Device Identification to MessageData * @brief Получить количество объектов в сообщении
* @param mbdata Указатель на массив данных в структуре RS_MsgTypeDef. * @param modbus_msg Указатель на структуру сообщения
* @return obj Объект для записи. * @return int Количество объектов
*/ */
int MB_RespGet_NumberOfObjects(RS_MsgTypeDef *modbus_msg)
{
if(modbus_msg == NULL)
{
return 0;
}
// Проверяем что ответ связан с диагностикой
if(modbus_msg->Func_Code != MB_R_DEVICE_INFO)
{
return 0;
}
return modbus_msg->DevId.NumbOfObj;
}
/**
* @brief Найти объект по ID в сообщении
* @param modbus_msg Указатель на структуру сообщения
* @param obj_id ID искомого объекта
* @param obj_data Буфер для данных объекта (может быть NULL)
* @param obj_length Указатель для длины объекта
* @return int Найден ли объект (1 - да, 0 - нет)
*/
int MB_RespGet_ObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length)
{
if((modbus_msg == NULL) || (obj_data == NULL))
return 0;
// Проверяем что ответ связан с диагностикой
if(modbus_msg->Func_Code != MB_R_DEVICE_INFO)
{
return 0;
}
uint8_t *data = (uint8_t*)modbus_msg->DATA;
unsigned ind = 0;
for(int i = 0; i < modbus_msg->DevId.NumbOfObj; i++)
{
uint8_t current_id = data[ind++];
uint8_t current_length = data[ind++];
if(current_id == obj_id)
{
if(obj_length)
*obj_length = current_length;
for(int j = 0; j < current_length; j++)
{
obj_data[j] = data[ind++];
}
obj_data[current_length] = '\0'; // добавляем \0
return 1;
}
else
{
// Пропускаем данные этого объекта
ind += current_length;
}
}
return 0;
}
/**
* @brief Получить объект по индексу в сообщении
* @param modbus_msg Указатель на структуру сообщения
* @param index Индекс объекта (0..N-1)
* @param obj_id Указатель для ID объекта
* @param obj_data Буфер для данных объекта
* @param obj_length Указатель для длины объекта
* @return int Успешность получения (1 - получен, 0 - не найден)
*/
int MB_RespGet_ObjectByIndex(RS_MsgTypeDef *modbus_msg, int index, uint8_t *obj_id, char *obj_data, uint8_t *obj_length)
{
if((modbus_msg == NULL) || (obj_data == NULL))
return 0;
// Проверяем что ответ связан с диагностикой
if(modbus_msg->Func_Code != MB_R_DEVICE_INFO)
{
return 0;
}
if(index >= modbus_msg->DevId.NumbOfObj)
return 0;
uint8_t *data = (uint8_t*)modbus_msg->DATA;
unsigned ind = 0;
for(int i = 0; i <= index; i++)
{
uint8_t current_id = data[ind++];
uint8_t current_length = data[ind++];
if(obj_id)
*obj_id = current_id;
if(obj_length)
*obj_length = current_length;
if(i == index)
{
for(int j = 0; j < current_length; j++)
{
obj_data[j] = data[ind++];
}
obj_data[current_length] = '\0'; // добавляем \0
return 1;
}
else
{
// Пропускаем данные этого объекта
ind += current_length;
}
}
return 0;
}
/**
* @brief Записать Один Объект Идентификатора в массив данных
* @param mbdata Указатель на массив данных в структуре RS_MsgTypeDef.
* @return obj Объект для записи.
*/
void MB_WriteSingleObjectToMessage(char *mbdata, unsigned *ind, MB_DeviceObjectTypeDef *obj) void MB_WriteSingleObjectToMessage(char *mbdata, unsigned *ind, MB_DeviceObjectTypeDef *obj)
{ {
mbdata[(*ind)++] = obj->length; mbdata[(*ind)++] = obj->length;
@@ -34,10 +165,10 @@ void MB_WriteSingleObjectToMessage(char *mbdata, unsigned *ind, MB_DeviceObjectT
/** /**
* @brief Write Object of Device Identification to MessageData * @brief Записать Массив Объектов Идентификатора в массив данных
* @param mbdata Указатель на массив данных в структуре RS_MsgTypeDef. * @param mbdata Указатель на массив данных в структуре RS_MsgTypeDef.
* @return obj Объект для записи. * @return obj Объект для записи.
*/ */
void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj) void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj)
{ {
MB_DeviceObjectTypeDef *obj = (MB_DeviceObjectTypeDef *)&MB_DEVID; MB_DeviceObjectTypeDef *obj = (MB_DeviceObjectTypeDef *)&MB_DEVID;
@@ -85,16 +216,16 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj)
/** /**
* @brief Proccess command Read Device Identification (43/14 - 0x2B/0E). * @brief Обработать функцию Read Device Identifications (43/14 - 0x2B/0E).
* @param modbus_msg Указатель на структуру собщения modbus. * @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды. * @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Single Register. * @details Обработка команды Write Single Register.
*/ */
uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg) uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg)
{ {
switch(modbus_msg->DevId.ReadDevId) switch(modbus_msg->DevId.ReadDevId)
{ {
case MB_BASIC_IDENTIFICATION: case MB_BASIC_IDENTIFICATIONS:
if (modbus_msg->DevId.NextObjId == 0) if (modbus_msg->DevId.NextObjId == 0)
{ {
modbus_msg->DevId.NextObjId = 0; modbus_msg->DevId.NextObjId = 0;
@@ -104,7 +235,7 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg)
modbus_msg->DevId.NumbOfObj = 3; modbus_msg->DevId.NumbOfObj = 3;
break; break;
case MB_REGULAR_IDENTIFICATION: case MB_REGULAR_IDENTIFICATIONS:
if (modbus_msg->DevId.NextObjId == 0) if (modbus_msg->DevId.NextObjId == 0)
{ {
modbus_msg->DevId.NextObjId = 3; modbus_msg->DevId.NextObjId = 3;
@@ -114,7 +245,12 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg)
modbus_msg->DevId.NumbOfObj = 4; modbus_msg->DevId.NumbOfObj = 4;
break; break;
case MB_EXTENDED_IDENTIFICATION: case MB_EXTENDED_IDENTIFICATIONS:
if(MODBUS_NUMB_OF_USEROBJECTS <= 0 || MODBUS_NUMB_OF_USEROBJECTS > 128)
{
return 0;
}
if (modbus_msg->DevId.NextObjId == 0) if (modbus_msg->DevId.NextObjId == 0)
{ {
modbus_msg->DevId.NextObjId = 0x80; modbus_msg->DevId.NextObjId = 0x80;
@@ -124,7 +260,7 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg)
modbus_msg->DevId.NumbOfObj = MODBUS_NUMB_OF_USEROBJECTS; modbus_msg->DevId.NumbOfObj = MODBUS_NUMB_OF_USEROBJECTS;
break; break;
case MB_SPEDIFIC_IDENTIFICATION: case MB_SPEDIFIC_IDENTIFICATIONS:
MB_WriteObjectsToMessage(modbus_msg, modbus_msg->DevId.NextObjId); MB_WriteObjectsToMessage(modbus_msg, modbus_msg->DevId.NextObjId);
modbus_msg->DevId.NumbOfObj = 1; modbus_msg->DevId.NumbOfObj = 1;
break; break;
@@ -136,8 +272,9 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg)
/**
* @brief Инициализация идентификаторов.
*/
void MB_DeviceInentificationInit(void) void MB_DeviceInentificationInit(void)
{ {
MB_ObjectInit(&MB_DEVID.VendorName, MODBUS_VENDOR_NAME); MB_ObjectInit(&MB_DEVID.VendorName, MODBUS_VENDOR_NAME);
@@ -533,3 +670,16 @@ void MB_DeviceInentificationInit(void)
#endif #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

336
Modbus/Src/modbus_diag.c Normal file
View File

@@ -0,0 +1,336 @@
/**
******************************************************************************
* @file modbus_diag.c
* @brief Реализация диагностики устройства Modbus
******************************************************************************
* @details
Модуль обработки запросов диагностической информации (0x08):
- Полная поддержка всех подфункций диагностики согласно спецификации Modbus
- Выставление любого бита в Diagnostics Register
- Сбор статистики работы устройства
- Управление режимами работы (Normal/Listen Only)
******************************************************************************/
#include "modbus_diag.h"
#ifdef MODBUS_ENABLE_DIAGNOSTICS
MB_DiagnosticsInfoTypeDef MB_DIAG = {0}; ///< Глобальная структура диагностики
/**
* @brief Инициализация диагностических счетчиков
*/
void MB_DiagnosticsInit(void)
{
MB_DIAG.DiagnosticRegister = 0;
MB_DIAG.DeviceMode = MODBUS_NORMAL_MODE;
// Инициализация счетчиков
MB_DIAG.Counters.BusMessage = 0;
MB_DIAG.Counters.BusCommunicationErr = 0;
MB_DIAG.Counters.BusExceptionErr = 0;
MB_DIAG.Counters.SlaveMessage = 0;
MB_DIAG.Counters.SlaveNoResponse = 0;
MB_DIAG.Counters.SlaveNAK = 0;
MB_DIAG.Counters.SlaveBusy = 0;
MB_DIAG.Counters.BusCharacterOverrun = 0;
}
/**
* @brief Получить данные диагностики из сообщения (DATA[1])
* @param modbus_msg Указатель на структуру сообщения
* @param data Указатель куда положить данные
* @return 1 - успех, 0 - ошибка
*/
int MB_RespGet_Diagnostic(RS_MsgTypeDef *modbus_msg, uint16_t *data)
{
if(modbus_msg == NULL || data == NULL)
return 0;
// Проверяем что ответ связан с диагностикой
if(modbus_msg->Func_Code != MB_R_DIAGNOSTIC)
{
return 0;
}
*data = modbus_msg->DATA[1];
return 1;
}
/**
* @brief Выставить бит в регистре диагностике
* @param bit_num Номер бита для выставления (1-15, 0 бит нельзя выставить)
* @param bit_state Состояние бита для выставления (Выставить/Сбросить)
* @return >0 - номер выставленного бита, 0 - ошибка
*/
int MB_Diagnostics_WriteBit(int bit_num, int bit_state)
{
if(bit_num == 0 || bit_num > 15)
return 0;
if(bit_state)
MB_DIAG.DiagnosticRegister |= (1 << bit_num);
else
MB_DIAG.DiagnosticRegister &= ~(1 << bit_num);
return bit_num;
}
/**
* @brief Прочитать состояние бита диагностического регистра
* @param bit_num Номер бита (0-15)
* @return 1 - бит установлен, 0 - бит сброшен или ошибка
*/
int MB_Diagnostics_GetBit(int bit_num)
{
if(bit_num < 0 || bit_num > 15)
return 0;
return (MB_DIAG.DiagnosticRegister >> bit_num) & 0x01;
}
/**
* @brief Обработать функцию Diagnostics (Serial Line only) (0x08)
* @param modbus_msg Указатель на структуру сообщения modbus
* @return fMessageHandled Статус обработки команды
*/
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];
// Если устройство в режиме Listen Only, отвечаем только на sub-function 0x01
if (MB_DIAG.DeviceMode == MODBUS_LISTEN_ONLY_MODE && sub_function != 0x0001)
{
return 0; // Не отвечаем в режиме Listen Only
}
switch(sub_function)
{
case 0x0000: // Return Query Data
// Эхо-ответ с теми же данными
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = request_data;
modbus_msg->ByteCnt = 4;
break;
case 0x0001: // Restart Communications
// Перезапуск коммуникаций - выходим из Listen Only режима
MB_DIAG.DeviceMode = MODBUS_NORMAL_MODE;
// Если request_data = 0xFF00, очищаем лог событий
if (request_data == 0xFF00)
{
MB_DiagnosticsInit(); // Полный сброс
}
else
{
// Очищаем только счетчики, но не регистр диагностики
MB_DIAG.Counters.BusMessage = 0;
MB_DIAG.Counters.BusCommunicationErr = 0;
MB_DIAG.Counters.BusExceptionErr = 0;
MB_DIAG.Counters.SlaveMessage = 0;
MB_DIAG.Counters.SlaveNoResponse = 0;
MB_DIAG.Counters.SlaveNAK = 0;
MB_DIAG.Counters.SlaveBusy = 0;
MB_DIAG.Counters.BusCharacterOverrun = 0;
}
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[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->ByteCnt = 4;
break;
case 0x0003: // Change ASCII Input Delimiter
// В RTU режиме не поддерживается
modbus_msg->Func_Code |= ERR_VALUES_START;
modbus_msg->Except_Code = ILLEGAL_FUNCTION;
return 0;
case 0x0004: // Force Listen Only Mode
MB_DIAG.DeviceMode = MODBUS_LISTEN_ONLY_MODE;
// В режиме Listen Only не отправляем ответ
return 0;
case 0x000A: // Clear Counters and Diagnostic Register
MB_DiagnosticsInit(); // Полный сброс
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[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->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->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->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->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->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->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->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->ByteCnt = 4;
break;
case 0x0014: // Clear Overrun Counter and Flag
MB_DIAG.Counters.BusCharacterOverrun = 0;
// Сбрасываем флаг переполнения в DiagnosticRegister
MB_DIAG.DiagnosticRegister &= ~(1<<0);
modbus_msg->DATA[0] = sub_function;
modbus_msg->DATA[1] = 0;
modbus_msg->ByteCnt = 4;
break;
default:
modbus_msg->Func_Code |= ERR_VALUES_START;
modbus_msg->Except_Code = ILLEGAL_FUNCTION;
return 0;
}
return 1;
}
/**
* @brief Увеличивает счетчик сообщений на шине
*/
void MB_Diagnostics_BusMessageCnt(void)
{
MB_DIAG.Counters.BusMessage++;
}
/**
* @brief Увеличивает счетчик ошибок связи
*/
void MB_Diagnostics_CommunicationErrorCnt(void)
{
if (MB_DIAG.Counters.BusCommunicationErr < 0xFFFF)
MB_DIAG.Counters.BusCommunicationErr++;
}
/**
* @brief Увеличивает счетчик исключений
*/
void MB_Diagnostics_ExceptionErrorCnt(void)
{
if (MB_DIAG.Counters.BusExceptionErr < 0xFFFF)
MB_DIAG.Counters.BusExceptionErr++;
}
/**
* @brief Увеличивает счетчик переполнения символов
*/
void MB_Diagnostics_CharacterOverrunCnt(void)
{
if (MB_DIAG.Counters.BusCharacterOverrun < 0xFFFF)
{
MB_DIAG.Counters.BusCharacterOverrun++;
// Устанавливаем флаг переполнения в DiagnosticRegister
MB_DIAG.DiagnosticRegister |= (1 << 0);
}
}
/**
* @brief Увеличивает счетчик отсутствия ответов
*/
void MB_Diagnostics_SlaveMessageCnt(void)
{
if (MB_DIAG.Counters.SlaveMessage < 0xFFFF)
MB_DIAG.Counters.SlaveMessage++;
}
/**
* @brief Увеличивает счетчик отсутствия ответов
*/
void MB_Diagnostics_SlaveNoResponseCnt(void)
{
if (MB_DIAG.Counters.SlaveNoResponse < 0xFFFF)
MB_DIAG.Counters.SlaveNoResponse++;
}
/**
* @brief Увеличивает счетчик NAK ответов
*/
void MB_Diagnostics_SlaveNAKCnt(void)
{
if (MB_DIAG.Counters.SlaveNAK < 0xFFFF)
MB_DIAG.Counters.SlaveNAK++;
}
/**
* @brief Увеличивает счетчик занятости устройства
*/
void MB_Diagnostics_SlaveBusyCnt(void)
{
if (MB_DIAG.Counters.SlaveBusy < 0xFFFF)
MB_DIAG.Counters.SlaveBusy++;
}
/**
* @brief Получение текущего режима устройства
* @return Текущий режим работы устройства
*/
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

View File

@@ -9,25 +9,28 @@
- Запись одиночного регистра (0x06) - прямая запись значения - Запись одиночного регистра (0x06) - прямая запись значения
- Запись множественных регистров (0x10) - пакетная запись из буфера - Запись множественных регистров (0x10) - пакетная запись из буфера
@section Валидация данных: @section Валидация данных:
- Проверка соответствия количества байт и регистров - Проверка соответствия количества байт и регистров
Валидация адресов через MB_DefineRegistersAddress() Валидация адресов через MB_DefineRegistersAddress()
- Обработка исключений при некорректных запросах - Обработка исключений при некорректных запросах
@section Echo-ответы: @section Echo-ответы:
При успешной записи формируется echo-ответ с теми же данными, При успешной записи формируется echo-ответ с теми же данными,
что были в запросе (для функций 0x05, 0x06, 0x0F, 0x10). что были в запросе (для функций 0x05, 0x06, 0x0F, 0x10).
******************************************************************************/ ******************************************************************************/
#include "modbus_inputregs.h" #include "modbus_inputregs.h"
#ifdef MODBUS_ENABLE_HOLDINGS
/** /**
* @brief Proccess command Read Holding Registers (03 - 0x03). * @brief Обработать функцию Read Holding Registers (03 - 0x03).
* @param modbus_msg Указатель на структуру собщения modbus. * @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды. * @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Read Holding Registers. * @details Обработка команды Read Holding Registers.
*/ */
uint8_t MB_Proccess_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg) uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg)
{ {
//---------CHECK FOR ERRORS---------- //---------CHECK FOR ERRORS----------
// get origin address for data // get origin address for data
@@ -50,12 +53,12 @@ uint8_t MB_Proccess_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg)
} }
/** /**
* @brief Proccess command Write Single Register (06 - 0x06). * @brief Обработать функцию Write Single Register (06 - 0x06).
* @param modbus_msg Указатель на структуру собщения modbus. * @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды. * @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Single Register. * @details Обработка команды Write Single Register.
*/ */
uint8_t MB_Proccess_Write_Single_Reg(RS_MsgTypeDef *modbus_msg) uint8_t MB_Process_Write_Single_Reg(RS_MsgTypeDef *modbus_msg)
{ {
// get origin address for data // get origin address for data
uint16_t *pHoldRegs; uint16_t *pHoldRegs;
@@ -69,12 +72,12 @@ uint8_t MB_Proccess_Write_Single_Reg(RS_MsgTypeDef *modbus_msg)
} }
/** /**
* @brief Proccess command Write Multiple Registers (16 - 0x10). * @brief Обработать функцию Write Multiple Registers (16 - 0x10).
* @param modbus_msg Указатель на структуру собщения modbus. * @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды. * @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Multiple Registers. * @details Обработка команды Write Multiple Registers.
*/ */
uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg) uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg)
{ {
//---------CHECK FOR ERRORS---------- //---------CHECK FOR ERRORS----------
if (modbus_msg->Qnt*2 != modbus_msg->ByteCnt) if (modbus_msg->Qnt*2 != modbus_msg->ByteCnt)
@@ -95,3 +98,12 @@ uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg)
} }
return 1; 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

View File

@@ -9,17 +9,18 @@
Копирование данных из структур устройства в буфер ответа Копирование данных из структур устройства в буфер ответа
- Поддержка знаковых и беззнаковых значений - Поддержка знаковых и беззнаковых значений
******************************************************************************/ ******************************************************************************/
#include "modbus_inputregs.h" #include "modbus_inputregs.h"
#ifdef MODBUS_ENABLE_INPUTS
/** /**
* @brief Proccess command Read Input Registers (04 - 0x04). * @brief Обработать функцию Read Input Registers (04 - 0x04).
* @param modbus_msg Указатель на структуру собщения modbus. * @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды. * @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Read Input Registers. * @details Обработка команды Read Input Registers.
*/ */
uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg)
{ {
//---------CHECK FOR ERRORS---------- //---------CHECK FOR ERRORS----------
// get origin address for data // get origin address for data
@@ -43,3 +44,9 @@ uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg)
} }
return 1; return 1;
} }
#else //MODBUS_ENABLE_INPUTS
uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) {return 0;}
#endif

527
Modbus/Src/modbus_master.c Normal file
View File

@@ -0,0 +1,527 @@
/**
**************************************************************************
* @file modbus_master.c
* @brief Модуль для реализации мастера MODBUS.
**************************************************************************
* @details
Файл содержит реализацию функций для работы Modbus в режиме мастера.
@section Функции и макросы
- MB_Master_Collect_Message() — Сбор сообщения в режиме мастера
- MB_Master_Parse_Message() — Парс сообщения в режиме мастера
******************************************************************************/
#include "modbus.h"
#ifdef MODBUS_ENABLE_MASTER
/**
* @brief Получить значение регистра из ответа по его адресу
* @param modbus_msg Указатель на структуру сообщения
* @param reg_addr Адрес регистра, значение которого нужно получить
* @param reg_value Указатель для значения регистра
* @return 1 - успех, 0 - ошибка или reg_addr вне диапазона запроса
*/
int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value)
{
if(modbus_msg == NULL || reg_value == NULL)
return 0;
// Проверяем что ответ связан с регистрами
if((modbus_msg->Func_Code != MB_R_DISC_IN) &&
(modbus_msg->Func_Code != MB_R_HOLD_REGS) &&
(modbus_msg->Func_Code != MB_R_IN_REGS))
{
return 0;
}
// Проверяем что reg_addr в пределах запрошенного диапазона
if(reg_addr < modbus_msg->Addr || reg_addr >= modbus_msg->Addr + modbus_msg->Qnt)
return 0;
// Вычисляем индекс регистра в полученных данных
uint16_t reg_index = reg_addr - modbus_msg->Addr;
// Проверяем что регистр существует в данных
if(reg_index >= modbus_msg->ByteCnt / 2)
return 0;
// Получаем значение регистра
*reg_value = modbus_msg->DATA[reg_index];
return 1;
}
/**
* @brief Определить размер модбас запроса (МАСТЕР версия).
* @param hRS Указатель на хендлер RS.
* @param rx_data_size Указатель на переменную для записи кол-ва байт для принятия.
* @return RS_RES Статус о корректности рассчета кол-ва байт для принятия.
* @details Определение сколько байтов надо принять по протоколу.
*/
static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
RS_StatusTypeDef MB_RES = 0;
int mb_func_size = 0;
// Master mode - calculating response size from slave
if (modbus_msg->Func_Code & ERR_VALUES_START)
{
// Error response: [Addr][Func|0x80][ExceptCode][CRC]
mb_func_size = -1; // Only Exception Code
}
else if (modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
{
// Diagnostics response: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
mb_func_size = 1;
}
else if (modbus_msg->Func_Code == MB_R_DEVICE_INFO)
{
// Device identifications: variable size, need to read first to determine
mb_func_size = 0; // Will be determined after reading header
}
else
{
switch (modbus_msg->Func_Code & ~ERR_VALUES_START)
{
case 0x01: // Read Coils
case 0x02: // Read Discrete Inputs
case 0x03: // Read Holding Registers
case 0x04: // Read Input Registers
// Response: [ByteCount][Data...]
mb_func_size = modbus_msg->ByteCnt + 2; // ByteCount + variable data
break;
case 0x05: // Write Single Coil
case 0x06: // Write Single Register
// Echo response: [Addr][Value][CRC]
mb_func_size = 4; // Address(2) + Value(2)
break;
case 0x0F: // Write Multiple Coils
case 0x10: // Write Multiple Registers
// Echo response: [Addr][Qty][CRC]
mb_func_size = 4; // Address(2) + Quantity(2)
break;
default:
mb_func_size = 0;
}
}
mb_func_size = RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
return mb_func_size;
}
/**
* @brief Сбор сообщения в буфер UART в режиме мастер (фрейм мастера из msg -> uart).
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения буфера.
*/
RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
int ind = 0; // ind for modbus-uart buffer
//------INFO ABOUT DATA/MESSAGE------
//-----------[first bytes]-----------
// set ID of slave device
modbus_uart_buff[ind++] = modbus_msg->MbAddr;
// set function code
modbus_uart_buff[ind++] = modbus_msg->Func_Code;
if(modbus_msg->Func_Code < ERR_VALUES_START) // if no error occur
{
// fill modbus header
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // device identifications request
{
modbus_uart_buff[ind++] = modbus_msg->DevId.MEI_Type;
modbus_uart_buff[ind++] = modbus_msg->DevId.ReadDevId;
modbus_uart_buff[ind++] = modbus_msg->DevId.NextObjId;
}
else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
{
// Diagnostics: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
modbus_uart_buff[ind++] = modbus_msg->DATA[0] >> 8; // Sub-function HI
modbus_uart_buff[ind++] = modbus_msg->DATA[0] & 0xFF; // Sub-function LO
modbus_uart_buff[ind++] = modbus_msg->DATA[1] >> 8; // Data HI
modbus_uart_buff[ind++] = modbus_msg->DATA[1] & 0xFF; // Data LO
}
else // classic modbus request
{
// set address
modbus_uart_buff[ind++] = modbus_msg->Addr >> 8;
modbus_uart_buff[ind++] = modbus_msg->Addr & 0xFF;
// set quantity
modbus_uart_buff[ind++] = modbus_msg->Qnt >> 8;
modbus_uart_buff[ind++] = modbus_msg->Qnt & 0xFF;
// for write multiple functions
if((modbus_msg->Func_Code == 0x0F) || (modbus_msg->Func_Code == 0x10))
{
modbus_uart_buff[ind++] = modbus_msg->ByteCnt;
// write data bytes
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA;
for(int i = 0; i < modbus_msg->ByteCnt; i++)
{
modbus_uart_buff[ind++] = tmp_data_addr[i];
}
}
}
}
if(ind < 0)
return RS_COLLECT_MSG_ERR;
//---------------CRC----------------
//---------[last 2 bytes]----------
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
modbus_msg->MB_CRC = CRC_VALUE;
modbus_uart_buff[ind++] = CRC_VALUE & 0xFF;
modbus_uart_buff[ind++] = CRC_VALUE >> 8;
hmodbus->RS_Message_Size = ind;
return RS_OK;
}
/**
* @brief Парс сообщения в режиме мастер (фрейм слейва из uart -> msg).
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения структуры.
*/
RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
int ind = 0; // ind for modbus-uart buffer
int expected_size = 0;
// get ID of slave device
modbus_msg->MbAddr = modbus_uart_buff[ind++];
// get function code (check if error response)
modbus_msg->Func_Code = modbus_uart_buff[ind++];
if(modbus_msg->Func_Code & ERR_VALUES_START) // error response
{
modbus_msg->Except_Code = modbus_uart_buff[ind++];
}
else if(modbus_msg->Func_Code < ERR_VALUES_START) // normal response
{
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // device identifications response
{
modbus_msg->DevId.MEI_Type = modbus_uart_buff[ind++];
modbus_msg->DevId.ReadDevId = modbus_uart_buff[ind++];
modbus_msg->DevId.Conformity = modbus_uart_buff[ind++];
modbus_msg->DevId.MoreFollows = modbus_uart_buff[ind++];
modbus_msg->DevId.NextObjId = modbus_uart_buff[ind++];
modbus_msg->DevId.NumbOfObj = modbus_uart_buff[ind++];
modbus_msg->ByteCnt = 0;
// Парсинг объектов идентификации устройства
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA;
int data_index = 0;
for(int obj = 0; obj < modbus_msg->DevId.NumbOfObj; obj++)
{
// Читаем ID объекта
uint8_t object_id = modbus_uart_buff[ind++];
tmp_data_addr[data_index++] = object_id;
// Читаем длину объекта
uint8_t object_length = modbus_uart_buff[ind++];
tmp_data_addr[data_index++] = object_length;
// Читаем данные объекта
for(int i = 0; i < object_length; i++)
{
tmp_data_addr[data_index++] = modbus_uart_buff[ind++];
}
modbus_msg->ByteCnt += (2 + object_length); // ID + длина + данные
}
}
else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
{
// Diagnostics response: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
modbus_msg->DATA[0] = modbus_uart_buff[ind++] << 8;
modbus_msg->DATA[0] |= modbus_uart_buff[ind++];
modbus_msg->DATA[1] = modbus_uart_buff[ind++] << 8;
modbus_msg->DATA[1] |= modbus_uart_buff[ind++];
}
else // classic modbus response
{
// get byte count for read functions
if((modbus_msg->Func_Code == 0x01) || (modbus_msg->Func_Code == 0x02) ||
(modbus_msg->Func_Code == 0x03) || (modbus_msg->Func_Code == 0x04))
{
modbus_msg->ByteCnt = modbus_uart_buff[ind++];
// read data bytes
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA;
for(int i = 0; i < modbus_msg->ByteCnt; i++)
{
if(i % 2 == 0) // HI byte
tmp_data_addr[i/2] = (uint16_t)modbus_uart_buff[ind++] << 8;
else // LO byte
tmp_data_addr[i/2] |= modbus_uart_buff[ind++];
}
}
// for write functions - echo address and quantity
else if((modbus_msg->Func_Code == 0x05) || (modbus_msg->Func_Code == 0x06) ||
(modbus_msg->Func_Code == 0x0F) || (modbus_msg->Func_Code == 0x10))
{
modbus_msg->Addr = modbus_uart_buff[ind++] << 8;
modbus_msg->Addr |= modbus_uart_buff[ind++];
modbus_msg->Qnt = modbus_uart_buff[ind++] << 8;
modbus_msg->Qnt |= modbus_uart_buff[ind++];
}
}
}
//---------------CRC----------------
//----------[last 2 bytes]----------
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
modbus_msg->MB_CRC = modbus_uart_buff[ind++];
modbus_msg->MB_CRC |= modbus_uart_buff[ind++] << 8;
if(modbus_msg->MB_CRC != CRC_VALUE)
{
TrackerCnt_Err(hmodbus->rs_err);
return RS_PARSE_MSG_ERR;
}
return RS_OK;
}
/** @brief Сформировать запрос на чтение коилов */
RS_MsgTypeDef MB_REQUEST_READ_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity)
{
RS_MsgTypeDef msg = {slave_addr, MB_R_COILS, {0}, start_addr, quantity, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на чтение дискретных регистров */
RS_MsgTypeDef MB_REQUEST_READ_DISCRETE_INPUTS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity)
{
RS_MsgTypeDef msg = {slave_addr, MB_R_DISC_IN, {0}, start_addr, quantity, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на чтение холдинг регистров */
RS_MsgTypeDef MB_REQUEST_READ_HOLDING_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity)
{
RS_MsgTypeDef msg = {slave_addr, MB_R_HOLD_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на чтение инпут регистров */
RS_MsgTypeDef MB_REQUEST_READ_INPUT_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity)
{
RS_MsgTypeDef msg = {slave_addr, MB_R_IN_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на запись одного коила */
RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_COIL(uint8_t slave_addr, uint16_t coil_addr, uint8_t value)
{
RS_MsgTypeDef msg = {slave_addr, MB_W_COIL, {0}, coil_addr, (value ? 0xFF00 : 0x0000), 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на запись одного регистра */
RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_REG(uint8_t slave_addr, uint16_t reg_addr, uint16_t value)
{
RS_MsgTypeDef msg = {slave_addr, MB_W_HOLD_REG, {0}, reg_addr, value, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на запись нескольких регистров */
RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint8_t *coils_data)
{
RS_MsgTypeDef msg = {slave_addr, MB_W_COILS, {0}, start_addr, quantity, 0, {0}, 0, 0};
// Calculate byte count and prepare data
uint8_t byte_count = (quantity + 7) / 8;
msg.ByteCnt = byte_count;
// Copy coil data to message DATA array
for(int i = 0; i < byte_count; i++) {
if(i < DATA_SIZE) {
msg.DATA[i] = coils_data[i];
}
}
return msg;
}
/** @brief Сформировать запрос на запись нескольких коилов */
RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint16_t *regs_data)
{
RS_MsgTypeDef msg = {slave_addr, MB_W_HOLD_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0};
msg.ByteCnt = quantity * 2; // Each register is 2 bytes
// Copy register data to message DATA array
for(int i = 0; i < quantity && i < DATA_SIZE; i++) {
msg.DATA[i] = regs_data[i];
}
return msg;
}
//---------ДИАГНОСТИЧЕСКИЕ ДАННЫЕ-----------
RS_MsgTypeDef MB_REQUEST_DIAGNOSTIC_QUERY(uint8_t slave_addr, uint16_t sub_function, uint16_t data)
{
RS_MsgTypeDef msg = {slave_addr, MB_R_DIAGNOSTIC, {0}, 0, 0, 0, {sub_function, data}, 0, 0};
return msg;
}
RS_MsgTypeDef MB_REQUEST_RETURN_QUERY_DATA(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0000, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RESTART_COMMUNICATIONS(uint8_t slave_addr, uint16_t data)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0001, data);
}
RS_MsgTypeDef MB_REQUEST_RETURN_DIAGNOSTIC_REGISTER(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0002, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_FORCE_LISTEN_ONLY_MODE(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0004, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000A, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000B, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_COMMUNICATION_ERROR_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000C, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_EXCEPTION_ERROR_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000D, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_MESSAGE_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000E, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NO_RESPONSE_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000F, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NAK_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0010, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_BUSY_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0011, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_CHARACTER_OVERRUN_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0012, 0x0000);
}
//---------ИДЕНТИФИКАТОРЫ МОДБАС-----------
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_BASIC(uint8_t slave_addr)
{
RS_MsgTypeDef msg = {slave_addr, MB_R_DEVICE_INFO, {0x0E, 0x01, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
return msg;
}
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_REGULAR(uint8_t slave_addr)
{
RS_MsgTypeDef msg = {slave_addr, MB_R_DEVICE_INFO, {0x0E, 0x02, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
return msg;
}
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_EXTENDED(uint8_t slave_addr)
{
RS_MsgTypeDef msg = {slave_addr, MB_R_DEVICE_INFO, {0x0E, 0x03, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
return msg;
}
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_SPECIFIC(uint8_t slave_addr, uint8_t object_id)
{
RS_MsgTypeDef msg = {slave_addr, MB_R_DEVICE_INFO, {0x0E, 0x04, object_id, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
return msg;
}
#else
RS_MsgTypeDef msg_dummy = {0};
int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value) {return 0;}
RS_MsgTypeDef MB_REQUEST_READ_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_READ_DISCRETE_INPUTS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_READ_HOLDING_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_READ_INPUT_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_COIL(uint8_t slave_addr, uint16_t coil_addr, uint8_t value) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_REG(uint8_t slave_addr, uint16_t reg_addr, uint16_t value) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint8_t *coils_data) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint16_t *regs_data) {return msg_dummy;}
//---------ДИАГНОСТИЧЕСКИЕ ДАННЫЕ-----------
RS_MsgTypeDef MB_REQUEST_DIAGNOSTIC_QUERY(uint8_t slave_addr, uint16_t sub_function, uint16_t data) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_QUERY_DATA(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RESTART_COMMUNICATIONS(uint8_t slave_addr, uint16_t data) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_DIAGNOSTIC_REGISTER(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_FORCE_LISTEN_ONLY_MODE(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_COMMUNICATION_ERROR_COUNT(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_EXCEPTION_ERROR_COUNT(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_MESSAGE_COUNT(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NO_RESPONSE_COUNT(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NAK_COUNT(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_BUSY_COUNT(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_CHARACTER_OVERRUN_COUNT(uint8_t slave_addr) {return msg_dummy;}
//---------ИДЕНТИФИКАТОРЫ МОДБАС-----------
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_BASIC(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_REGULAR(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_EXTENDED(uint8_t slave_addr) {return msg_dummy;}
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_SPECIFIC(uint8_t slave_addr, uint8_t object_id) {return msg_dummy;}
RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) {return RS_ERR;}
RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) {return RS_ERR;}
#endif

437
Modbus/Src/modbus_slave.c Normal file
View File

@@ -0,0 +1,437 @@
/**
**************************************************************************
* @file modbus_slave.c
* @brief Модуль для реализации слейв MODBUS.
**************************************************************************
* @details
Файл содержит реализацию функций для работы Modbus в режиме слейва.
@section Функции и макросы
- MB_Slave_Response() — Ответ на запрос
- MB_Slave_Collect_Message() — Сбор сообщения в режиме слейва.
- MB_Slave_Parse_Message() — Парс сообщения в режиме слейва.
******************************************************************************/
#include "modbus.h"
#ifdef MODBUS_ENABLE_SLAVE
/**
* @brief Ответ на сообщение в режиме слейва.
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @return RS_RES Статус о результате ответа на комманду.
*/
RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
RS_StatusTypeDef MB_RES = 0;
hmodbus->f.MessageHandled = 0;
hmodbus->f.EchoResponse = 0;
RS_Reset_TX_Flags(hmodbus); // reset flag for correct transmit
MB_Diagnostics_BusMessageCnt();
if(hmodbus->ID == 0 || modbus_msg->MbAddr == 0)
{
MB_Diagnostics_SlaveNoResponseCnt(); // <-- Устройство не отвечает на широковещательные сообщения
hmodbus->RS_STATUS = RS_SKIP;
return RS_Handle_Receive_Start(hmodbus, modbus_msg);
}
MB_Diagnostics_SlaveMessageCnt();
if(modbus_msg->Func_Code < ERR_VALUES_START)// if no errors after parsing
{
switch (modbus_msg->Func_Code)
{
// Read Coils
case MB_R_COILS:
hmodbus->f.MessageHandled = MB_Process_Read_Coils(hmodbus->pMessagePtr);
break;
// Read Hodling Registers
case MB_R_HOLD_REGS:
hmodbus->f.MessageHandled = MB_Process_Read_Hold_Regs(hmodbus->pMessagePtr);
break;
case MB_R_IN_REGS:
hmodbus->f.MessageHandled = MB_Process_Read_Input_Regs(hmodbus->pMessagePtr);
break;
// Write Single Coils
case MB_W_COIL:
hmodbus->f.MessageHandled = MB_Process_Write_Single_Coil(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
hmodbus->f.DataUpdated = 1;
hmodbus->f.EchoResponse = 1;
hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes)
}
break;
case MB_W_HOLD_REG:
hmodbus->f.MessageHandled = MB_Process_Write_Single_Reg(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
hmodbus->f.DataUpdated = 1;
hmodbus->f.EchoResponse = 1;
hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes)
}
break;
// Write Multiple Coils
case MB_W_COILS:
hmodbus->f.MessageHandled = MB_Process_Write_Miltuple_Coils(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
hmodbus->f.DataUpdated = 1;
hmodbus->f.EchoResponse = 1;
hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes)
}
break;
// Write Multiple Registers
case MB_W_HOLD_REGS:
hmodbus->f.MessageHandled = MB_Process_Write_Miltuple_Regs(hmodbus->pMessagePtr);
if(hmodbus->f.MessageHandled)
{
hmodbus->f.DataUpdated = 1;
hmodbus->f.EchoResponse = 1;
hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes)
}
break;
case MB_R_DEVICE_INFO:
hmodbus->f.MessageHandled = MB_Process_Read_Device_Identifications(hmodbus->pMessagePtr);
break;
// Добавить в switch-case после других case:
case MB_R_DIAGNOSTIC:
hmodbus->f.MessageHandled = MB_Process_Diagnostics(hmodbus->pMessagePtr);
break;
/* unknown func code */
default:
modbus_msg->Except_Code = 0x01; /* set exception code: illegal function */
}
// Проверяем режим устройства - если Listen Only, не обрабатываем команды
if (MB_GetDeviceMode() == MODBUS_LISTEN_ONLY_MODE)
{
MB_Diagnostics_SlaveNoResponseCnt();
hmodbus->RS_STATUS = RS_SKIP;
return RS_Handle_Receive_Start(hmodbus, modbus_msg);;
}
// Проверяем статус обработки запроса
if(hmodbus->f.MessageHandled == 0)
{
MB_Diagnostics_ExceptionErrorCnt();
TrackerCnt_Warn(hmodbus->rs_err);
modbus_msg->Func_Code |= ERR_VALUES_START;
}
else
{
TrackerCnt_Ok(hmodbus->rs_err);
}
}
// if we need response - check that transmit isnt busy
if( RS_Is_TX_Busy(hmodbus) )
RS_Abort(hmodbus, ABORT_TX); // if tx busy - set it free
// Transmit right there, or sets (fDeferredResponse) to transmit response in main code
if(hmodbus->f.DeferredResponse == 0)
{
MB_RES = RS_Handle_Transmit_Start(hmodbus, modbus_msg);
}
else
{
RS_Handle_Receive_Start(hmodbus, modbus_msg);
hmodbus->f.DeferredResponse = 0;
}
hmodbus->RS_STATUS = MB_RES;
return MB_RES;
}
/**
* @brief Сбор сообщения в буфер UART в режиме слейв (фрейм слейва из msg -> uart).
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения буфера.
*/
RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
int ind = 0; // ind for modbus-uart buffer
if(hmodbus->f.EchoResponse && hmodbus->f.MessageHandled) // if echo response need
ind = hmodbus->RS_Message_Size;
else
{
//------INFO ABOUT DATA/MESSAGE------
//-----------[first bytes]-----------
// set ID of message/user
modbus_uart_buff[ind++] = modbus_msg->MbAddr;
// set dat or err response
modbus_uart_buff[ind++] = modbus_msg->Func_Code;
if (modbus_msg->Func_Code < ERR_VALUES_START) // if no error occur
{
// fill modbus header
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // devide identifications header
{
modbus_uart_buff[ind++] = modbus_msg->DevId.MEI_Type;
modbus_uart_buff[ind++] = modbus_msg->DevId.ReadDevId;
modbus_uart_buff[ind++] = modbus_msg->DevId.Conformity;
modbus_uart_buff[ind++] = modbus_msg->DevId.MoreFollows;
modbus_uart_buff[ind++] = modbus_msg->DevId.NextObjId;
modbus_uart_buff[ind++] = modbus_msg->DevId.NumbOfObj;
if (modbus_msg->ByteCnt > DATA_SIZE*2) // if ByteCnt less than DATA_SIZE
{
TrackerCnt_Err(hmodbus->rs_err);
return RS_COLLECT_MSG_ERR;
}
//---------------DATA----------------
//-----------[data bytes]------------
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA;
for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data
{ // set data
modbus_uart_buff[ind++] = *tmp_data_addr;
tmp_data_addr++;
}
}
else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
{
// Diagnostics special format: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
modbus_uart_buff[ind++] = modbus_msg->DATA[0] >> 8; // Sub-function HI
modbus_uart_buff[ind++] = modbus_msg->DATA[0] & 0xFF; // Sub-function LO
modbus_uart_buff[ind++] = modbus_msg->DATA[1] >> 8; // Data HI
modbus_uart_buff[ind++] = modbus_msg->DATA[1] & 0xFF; // Data LO
}
else // modbus data header
{
// set size of received data
if (modbus_msg->ByteCnt <= DATA_SIZE*2) // if ByteCnt less than DATA_SIZE
modbus_uart_buff[ind++] = modbus_msg->ByteCnt;
else // otherwise return data_size err
{
TrackerCnt_Err(hmodbus->rs_err);
return RS_COLLECT_MSG_ERR;
}
//---------------DATA----------------
//-----------[data bytes]------------
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA;
for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data
{ // set data
if (i%2 == 0) // HI byte
modbus_uart_buff[ind++] = (*tmp_data_addr)>>8;
else // LO byte
{
modbus_uart_buff[ind++] = *tmp_data_addr;
tmp_data_addr++;
}
}
}
}
else // if some error occur
{ // send expection code
modbus_uart_buff[ind++] = modbus_msg->Except_Code;
}
}
if(ind < 0)
return RS_COLLECT_MSG_ERR;
//---------------CRC----------------
//---------[last 16 bytes]----------
// calc crc of received data
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
// write crc to message structure and modbus-uart buffer
modbus_msg->MB_CRC = CRC_VALUE;
modbus_uart_buff[ind++] = CRC_VALUE;
modbus_uart_buff[ind++] = CRC_VALUE >> 8;
hmodbus->RS_Message_Size = ind;
return RS_OK; // returns ok
}
/**
* @brief Определить размер модбас запроса (СЛЕЙВ версия).
* @param hRS Указатель на хендлер RS.
* @param rx_data_size Указатель на переменную для записи кол-ва байт для принятия.
* @return RS_RES Статус о корректности рассчета кол-ва байт для принятия.
* @details Определение сколько байтов надо принять по протоколу.
*/
static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
RS_StatusTypeDef MB_RES = 0;
int mb_func_size = 0;
if (modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
{
mb_func_size = 1;
}
else if(modbus_msg->Func_Code == MB_R_DEVICE_INFO)
{
mb_func_size = 0;
}
else if ((modbus_msg->Func_Code & ~ERR_VALUES_START) < 0x0F)
{
mb_func_size = 1;
}
else
{
mb_func_size = modbus_msg->ByteCnt + 2;
}
mb_func_size = RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
return mb_func_size;
}
/**
* @brief Парс сообщения в режиме слейв (фрейм мастера из uart -> msg).
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения структуры.
* @details Заполнение структуры сообщения из буффера UART.
*/
RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
uint32_t check_empty_buff;
int ind = 0; // ind for modbus-uart buffer
hmodbus->f.RX_Continue = 0;
int expected_size = 0;
//-----INFO ABOUT DATA/MESSAGE-------
//-----------[first bits]------------
// get ID of message/user
if(modbus_uart_buff[ind] != hmodbus->ID)
{
modbus_msg->MbAddr = 0;
ind++;
}
else
{
modbus_msg->MbAddr = modbus_uart_buff[ind++];
}
// get func code
modbus_msg->Func_Code = modbus_uart_buff[ind++];
if(modbus_msg->Func_Code & ERR_VALUES_START) // явная херня
{
MB_Diagnostics_SlaveNAKCnt();
modbus_msg->MbAddr = 0;
return RS_SKIP;
}
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // if it device identifications request
{
modbus_msg->DevId.MEI_Type = modbus_uart_buff[ind++];
modbus_msg->DevId.ReadDevId = modbus_uart_buff[ind++];
modbus_msg->DevId.NextObjId = modbus_uart_buff[ind++];
modbus_msg->ByteCnt = 0;
}
else if(modbus_msg->Func_Code == MB_R_DIAGNOSTIC)
{
// Diagnostics: читаем 4 байта в DATA[0] и DATA[1]
// Sub-function
modbus_msg->DATA[0] = modbus_uart_buff[ind++] << 8;
modbus_msg->DATA[0] |= modbus_uart_buff[ind++];
// Data
modbus_msg->DATA[1] = modbus_uart_buff[ind++] << 8;
modbus_msg->DATA[1] |= modbus_uart_buff[ind++];
modbus_msg->Addr = 0; // не использует Addr
modbus_msg->Qnt = 0; // не использует Qnt
}
else // if its classic modbus request
{
// get address from CMD
modbus_msg->Addr = modbus_uart_buff[ind++] << 8;
modbus_msg->Addr |= modbus_uart_buff[ind++];
// get address from CMD
modbus_msg->Qnt = modbus_uart_buff[ind++] << 8;
modbus_msg->Qnt |= modbus_uart_buff[ind++];
}
if((hmodbus->pMessagePtr->Func_Code == 0x0F) || (hmodbus->pMessagePtr->Func_Code == 0x10))
hmodbus->pMessagePtr->ByteCnt = modbus_uart_buff[ind++];
else
hmodbus->pMessagePtr->ByteCnt = 0;
expected_size = MB_Define_Size_of_Function(hmodbus, modbus_msg);
// если размер меньше ожидаемого - продолжаем принимать
if(hmodbus->RS_Message_Size < expected_size)
{
hmodbus->f.RX_Continue = 1;
return RS_SKIP;
}
// если больше Ошибка
else if (hmodbus->RS_Message_Size > expected_size)
{
MB_Diagnostics_CommunicationErrorCnt();
return RS_PARSE_MSG_ERR;
}
//---------------DATA----------------
// (optional)
if (modbus_msg->ByteCnt != 0)
{
//check that data size is correct
if (modbus_msg->ByteCnt > DATA_SIZE*2)
{
TrackerCnt_Err(hmodbus->rs_err);
modbus_msg->Func_Code |= ERR_VALUES_START;
MB_Diagnostics_CommunicationErrorCnt();
return RS_PARSE_MSG_ERR;
}
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA;
for(int i = 0; i < modbus_msg->ByteCnt; i++)
{ // set data
if (i%2 == 0)
*tmp_data_addr = ((uint16_t)modbus_uart_buff[ind++] << 8);
else
{
*tmp_data_addr |= modbus_uart_buff[ind++];
tmp_data_addr++;
}
}
}
//---------------CRC----------------
//----------[last 16 bits]----------
// calc crc of received data
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
// get crc of received data
modbus_msg->MB_CRC = modbus_uart_buff[ind++];
modbus_msg->MB_CRC |= modbus_uart_buff[ind++] << 8;
// compare crc
if (modbus_msg->MB_CRC != CRC_VALUE)
{
MB_Diagnostics_CommunicationErrorCnt();
TrackerCnt_Err(hmodbus->rs_err);
modbus_msg->Func_Code |= ERR_VALUES_START;
}
return RS_OK;
}
#else // MODBUS_ENABLE_SLAVE
RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) {return RS_ERR;}
RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) {return RS_ERR;}
RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff) {return RS_ERR;}
#endif

View File

@@ -10,7 +10,7 @@
- Таймаутов через таймер - Таймаутов через таймер
- Двухстадийного приема (заголовок + данные) - Двухстадийного приема (заголовок + данные)
@section Архитектура: @section Архитектура:
В режиме слейв: В режиме слейв:
- Инициализация приема с сообщения с максимальным размером MSG_SIZE_MAX - Инициализация приема с сообщения с максимальным размером MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного сообщения - При срабатывании прерывания IDLE - обработка полученного сообщения
@@ -18,11 +18,12 @@
- Отправка запроса и переход в режим приема сообщения с максимальным размером MSG_SIZE_MAX - Отправка запроса и переход в режим приема сообщения с максимальным размером MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного ответа - При срабатывании прерывания IDLE - обработка полученного ответа
@section Необходимые обработчики: @section Необходимые обработчики:
- RS_UART_Handler() в UARTx_IRQHandler вместо HAL_UART_IRQHandler() - RS_UART_Handler() в UARTx_IRQHandler вместо HAL_UART_IRQHandler()
- RS_TIM_Handler() в TIMx_IRQHandler вместо HAL_TIM_IRQHandler() - RS_TIM_Handler() в TIMx_IRQHandler вместо HAL_TIM_IRQHandler()
******************************************************************************/ ******************************************************************************/
#include "rs_message.h" #include "rs_message.h"
#include "modbus_diag.h"
uint8_t RS_Buffer[MSG_SIZE_MAX]; // uart buffer uint8_t RS_Buffer[MSG_SIZE_MAX]; // uart buffer
@@ -34,7 +35,7 @@ extern void RS_TIM_DeInit(TIM_HandleTypeDef *htim);
//------------------------------------------------------------------- //-------------------------------------------------------------------
//-------------------------GENERAL FUNCTIONS------------------------- //-------------------------GENERAL FUNCTIONS-------------------------
/** /**
* @brief Start receive IT. * @brief Начать прием по прерываниям.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения. * @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации приема. * @return RS_RES Статус о состоянии RS после инициализации приема.
@@ -60,7 +61,7 @@ RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
// start receiving // start receiving
__HAL_UART_ENABLE_IT(hRS->huart, UART_IT_IDLE); __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->pBufferPtr[hRS->RS_Message_Size], MSG_SIZE_MAX); // receive until ByteCnt+1 byte,
// then in Callback restart receive for rest bytes // then in Callback restart receive for rest bytes
// if receive isnt started - abort RS // if receive isnt started - abort RS
@@ -82,7 +83,7 @@ RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
} }
/** /**
* @brief Start transmit IT. * @brief Начать передачу по прерываниям.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения. * @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации передачи. * @return RS_RES Статус о состоянии RS после инициализации передачи.
@@ -111,7 +112,6 @@ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
//----------INITIALIZE TRANSMIT------------- //----------INITIALIZE TRANSMIT-------------
RS_EnableTransmit(); RS_EnableTransmit();
// for(int i = 0; i < hRS->sRS_Timeout; i++);
RS_Set_Busy(hRS); // set RS busy RS_Set_Busy(hRS); // set RS busy
RS_Set_TX_Flags(hRS); // initialize flags for transmit IT RS_Set_TX_Flags(hRS); // initialize flags for transmit IT
@@ -145,13 +145,13 @@ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
} }
/** /**
* @brief Initialize UART and handle RS stucture. * @brief Инициалазация структуры @ref RS_HandleTypeDef.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @param suart Указатель на структуру с настройками UART. * @param suart Указатель на структуру с настройками UART.
* @param stim Указатель на структуру с настройками таймера. * @param stim Указатель на структуру с настройками таймера.
* @param pRS_BufferPtr Указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер. * @param pRS_BufferPtr Указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер.
* @return RS_RES Статус о состоянии RS после инициализации. * @return RS_RES Статус о состоянии RS после инициализации.
* @note Инициализация перефирии и структуры для приема-передачи по RS. * @details Инициализация перефирии и структуры для приема-передачи по RS.
*/ */
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr) RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr)
{ {
@@ -166,9 +166,6 @@ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_H
hRS->htim = htim; hRS->htim = htim;
if (hRS->sRS_RX_Size_Mode == NULL)
return RS_ERR;
// check that buffer is defined // check that buffer is defined
if (hRS->pBufferPtr == NULL) if (hRS->pBufferPtr == NULL)
{ {
@@ -182,7 +179,7 @@ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_H
/** /**
* @brief Abort RS/UART. * @brief Отменить прием/передачу RS/UART.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @param AbortMode Выбор, что надо отменить. * @param AbortMode Выбор, что надо отменить.
- ABORT_TX: Отмена передачи по ЮАРТ, с очищением флагов TX, - ABORT_TX: Отмена передачи по ЮАРТ, с очищением флагов TX,
@@ -190,20 +187,14 @@ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_H
- ABORT_RX_TX: Отмена приема и передачи по ЮАРТ, - ABORT_RX_TX: Отмена приема и передачи по ЮАРТ,
- ABORT_RS: Отмена приема-передачи RS, с очищением всей структуры. - ABORT_RS: Отмена приема-передачи RS, с очищением всей структуры.
* @return RS_RES Статус о состоянии RS после аборта. * @return RS_RES Статус о состоянии RS после аборта.
* @note Отмена работы UART в целом или отмена приема/передачи RS. * @details Отмена работы UART в целом или отмена приема/передачи RS.
Также очищается хендл hRS. Также очищается хендл hRS.
*/ */
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode) RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode)
{ {
HAL_StatusTypeDef uart_res = 0; HAL_StatusTypeDef uart_res = 0;
if(hRS->htim) RS_Timeout_Stop(hRS);
{
if(hRS->sRS_Timeout) // if timeout setted
HAL_TIM_Base_Stop_IT(hRS->htim); // stop timeout
hRS->htim->Instance->CNT = 0;
__HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE);
}
if((AbortMode&ABORT_RS) == 0x00) if((AbortMode&ABORT_RS) == 0x00)
{ {
@@ -237,11 +228,11 @@ RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode)
//------------------------------------------------------------------- //-------------------------------------------------------------------
//--------------------CALLBACK/HANDLER FUNCTIONS--------------------- //--------------------CALLBACK/HANDLER FUNCTIONS---------------------
/** /**
* @brief Handle for starting receive. * @brief Обработчик для начала приема.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения. * @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации приема или окончания общения. * @return RS_RES Статус о состоянии RS после инициализации приема или окончания общения.
* @note Определяет начинать прием команды/ответа или нет. * @details Определяет начинать прием команды/ответа или нет.
*/ */
RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{ {
@@ -249,9 +240,15 @@ RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *R
switch(hRS->sRS_Mode) switch(hRS->sRS_Mode)
{ {
case RS_SLAVE_ALWAYS_WAIT: // in slave mode with permanent waiting // В режиме мастер
RS_RES = RS_Receive_IT(hRS, RS_msg); break; // start receiving again case RS_MASTER_REQUEST:
case RS_SLAVE_TIMEOUT_WAIT: // in slave mode with timeout waiting (start receiving cmd by request) RS_Timeout_Start(hRS); // сразу запускаем таймаут и начинаем прием
// В режиме слейв
case RS_SLAVE_ALWAYS_WAIT:
RS_RES = RS_Receive_IT(hRS, RS_msg); // Просто запускаем фоновый прием
break;
case RS_RESERVED:
RS_Set_Free(hRS); RS_RES = RS_OK; break; // end RS communication (set RS unbusy) RS_Set_Free(hRS); RS_RES = RS_OK; break; // end RS communication (set RS unbusy)
} }
@@ -263,11 +260,11 @@ RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *R
return RS_RES; return RS_RES;
} }
/** /**
* @brief Handle for starting transmit. * @brief Обработчик для начала передачи.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения. * @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации передачи. * @return RS_RES Статус о состоянии RS после инициализации передачи.
* @note Определяет отвечать ли на команду или нет. * @details Определяет отвечать ли на команду или нет.
*/ */
RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{ {
@@ -276,13 +273,14 @@ RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *
switch(hRS->sRS_Mode) switch(hRS->sRS_Mode)
{ {
case RS_SLAVE_ALWAYS_WAIT: // in slave mode always response case RS_SLAVE_ALWAYS_WAIT: // in slave mode always response
case RS_SLAVE_TIMEOUT_WAIT: // transmit response case RS_RESERVED: // transmit response
case RS_MASTER_REQUEST: // transmit response
RS_RES = RS_Transmit_IT(hRS, RS_msg); break; RS_RES = RS_Transmit_IT(hRS, RS_msg); break;
} }
if(RS_RES != RS_OK) if(RS_RES != RS_OK)
{ {
if(hRS->sRS_Mode < RS_MASTER_START) if(hRS->sRS_Mode < RS_MASTER_MODE_START)
{ {
RS_Handle_Receive_Start(hRS, RS_msg); RS_Handle_Receive_Start(hRS, RS_msg);
} }
@@ -294,10 +292,10 @@ RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *
} }
/** /**
* @brief UART TX Callback: define behaviour after transmiting message. * @brief UART TX Callback: коллбек после окончания передачи.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @return RS_RES Статус о состоянии RS после обработки приема. * @return RS_RES Статус о состоянии RS после обработки приема.
* @note Определяет поведение RS после передачи сообщения. * @details Определяет поведение RS после передачи сообщения.
*/ */
RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS) RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS)
{ {
@@ -306,24 +304,18 @@ RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS)
//--------------ENDING TRANSMITTING------------- //--------------ENDING TRANSMITTING-------------
RS_Set_TX_End(hRS); RS_Set_TX_End(hRS);
RS_EnableReceive();
// for(int i = 0; i < hRS->sRS_Timeout; i++);
//-----------START RECEIVING or END RS---------- //-----------START RECEIVING or END RS----------
RS_RES = RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); RS_RES = RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
// if(RS_RES != RS_OK)
// {
// __NOP();
// }
return RS_RES; return RS_RES;
} }
/** /**
* @brief Handler for UART. * @brief Обработчик прерывания UART.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @note Обрабатывает ошибки если есть и вызывает RS Коллбеки. * @details Обрабатывает ошибки если есть и вызывает RS Коллбеки.
* Добавить вызов этой функции в UARTx_IRQHandler() после HAL_UART_IRQHandler(). * Добавить вызов этой функции в UARTx_IRQHandler() ВМЕСТО HAL_UART_IRQHandler().
*/ */
void RS_UART_Handler(RS_HandleTypeDef *hRS) void RS_UART_Handler(RS_HandleTypeDef *hRS)
{ {
@@ -338,6 +330,7 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
__HAL_UART_CLEAR_IDLEFLAG(hRS->huart); // Важно: очистить флаг IDLE __HAL_UART_CLEAR_IDLEFLAG(hRS->huart); // Важно: очистить флаг IDLE
//-------------STANDARD UART HANDLING------------- //-------------STANDARD UART HANDLING-------------
HAL_UART_IRQHandler(hRS->huart); HAL_UART_IRQHandler(hRS->huart);
hRS->f.RX_Continue = 0;
// Если прием активен и мы получили IDLE - это конец фрейма // Если прием активен и мы получили IDLE - это конец фрейма
if(RS_Is_RX_Busy(hRS) && hRS->f.RX_Ongoing) if(RS_Is_RX_Busy(hRS) && hRS->f.RX_Ongoing)
@@ -357,28 +350,22 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
// Парсим наше сообщение // Парсим наше сообщение
RS_StatusTypeDef parse_res = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr); RS_StatusTypeDef parse_res = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
// Проверяем адрес Modbus перед обработкой // Если сообещине принято корректно
if(hRS->pMessagePtr->MbAddr != hRS->ID) if(parse_res == RS_OK)
{ {
// Чужое сообщение - игнорируем и начинаем новый прием RS_Timeout_Stop(hRS);
RS_Abort(hRS, ABORT_RX);
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
return;
}
// Если сообещине принято корректно - отвечаем на него
if(parse_res != RS_SKIP)
{
if(hRS->htim)
{
// Останавливаем таймаут
if(hRS->sRS_Timeout)
HAL_TIM_Base_Stop_IT(hRS->htim);
}
hRS->lastPacketTick = uwTick; hRS->lastPacketTick = uwTick;
RS_Response(hRS, hRS->pMessagePtr); if(hRS->sRS_Mode < RS_MASTER_MODE_START)
RS_Response(hRS, hRS->pMessagePtr); // отвечаем на запрос
else
{
if(hRS->pCallback)
{
hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
RS_Set_Free(hRS); // освобожднаем RS
}
}
} }
else else
{ {
@@ -400,18 +387,12 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
if(hRS->huart->ErrorCode == 0) if(hRS->huart->ErrorCode == 0)
{ {
if(hRS->htim) // if first byte is received and receive is active
if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS))
{ {
hRS->htim->Instance->CNT = 0; // reset cnt; RS_Timeout_Start(hRS);
/* Start timeout при получении первого байта */
if(hRS->sRS_Timeout) // if timeout setted
if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS)) // if first byte is received and receive is active
{
hRS->htim->Instance->ARR = hRS->sRS_Timeout;
HAL_TIM_Base_Start_IT(hRS->htim);
RS_Set_RX_Active_Flags(hRS);
}
} }
RS_Timeout_Update(hRS);
/* RX Callback - теперь НЕ вызываем здесь, ждем IDLE */ /* RX Callback - теперь НЕ вызываем здесь, ждем IDLE */
@@ -425,6 +406,10 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
//----------------ERRORS HANDLER---------------- //----------------ERRORS HANDLER----------------
else else
{ {
if (hRS->huart->ErrorCode & HAL_UART_ERROR_ORE)
{
MB_Diagnostics_CharacterOverrunCnt(); // <-- Обнаружено переполнение
}
//TrackerCnt_Err(hRS->rs_err); //TrackerCnt_Err(hRS->rs_err);
/* de-init uart transfer */ /* de-init uart transfer */
RS_Abort(hRS, ABORT_RS); RS_Abort(hRS, ABORT_RS);
@@ -436,10 +421,10 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
/** /**
* @brief Handler for TIM. * @brief Обработчик прерывания TIM.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @note Попадание сюда = таймаут и перезапуск RS приема * @details Попадание сюда = таймаут и перезапуск RS приема
* Добавить вызов этой функции в TIMx_IRQHandler() после HAL_TIM_IRQHandler(). * Добавить вызов этой функции в TIMx_IRQHandler() ВМЕСТО HAL_TIM_IRQHandler().
*/ */
void RS_TIM_Handler(RS_HandleTypeDef *hRS) void RS_TIM_Handler(RS_HandleTypeDef *hRS)
{ {
@@ -449,13 +434,73 @@ void RS_TIM_Handler(RS_HandleTypeDef *hRS)
} }
HAL_TIM_IRQHandler(hRS->htim); HAL_TIM_IRQHandler(hRS->htim);
RS_Abort(hRS, ABORT_RS); RS_Abort(hRS, ABORT_RS);
if(hRS->pMessagePtr->MbAddr == hRS->ID) // ошибка если таймаут по нашему сообщению if(hRS->pMessagePtr->MbAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
TrackerCnt_Err(hRS->rs_err); TrackerCnt_Err(hRS->rs_err);
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); if(hRS->sRS_Mode == RS_MASTER_REQUEST) {
// Мастер: таймаут ответа -> освобождаем для нового запроса
RS_Set_Free(hRS);
} else {
// Слейв: перезапускаем прием
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
}
} }
/**
* @brief Запуск таймаута приема.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус операции.
* @details Запускает таймер для отсчета времени ожидания следующего байта.
*/
RS_StatusTypeDef RS_Timeout_Start(RS_HandleTypeDef *hRS)
{
if(hRS->htim)
{
hRS->htim->Instance->CNT = 0; // reset cnt;
if(hRS->sRS_Timeout) // if timeout setted
{
hRS->htim->Instance->ARR = hRS->sRS_Timeout;
HAL_TIM_Base_Start_IT(hRS->htim);
RS_Set_RX_Active_Flags(hRS);
}
}
return RS_OK;
}
/**
* @brief Остановка таймаута приема.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус операции.
* @details Останавливает таймер ожидания.
*/
RS_StatusTypeDef RS_Timeout_Stop(RS_HandleTypeDef *hRS)
{
if(hRS->htim)
{
// Останавливаем таймаут
if(hRS->sRS_Timeout)
HAL_TIM_Base_Stop_IT(hRS->htim);
hRS->htim->Instance->CNT = 0;
__HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE);
}
return RS_OK;
}
/**
* @brief Обновление (сброс) таймаута приема.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус операции.
* @details Сбрасывает счетчик таймера в 0.
*/
RS_StatusTypeDef RS_Timeout_Update(RS_HandleTypeDef *hRS)
{
if(hRS->htim)
{
hRS->htim->Instance->CNT = 0; // reset cnt;
}
return RS_OK;
}
//--------------------CALLBACK/HANDLER FUNCTIONS--------------------- //--------------------CALLBACK/HANDLER FUNCTIONS---------------------
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -468,7 +513,6 @@ void RS_TIM_Handler(RS_HandleTypeDef *hRS)
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения. * @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о результате ответа на комманду. * @return RS_RES Статус о результате ответа на комманду.
* @note Обработка принятой комманды и ответ на неё.
*/ */
__weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg) __weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{ {
@@ -477,12 +521,11 @@ __weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg
} }
/** /**
* @brief Собрать сообщение в буфер UART. * @brief Пользовательская функция для сбора сообщения в буфер UART.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения. * @param RS_msg Указатель на структуру сообщения.
* @param msg_uart_buff Указатель на буффер UART. * @param msg_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения буфера. * @return RS_RES Статус о результате заполнения буфера.
* @note Заполнение буффера UART из структуры сообщения.
*/ */
__weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff) __weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{ {
@@ -491,12 +534,11 @@ __weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef
} }
/** /**
* @brief Разпарсить сообщение из буфера UART. * @brief Пользовательская функция для парса сообщения из буфера UART.
* @param hRS Указатель на хендлер RS. * @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения. * @param RS_msg Указатель на структуру сообщения.
* @param msg_uart_buff Указатель на буффер UART. * @param msg_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения структуры. * @return RS_RES Статус о результате заполнения структуры.
* @note Заполнение структуры сообщения из буффера UART.
*/ */
__weak RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff) __weak RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{ {

133
README.md
View File

@@ -3,7 +3,7 @@
Библиотека реализует Modbus RTU Slave для STM32 с использованием HAL. Работает в прерываниях с детектированием конца фрейма по IDLE линии и таймаутами через прерывание таймера. Библиотека реализует Modbus RTU Slave для STM32 с использованием HAL. Работает в прерываниях с детектированием конца фрейма по IDLE линии и таймаутами через прерывание таймера.
## Быстрый старт ## Быстрый старт
_Note: Здесь описано подключение просто архива c исходниками. Если надо подключить библиотеку как субмодуль: см. ветку release в этом репозитории._ _Note: Здесь описано подключение просто архива c исходниками. Если надо подключить библиотеку как субмодуль: см. ветку **release** в этом репозитории._
### 1. Настройка периферии ### 1. Настройка периферии
@@ -20,14 +20,14 @@ _Note: Здесь описано подключение просто архив
void USARTx_IRQHandler(void) void USARTx_IRQHandler(void)
{ {
RS_UART_Handler(&modbus1); RS_UART_Handler(&hmodbus1);
return; return;
HAL_UART_IRQHandler(&huart); HAL_UART_IRQHandler(&huart);
} }
void TIMx_IRQHandler(void) void TIMx_IRQHandler(void)
{ {
RS_TIM_Handler(&modbus1); RS_TIM_Handler(&hmodbus1);
return; return;
HAL_TIM_IRQHandler(&htim); HAL_TIM_IRQHandler(&htim);
} }
@@ -39,8 +39,8 @@ void TIMx_IRQHandler(void)
```c ```c
// MODBUS PARAMS // MODBUS PARAMS
#define MODBUS_DEVICE_ID 1 // Адрес устройства в сети Modbus #define MODBUS_DEVICE_ID 1 ///< Адрес устройства в сети Modbus
#define MODBUS_TIMEOUT 100 // Таймаут в тиках таймера (рекомендуется 100-200ms) #define MODBUS_TIMEOUT 5000 ///< Таймаут в тиках таймера
// Строковые идентификаторы устройства // Строковые идентификаторы устройства
#define MODBUS_VENDOR_NAME "NIO-12" #define MODBUS_VENDOR_NAME "NIO-12"
@@ -55,37 +55,87 @@ void TIMx_IRQHandler(void)
// Периферия // Периферия
#define mb_huart huart1 // Удобный дефайн для модбасовского UART #define mb_huart huart1 // Удобный дефайн для модбасовского UART
#define mb_htim htim3 // Удобный дефайн для модбасовского таймера #define mb_htim htim3 // Удобный дефайн для модбасовского таймера
// Модули modbus
#define MODBUS_ENABLE_SLAVE ///< Включить обработку МАСТЕР режима
//#define MODBUS_ENABLE_MASTER ///< Включить обработку СЛЕЙВ режима
#define MODBUS_ENABLE_COILS ///< Включить обработку коилов
#define MODBUS_ENABLE_HOLDINGS ///< Включить обработку регистров хранения
#define MODBUS_ENABLE_INPUTS ///< Включить обработку входных регистров
#define MODBUS_ENABLE_DEVICE_IDENTIFICATIONS ///< Включить обработку идентификаторы устройства
#define MODBUS_ENABLE_DIAGNOSTICS ///< Включить обработку диагностики модбас
``` ```
### 4. Инициализация в коде ### 4. Инициализация в коде
В `main()` после инициализации HAL: Чтобы настроить Slave-режим `main()` после инициализации HAL:
```c ```c
#include "modbus.h" #include "modbus.h"
int main(void) int main(void)
{ {
// Инициализация HAL // Инициализация HAL
HAL_Init(); HAL_Init();
SystemClock_Config(); SystemClock_Config();
MX_GPIO_Init(); MX_GPIO_Init();
MX_USART1_UART_Init(); MX_USART1_UART_Init();
MX_TIM3_Init(); MX_TIM3_Init();
// Инициализация Modbus // Инициализация Modbus
MODBUS_SetupHardware(&hmodbus1, &mb_huart, &mb_htim); MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim);
MODBUS_Config(&hmodbus1, MODBUS_DEVICE_ID, MODBUS_TIMEOUT, MODBUS_MODE_SLAVE);
// Запуск приема Modbus // Запуск приема Modbus
MODBUS_SlaveStart(&hmodbus1, &MODBUS_MSG); MODBUS_SlaveStart(&hmodbus1, NULL);
while (1) while (1)
{ {
// Основной цикл // Основной цикл
} }
} }
``` ```
Чтобы настроить Master-режим `main()` после инициализации HAL:
```c
#include "modbus.h"
read_hold[10];
// Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук
RS_MsgTypeDef read_hold_cmd = MB_MASTER_READ_HOLDING_REGS(1, 0, 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);
}
```
### 5. Настройка карты данных ### 5. Настройка карты данных
В `modbus_data.h` настройте регистры и coils под ваше устройство: В `modbus_data.h` настройте регистры и coils под ваше устройство:
@@ -134,6 +184,7 @@ typedef struct
### 6. Доступ к данным в коде ### 6. Доступ к данным в коде
В режиме слейва есть дефайны для удобного выставления Коилов. На случай если они не упакованы в битовые поля
```c ```c
// Чтение входных регистров // Чтение входных регистров
uint16_t temp = MB_DATA.InRegs.Temperature; uint16_t temp = MB_DATA.InRegs.Temperature;
@@ -142,15 +193,50 @@ uint16_t temp = MB_DATA.InRegs.Temperature;
MB_DATA.HoldRegs.SetpointTemp = 2500; MB_DATA.HoldRegs.SetpointTemp = 2500;
// Управление coils // Управление coils
MB_Set_Coil_Local(&MB_DATA.Coils, 0); // Включить Relay1 MB_Coil_Set_Local(&MB_DATA.Coils, 0); // Включить Relay1
MB_Reset_Coil_Local(&MB_DATA.Coils, 1); // Выключить Relay2 MB_Coil_Reset_Local(&MB_DATA.Coils, 1); // Выключить Relay2
// Чтение coil // Чтение coil
if (MB_Read_Coil_Local(&MB_DATA.Coils, 2)) { if (MB_Coil_Read_Local(&MB_DATA.Coils, 2)) {
// Pump включен // 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);
}
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);
}
```
## Поддерживаемые функции Modbus ## Поддерживаемые функции Modbus
| Функция | Код | Описание | | Функция | Код | Описание |
@@ -160,6 +246,7 @@ if (MB_Read_Coil_Local(&MB_DATA.Coils, 2)) {
| Read Holding Registers | 0x03 | Чтение регистров хранения | | Read Holding Registers | 0x03 | Чтение регистров хранения |
| Write Single Coil | 0x05 | Запись одиночного coil | | Write Single Coil | 0x05 | Запись одиночного coil |
| Write Single Register | 0x06 | Запись одиночного регистра | | Write Single Register | 0x06 | Запись одиночного регистра |
| Diagnostics (Serial Line only) | 0x08 | Чтение диагностически и управление режимом работы |
| Write Multiple Coils | 0x0F | Запись множественных coils | | Write Multiple Coils | 0x0F | Запись множественных coils |
| Write Multiple Registers | 0x10 | Запись множественных регистров | | Write Multiple Registers | 0x10 | Запись множественных регистров |
| Read Device Identification | 0x2B | Чтение идентификации устройства | | Read Device Identification | 0x2B | Чтение идентификации устройства |

View File

@@ -56,7 +56,16 @@ void SystemClock_Config(void);
/* Private user code ---------------------------------------------------------*/ /* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */ /* USER CODE BEGIN 0 */
int coil_to_toggle = 0;
int coil_state = 0;
int coil_real_state = 0;
void master_callback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *msg)
{
if(MB_RespGet_CoilState(msg, coil_to_toggle, &coil_real_state))
{
coil_state = coil_real_state;
}
}
/* USER CODE END 0 */ /* USER CODE END 0 */
/** /**
@@ -91,14 +100,27 @@ int main(void)
MX_USART1_UART_Init(); MX_USART1_UART_Init();
MX_TIM3_Init(); MX_TIM3_Init();
/* USER CODE BEGIN 2 */ /* USER CODE BEGIN 2 */
MODBUS_SetupHardware(&hmodbus1, &huart1, &htim3); MODBUS_FirstInit(&hmodbus1, &huart1, &htim3);
RS_Receive_IT(&hmodbus1, &MODBUS_MSG); #ifdef MODBUS_MODE_MASTER
MODBUS_Config(&hmodbus1, 0, MODBUS_TIMEOUT, MODBUS_MODE_MASTER);
#else
MODBUS_Config(&hmodbus1, MODBUS_DEVICE_ID, MODBUS_TIMEOUT, MODBUS_MODE_SLAVE);
MODBUS_SlaveStart(&hmodbus1, NULL);
#endif
/* USER CODE END 2 */ /* USER CODE END 2 */
/* Infinite loop */ /* Infinite loop */
/* USER CODE BEGIN WHILE */ /* USER CODE BEGIN WHILE */
while (1) while (1)
{ {
#ifdef MODBUS_MODE_MASTER
HAL_Delay(1000);
MODBUS_MSG = MB_REQUEST_READ_COILS(1, 0, 5);
MODBUS_MasterRequest(&hmodbus1, &MODBUS_MSG, &master_callback);
HAL_Delay(1000);
MODBUS_MSG = MB_REQUEST_WRITE_SINGLE_COIL(1, coil_to_toggle, !coil_state);
MODBUS_MasterRequest(&hmodbus1, &MODBUS_MSG, &master_callback);
#endif
/* USER CODE END WHILE */ /* USER CODE END WHILE */
/* USER CODE BEGIN 3 */ /* USER CODE BEGIN 3 */

File diff suppressed because one or more lines are too long

View File

@@ -145,27 +145,10 @@
<SetRegEntry> <SetRegEntry>
<Number>0</Number> <Number>0</Number>
<Key>ST-LINKIII-KEIL_SWO</Key> <Key>ST-LINKIII-KEIL_SWO</Key>
<Name>-U005600373433510237363934 -O2254 -SF10000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP (ARM Core") -D00(1BA01477) -L00(0) -TO131123 -TC8000000 -TT10000000 -TP21 -TDS8000 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO7 -FD20000000 -FC800 -FN1 -FF0STM32F10x_128.FLM -FS08000000 -FL010000 -FP0($$Device:STM32F103C8$Flash\STM32F10x_128.FLM) -WA0 -WE0 -WVCE4 -WS2710 -WM0 -WP2</Name> <Name>-U005600373433510237363934 -O2254 -SF10000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP (ARM Core") -D00(2BA01477) -L00(0) -TO131123 -TC8000000 -TT10000000 -TP21 -TDS8003 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO7 -FD20000000 -FC800 -FN1 -FF0STM32F10x_128.FLM -FS08000000 -FL010000 -FP0($$Device:STM32F103C8$Flash\STM32F10x_128.FLM) -WA0 -WE0 -WVCE4 -WS2710 -WM0 -WP2</Name>
</SetRegEntry> </SetRegEntry>
</TargetDriverDllRegistry> </TargetDriverDllRegistry>
<Breakpoint> <Breakpoint/>
<Bp>
<Number>0</Number>
<Type>0</Type>
<LineNumber>227</LineNumber>
<EnabledFlag>1</EnabledFlag>
<Address>134230452</Address>
<ByteObject>0</ByteObject>
<HtxType>0</HtxType>
<ManyObjects>0</ManyObjects>
<SizeOfObject>0</SizeOfObject>
<BreakByAccess>0</BreakByAccess>
<BreakIfRCount>1</BreakIfRCount>
<Filename>E:\.WORK\STM32\STM32_Modbus\Modbus\modbus.c</Filename>
<ExecCommand></ExecCommand>
<Expression>\\Modbus_example\../../Modbus/modbus.c\227</Expression>
</Bp>
</Breakpoint>
<WatchWindow1> <WatchWindow1>
<Ww> <Ww>
<count>0</count> <count>0</count>
@@ -175,7 +158,52 @@
<Ww> <Ww>
<count>1</count> <count>1</count>
<WinNumber>1</WinNumber> <WinNumber>1</WinNumber>
<ItemText>RS_Buffer</ItemText> <ItemText>RS_Buffer,0x10</ItemText>
</Ww>
<Ww>
<count>2</count>
<WinNumber>1</WinNumber>
<ItemText>MODBUS_MSG,0x10</ItemText>
</Ww>
<Ww>
<count>3</count>
<WinNumber>1</WinNumber>
<ItemText>coil_to_toggle</ItemText>
</Ww>
<Ww>
<count>4</count>
<WinNumber>1</WinNumber>
<ItemText>coil_real_state</ItemText>
</Ww>
<Ww>
<count>5</count>
<WinNumber>1</WinNumber>
<ItemText>(uint8_t *)modbus_msg-&gt;DATA</ItemText>
</Ww>
<Ww>
<count>6</count>
<WinNumber>1</WinNumber>
<ItemText>coil_word &gt;&gt; (bit_index+8)</ItemText>
</Ww>
<Ww>
<count>7</count>
<WinNumber>1</WinNumber>
<ItemText>bit_index+8,0x0A</ItemText>
</Ww>
<Ww>
<count>8</count>
<WinNumber>1</WinNumber>
<ItemText>modbus_uart_buff[ind]</ItemText>
</Ww>
<Ww>
<count>9</count>
<WinNumber>1</WinNumber>
<ItemText>msg</ItemText>
</Ww>
<Ww>
<count>10</count>
<WinNumber>1</WinNumber>
<ItemText>MODBUS_MSG.DATA[0]</ItemText>
</Ww> </Ww>
</WatchWindow1> </WatchWindow1>
<WatchWindow2> <WatchWindow2>
@@ -227,20 +255,6 @@
<pszMrulep></pszMrulep> <pszMrulep></pszMrulep>
<pSingCmdsp></pSingCmdsp> <pSingCmdsp></pSingCmdsp>
<pMultCmdsp></pMultCmdsp> <pMultCmdsp></pMultCmdsp>
<SystemViewers>
<Entry>
<Name>System Viewer\DBG</Name>
<WinId>35903</WinId>
</Entry>
<Entry>
<Name>System Viewer\TIM3</Name>
<WinId>35904</WinId>
</Entry>
<Entry>
<Name>System Viewer\USART1</Name>
<WinId>35905</WinId>
</Entry>
</SystemViewers>
<DebugDescription> <DebugDescription>
<Enable>1</Enable> <Enable>1</Enable>
<EnableFlashSeq>0</EnableFlashSeq> <EnableFlashSeq>0</EnableFlashSeq>
@@ -556,114 +570,6 @@
<File> <File>
<GroupNumber>5</GroupNumber> <GroupNumber>5</GroupNumber>
<FileNumber>23</FileNumber> <FileNumber>23</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\__crc_algs.c</PathWithFileName>
<FilenameWithoutPath>__crc_algs.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>24</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus.c</PathWithFileName>
<FilenameWithoutPath>modbus.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>25</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_coils.c</PathWithFileName>
<FilenameWithoutPath>modbus_coils.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>26</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_core.c</PathWithFileName>
<FilenameWithoutPath>modbus_core.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>27</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_data.c</PathWithFileName>
<FilenameWithoutPath>modbus_data.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>28</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_devid.c</PathWithFileName>
<FilenameWithoutPath>modbus_devid.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>29</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_holdregs.c</PathWithFileName>
<FilenameWithoutPath>modbus_holdregs.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>30</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_inputregs.c</PathWithFileName>
<FilenameWithoutPath>modbus_inputregs.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>31</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\rs_message.c</PathWithFileName>
<FilenameWithoutPath>rs_message.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>32</FileNumber>
<FileType>5</FileType> <FileType>5</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@@ -675,7 +581,19 @@
</File> </File>
<File> <File>
<GroupNumber>5</GroupNumber> <GroupNumber>5</GroupNumber>
<FileNumber>33</FileNumber> <FileNumber>24</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_data.c</PathWithFileName>
<FilenameWithoutPath>modbus_data.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>25</FileNumber>
<FileType>5</FileType> <FileType>5</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
@@ -685,6 +603,138 @@
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
<bShared>0</bShared> <bShared>0</bShared>
</File> </File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>26</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus.c</PathWithFileName>
<FilenameWithoutPath>modbus.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>27</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_master.c</PathWithFileName>
<FilenameWithoutPath>modbus_master.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>28</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_slave.c</PathWithFileName>
<FilenameWithoutPath>modbus_slave.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>29</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_coils.c</PathWithFileName>
<FilenameWithoutPath>modbus_coils.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>30</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_core.c</PathWithFileName>
<FilenameWithoutPath>modbus_core.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>31</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_holdregs.c</PathWithFileName>
<FilenameWithoutPath>modbus_holdregs.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>32</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_inputregs.c</PathWithFileName>
<FilenameWithoutPath>modbus_inputregs.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>33</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_devid.c</PathWithFileName>
<FilenameWithoutPath>modbus_devid.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>34</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\modbus_diag.c</PathWithFileName>
<FilenameWithoutPath>modbus_diag.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>35</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\rs_message.c</PathWithFileName>
<FilenameWithoutPath>rs_message.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>36</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\..\Modbus\Src\__crc_algs.c</PathWithFileName>
<FilenameWithoutPath>__crc_algs.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group> </Group>
<Group> <Group>

View File

@@ -567,15 +567,35 @@
<GroupName>Modbus</GroupName> <GroupName>Modbus</GroupName>
<Files> <Files>
<File> <File>
<FileName>__crc_algs.c</FileName> <FileName>modbus_config.h</FileName>
<FileType>5</FileType>
<FilePath>..\..\Modbus\Inc\modbus_config.h</FilePath>
</File>
<File>
<FileName>modbus_data.c</FileName>
<FileType>1</FileType> <FileType>1</FileType>
<FilePath>..\..\Modbus\Src\__crc_algs.c</FilePath> <FilePath>..\..\Modbus\Src\modbus_data.c</FilePath>
</File>
<File>
<FileName>modbus_data.h</FileName>
<FileType>5</FileType>
<FilePath>..\..\Modbus\Inc\modbus_data.h</FilePath>
</File> </File>
<File> <File>
<FileName>modbus.c</FileName> <FileName>modbus.c</FileName>
<FileType>1</FileType> <FileType>1</FileType>
<FilePath>..\..\Modbus\Src\modbus.c</FilePath> <FilePath>..\..\Modbus\Src\modbus.c</FilePath>
</File> </File>
<File>
<FileName>modbus_master.c</FileName>
<FileType>1</FileType>
<FilePath>..\..\Modbus\Src\modbus_master.c</FilePath>
</File>
<File>
<FileName>modbus_slave.c</FileName>
<FileType>1</FileType>
<FilePath>..\..\Modbus\Src\modbus_slave.c</FilePath>
</File>
<File> <File>
<FileName>modbus_coils.c</FileName> <FileName>modbus_coils.c</FileName>
<FileType>1</FileType> <FileType>1</FileType>
@@ -586,16 +606,6 @@
<FileType>1</FileType> <FileType>1</FileType>
<FilePath>..\..\Modbus\Src\modbus_core.c</FilePath> <FilePath>..\..\Modbus\Src\modbus_core.c</FilePath>
</File> </File>
<File>
<FileName>modbus_data.c</FileName>
<FileType>1</FileType>
<FilePath>..\..\Modbus\Src\modbus_data.c</FilePath>
</File>
<File>
<FileName>modbus_devid.c</FileName>
<FileType>1</FileType>
<FilePath>..\..\Modbus\Src\modbus_devid.c</FilePath>
</File>
<File> <File>
<FileName>modbus_holdregs.c</FileName> <FileName>modbus_holdregs.c</FileName>
<FileType>1</FileType> <FileType>1</FileType>
@@ -606,20 +616,25 @@
<FileType>1</FileType> <FileType>1</FileType>
<FilePath>..\..\Modbus\Src\modbus_inputregs.c</FilePath> <FilePath>..\..\Modbus\Src\modbus_inputregs.c</FilePath>
</File> </File>
<File>
<FileName>modbus_devid.c</FileName>
<FileType>1</FileType>
<FilePath>..\..\Modbus\Src\modbus_devid.c</FilePath>
</File>
<File>
<FileName>modbus_diag.c</FileName>
<FileType>1</FileType>
<FilePath>..\..\Modbus\Src\modbus_diag.c</FilePath>
</File>
<File> <File>
<FileName>rs_message.c</FileName> <FileName>rs_message.c</FileName>
<FileType>1</FileType> <FileType>1</FileType>
<FilePath>..\..\Modbus\Src\rs_message.c</FilePath> <FilePath>..\..\Modbus\Src\rs_message.c</FilePath>
</File> </File>
<File> <File>
<FileName>modbus_config.h</FileName> <FileName>__crc_algs.c</FileName>
<FileType>5</FileType> <FileType>1</FileType>
<FilePath>..\..\Modbus\Inc\modbus_config.h</FilePath> <FilePath>..\..\Modbus\Src\__crc_algs.c</FilePath>
</File>
<File>
<FileName>modbus_data.h</FileName>
<FileType>5</FileType>
<FilePath>..\..\Modbus\Inc\modbus_data.h</FilePath>
</File> </File>
</Files> </Files>
</Group> </Group>