Files
STM32_Modbus/README.md
Razvalyaev 279dc4cee7 Бета добавление новых кодов исключения
Улучшение механики ответа с задержкой, когда время ответа определяется пользователем, а не в прерывании сразу
2026-02-20 16:38:34 +03:00

12 KiB
Raw Blame History

Инструкция по подключению релиза библиотеки STM Modbus

Данная библиотека подключается напрямую из Git, как субмодуль. Позволяя при желании обновлять её напрямую через git.

Структура библиотеки

Note: Файлы начинающиеся с __ и которых нет в этом дереве являются внутренними/непротестированными/недокументированными

Modbus/                                                                             Иерархия модулей:
│   inc/                                                                             modbus
│   ├── modbus.h                # Главный заголовочный файл                          modbus_slave
│   ├── modbus_core.h           # Базовые определения и структуры                    modbus_master
│   ├── modbus_coils.h          # Работа с дискретными коилами                          ├── modbus_coils
│   ├── modbus_holdregs.h       # Работа с регистрами хранения                          ├── modbus_inputregs
│   ├── modbus_inputregs.h      # Работа с входными регистрами                          ├── modbus_inputregs
│   ├── modbus_devid.h          # Идентификаторы устройства                             ├── modbus_devid
│   ├── modbus_master.h         # Заголовочный файл Master режима                       ├── modbus_diag
│   ├── modbus_slave.h          # Заголовочный файл Slave режима                        ├── modbus_oscil
│   ├── modbus_diag.h           # Диагностика Modbus                                    └── rs_message
│   ├── modbus_oscil.h          # Осциллографирование данных                                    │
│   └── rs_message.h            # Драйвер обмена по RS/UART                                      └── modbus_core (единое ядро)
├── src/                                                                                             ├── modbus_config
│   ├── modbus.c                # Основная логика Modbus                                             ├── modbus_data
│   ├── modbus_slave.c          # Основная логика Slave Modbus                                       └── __crc_algs
│   ├── modbus_master.c         # Основная логика Master Modbus
│   ├── modbus_coils.c          # Реализация работы с coils
│   ├── modbus_holdregs.c       # Реализация регистров хранения
│   ├── modbus_inputregs.c      # Реализация входных регистров
│   ├── modbus_devid.c          # Реализация идентификации устройства
│   ├── modbus_diag.c           # Диагностические функции и счетчики ошибок
│   ├── modbus_oscil.c          # Сбор и хранение осциллограмм
│   └── rs_message.c            # Реализация драйвера RS                        
├── __modbus_config.h       # Конфигурация Modbus (надо заменить)                       
├── __modbus_data.h         # Структуры данных (надо заменить)                      
└── __modbus_data.c         # Функции доступа (надо заменить)     

Инструкция по подключению

1. Склонируйте субмодуль в ваш проект:

git submodule add https://git.arktika.cyou/set506/STM32_Modbus path/to/Modbus
git submodule update --init --recursive

2. Скопируйте файлы конфигурации в отдельную папку в вашем проекте (вне субмодуля) и удалите __ из имени файлов:

ProjectRoot/
├── Configs/
│   ├── modbus_config.h          # скопировать из __modbus_config.h
│   ├── modbus_data.h            # скопировать из __modbus_data.h
│   └── modbus_data.c            # скопировать из __modbus_data.c
└── Modbus/                      # Субмодуль

3. Настройте конфигурацию под ваш проект:

3.1. Настройка периферии

  • UART: Настройте в режиме Asynchronous, нужная скорость (9600, 19200, etc), 8N1
  • TIM: Настройте таймер для генерации прерываний (например, 1ms tick)
  • Включите прерывания для UART и TIM

3.2. Подключение обработчиков прерываний

Подключите обработчики прерываний UART и TIM в свои IRQ обработчики вместо HAL-обработчиков:

#include "modbus.h"

void USARTx_IRQHandler(void)
{
    RS_UART_Handler(&hmodbus1);
    return;
    HAL_UART_IRQHandler(&huart);
}

void TIMx_IRQHandler(void)
{
    RS_TIM_Handler(&hmodbus1);
    return;
    HAL_TIM_IRQHandler(&htim);
}

3.3. В modbus_config.h укажите параметры устройства

3.4. Инициализация в коде

Чтобы настроить Slave-режим main() после инициализации HAL:

#include "modbus.h"

// TxEnable: 1 - передача, 0 - прием
void SetTxDirectionFunc(int TxEnable)
{
  HAL_GPIO_WritePin(SCIDE1_GPIO_Port, SCIDE1_Pin, !TxEnable);
}

int main(void)
{
    // Инициализация HAL
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_TIM3_Init();
    
    // Инициализация Modbus
    MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim, &SetTxDirectionFunc);
    MODBUS_Config(&hmodbus1, MODBUS_DEVICE_ID, MODBUS_TIMEOUT, MODBUS_MODE_SLAVE);
    
    // Запуск приема Modbus
    MODBUS_SlaveStart(&hmodbus1, NULL);
    
    while (1)
    {
        // Основной цикл
    }
}

Чтобы настроить Master-режим main() после инициализации HAL:

#include "modbus.h"

// Инициализация Modbus
MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim, &SetTxDirectionFunc);
MODBUS_Config(&hmodbus1, 0, MODBUS_TIMEOUT, MODBUS_MODE_MASTER);
    

// Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук
// При получении ответа вызовется функция callback_func()
RS_MsgTypeDef msg = MB_REQUEST_READ_HOLDING_REGS(1, 0, 10);
MODBUS_MasterRequest(&hmodbus1, &msg, &callback_func);

// коллбек, вызовется при получении ответа от слейва
read_hold[10];
void callback_func(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) 
{
    // MB_RespGet_... Чтобы достать нужные данные из ответа

    if(hmodbus->RS_STATUS == RS_OK) // Получен ответ без ошибок
    {
        for(int addr = MODBUS_MSG.Addr; addr < MODBUS_MSG.Addr + MODBUS_MSG.Qnt; addr++)
        {
        // Запись регистров из ответа в массив
        uint16_t value;
        if(MB_RespGet_RegisterValue(&MODBUS_MSG, addr, &value))
        {
            read_hold[i] = value;
        }
        }
    }
    else // Ответ получен с ошибкой или не получен вовсе
    {
    }
}

int main(void)
{
    // Инициализация HAL
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_TIM3_Init();
    
    // Инициализация Modbus
    MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim);
    MODBUS_Config(&hmodbus1, 0, MODBUS_TIMEOUT, MODBUS_MODE_MASTER);
    
    // Запрос по Modbus
    MODBUS_MasterRequest(&hmodbus1, &read_hold_cmd, &callback_func);

}

3.5. Настройка карты данных (только для режима Slave)

В modbus_data.h настройте регистры и coils под ваше устройство:

Input Registers (только чтение)

typedef struct
{
    uint16_t Temperature;      // Адрес 0
    uint16_t Humidity;         // Адрес 1  
    uint16_t Pressure;         // Адрес 2
    uint16_t Voltage;          // Адрес 3
} MB_DataInRegsTypeDef;

#define R_INPUT_ADDR        0  // Начальный адрес Input регистров
#define R_INPUT_QNT         4  // Количество Input регистров

Holding Registers (чтение/запись)

typedef struct
{
    uint16_t SetpointTemp;     // Адрес 0
    uint16_t SetpointHumidity; // Адрес 1
    uint16_t ControlMode;      // Адрес 2
} MB_DataHoldRegsTypeDef;

#define R_HOLDING_ADDR      0  // Начальный адрес Holding регистров  
#define R_HOLDING_QNT       3  // Количество Holding регистров

Coils (1-битные)

typedef struct
{
    unsigned Relay1    : 1;    // Адрес 0
    unsigned Relay2    : 1;    // Адрес 1  
    unsigned Pump      : 1;    // Адрес 2
    unsigned Heater    : 1;    // Адрес 3
    unsigned reserved  : 12;   // Резерв (выравнивание до 16 бит)
} MB_DataCoilsTypeDef;

#define C_COILS_ADDR        0  // Начальный адрес Coils
#define C_COILS_QNT         4  // Количество Coils

3.6. Доступ к данным в коде

В режиме слейва есть дефайны для удобного выставления Коилов. На случай если они не упакованы в битовые поля

// Чтение входных регистров
uint16_t temp = MB_DATA.InRegs.Temperature;

// Запись в регистры хранения
MB_DATA.HoldRegs.SetpointTemp = 2500;

// Управление coils
MB_Coil_Set_Local(&MB_DATA.Coils, 0);  // Включить 0 бит в Coils
MB_Coil_Reset_Local(&MB_DATA.Coils, 1); // Выключить 1 бит в Coils

// Чтение coil
if (MB_Coil_Read_Local(&MB_DATA.Coils, 2)) {
    // Pump включен
}

В режиме мастера есть функции для получения информации из ответа MB_RespGet_...()

//  Чтение регистров: Получить запрошенные регистры
uint16_t value;
if(MB_RespGet_RegisterValue(&MODBUS_MSG, 105, &reg_value))
{
    printf("Register 105 value: %d\n", reg_value);
}
//  Чтение коилов: Получить запрошенные коилы
int state;
if(MB_RespGet_CoilState(&MODBUS_MSG, 25, &coil_state))
{
    printf("Coil 25 state: %s\n", coil_state ? "ON" : "OFF");
}
//  Чтение диагностики: Получить запрошенныую диагностику
uint16_t counter_value;
if(MB_RespGet_DiagnosticResponse(&MODBUS_MSG, &counter_value))
{
    printf("Counter value: %d\n", counter_value);
}
//  Чтение идентификаторов: Получить запрошенные идентификаторы
uint8_t length;
char vendor_name[64];
if(MB_RespGet_ObjectById(&MODBUS_MSG, 0x00, vendor_name, &length))
{
    printf("Vendor Name: %s (length: %d)\n", vendor_name, length);
}   

uint8_t obj_id, obj_length;
char obj_data[64];
if(MB_RespGet_ObjectByIndex(&MODBUS_MSG, 0x00, &obj_id, obj_data, &obj_length))
{
    printf("First object - ID: 0x%02X, Data: %s\n", obj_id, obj_data);
}   

5. Обновление библиотеки:

После обновления субмодуля из Git, исходные файлы библиотеки будут обновлены, и ваши конфиги вне субмодуля не перезапишутся:

git submodule update --remote