diff --git a/Inc/modbus_core.h b/Inc/modbus_core.h index 48f32b4..e64af19 100644 --- a/Inc/modbus_core.h +++ b/Inc/modbus_core.h @@ -72,15 +72,17 @@ first receive info part of message, than defines size of rest message*/ */ typedef enum //MB_ExceptionTypeDef { - // reading + /* Регулярные коды ошибок, которые определены конкретной программой */ ET_NO_ERRORS = 0x00, ///< no errors ET_ILLEGAL_FUNCTION = 0x01, ///< Принятый код функции не может быть обработан ET_ILLEGAL_DATA_ADDRESS = 0x02, ///< Адрес данных, указанный в запросе, недоступен ET_ILLEGAL_DATA_VALUE = 0x03, ///< Значение, содержащееся в поле данных запроса, является недопустимой величиной ET_SLAVE_DEVICE_FAILURE = 0x04, ///< Невосстанавливаемая ошибка имела место, пока ведомое устройство пыталось выполнить затребованное действие -// ET_ACKNOWLEDGE = 0x05, ///< idk -// ET_SLAVE_DEVICE_BUSY = 0x06, ///< idk -// ET_MEMORY_PARITY_ERROR = 0x08, ///< idk + + /* Специальные коды ошибок, которые определены конкретной программой */ + ET_ACKNOWLEDGE = 0x05, ///< Устройство приняло запрос, но на обработку требуется время. Нужно чтобы не было Timeout ошибки + ET_SLAVE_DEVICE_BUSY = 0x06, ///< Устройство занято обработкой комманды + ET_MEMORY_PARITY_ERROR = 0x08, ///< Ошибка внешней памяти }MB_ExceptionTypeDef; #define FC_ERR_VALUES_START 0x80U ///< from this value starts error func codes diff --git a/Inc/modbus_slave.h b/Inc/modbus_slave.h index bfc06bd..a3a97fc 100644 --- a/Inc/modbus_slave.h +++ b/Inc/modbus_slave.h @@ -21,6 +21,10 @@ * @{ */ +/* Ответить позже, не в прерывании */ +HAL_StatusTypeDef MB_Slave_ResponseLater(RS_HandleTypeDef *hmodbus, uint8_t ResponseCode); +/* Ответить на запрос */ +HAL_StatusTypeDef MB_Slave_SendResponse(RS_HandleTypeDef *hmodbus, uint8_t ResponseCode, MB_ExceptionTypeDef error); /* Ответ на сообщение в режиме слейва */ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg); /* Сбор сообщения в буфер UART в режиме слейв (фрейм слейва из msg -> uart) */ diff --git a/Inc/rs_message.h b/Inc/rs_message.h index 9d19273..3cca87b 100644 --- a/Inc/rs_message.h +++ b/Inc/rs_message.h @@ -211,11 +211,13 @@ typedef struct unsigned TX_Done:1; ///< 1 - Передача закончена, 0 - Передача еще в процессе или не инициализирована // Выставление следующие флагов определяет пользователь - unsigned RX_Continue:1; ///< 0 - Продолжить принимать, 0 - Начать прием сначала + unsigned RX_Continue:1; ///< 1 - Продолжить принимать, 0 - Начать прием сначала unsigned MessageHandled:1; ///< 1 - Обработка запроса успешна, 0 - Обработка запроса в процессе или ошибка unsigned EchoResponse:1; ///< 1 - Ответить эхом, 0 - Ответить своим сообщением - unsigned DeferredResponse:1; ///< 1 - Не начинать передачу в IT, 0 - Ответить в прерывании - unsigned DataUpdated:1; ///< 1 - Данные были обновлены + unsigned DataUpdated:1; ///< 1 - Данные были обновлены: + unsigned DeviceBusy:1; ///< 1 - Устройство занято (важно! не rs а именно устрройство) + unsigned DeferredResponse:8; ///< >0 - Не начинать передачу в IT, 0 - Ответить в прерывании + }RS_FlagsTypeDef; diff --git a/README.md b/README.md index ca03f7e..0690d66 100644 --- a/README.md +++ b/README.md @@ -56,16 +56,15 @@ ProjectRoot/ ``` ### 3. **Настройте конфигурацию** под ваш проект: - #### 3.1. Настройка периферии +#### 3.1. Настройка периферии - - **UART**: Настройте в режиме Asynchronous, нужная скорость (9600, 19200, etc), 8N1 - - **TIM**: Настройте таймер для генерации прерываний (например, 1ms tick) - - **Включите прерывания** для UART и TIM +- **UART**: Настройте в режиме Asynchronous, нужная скорость (9600, 19200, etc), 8N1 +- **TIM**: Настройте таймер для генерации прерываний (например, 1ms tick) +- **Включите прерывания** для UART и TIM - #### 3.2. Подключение обработчиков прерываний - - Подключите обработчики прерываний **UART** и **TIM** в свои IRQ обработчики ***вместо*** HAL-обработчиков: +#### 3.2. Подключение обработчиков прерываний +Подключите обработчики прерываний **UART** и **TIM** в свои IRQ обработчики ***вместо*** HAL-обработчиков: ```c #include "modbus.h" @@ -83,15 +82,21 @@ void TIMx_IRQHandler(void) HAL_TIM_IRQHandler(&htim); } ``` - #### 3.3. В `modbus_config.h` укажите параметры устройства +#### 3.3. В `modbus_config.h` укажите параметры устройства - #### 3.4. Инициализация в коде - - Чтобы настроить Slave-режим `main()` после инициализации HAL: +#### 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 @@ -102,7 +107,7 @@ int main(void) MX_TIM3_Init(); // Инициализация Modbus - MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim, SetTxDirectionFunc); + MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim, &SetTxDirectionFunc); MODBUS_Config(&hmodbus1, MODBUS_DEVICE_ID, MODBUS_TIMEOUT, MODBUS_MODE_SLAVE); // Запуск приема Modbus @@ -114,13 +119,13 @@ int main(void) } } ``` - Чтобы настроить Master-режим `main()` после инициализации HAL: +Чтобы настроить Master-режим `main()` после инициализации HAL: ```c #include "modbus.h" // Инициализация Modbus -MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim, SetTxDirectionFunc); +MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim, &SetTxDirectionFunc); MODBUS_Config(&hmodbus1, 0, MODBUS_TIMEOUT, MODBUS_MODE_MASTER); @@ -170,11 +175,11 @@ int main(void) } ``` - #### 3.5. Настройка карты данных (только для режима Slave) +#### 3.5. Настройка карты данных (только для режима Slave) - В `modbus_data.h` настройте регистры и coils под ваше устройство: +В `modbus_data.h` настройте регистры и coils под ваше устройство: - **Input Registers (только чтение)** +**Input Registers (только чтение)** ```c typedef struct { @@ -187,7 +192,7 @@ typedef struct #define R_INPUT_ADDR 0 // Начальный адрес Input регистров #define R_INPUT_QNT 4 // Количество Input регистров ``` - **Holding Registers (чтение/запись)** +**Holding Registers (чтение/запись)** ```c typedef struct { @@ -199,7 +204,7 @@ typedef struct #define R_HOLDING_ADDR 0 // Начальный адрес Holding регистров #define R_HOLDING_QNT 3 // Количество Holding регистров ``` - **Coils (1-битные)** +**Coils (1-битные)** ```c typedef struct { @@ -213,9 +218,9 @@ typedef struct #define C_COILS_ADDR 0 // Начальный адрес Coils #define C_COILS_QNT 4 // Количество Coils ``` - #### 3.6. Доступ к данным в коде +#### 3.6. Доступ к данным в коде - В режиме **слейва** есть дефайны для удобного выставления Коилов. На случай если они не упакованы в битовые поля +В режиме **слейва** есть дефайны для удобного выставления Коилов. На случай если они не упакованы в битовые поля ```c // Чтение входных регистров uint16_t temp = MB_DATA.InRegs.Temperature; @@ -232,7 +237,7 @@ if (MB_Coil_Read_Local(&MB_DATA.Coils, 2)) { // Pump включен } ``` - В режиме **мастера** есть функции для получения информации из ответа `MB_RespGet_...()` +В режиме **мастера** есть функции для получения информации из ответа `MB_RespGet_...()` ```c // Чтение регистров: Получить запрошенные регистры uint16_t value; diff --git a/Src/modbus_slave.c b/Src/modbus_slave.c index 310abf6..a838314 100644 --- a/Src/modbus_slave.c +++ b/Src/modbus_slave.c @@ -7,6 +7,8 @@ Файл содержит реализацию функций для работы Modbus в режиме слейва. @section slave Функции и макросы +- MB_Slave_ResponseLater() — Выставить флаг на ответ вне прерывания +- MB_Slave_SendResponse() — Ответить на запрос - MB_Slave_Response() — Ответ на запрос - MB_Slave_Collect_Message() — Сбор сообщения в режиме слейва. @@ -15,6 +17,66 @@ #include "modbus.h" #ifdef MODBUS_ENABLE_SLAVE + +/** + * @brief Ответить позже, не в прерывании. + * @param hmodbus Указатель на хендлер RS. + * @param ResponseCode Код запроса на который надо ответить. + * @return HAL Status. + * @details Отменяет ответ в прерывании на запрос. + Используется вместе с @ref MB_Slave_SendResponse + */ +HAL_StatusTypeDef MB_Slave_ResponseLater(RS_HandleTypeDef *hmodbus, uint8_t ResponseCode) +{ + hmodbus->f.DeferredResponse = ResponseCode; + + return HAL_OK; +} + +/** + * @brief Ответить на запрос. + * @param hmodbus Указатель на хендлер RS. + * @param ResponseCode Код запроса на который надо ответить. + * @param error Тип ошибки или её отсутствия в ответе. + * @return HAL Status. + * @details Ответить на запрос вне прерывания. + Используется вместе с @ref MB_Slave_ResponseLater + */ +HAL_StatusTypeDef MB_Slave_SendResponse(RS_HandleTypeDef *hmodbus, uint8_t ResponseCode, MB_ExceptionTypeDef error) +{ + RS_StatusTypeDef MB_RES = 0; + if(hmodbus->f.DeferredResponse == ResponseCode) + { + hmodbus->f.DeferredResponse = 0; + switch(error) + { + case ET_NO_ERRORS: + break; + + case ET_ACKNOWLEDGE: + MB_Diagnostics_SlaveNAKCnt(); + break; + case ET_SLAVE_DEVICE_BUSY: + MB_Diagnostics_SlaveBusyCnt(); + break; + default: + break; + } + + if(error != ET_NO_ERRORS) + { + MB_Diagnostics_ExceptionErrorCnt(); + TrackerCnt_Warn(hmodbus->rs_err); + hmodbus->pMessagePtr->FuncCode |= FC_ERR_VALUES_START; + hmodbus->pMessagePtr->Except_Code = error; + } + + hmodbus->RS_STATUS = RS_Handle_Transmit_Start(hmodbus, hmodbus->pMessagePtr); + } + return HAL_OK; +} + + /** * @brief Ответ на сообщение в режиме слейва. * @param hmodbus Указатель на хендлер RS. @@ -115,8 +177,7 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod /* unknown func code */ default: modbus_msg->Except_Code = 0x01; /* set exception code: illegal function */ - } - + } // Проверяем режим устройства - если Listen Only, не обрабатываем команды if (MB_GetDeviceMode() == MODBUS_LISTEN_ONLY_MODE) @@ -152,8 +213,8 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod } else { - RS_Handle_Receive_Start(hmodbus, modbus_msg); - hmodbus->f.DeferredResponse = 0; +// RS_Handle_Receive_Start(hmodbus, modbus_msg); +// hmodbus->f.DeferredResponse = 0; } hmodbus->RS_STATUS = MB_RES; @@ -173,7 +234,7 @@ RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeD { int ind = 0; // ind for modbus-uart buffer - if(hmodbus->f.EchoResponse && hmodbus->f.MessageHandled) // if echo response need + if(hmodbus->f.EchoResponse && (modbus_msg->Except_Code == ET_NO_ERRORS)) // if echo response need ind = hmodbus->RS_Message_Size; else {