# Инструкция по подключению релиза библиотеки `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. **Склонируйте субмодуль** в ваш проект: ```bash 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-обработчиков: ```c #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: ```c #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: ```c #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 (только чтение)** ```c 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 (чтение/запись)** ```c 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-битные)** ```c 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. Доступ к данным в коде В режиме **слейва** есть дефайны для удобного выставления Коилов. На случай если они не упакованы в битовые поля ```c // Чтение входных регистров 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_...()` ```c // Чтение регистров: Получить запрошенные регистры 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); } ``` ### 5. **Обновление библиотеки**: После обновления субмодуля из Git, исходные файлы библиотеки будут обновлены, и ваши конфиги вне субмодуля не перезапишутся: ```bash git submodule update --remote ```