2025-11-05 11:06:04 +03:00
2025-11-05 00:00:30 +03:00
2025-11-05 00:00:30 +03:00
2025-11-05 00:00:30 +03:00
2025-11-05 11:06:04 +03:00

STM Modbus: подключение и использование

Библиотека реализует Modbus RTU Slave для STM32 с использованием HAL. Работает в прерываниях с детектированием конца фрейма по IDLE линии и таймаутами через прерывание таймера.

Быстрый старт

Note: Здесь описано подключение просто архива c исходниками. Если надо подключить библиотеку как субмодуль: см. ветку release в этом репозитории.

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

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

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. Настройка конфигурации

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

// MODBUS PARAMS
#define MODBUS_DEVICE_ID              1       ///< Адрес устройства в сети Modbus
#define MODBUS_TIMEOUT                5000    ///< Таймаут в тиках таймера

// Строковые идентификаторы устройства
#define MODBUS_VENDOR_NAME            "NIO-12"
#define MODBUS_PRODUCT_CODE           ""
#define MODBUS_REVISION               "Ver. 1.0"
#define MODBUS_VENDOR_URL             ""
#define MODBUS_PRODUCT_NAME           ""
#define MODBUS_MODEL_NAME             "STM32F103"
#define MODBUS_USER_APPLICATION_NAME  ""
#define MODBUS_NUMB_OF_USEROBJECTS    0

// Периферия
#define mb_huart                      huart1  // Удобный дефайн для модбасовского UART
#define mb_htim                       htim3   // Удобный дефайн для модбасовского таймера

// Модули modbus
#define MODBUS_ENABLE_SLAVE                   ///< Включить обработку МАСТЕР режима
//#define MODBUS_ENABLE_MASTER                  ///< Включить обработку СЛЕЙВ режима

#define MODBUS_ENABLE_COILS                   ///< Включить обработку коилов
#define MODBUS_ENABLE_HOLDINGS                ///< Включить обработку регистров хранения
#define MODBUS_ENABLE_INPUTS                  ///< Включить обработку входных регистров
#define MODBUS_ENABLE_DEVICE_IDENTIFICATIONS   ///< Включить обработку идентификаторы устройства
#define MODBUS_ENABLE_DIAGNOSTICS             ///< Включить обработку диагностики модбас

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

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

#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, MODBUS_DEVICE_ID, MODBUS_TIMEOUT, MODBUS_MODE_SLAVE);
	
	// Запуск приема Modbus
	MODBUS_SlaveStart(&hmodbus1, NULL);
	
	while (1)
	{
		// Основной цикл
	}
}

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

#include "modbus.h"
read_hold[10];
// Запрос на 1 ID, считать холдинг регистры с 0 адреса 10 штук
RS_MsgTypeDef read_hold_cmd = MB_MASTER_READ_HOLDING_REGS(1, 0, 10);
// коллбек, вызовется при получении ответа от слейва
void callback_func(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg) 
{
	// MB_RespGet_... Чтобы достать нужные данные из ответа
	for(int addr = MODBUS_MSG.Addr; addr < MODBUS_MSG.Addr + MODBUS_MSG.Qnt; addr++)
	{
		uint16_t value;
		if(MB_RespGet_RegisterValue(&MODBUS_MSG, addr, &value))
		{
			read_hold[i] = value;
		}
	}
}
int main(void)
{
	// Инициализация HAL
	HAL_Init();
	SystemClock_Config();
	MX_GPIO_Init();
	MX_USART1_UART_Init();
	MX_TIM3_Init();
	
	// Инициализация Modbus
	MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim);
	MODBUS_Config(&hmodbus1, 0, MODBUS_TIMEOUT, MODBUS_MODE_MASTER);
	
	// Запрос по Modbus
	MODBUS_MasterRequest(&hmodbus1, &read_hold_cmd, &callback_func);

}

5. Настройка карты данных

В 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

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

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

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

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

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

// Чтение 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);
	}	

Поддерживаемые функции Modbus

Функция Код Описание
Read Coils 0x01 Чтение дискретных выходов
Read Input Registers 0x04 Чтение входных регистров
Read Holding Registers 0x03 Чтение регистров хранения
Write Single Coil 0x05 Запись одиночного coil
Write Single Register 0x06 Запись одиночного регистра
Diagnostics (Serial Line only) 0x08 Чтение диагностически и управление режимом работы
Write Multiple Coils 0x0F Запись множественных coils
Write Multiple Registers 0x10 Запись множественных регистров
Read Device Identification 0x2B Чтение идентификации устройства
Description
Библиотека модбас для STM32
Readme 18 MiB
Languages
C 100%