release 0.3

Добавлен master, НО до конца не проверен
This commit is contained in:
2025-11-04 21:45:09 +03:00
parent 465f293397
commit 1d0d2c1650
21 changed files with 1728 additions and 663 deletions

View File

@@ -5,7 +5,7 @@
******************************************************************************
@addtogroup MODBUS Modbus tools
******************************************************************************
@addtogroup MODBUS_FUNCTIONS Modbus library funtions
@addtogroup MODBUS_FUNCTIONS Main API for Modbus Library
@ingroup MODBUS
@{
******************************************************************************
@@ -24,13 +24,28 @@
- Инициализировать хендл мобдас. По умолчанию глобально создается hmodbus1
- После для запуска Modbus:
@verbatim
//----------------Прием модбас----------------//
//----------------Слейв модбас----------------//
#include "modbus.h"
MODBUS_FirstInit(&hmodbus1, &huart1, &htim3);
//MODBUS_Config(&hmodbus1, 1, 1000, 0); // - если нужны другие настройки, не из modbus_config.h
MODBUS_Config(&hmodbus1, 1, 1000, MODBUS_MODE_SLAVE);
MODBUS_SlaveStart(&hmodbus1, NULL);
@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 Подключаемые модули:
@@ -58,6 +73,8 @@
#define __MODBUS_H_
#include "rs_message.h"
#include "modbus_master.h"
#include "modbus_slave.h"
#include "modbus_coils.h"
#include "modbus_holdregs.h"
#include "modbus_inputregs.h"
@@ -71,21 +88,16 @@
//----------------FUNCTIONS FOR USER----------------
/**
* @addtogroup MODBUS_INIT_FUNCTIONS Functions for Init
* @ingroup MODBUS_FUNCTIONS
* @brief Функции для инициализации
@{
*/
/* Инициализация периферии модбас. */
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);
/** MODBUS_INIT_FUNCTIONS
* @}
*/
/* Реквест мастера модбас */
HAL_StatusTypeDef MODBUS_MasterRequest(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, void (*pClbk)(RS_HandleTypeDef*, RS_MsgTypeDef*));
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/////////////////////////---FUNCTIONS---/////////////////////////////

View File

@@ -82,12 +82,47 @@ typedef enum
/////////////////////////---FUNCTIONS---/////////////////////////////
/**
* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS Modbus Data Access
* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS API for Data Access
* @ingroup MODBUS_FUNCTIONS
* @brief Функции для доступа к данным модбас
@{
*/
/**
* @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_GetCoilState(&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_GetCoilState(&MODBUS_MSG, addr, &state))
* {
* printf("Coil %d: %s\n", addr, state ? "ON" : "OFF");
* }
* }
* @endcode
*/
int MB_GetCoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state);
/** MODBUS_REQ_COILS_API
* @}
*/
/**
* @brief Считать коил по локальному адресу.
* @param _parr_ - массив коилов.
@@ -137,9 +172,9 @@ uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception);
@{
*/
/* Обработать функцию Read Coils (01 - 0x01) */
uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg);
uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg);
/* Обработать функцию 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);
/* Обработать функцию Write Multiple Coils (15 - 0x0F) */
uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg);

View File

@@ -117,39 +117,39 @@ typedef enum //MB_FunctonTypeDef
/** @brief Structure for MEI func codes */
typedef enum //MB_FunctonTypeDef
{
MEI_DEVICE_IDENTIFICATION = 0x0E,
MEI_DEVICE_IDENTIFICATIONS = 0x0E,
}MB_MEITypeDef;
/** @brief Structure for comformity */
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:
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
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
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. */
MB_SPEDIFIC_IDENTIFICATION = 0x04, /*!< @brief Specific Device Identification.
@details The device provides one specific identification object. */
MB_SPEDIFIC_IDENTIFICATIONS = 0x04, /*!< @brief Specific Device Identifications.
@details The device provides one specific identifications object. */
/* ERRORS */
MB_ERR_BASIC_IDENTIFICATION = MB_BASIC_IDENTIFICATION + ERR_VALUES_START,
MB_ERR_REGULAR_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START,
MB_ERR_EXTENDED_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START,
MB_ERR_SPEDIFIC_IDENTIFICATION = MB_REGULAR_IDENTIFICATION + ERR_VALUES_START,
MB_ERR_BASIC_IDENTIFICATIONS = MB_BASIC_IDENTIFICATIONS + ERR_VALUES_START,
MB_ERR_REGULAR_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START,
MB_ERR_EXTENDED_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START,
MB_ERR_SPEDIFIC_IDENTIFICATIONS = MB_REGULAR_IDENTIFICATIONS + ERR_VALUES_START,
}MB_ConformityTypeDef;
/** @brief Structure for decive identification message type */
/** @brief Structure for decive identifications message type */
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 Conformity;
uint8_t MoreFollows;
@@ -163,7 +163,7 @@ typedef struct // RS_MsgTypeDef
{
uint8_t MbAddr; ///< Modbus Slave Address
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 Qnt; ///< Quantity of modbus data
uint8_t ByteCnt; ///< Quantity of bytes of data in message to transmit/receive

View File

@@ -3,12 +3,12 @@
* @file modbus_devid.h
* @brief Идентификаторы устройства Modbus
******************************************************************************
@addtogroup MODBUS_DEVID Device Identificators Tools
@addtogroup MODBUS_DEVID Device Identifications Tools
@ingroup MODBUS_INTERNAL
@{
******************************************************************************
* @details
Модуль реализации функции Read Device Identification (0x2B):
Модуль реализации функции Read Device Identifications (0x2B):
- Базовая идентификация (Vendor, Product, Revision)
- Расширенная идентификация (URL, Model, User fields)
- Поддержка потоковой передачи больших объектов
@@ -48,8 +48,8 @@ typedef struct
MB_DeviceObjectTypeDef Reserved[0x79];
MB_DeviceObjectTypeDef User[MODBUS_NUMB_OF_USEROBJECTS];
}MB_DeviceIdentificationTypeDef;
extern MB_DeviceIdentificationTypeDef MB_DEVID;
}MB_DeviceIdentificationsTypeDef;
extern MB_DeviceIdentificationsTypeDef MB_DEVID;
void MB_DeviceInentificationInit(void);
///////////////---DEVICE IDENTIVICATIONS DEFINES---//////////////////
@@ -77,6 +77,52 @@ void MB_DeviceInentificationInit(void);
/////////////////////////////////////////////////////////////////////
/////////////////////////---FUNCTIONS---/////////////////////////////
/**
* @addtogroup MODBUS_REQ_DEFID_API API for Device Identifications
* @ingroup MODBUS_REQUEST_MSG
* @brief Макросы для чтения идентификторов из ответа в режиме мастер
* @details Примеры использования:
*
* @code
* // Пример 1: Получить VendorName (ID = 0x00)
* uint8_t length;
* char vendor_name[64];
* if(MB_FindObjectById(&MODBUS_MSG, 0x00, vendor_name, &length))
* {
* // получено
* }
*
* // Пример 2: Перебрать все объекты в сообщении
* uint8_t obj_id, obj_length;
* char obj_data[256];
*
* int obj_count = MB_GetNumberOfObjects(&MODBUS_MSG);
* printf("Total objects: %d\n", obj_count);
*
* for(int i = 0; i < obj_count; i++)
* {
* if(MB_GetObjectByIndex(&MODBUS_MSG, i, &obj_id, obj_data, &obj_length))
* {
* // получено
* }
* }
* @endcode
*/
/* Получить количество объектов в сообщении */
int MB_GetNumberOfObjects(RS_MsgTypeDef *modbus_msg);
/* Найти объект по ID в сообщении */
int MB_FindObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length);
/* Получить объект по индексу в сообщении */
int MB_GetObjectByIndex(RS_MsgTypeDef *modbus_msg, int index, uint8_t *obj_id, char *obj_data, uint8_t *obj_length);
/** MODBUS_REQ_DEFID_API
* @}
*/
/* Записать Один Объект Идентификатора в массив данных */
void MB_WriteSingleObjectToMessage(char *mbdata, unsigned *ind, MB_DeviceObjectTypeDef *obj);
/* Записать Массив Объектов Идентификатора в массив данных */
@@ -88,8 +134,8 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj);
@{
*/
/* Обработать функцию Read Device Identification (43/14 - 0x2B/0E) */
uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg);
/* Обработать функцию Read Device Identifications (43/14 - 0x2B/0E) */
uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg);
/** MODBUS_CMD_PROCESS_FUNCTIONS
* @}
*/

View File

@@ -58,7 +58,34 @@ extern MB_DiagnosticsInfoTypeDef MB_DIAG;
void MB_DiagnosticsInit(void);
/**
* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS Modbus Data Access
* @addtogroup MODBUS_REQ_DIAG_API API for Diagnostics
* @ingroup MODBUS_REQUEST_MSG
* @brief API для чтения диагностической информации из ответа в режиме мастер
* @details Примеры использования:
*
* @code
* Получить данные диагностики (значение счетчика)
* uint16_t counter_value;
* if(MB_GetDiagnosticResponse(&MODBUS_MSG, &counter_value))
* {
* printf("Counter value: %d\n", counter_value);
* }
* @endcode
*/
int MB_GetDiagnosticResponse(RS_MsgTypeDef *modbus_msg, uint16_t *data);
/** MODBUS_REQ_DIAG_API
* @}
*/
/**
* @addtogroup MODBUS_DATA_ACCESS_FUNCTIONS
@{
*/
@@ -79,7 +106,7 @@ MB_DeviceModeTypeDef MB_GetDeviceMode(void);
*/
/* Обработка команды диагностики (0x08) */
uint8_t MB_Proccess_Diagnostics(RS_MsgTypeDef *modbus_msg);
uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg);
/** MODBUS_CMD_PROCESS_FUNCTIONS
* @}

View File

@@ -30,11 +30,11 @@
@{
*/
/* Обработать функцию 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);
/* Обработать функцию 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);
/* Обработать функцию 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
* @}

View File

@@ -24,13 +24,13 @@
//---------PROCESS MODBUS COMMAND FUNCTIONS---------
/**
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS Proccess Functions
* @addtogroup MODBUS_CMD_PROCESS_FUNCTIONS Internal Process Functions
* @ingroup MODBUS_FUNCTIONS
* @brief Функции обработки запросов модбас
@{
*/
/* Обработать функцию 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
* @}

121
Inc/modbus_master.h Normal file
View File

@@ -0,0 +1,121 @@
/**
******************************************************************************
* @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 Примеры использования:
*
* // Чтение 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);
*/
//---------КЛАССИЧЕСКИЕ ДАННЫЕ-----------
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_GetRegisterValue(&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_GetRegisterValue(&MODBUS_MSG, addr, &value))
* {
* printf("Register %d: %d\n", addr, value);
* }
* }
* @endcode
*/
int MB_GetRegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value);
/** MODBUS_REQ_REGS_API
* @}
*/
/* Сбор сообщения в буфер 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);
#endif //__MODBUS_MASTER_H_
/** MODBUS_MASTER
* @}
*/

34
Inc/modbus_slave.h Normal file
View File

@@ -0,0 +1,34 @@
/**
******************************************************************************
* @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
/* Ответ на сообщение в режиме слейва */
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);
#endif //__MODBUS_SLAVE_H_
/** MODBUS_MASTER
* @}
*/

View File

@@ -40,10 +40,6 @@
#error Define MSG_SIZE_MAX (Maximum size of message). This is necessary to create buffer for UART.
#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 Заглушки и внутренний недокументированный стаф
*/
@@ -55,14 +51,14 @@
#define RS_Set_Free(_hRS_) _hRS_->f.RS_Busy = 0
#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_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; _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_Set_RX_End_Flag(_hRS_) _hRS_->f.RX_Done = 1;
@@ -79,13 +75,6 @@
#define RS_Is_RX_Busy(_hRS_) (_hRS_->f.RX_Busy == 1)
#define RS_Is_TX_Busy(_hRS_) (_hRS_->f.TX_Busy == 1)
// направление передачи rs485
#ifndef RS_EnableReceive
#define RS_EnableReceive()
#endif
#ifndef RS_EnableTransmit
#define RS_EnableTransmit()
#endif
#ifndef printf_rs_err
#define printf_rs_err(...)
@@ -122,10 +111,20 @@ static int dummy;
#include "mylibs_include.h"
#endif
#ifndef RS_USER_VARS_NUMB
#define RS_USER_VARS_NUMB 0
#endif
/** @endcond */
// направление передачи rs485
#ifndef RS_EnableReceive
#define RS_EnableReceive() ///< Функция изменения направления передачи на ПРИЕМ для RS-485
#endif
#ifndef RS_EnableTransmit
#define RS_EnableTransmit() ///< Функция изменения направления передачи на ПЕРЕДАЧУ для RS-485
#endif
////////////////////////////---DEFINES---////////////////////////////
@@ -151,61 +150,44 @@ typedef enum // RS_StatusTypeDef
}RS_StatusTypeDef;
#define RS_MASTER_START 0x3
#define RS_MASTER_MODE_START 0x3 ///< Начало режимов мастера (до него - режим слейв)
/** @brief Enums for RS Modes */
typedef enum // RS_ModeTypeDef
{
RS_SLAVE_ALWAYS_WAIT = 0x01, ///< Slave mode with infinity waiting
RS_SLAVE_TIMEOUT_WAIT = 0x02, ///< Slave mode with waiting with timeout
// RS_MASTER = 0x03, ///< Master mode
RS_SLAVE_ALWAYS_WAIT = 0x01, ///< Слейв в постоянном ожидании
RS_RESERVED = 0x02, ///< резерв
RS_MASTER_REQUEST = 0x03, ///< Мастер с ручным запросом
//RS_MASTER_POLLING = 0x04, ///< Мастер с опросом в фоновом режиме
}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 */
typedef enum // RS_AbortTypeDef
{
ABORT_TX = 0x01, ///< Abort transmit
ABORT_RX = 0x02, ///< Abort receive
ABORT_RX_TX = 0x03, ///< Abort receive and transmit
ABORT_RS = 0x04, ///< Abort uart and reset RS structure
ABORT_TX = 0x01, ///< Отменить передачу
ABORT_RX = 0x02, ///< Отменить прием
ABORT_RX_TX = 0x03, ///< Отменить прием и передачу
ABORT_RS = 0x04, ///< Отменить любую работу UART в целом
}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------------
/** @brief Struct for flags RS */
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_Ongoing:1; ///< flag: 1 - receiving data right now, 0 - waiting for receiving data
unsigned RX_Busy:1; ///< 1 - Режим приема активен, 0 - Прием не активен
unsigned TX_Busy:1; ///< 1 - Режим передачи активен, 0 - Прием не активен
unsigned RX_Busy:1; ///< flag: 1 - receiving is active, 0 - receiving isnt active
unsigned TX_Busy:1; ///< flag: 1 - transmiting is active, 0 - transmiting isnt active
unsigned RX_Done:1; ///< 1 - Прием закончен, 0 - Прием еще в процессе или не инициализирован
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
// setted by user
unsigned RX_Continue:1; ///< flag: 0 - continue receiving, 0 - start receiving from ind = 0
unsigned MessageHandled:1; ///< flag: 1 - RS command is handled, 0 - RS command isnt handled yet
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
// Выставление следующие флагов определяет пользователь
unsigned RX_Continue:1; ///< 0 - Продолжить принимать, 0 - Начать прием сначала
unsigned MessageHandled:1; ///< 1 - Обработка запроса успешна, 0 - Обработка запроса в процессе или ошибка
unsigned EchoResponse:1; ///< 1 - Ответить эхом, 0 - Ответить своим сообщением
unsigned DeferredResponse:1; ///< 1 - Не начинать передачу в IT, 0 - Ответить в прерывании
unsigned DataUpdated:1; ///< 1 - Данные были обновлены
}RS_FlagsTypeDef;
@@ -217,25 +199,24 @@ typedef struct
typedef struct // RS_HandleTypeDef
{
/* MESSAGE */
uint8_t ID; ///< ID of RS "channel"
RS_MsgTypeDef *pMessagePtr; ///< pointer to message struct
uint8_t *pBufferPtr; ///< pointer to message buffer
uint8_t ID; ///< ID хендла
RS_MsgTypeDef *pMessagePtr; ///< Указатель на структуру протокола
uint8_t *pBufferPtr; ///< Указатеь на буфер UART
int32_t RS_Message_Size; ///< size of whole message, not only data
/* HANDLERS and SETTINGS */
UART_HandleTypeDef *huart; ///< handler for used uart
TIM_HandleTypeDef *htim; ///< handler for used tim
RS_ModeTypeDef sRS_Mode; ///< setting: slave or master @ref RS_ModeTypeDef
RS_ITModeTypeDef sRS_IT_Mode; ///< setting: 1 - IT mode, 0 - Blocking mode
uint16_t sRS_Timeout; ///< setting: timeout in ms
RS_RXSizeTypeDef sRS_RX_Size_Mode; ///< setting: 1 - not const, 0 - const
UART_HandleTypeDef *huart; ///< Хендл UART
TIM_HandleTypeDef *htim; ///< Хендл TIM
RS_ModeTypeDef sRS_Mode; ///< Настройка: слейв/мастер @ref RS_ModeTypeDef
uint16_t sRS_Timeout; ///< Настройка: Таймаут в тиках таймера
void (*pCallback)(void*, void*); ///< Указатель на коллбек: принят ответ в режиме мастер
/* FLAGS */
RS_FlagsTypeDef f; ///< These flags for controling receive/transmit
RS_FlagsTypeDef f; ///< Флаги для контроля приема/передачи
/* RS STATUS */
uint32_t lastPacketTick;
RS_StatusTypeDef RS_STATUS; ///< RS status
uint32_t lastPacketTick; ///< Время последнего принятого пакета
RS_StatusTypeDef RS_STATUS; ///< Статус RS
TrackerTypeDef(RS_USER_VARS_NUMB) rs_err;
}RS_HandleTypeDef;
@@ -249,47 +230,53 @@ extern RS_HandleTypeDef hmodbus1;
///////////////////////////---FUNCTIONS---///////////////////////////
//----------------FUNCTIONS FOR PROCESSING MESSAGE-------------------
/*--------------------Defined by users purposes--------------------*/
/* Respond accord to received message */
/* Пользовательская функция для ответа на запрос по UART */
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);
/* 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);
//-------------------------GENERAL FUNCTIONS-------------------------
/*-----------------Should be called from main code-----------------*/
/* Start receive IT */
/* Начать прием по прерываниям */
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);
/* 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);
/* ReInitialize UART and RS receive */
HAL_StatusTypeDef RS_ReInit_UART(RS_HandleTypeDef *hRS, UART_HandleTypeDef *suart);
/* Abort RS/UART */
/* Отменить прием/передачу RS/UART */
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode);
//-------------------------GENERAL FUNCTIONS-------------------------
//-------------------------------------------------------------------
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
/* Handle for starting receive */
/* Обработчик для начала приема */
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);
/* UART TX Callback: define behaviour after transmiting message */
/* UART TX Callback: коллбек после окончания передачи */
RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS);
/* Handler for UART */
/* Обработчик прерывания UART */
void RS_UART_Handler(RS_HandleTypeDef *hRS);
/* Handler for TIM */
/* Обработчик прерывания TIM */
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---------------------
///////////////////////////---FUNCTIONS---///////////////////////////

View File

@@ -82,7 +82,7 @@ ProjectRoot/
3.4. Инициализация в коде
В `main()` после инициализации HAL:
Чтобы настроить Slave-режим `main()` после инициализации HAL:
```c
#include "modbus.h"
@@ -97,10 +97,11 @@ ProjectRoot/
MX_TIM3_Init();
// Инициализация Modbus
MODBUS_SetupHardware(&hmodbus1, &mb_huart, &mb_htim);
MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim);
MODBUS_Config(&hmodbus1, 1, 1000, MODBUS_MODE_SLAVE);
// Запуск приема Modbus
MODBUS_SlaveStart(&hmodbus1, &MODBUS_MSG);
MODBUS_SlaveStart(&hmodbus1, NULL);
while (1)
{
@@ -109,6 +110,34 @@ ProjectRoot/
}
```
Чтобы настроить Master-режим `main()` после инициализации HAL:
```c
#include "modbus.h"
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, 1000, MODBUS_MODE_MASTER);
// Запуск приема Modbus
// Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук
RS_MsgTypeDef msg = MB_MASTER_READ_HOLDING_REGS(1, 0, 10);
MODBUS_MasterRequest(&hmodbus1, &msg, &callback_func);
void callback_func(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
// modbus_msg содержит ответ от устройства
}
}
```
3.5. Настройка карты данных
В `modbus_data.h` настройте регистры и coils под ваше устройство:
@@ -178,7 +207,7 @@ ProjectRoot/
5. **Обновление библиотеки**:
После обновления субмодуля из Git, исходные файлы библиотеки будут обновлены, но ваши конфиги останутся в целевой папке и не перезапишутся:
После обновления субмодуля из Git, исходные файлы библиотеки будут обновлены, и ваши конфиги вне субмодуля не перезапишутся:
```bash
git submodule update --remote

View File

@@ -12,13 +12,7 @@
- MODBUS_FirstInit() — Инициализация Modbus (подключение UART, TIM)
- MODBUS_Config() — Конфигурацмя Modbus (ID, Timeout).
- MODBUS_SlaveStart() — Запуск Modbus как Slave.
### Функции для Modbus
- MB_Slave_Response() — Ответ на запрос
- MB_Slave_Collect_Message() — Сбор сообщения в режиме слейва.
- MB_Slave_Parse_Message() — Парс сообщения в режиме слейва.
- MB_Master_Collect_Message() — Сбор сообщения в режиме мастера
- MB_Master_Parse_Message() — Парс сообщения в режиме мастера
- MODBUS_MasterRequest() — Отправить запрос в MODBUS как Master.
### Функции для работы с RS (UART):
- RS_Parse_Message() / RS_Collect_Message() — Парсинг и сборка сообщения.
@@ -33,6 +27,8 @@ RS_MsgTypeDef MODBUS_MSG; ///< Default Message Struct for Modbus
/* DEFINE DATA FOR MODBUS */
MB_DataStructureTypeDef MB_DATA = {0};; ///< Coils & Registers
static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg);
//-------------------------------------------------------------------
//-----------------------------FOR USER------------------------------
/**
@@ -55,7 +51,6 @@ HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef
hmodbus->ID = MODBUS_DEVICE_ID;
hmodbus->sRS_Timeout = MODBUS_TIMEOUT;
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
hmodbus->sRS_RX_Size_Mode = RS_RX_Size_NotConst;
// INIT
hmodbus->RS_STATUS = RS_Init(hmodbus, huart, htim, 0);
@@ -80,19 +75,22 @@ HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t
{
return HAL_ERROR;
}
if((ID < 1) || (ID > 247))
if(!master)
{
return HAL_ERROR;
if((ID < 1) || (ID > 247))
{
return HAL_ERROR;
}
hmodbus->ID = ID;
}
//-----------SETUP MODBUS-------------
// set up modbus: MB_RX_Size_NotConst and Timeout enable
hmodbus->ID = ID;
else
hmodbus->ID = 0;
hmodbus->sRS_Timeout = Timeout;
if(master)
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
hmodbus->sRS_Mode = RS_MASTER_REQUEST;
else
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
hmodbus->sRS_RX_Size_Mode = RS_RX_Size_NotConst;
return HAL_OK;
}
@@ -111,7 +109,7 @@ HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mo
return HAL_ERROR;
}
if(hmodbus->sRS_Mode >= RS_MASTER_START)
if(hmodbus->sRS_Mode >= RS_MASTER_MODE_START)
{
return HAL_ERROR;
}
@@ -129,467 +127,57 @@ HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mo
return HAL_ERROR;
}
//-------------------------------------------------------------------
//-----------------------------INTERNAL------------------------------
/**
* @brief Ответ на сообщение в режиме слейва.
* @brief Реквест мастера модбас.
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @return RS_RES Статус о результате ответа на комманду.
* @param modbus_msg Указатель на структуру сообщения
* @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;
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)
if(hmodbus == NULL)
{
MB_Diagnostics_SlaveNoResponseCnt(); // <-- Устройство не отвечает на широковещательные сообщения
hmodbus->RS_STATUS = RS_SKIP;
return RS_Handle_Receive_Start(hmodbus, modbus_msg);
return HAL_ERROR;
}
MB_Diagnostics_SlaveMessageCnt();
if(modbus_msg->Func_Code < ERR_VALUES_START)// if no errors after parsing
if(modbus_msg == NULL)
{
switch (modbus_msg->Func_Code)
{
// Read Coils
case MB_R_COILS:
hmodbus->f.MessageHandled = MB_Proccess_Read_Coils(hmodbus->pMessagePtr);
break;
// Read Hodling Registers
case MB_R_HOLD_REGS:
hmodbus->f.MessageHandled = MB_Proccess_Read_Hold_Regs(hmodbus->pMessagePtr);
break;
case MB_R_IN_REGS:
hmodbus->f.MessageHandled = MB_Proccess_Read_Input_Regs(hmodbus->pMessagePtr);
break;
// Write Single Coils
case MB_W_COIL:
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;
// Добавить в switch-case после других case:
case MB_R_DIAGNOSTIC:
hmodbus->f.MessageHandled = MB_Proccess_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);
}
return HAL_ERROR;
}
if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
{
return HAL_ERROR;
}
// 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
if(hmodbus->f.RS_Busy)
return HAL_BUSY;
// 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);
}
if(pClbk) // если задан используем пользовательский коллбек
hmodbus->pCallback = (void (*)(void*, void*))(pClbk);
else // иначе дефолтный
hmodbus->pCallback = (void (*)(void*, void*))(&MB_DefaultCallback);
hmodbus->RS_STATUS = RS_Transmit_IT(hmodbus, modbus_msg);
if(hmodbus->RS_STATUS == RS_OK)
return HAL_OK;
else
{
RS_Handle_Receive_Start(hmodbus, modbus_msg);
hmodbus->f.DeferredResponse = 0;
}
hmodbus->RS_STATUS = MB_RES;
return MB_RES;
return HAL_ERROR;
}
/**
* @brief Сбор сообщения в буфер UART в режиме слейв.
* @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)
static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
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 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 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
__NOP();
return;
}
/**
* @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 Парс сообщения в режиме слейв.
* @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;
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 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(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;
}
/**
* @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_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;
}
@@ -598,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)
{
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);
}
@@ -610,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)
{
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);
}

View File

@@ -86,13 +86,48 @@ uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
}
/**
* @brief Получить состояние coil в ответе по его адресу
* @param modbus_msg Указатель на структуру сообщения
* @param coil_addr Адрес coil, состояние которого нужно получить
* @param coil_state Указатель для состояния coil (1 - ON, 0 - OFF)
* @return 1 - успех, 0 - ошибка или coil_addr вне диапазона запроса
*/
int MB_GetCoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state)
{
if(modbus_msg == NULL || coil_state == NULL)
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 bit_index = coil_index % 8;
// Проверяем что байт существует в данных
if(byte_index >= modbus_msg->ByteCnt)
return 0;
// Получаем байт и проверяем бит
uint8_t coil_byte = modbus_msg->DATA[byte_index] & 0xFF;
*coil_state = (coil_byte >> bit_index) & 0x01;
return 1;
}
/**
* @brief Обработать функцию Read Coils (01 - 0x01).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @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----------
uint16_t *coils;
@@ -145,7 +180,7 @@ uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg)
* @return fMessageHandled Статус о результате обработки комманды.
* @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----------
if ((modbus_msg->Qnt != 0x0000) && (modbus_msg->Qnt != 0xFF00))
@@ -238,10 +273,12 @@ uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg)
}
#else //MODBUS_ENABLE_COILS
int MB_GetCoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state) {return 0;}
MB_ExceptionTypeDef MB_Write_Coil_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal) {return ILLEGAL_FUNCTION;}
uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception) {return 0;}
uint8_t MB_Proccess_Read_Coils(RS_MsgTypeDef *modbus_msg) {return 0;}
uint8_t MB_Proccess_Write_Single_Coil(RS_MsgTypeDef *modbus_msg) {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_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg) {return 0;}
#endif

View File

@@ -16,9 +16,115 @@
******************************************************************************/
#include "modbus_devid.h"
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATORS
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
MB_DeviceIdentificationsTypeDef MB_DEVID; ///< Глобальная структура идентификаторов устройства
/**
* @brief Получить количество объектов в сообщении
* @param modbus_msg Указатель на структуру сообщения
* @return int Количество объектов
*/
int MB_GetNumberOfObjects(RS_MsgTypeDef *modbus_msg)
{
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_FindObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length)
{
if((modbus_msg == NULL) || (obj_data == NULL))
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_GetObjectByIndex(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(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;
}
MB_DeviceIdentificationTypeDef MB_DEVID; ///< Глобальная структура идентификаторов устройства
/**
@@ -88,16 +194,16 @@ void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj)
/**
* @brief Обработать функцию Read Device Identification (43/14 - 0x2B/0E).
* @brief Обработать функцию Read Device Identifications (43/14 - 0x2B/0E).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @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)
{
case MB_BASIC_IDENTIFICATION:
case MB_BASIC_IDENTIFICATIONS:
if (modbus_msg->DevId.NextObjId == 0)
{
modbus_msg->DevId.NextObjId = 0;
@@ -107,7 +213,7 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg)
modbus_msg->DevId.NumbOfObj = 3;
break;
case MB_REGULAR_IDENTIFICATION:
case MB_REGULAR_IDENTIFICATIONS:
if (modbus_msg->DevId.NextObjId == 0)
{
modbus_msg->DevId.NextObjId = 3;
@@ -117,7 +223,7 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg)
modbus_msg->DevId.NumbOfObj = 4;
break;
case MB_EXTENDED_IDENTIFICATION:
case MB_EXTENDED_IDENTIFICATIONS:
if(MODBUS_NUMB_OF_USEROBJECTS <= 0 || MODBUS_NUMB_OF_USEROBJECTS > 128)
{
return 0;
@@ -132,7 +238,7 @@ uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg)
modbus_msg->DevId.NumbOfObj = MODBUS_NUMB_OF_USEROBJECTS;
break;
case MB_SPEDIFIC_IDENTIFICATION:
case MB_SPEDIFIC_IDENTIFICATIONS:
MB_WriteObjectsToMessage(modbus_msg, modbus_msg->DevId.NextObjId);
modbus_msg->DevId.NumbOfObj = 1;
break;
@@ -543,12 +649,15 @@ void MB_DeviceInentificationInit(void)
}
#else //MODBUS_ENABLE_DEVICE_IDENTIFICATORS
#else //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
/* Получить количество объектов в сообщении */
int MB_GetNumberOfObjects(RS_MsgTypeDef *modbus_msg) {return 0;}
int MB_FindObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length) {return 0;}
int MB_GetObjectByIndex(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_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg) {return 0;}
uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg) {return 0;}
void MB_DeviceInentificationInit(void) {}
#endif

View File

@@ -35,6 +35,22 @@ void MB_DiagnosticsInit(void)
MB_DIAG.Counters.BusCharacterOverrun = 0;
}
/**
* @brief Получить данные диагностики из сообщения (DATA[1])
* @param modbus_msg Указатель на структуру сообщения
* @param data Указатель куда положить данные
* @return 1 - успех, 0 - ошибка
*/
int MB_GetDiagnosticResponse(RS_MsgTypeDef *modbus_msg, uint16_t *data)
{
if(modbus_msg == NULL || data == NULL)
return 0;
*data = modbus_msg->DATA[1];
return 1;
}
/**
* @brief Выставить бит в регистре диагностике
* @param bit_num Номер бита для выставления (1-15, 0 бит нельзя выставить)
@@ -71,7 +87,7 @@ int MB_Diagnostics_GetBit(int bit_num)
* @param modbus_msg Указатель на структуру сообщения modbus
* @return fMessageHandled Статус обработки команды
*/
uint8_t MB_Proccess_Diagnostics(RS_MsgTypeDef *modbus_msg)
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];
@@ -295,9 +311,10 @@ MB_DeviceModeTypeDef MB_GetDeviceMode(void)
#else //MODBUS_ENABLE_DIAGNOSTICS
void MB_DiagnosticsInit(void) {}
int MB_GetDiagnosticResponse(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_Proccess_Diagnostics(RS_MsgTypeDef *modbus_msg) {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) {}

View File

@@ -30,7 +30,7 @@
* @return fMessageHandled Статус о результате обработки комманды.
* @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----------
// get origin address for data
@@ -58,7 +58,7 @@ uint8_t MB_Proccess_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg)
* @return fMessageHandled Статус о результате обработки комманды.
* @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
uint16_t *pHoldRegs;
@@ -77,7 +77,7 @@ uint8_t MB_Proccess_Write_Single_Reg(RS_MsgTypeDef *modbus_msg)
* @return fMessageHandled Статус о результате обработки комманды.
* @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----------
if (modbus_msg->Qnt*2 != modbus_msg->ByteCnt)
@@ -102,8 +102,8 @@ uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg)
#else //MODBUS_ENABLE_HOLDINGS
uint8_t MB_Proccess_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg) {return 0;}
uint8_t MB_Proccess_Write_Single_Reg(RS_MsgTypeDef *modbus_msg) {return 0;}
uint8_t MB_Proccess_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg) {return 0;}
uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg) {return 0;}
uint8_t MB_Process_Write_Single_Reg(RS_MsgTypeDef *modbus_msg) {return 0;}
uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg) {return 0;}
#endif

View File

@@ -20,7 +20,7 @@
* @return fMessageHandled Статус о результате обработки комманды.
* @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----------
// get origin address for data
@@ -47,6 +47,6 @@ uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg)
#else //MODBUS_ENABLE_INPUTS
uint8_t MB_Proccess_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) {return 0;}
uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg) {return 0;}
#endif

519
Src/modbus_master.c Normal file
View File

@@ -0,0 +1,519 @@
/**
**************************************************************************
* @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_GetRegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value)
{
if(modbus_msg == NULL || reg_value == NULL)
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_GetRegisterValue(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
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_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

@@ -151,7 +151,7 @@ RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
* @param stim Указатель на структуру с настройками таймера.
* @param pRS_BufferPtr Указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер.
* @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)
{
@@ -166,9 +166,6 @@ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_H
hRS->htim = htim;
if (hRS->sRS_RX_Size_Mode == NULL)
return RS_ERR;
// check that buffer is defined
if (hRS->pBufferPtr == NULL)
{
@@ -190,20 +187,14 @@ RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_H
- ABORT_RX_TX: Отмена приема и передачи по ЮАРТ,
- ABORT_RS: Отмена приема-передачи RS, с очищением всей структуры.
* @return RS_RES Статус о состоянии RS после аборта.
* @note Отмена работы UART в целом или отмена приема/передачи RS.
Также очищается хендл hRS.
* @details Отмена работы UART в целом или отмена приема/передачи RS.
Также очищается хендл hRS.
*/
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode)
{
HAL_StatusTypeDef uart_res = 0;
if(hRS->htim)
{
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);
}
RS_Timeout_Stop(hRS);
if((AbortMode&ABORT_RS) == 0x00)
{
@@ -241,7 +232,7 @@ RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode)
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации приема или окончания общения.
* @note Определяет начинать прием команды/ответа или нет.
* @details Определяет начинать прием команды/ответа или нет.
*/
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)
{
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_SLAVE_TIMEOUT_WAIT: // in slave mode with timeout waiting (start receiving cmd by request)
// В режиме мастер
case RS_MASTER_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)
}
@@ -267,7 +264,7 @@ RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *R
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации передачи.
* @note Определяет отвечать ли на команду или нет.
* @details Определяет отвечать ли на команду или нет.
*/
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)
{
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;
}
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);
}
@@ -294,10 +292,10 @@ RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *
}
/**
* @brief UART TX Callback: коллбек после окончания передачи.
* @brief UART TX Callback: коллбек после окончания передачи.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус о состоянии RS после обработки приема.
* @note Определяет поведение RS после передачи сообщения.
* @details Определяет поведение RS после передачи сообщения.
*/
RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS)
{
@@ -316,8 +314,8 @@ RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS)
/**
* @brief Обработчик прерывания UART.
* @param hRS Указатель на хендлер RS.
* @note Обрабатывает ошибки если есть и вызывает RS Коллбеки.
* Добавить вызов этой функции в UARTx_IRQHandler() ВМЕСТО HAL_UART_IRQHandler().
* @details Обрабатывает ошибки если есть и вызывает RS Коллбеки.
* Добавить вызов этой функции в UARTx_IRQHandler() ВМЕСТО HAL_UART_IRQHandler().
*/
void RS_UART_Handler(RS_HandleTypeDef *hRS)
{
@@ -352,17 +350,22 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
// Парсим наше сообщение
RS_StatusTypeDef parse_res = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
// Если сообещине принято корректно - отвечаем на него
// Если сообещине принято корректно
if(parse_res == RS_OK)
{
if(hRS->htim)
{
// Останавливаем таймаут
if(hRS->sRS_Timeout)
HAL_TIM_Base_Stop_IT(hRS->htim);
}
RS_Timeout_Stop(hRS);
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
{
@@ -384,18 +387,12 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
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;
/* 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_Start(hRS);
}
RS_Timeout_Update(hRS);
/* RX Callback - теперь НЕ вызываем здесь, ждем IDLE */
@@ -426,8 +423,8 @@ void RS_UART_Handler(RS_HandleTypeDef *hRS)
/**
* @brief Обработчик прерывания TIM.
* @param hRS Указатель на хендлер RS.
* @note Попадание сюда = таймаут и перезапуск RS приема
* Добавить вызов этой функции в TIMx_IRQHandler() ВМЕСТО HAL_TIM_IRQHandler().
* @details Попадание сюда = таймаут и перезапуск RS приема
* Добавить вызов этой функции в TIMx_IRQHandler() ВМЕСТО HAL_TIM_IRQHandler().
*/
void RS_TIM_Handler(RS_HandleTypeDef *hRS)
{
@@ -442,8 +439,68 @@ void RS_TIM_Handler(RS_HandleTypeDef *hRS)
if(hRS->pMessagePtr->MbAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
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---------------------
//-------------------------------------------------------------------
@@ -456,7 +513,6 @@ void RS_TIM_Handler(RS_HandleTypeDef *hRS)
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о результате ответа на комманду.
* @note Обработка принятой комманды и ответ на неё.
*/
__weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
@@ -470,7 +526,6 @@ __weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg
* @param RS_msg Указатель на структуру сообщения.
* @param msg_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения буфера.
* @note Заполнение буффера UART из структуры сообщения.
*/
__weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{
@@ -484,7 +539,6 @@ __weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef
* @param RS_msg Указатель на структуру сообщения.
* @param msg_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения структуры.
* @note Заполнение структуры сообщения из буффера UART.
*/
__weak RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{

View File

@@ -25,17 +25,30 @@
// Строковые идентификаторы устройства
#define MODBUS_VENDOR_NAME "NIO-12"
#define MODBUS_PRODUCT_CODE ""
#define MODBUS_REVISION "Ver. 1.0"
#define MODBUS_REVISION ""
#define MODBUS_VENDOR_URL ""
#define MODBUS_PRODUCT_NAME ""
#define MODBUS_MODEL_NAME "STM32F103"
#define MODBUS_MODEL_NAME ""
#define MODBUS_USER_APPLICATION_NAME ""
#define MODBUS_NUMB_OF_USEROBJECTS 0
// Периферия (опционально)
#define mb_huart huart1 ///< Удобный дефайн для модбасовского uart
#define mb_htim htim3 ///< Удобный дефайн для модбасовского таймера
///<
#define mb_htim htim2 ///< Удобный дефайн для модбасовского таймера
//#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_IDENTIFICATORS ///< Включить обработку идентификаторы устройства
#define MODBUS_ENABLE_DIAGNOSTICS ///< Включить обработку диагностики модбас
/**
* @brief Поменять комманды 0x03 и 0x04 местами (для LabView терминалки от двигателей)