9.5 KiB
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, ®_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 | Чтение идентификации устройства |