From 9fb2c10469bbf6baebed1af38459d2e42eec9b80 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 5 Aug 2020 17:53:32 +0200 Subject: [PATCH 1/5] freemodbus: apply latest known fixes from latest master includes fixes of freemodbus component add fixes from uart driver (required for correct data handling) update adding UART_ISR_IN_IRAM option in modbus add mbascii_m.c file remove code used for debug purpose fix minor issues with return codes and formatting fix master example issue sync latest modbus fixes Closes https://github.com/espressif/esp-idf/issues/5694 --- components/driver/include/driver/uart.h | 16 + components/driver/uart.c | 198 ++++-- components/freemodbus/CMakeLists.txt | 1 + components/freemodbus/Kconfig | 49 +- .../freemodbus/common/esp_modbus_master.c | 54 +- .../freemodbus/common/esp_modbus_slave.c | 25 +- .../common/include/esp_modbus_common.h | 13 +- .../common/include/esp_modbus_master.h | 8 + .../common/include/esp_modbus_slave.h | 8 + .../freemodbus/common/include/mbcontroller.h | 4 +- components/freemodbus/modbus/ascii/mbascii.c | 49 +- components/freemodbus/modbus/ascii/mbascii.h | 23 + .../freemodbus/modbus/ascii/mbascii_m.c | 574 ++++++++++++++++++ .../freemodbus/modbus/functions/mbfunccoils.c | 5 +- .../modbus/functions/mbfunccoils_m.c | 6 +- .../freemodbus/modbus/functions/mbfuncdisc.c | 8 +- .../modbus/functions/mbfuncdisc_m.c | 5 +- .../modbus/functions/mbfuncholding.c | 5 +- .../modbus/functions/mbfuncholding_m.c | 25 +- .../freemodbus/modbus/functions/mbfuncinput.c | 6 +- .../freemodbus/modbus/functions/mbfuncother.c | 6 +- components/freemodbus/modbus/include/mb.h | 5 +- components/freemodbus/modbus/include/mb_m.h | 79 +-- .../freemodbus/modbus/include/mbconfig.h | 28 +- .../freemodbus/modbus/include/mbframe.h | 8 + components/freemodbus/modbus/include/mbport.h | 17 +- .../freemodbus/modbus/include/mbproto.h | 2 +- components/freemodbus/modbus/mb.c | 31 +- components/freemodbus/modbus/mb_m.c | 68 ++- components/freemodbus/modbus/rtu/mbcrc.c | 5 + components/freemodbus/modbus/rtu/mbrtu.c | 40 +- components/freemodbus/modbus/rtu/mbrtu.h | 8 +- components/freemodbus/modbus/rtu/mbrtu_m.c | 109 ++-- components/freemodbus/port/port.c | 20 +- components/freemodbus/port/port.h | 29 +- components/freemodbus/port/portevent.c | 23 +- components/freemodbus/port/portevent_m.c | 5 +- components/freemodbus/port/portother.c | 18 - components/freemodbus/port/portother_m.c | 16 - components/freemodbus/port/portserial.c | 123 ++-- components/freemodbus/port/portserial_m.c | 152 +++-- components/freemodbus/port/porttimer.c | 54 +- components/freemodbus/port/porttimer_m.c | 42 +- .../modbus_controller/mbc_serial_master.c | 116 +--- .../modbus_controller/mbc_serial_slave.c | 14 +- .../modbus_master/main/sense_modbus.c | 8 +- 46 files changed, 1458 insertions(+), 650 deletions(-) create mode 100644 components/freemodbus/modbus/ascii/mbascii_m.c diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 5b5ffce2e4a..ff202cf6d58 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -151,6 +151,8 @@ typedef enum { typedef struct { uart_event_type_t type; /*!< UART event type */ size_t size; /*!< UART data size for UART_DATA event*/ + bool timeout_flag; /*!< UART data read timeout flag for UART_DATA event (no new data received during configured RX TOUT)*/ + /*!< If the event is caused by FIFO-full interrupt, then there will be no event with the timeout flag before the next byte coming.*/ } uart_event_t; typedef intr_handle_t uart_isr_handle_t; @@ -852,6 +854,20 @@ esp_err_t uart_set_wakeup_threshold(uart_port_t uart_num, int wakeup_threshold); */ esp_err_t uart_get_wakeup_threshold(uart_port_t uart_num, int* out_wakeup_threshold); +/** + * @brief Configure behavior of UART RX timeout interrupt. + * + * When always_rx_timeout is true, timeout interrupt is triggered even if FIFO is full. + * This function can cause extra timeout interrupts triggered only to send the timeout event. + * Call this function only if you want to ensure timeout interrupt will always happen after a byte stream. + * + * @param uart_num UART number + * @param always_rx_timeout_en Set to false enable the default behavior of timeout interrupt, + * set it to true to always trigger timeout interrupt. + * + */ +void uart_set_always_rx_timeout(uart_port_t uart_num, bool always_rx_timeout_en); + #ifdef __cplusplus } #endif diff --git a/components/driver/uart.c b/components/driver/uart.c index 1b8bfd33174..fc592146276 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -88,6 +88,7 @@ typedef struct { intr_handle_t intr_handle; /*!< UART interrupt handle*/ uart_mode_t uart_mode; /*!< UART controller actual mode set by uart_set_mode() */ bool coll_det_flg; /*!< UART collision detection flag */ + bool rx_always_timeout_flg; /*!< UART always detect rx timeout flag */ //rx parameters int rx_buffered_len; /*!< UART cached data length */ @@ -513,7 +514,7 @@ esp_err_t uart_disable_pattern_det_intr(uart_port_t uart_num) return uart_disable_intr_mask(uart_num, UART_AT_CMD_CHAR_DET_INT_ENA_M); } -esp_err_t uart_enable_rx_intr(uart_port_t uart_num) +esp_err_t inline uart_enable_rx_intr(uart_port_t uart_num) { return uart_enable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); } @@ -768,6 +769,39 @@ static int UART_ISR_ATTR uart_find_pattern_from_last(uint8_t* buf, int length, u return len; } +static UART_ISR_ATTR bool uart_is_tx_idle(uart_port_t uart_num) +{ + typeof(UART[uart_num]->status) status = UART[uart_num]->status; + return ((status.txfifo_cnt == 0) && (status.st_utx_out == 0)); +} + +static UART_ISR_ATTR uint32_t uart_get_rxfifo_len(uart_port_t uart_num) +{ + uint32_t fifo_cnt = UART[uart_num]->status.rxfifo_cnt; + typeof(UART[uart_num]->mem_rx_status) rx_status = UART[uart_num]->mem_rx_status; + uint32_t len = 0; + + // When using DPort to read fifo, fifo_cnt is not credible, we need to calculate the real cnt based on the fifo read and write pointer. + // When using AHB to read FIFO, we can use fifo_cnt to indicate the data length in fifo. + if (rx_status.wr_addr > rx_status.rd_addr) { + len = rx_status.wr_addr - rx_status.rd_addr; + } else if (rx_status.wr_addr < rx_status.rd_addr) { + len = (rx_status.wr_addr + 128) - rx_status.rd_addr; + } else { + len = fifo_cnt > 0 ? 128 : 0; + } + return len; +} + +static UART_ISR_ATTR void uart_read_rxfifo(uart_port_t uart_num, uint8_t* buf, uint32_t rd_len) +{ + //Get the UART APB fifo addr. Read fifo, we use APB address + uint32_t fifo_addr = UART_FIFO_REG(uart_num); + for(int i = 0; i < rd_len; i++) { + buf[i] = READ_PERI_REG(fifo_addr); + } +} + //internal isr handler for default driver code. static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param) { @@ -891,27 +925,16 @@ static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param) || (uart_intr_status & UART_RXFIFO_FULL_INT_ST_M) || (uart_intr_status & UART_AT_CMD_CHAR_DET_INT_ST_M) ) { - rx_fifo_len = uart_reg->status.rxfifo_cnt; - typeof(uart_reg->mem_rx_status) rx_status = uart_reg->mem_rx_status; - - // When using DPort to read fifo, fifo_cnt is not credible, we need to calculate the real cnt based on the fifo read and write pointer. - // When using AHB to read FIFO, we can use fifo_cnt to indicate the data length in fifo. - if (rx_status.wr_addr > rx_status.rd_addr) { - rx_fifo_len = rx_status.wr_addr - rx_status.rd_addr; - } else if (rx_status.wr_addr < rx_status.rd_addr) { - rx_fifo_len = (rx_status.wr_addr + 128) - rx_status.rd_addr; - } else { - rx_fifo_len = rx_fifo_len > 0 ? 128 : 0; - } - if(pat_flg == 1) { + rx_fifo_len = uart_get_rxfifo_len(uart_num); + if (pat_flg == 1) { uart_intr_status |= UART_AT_CMD_CHAR_DET_INT_ST_M; pat_flg = 0; } if (p_uart->rx_buffer_full_flg == false) { - //We have to read out all data in RX FIFO to clear the interrupt signal - for(buf_idx = 0; buf_idx < rx_fifo_len; buf_idx++) { - p_uart->rx_data_buf[buf_idx] = uart_reg->fifo.rw_byte; + if ((p_uart_obj[uart_num]->rx_always_timeout_flg) && !(uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M)) { + rx_fifo_len--; // leave one byte in the fifo in order to trigger UART_RXFIFO_TOUT_INT } + uart_read_rxfifo(uart_num, p_uart->rx_data_buf, rx_fifo_len); uint8_t pat_chr = uart_reg->at_cmd_char.data; int pat_num = uart_reg->at_cmd_char.char_num; int pat_idx = -1; @@ -927,6 +950,7 @@ static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param) uart_clear_intr_status(uart_num, UART_RXFIFO_TOUT_INT_CLR_M | UART_RXFIFO_FULL_INT_CLR_M); uart_event.type = UART_DATA; uart_event.size = rx_fifo_len; + uart_event.timeout_flag = (uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M) ? true : false; UART_ENTER_CRITICAL_ISR(&uart_selectlock); if (p_uart->uart_select_notif_callback) { p_uart->uart_select_notif_callback(uart_num, UART_SELECT_READ_NOTIF, &HPTaskAwoken); @@ -1044,17 +1068,24 @@ static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param) UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_event.type = UART_EVENT_MAX; } else if(uart_intr_status & UART_TX_DONE_INT_ST_M) { - uart_disable_intr_mask_from_isr(uart_num, UART_TX_DONE_INT_ENA_M); - uart_clear_intr_status(uart_num, UART_TX_DONE_INT_CLR_M); - // If RS485 half duplex mode is enable then reset FIFO and - // reset RTS pin to start receiver driver - if (UART_IS_MODE_SET(uart_num, UART_MODE_RS485_HALF_DUPLEX)) { + if (UART_IS_MODE_SET(uart_num, UART_MODE_RS485_HALF_DUPLEX) && uart_is_tx_idle(uart_num) != true) { + // The TX_DONE interrupt is triggered but transmit is active + // then postpone interrupt processing for next interrupt + uart_event.type = UART_EVENT_MAX; + } else { + // Workaround for RS485: If the RS485 half duplex mode is active + // and transmitter is in idle state then reset received buffer and reset RTS pin + // skip this behavior for other UART modes UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); - uart_reset_rx_fifo(uart_num); // Allows to avoid hardware issue with the RXFIFO reset - uart_reg->conf0.sw_rts = 1; + uart_disable_intr_mask_from_isr(uart_num, UART_TX_DONE_INT_ENA_M); + if (UART_IS_MODE_SET(uart_num, UART_MODE_RS485_HALF_DUPLEX)) { + uart_reset_rx_fifo(uart_num); // Allows to avoid hardware issue with the RXFIFO reset + uart_reg->conf0.sw_rts = 1; + } UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_clear_intr_status(uart_num, UART_TX_DONE_INT_CLR_M); + xSemaphoreGiveFromISR(p_uart_obj[uart_num]->tx_done_sem, &HPTaskAwoken); } - xSemaphoreGiveFromISR(p_uart_obj[uart_num]->tx_done_sem, &HPTaskAwoken); } else { uart_reg->int_clr.val = uart_intr_status; /*simply clear all other intr status*/ uart_event.type = UART_EVENT_MAX; @@ -1084,9 +1115,7 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) return ESP_ERR_TIMEOUT; } xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, 0); - typeof(UART0.status) status = UART[uart_num]->status; - //Wait txfifo_cnt = 0, and the transmitter state machine is in idle state. - if(status.txfifo_cnt == 0 && status.st_utx_out == 0) { + if(uart_is_tx_idle(uart_num)) { xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return ESP_OK; } @@ -1389,6 +1418,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->uart_num = uart_num; p_uart_obj[uart_num]->uart_mode = UART_MODE_UART; p_uart_obj[uart_num]->coll_det_flg = false; + p_uart_obj[uart_num]->rx_always_timeout_flg = false; p_uart_obj[uart_num]->tx_fifo_sem = xSemaphoreCreateBinary(); xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary(); @@ -1431,8 +1461,6 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b return ESP_FAIL; } - r=uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num], intr_alloc_flags, &p_uart_obj[uart_num]->intr_handle); - if (r!=ESP_OK) goto err; uart_intr_config_t uart_intr = { .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M | UART_RXFIFO_TOUT_INT_ENA_M @@ -1444,6 +1472,10 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b .rx_timeout_thresh = UART_TOUT_THRESH_DEFAULT, .txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT }; + uart_disable_intr_mask(uart_num, UART_INTR_MASK); + uart_clear_intr_status(uart_num, UART_INTR_MASK); + r=uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num], intr_alloc_flags, &p_uart_obj[uart_num]->intr_handle); + if (r!=ESP_OK) goto err; r=uart_intr_config(uart_num, &uart_intr); if (r!=ESP_OK) goto err; return r; @@ -1585,28 +1617,6 @@ esp_err_t uart_set_mode(uart_port_t uart_num, uart_mode_t mode) return ESP_OK; } -esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); - UART_CHECK((tout_thresh < 127), "tout_thresh max value is 126", ESP_ERR_INVALID_ARG); - UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - // The tout_thresh = 1, defines TOUT interrupt timeout equal to - // transmission time of one symbol (~11 bit) on current baudrate - if (tout_thresh > 0) { - //Hardware issue workaround: when using ref_tick, the rx timeout threshold needs increase to 10 times. - //T_ref = T_apb * APB_CLK/(REF_TICK << CLKDIV_FRAG_BIT_WIDTH) - if(UART[uart_num]->conf0.tick_ref_always_on == 0) { - UART[uart_num]->conf1.rx_tout_thrhd = tout_thresh * UART_TOUT_REF_FACTOR_DEFAULT; - } else { - UART[uart_num]->conf1.rx_tout_thrhd = tout_thresh; - } - UART[uart_num]->conf1.rx_tout_en = 1; - } else { - UART[uart_num]->conf1.rx_tout_en = 0; - } - UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - return ESP_OK; -} esp_err_t uart_get_collision_flag(uart_port_t uart_num, bool* collision_flag) { @@ -1638,3 +1648,87 @@ esp_err_t uart_get_wakeup_threshold(uart_port_t uart_num, int* out_wakeup_thresh *out_wakeup_threshold = UART[uart_num]->sleep_conf.active_threshold + UART_MIN_WAKEUP_THRESH; return ESP_OK; } + +static uint8_t uart_get_symb_len(uart_port_t uart_num) +{ + uint8_t symbol_len = 1; // number of bits per symbol including start + uart_parity_t parity_mode; + uart_stop_bits_t stop_bit; + uart_word_length_t data_bit; + uart_get_word_length(uart_num, &data_bit); + uart_get_stop_bits(uart_num, &stop_bit); + uart_get_parity(uart_num, &parity_mode); + symbol_len += (data_bit < UART_DATA_BITS_MAX) ? (uint8_t)data_bit + 5 : 8; + symbol_len += (stop_bit > UART_STOP_BITS_1) ? 2 : 1; + symbol_len += (parity_mode > UART_PARITY_DISABLE) ? 1 : 0; + return symbol_len; +} + +static void uart_set_rx_tout_thrd(uart_port_t uart_num, uint16_t tout_thr) +{ + if (UART[uart_num]->conf0.tick_ref_always_on == 0) { + //Hardware issue workaround: when using ref_tick, the rx timeout threshold needs increase to 10 times. + //T_ref = T_apb * APB_CLK/(REF_TICK << CLKDIV_FRAG_BIT_WIDTH) + tout_thr = tout_thr * UART_TOUT_REF_FACTOR_DEFAULT; + } else { + //If APB_CLK is used: counting rate is BAUD tick rate / 8 + tout_thr = (tout_thr + 7) / 8; + } + if (tout_thr > 0) { + UART[uart_num]->conf1.rx_tout_thrhd = tout_thr; + UART[uart_num]->conf1.rx_tout_en = 1; + } else { + UART[uart_num]->conf1.rx_tout_en = 0; + } +} + +static inline uint16_t uart_get_max_rx_timeout_thrd(uart_port_t uart_num) +{ + uint8_t symb_len = uart_get_symb_len(uart_num); + uint16_t max_tout_thresh = 0; + if (UART[uart_num]->conf0.tick_ref_always_on == 0) { + max_tout_thresh = (uint16_t)(UART_RX_TOUT_THRHD_V / UART_TOUT_REF_FACTOR_DEFAULT); + } else { + max_tout_thresh = (uint16_t)(UART_RX_TOUT_THRHD_V << 3); + } + return (max_tout_thresh / symb_len); +} + +esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + // get maximum timeout threshold + uint16_t tout_max_thresh = uart_get_max_rx_timeout_thrd(uart_num); + if (tout_thresh > tout_max_thresh) { + ESP_LOGE(UART_TAG, "tout_thresh = %d > maximum value = %d", tout_thresh, tout_max_thresh); + return ESP_ERR_INVALID_ARG; + } + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + uint8_t symb_len = uart_get_symb_len(uart_num); + uart_set_rx_tout_thrd(uart_num, symb_len * tout_thresh); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +static inline uint16_t uart_get_rx_tout_thr(uart_port_t uart_num) +{ + uint16_t tout_thrd = 0; + if (UART[uart_num]->conf1.rx_tout_en > 0) { + if (UART[uart_num]->conf0.tick_ref_always_on == 0) { + tout_thrd = (uint16_t)(UART[uart_num]->conf1.rx_tout_thrhd / UART_TOUT_REF_FACTOR_DEFAULT); + } else { + tout_thrd = (uint16_t)(UART[uart_num]->conf1.rx_tout_thrhd << 3); + } + } + return tout_thrd; +} + +void uart_set_always_rx_timeout(uart_port_t uart_num, bool always_rx_timeout) +{ + uint16_t rx_tout = uart_get_rx_tout_thr(uart_num); + if (rx_tout) { + p_uart_obj[uart_num]->rx_always_timeout_flg = always_rx_timeout; + } else { + p_uart_obj[uart_num]->rx_always_timeout_flg = false; + } +} diff --git a/components/freemodbus/CMakeLists.txt b/components/freemodbus/CMakeLists.txt index 97112a53bea..f4cc4a38c25 100644 --- a/components/freemodbus/CMakeLists.txt +++ b/components/freemodbus/CMakeLists.txt @@ -6,6 +6,7 @@ set(srcs "modbus/mb.c" "modbus/mb_m.c" "modbus/ascii/mbascii.c" + "modbus/ascii/mbascii_m.c" "modbus/rtu/mbrtu_m.c" "modbus/rtu/mbrtu.c" "modbus/rtu/mbcrc.c" diff --git a/components/freemodbus/Kconfig b/components/freemodbus/Kconfig index 113202d1228..c257cc66dba 100644 --- a/components/freemodbus/Kconfig +++ b/components/freemodbus/Kconfig @@ -1,10 +1,21 @@ menu "Modbus configuration" + config FMB_COMM_MODE_RTU_EN + bool "Enable Modbus stack support for RTU mode" + default y + help + Enable RTU Modbus communication mode option for Modbus serial stack. + + config FMB_COMM_MODE_ASCII_EN + bool "Enable Modbus stack support for ASCII mode" + default y + help + Enable ASCII Modbus communication mode option for Modbus serial stack. + config FMB_MASTER_TIMEOUT_MS_RESPOND int "Slave respond timeout (Milliseconds)" default 150 range 50 400 - help If master sends a frame which is not broadcast, it has to wait sometime for slave response. if slave is not respond in this time, the master will process timeout error. @@ -13,7 +24,6 @@ menu "Modbus configuration" int "Slave conversion delay (Milliseconds)" default 200 range 50 400 - help If master sends a broadcast frame, it has to wait conversion time to delay, then master can send next frame. @@ -43,10 +53,27 @@ menu "Modbus configuration" This buffer is used for modbus frame transfer. The Modbus protocol maximum frame size is 256 bytes. Bigger size can be used for non standard implementations. + config FMB_SERIAL_ASCII_BITS_PER_SYMB + int "Number of data bits per ASCII character" + default 8 + range 7 8 + depends on FMB_COMM_MODE_ASCII_EN + help + This option defines the number of data bits per ASCII character. + + config FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS + int "Response timeout for ASCII communication mode (ms)" + default 1000 + range 300 2000 + depends on FMB_COMM_MODE_ASCII_EN + help + This option defines response timeout of slave in milliseconds for ASCII communication mode. + Thus the timeout will expire and allow the master program to handle the error. + config FMB_SERIAL_TASK_PRIO int "Modbus serial task priority" - range 3 10 - default 10 + range 3 23 + default 12 help Modbus UART driver event task priority. The priority of Modbus controller task is equal to (CONFIG_FMB_SERIAL_TASK_PRIO - 1). @@ -103,7 +130,7 @@ menu "Modbus configuration" config FMB_TIMER_PORT_ENABLED bool "Modbus slave stack use timer for 3.5T symbol time measurement" - default y + default n help If this option is set the Modbus stack uses timer for T3.5 time measurement. Else the internal UART TOUT timeout is used for 3.5T symbol time measurement. @@ -122,4 +149,16 @@ menu "Modbus configuration" help Modbus Timer Index in the group that is used for timeout measurement. + config FMB_TIMER_ISR_IN_IRAM + bool "Place timer interrupt handler into IRAM" + default y + select UART_ISR_IN_IRAM + help + This option places Modbus timer IRQ handler into IRAM. + This allows to avoid delays related to processing of non-IRAM-safe interrupts + during a flash write operation (NVS updating a value, or some other + flash API which has to perform an read/write operation and disable CPU cache). + This option has dependency with the UART_ISR_IN_IRAM option which places UART interrupt + handler into IRAM to prevent delays related to processing of UART events. + endmenu diff --git a/components/freemodbus/common/esp_modbus_master.c b/components/freemodbus/common/esp_modbus_master.c index badd0cabd70..fe899e50953 100644 --- a/components/freemodbus/common/esp_modbus_master.c +++ b/components/freemodbus/common/esp_modbus_master.c @@ -45,20 +45,20 @@ esp_err_t mbc_master_init(mb_port_type_t port_type, void** handler) MB_MASTER_CHECK((port_handler != NULL), ESP_ERR_INVALID_STATE, "Master interface initialization failure, error=(0x%x), port type=(0x%x).", - (uint16_t)error, (uint16_t)port_type); + error, (uint16_t)port_type); if ((port_handler != NULL) && (error == ESP_OK)) { master_interface_ptr = (mb_master_interface_t*) port_handler; *handler = port_handler; } - return error; + return error; } /** * Modbus controller destroy function */ -esp_err_t mbc_master_destroy() +esp_err_t mbc_master_destroy(void) { esp_err_t error = ESP_OK; MB_MASTER_CHECK((master_interface_ptr != NULL), @@ -69,9 +69,9 @@ esp_err_t mbc_master_destroy() "Master interface is not correctly initialized."); error = master_interface_ptr->destroy(); MB_MASTER_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, + error, "SERIAL master destroy failure error=(0x%x).", - (uint16_t)error); + error); return error; } @@ -86,9 +86,9 @@ esp_err_t mbc_master_get_cid_info(uint16_t cid, const mb_parameter_descriptor_t* "Master interface is not correctly initialized."); error = master_interface_ptr->get_cid_info(cid, param_info); MB_MASTER_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, + error, "SERIAL master get cid info failure error=(0x%x).", - (uint16_t)error); + error); return error; } @@ -106,9 +106,9 @@ esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uin "Master interface is not correctly initialized."); error = master_interface_ptr->get_parameter(cid, name, value, type); MB_MASTER_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL master get parameter failure error=(0x%x).", - (uint16_t)error); + error, + "SERIAL master get parameter failure error=(0x%x) (%s).", + error, esp_err_to_name(error)); return error; } @@ -126,9 +126,9 @@ esp_err_t mbc_master_send_request(mb_param_request_t* request, void* data_ptr) "Master interface is not correctly initialized."); error = master_interface_ptr->send_request(request, data_ptr); MB_MASTER_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL master get parameter failure error=(0x%x).", - (uint16_t)error); + error, + "Master send request failure error=(0x%x) (%s).", + error, esp_err_to_name(error)); return error; } @@ -146,9 +146,9 @@ esp_err_t mbc_master_set_descriptor(const mb_parameter_descriptor_t* descriptor, "Master interface is not correctly initialized."); error = master_interface_ptr->set_descriptor(descriptor, num_elements); MB_MASTER_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL master set descriptor failure error=(0x%x).", - (uint16_t)error); + error, + "Master set descriptor failure, error=(0x%x) (%s).", + error, esp_err_to_name(error)); return error; } @@ -166,9 +166,9 @@ esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uin "Master interface is not correctly initialized."); error = master_interface_ptr->set_parameter(cid, name, value, type); MB_MASTER_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL master set parameter failure error=(0x%x).", - (uint16_t)error); + error, + "Master set parameter failure, error=(0x%x) (%s).", + error, esp_err_to_name(error)); return error; } @@ -186,16 +186,16 @@ esp_err_t mbc_master_setup(void* comm_info) "Master interface is not correctly initialized."); error = master_interface_ptr->setup(comm_info); MB_MASTER_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL master setup failure error=(0x%x).", - (uint16_t)error); + error, + "Master setup failure, error=(0x%x) (%s).", + error, esp_err_to_name(error)); return error; } /** * Modbus controller stack start function */ -esp_err_t mbc_master_start() +esp_err_t mbc_master_start(void) { esp_err_t error = ESP_OK; MB_MASTER_CHECK((master_interface_ptr != NULL), @@ -206,9 +206,9 @@ esp_err_t mbc_master_start() "Master interface is not correctly initialized."); error = master_interface_ptr->start(); MB_MASTER_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL master start failure error=(0x%x).", - (uint16_t)error); + error, + "Master start failure, error=(0x%x) (%s).", + error, esp_err_to_name(error)); return error; } @@ -268,4 +268,4 @@ eMBErrorCode eMBMasterRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, "Master interface is not correctly initialized."); error = master_interface_ptr->master_reg_cb_input(pucRegBuffer, usAddress, usNRegs); return error; -} \ No newline at end of file +} diff --git a/components/freemodbus/common/esp_modbus_slave.c b/components/freemodbus/common/esp_modbus_slave.c index 8883a98f270..2d1dd6c0f64 100644 --- a/components/freemodbus/common/esp_modbus_slave.c +++ b/components/freemodbus/common/esp_modbus_slave.c @@ -13,8 +13,8 @@ * limitations under the License. */ -#include "esp_err.h" // for esp_err_t -#include "sdkconfig.h" // for KConfig defines +#include "esp_err.h" // for esp_err_t +#include "sdkconfig.h" // for KConfig defines #include "mbc_slave.h" // for slave private type definitions #include "esp_modbus_common.h" // for common defines #include "esp_modbus_slave.h" // for public slave defines @@ -77,7 +77,7 @@ esp_err_t mbc_slave_init(mb_port_type_t port_type, void** handler) /** * Modbus controller destroy function */ -esp_err_t mbc_slave_destroy() +esp_err_t mbc_slave_destroy(void) { esp_err_t error = ESP_OK; // Is initialization done? @@ -91,8 +91,7 @@ esp_err_t mbc_slave_destroy() // Call the slave port destroy function error = slave_interface_ptr->destroy(); MB_SLAVE_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL slave destroy failure error=(0x%x).", error); + error, "Slave destroy failure error=(0x%x).", error); return error; } @@ -110,15 +109,14 @@ esp_err_t mbc_slave_setup(void* comm_info) "Slave interface is not correctly initialized."); error = slave_interface_ptr->setup(comm_info); MB_SLAVE_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL slave setup failure error=(0x%x).", error); + error, "Slave setup failure error=(0x%x).", error); return error; } /** * Start Modbus controller start function */ -esp_err_t mbc_slave_start() +esp_err_t mbc_slave_start(void) { esp_err_t error = ESP_OK; MB_SLAVE_CHECK((slave_interface_ptr != NULL), @@ -134,8 +132,7 @@ esp_err_t mbc_slave_start() #endif error = slave_interface_ptr->start(); MB_SLAVE_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL slave start failure error=(0x%x).", error); + error, "Slave start failure error=(0x%x).", error); return error; } @@ -168,8 +165,7 @@ esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout) "Slave interface is not correctly initialized."); error = slave_interface_ptr->get_param_info(reg_info, timeout); MB_SLAVE_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL slave get parameter info failure error=(0x%x).", error); + error, "Slave get parameter info failure error=(0x%x).", error); return error; } @@ -187,8 +183,7 @@ esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data) "Slave interface is not correctly initialized."); error = slave_interface_ptr->set_descriptor(descr_data); MB_SLAVE_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, - "SERIAL slave set descriptor failure error=(0x%x).", error); + error, "Slave set descriptor failure error=(0x%x).", error); return error; } @@ -203,7 +198,7 @@ eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress, ESP_ERR_INVALID_STATE, "Slave interface is not correctly initialized."); MB_SLAVE_CHECK((slave_interface_ptr->slave_reg_cb_discrete != NULL), - ESP_ERR_INVALID_STATE, + error, "Slave interface is not correctly initialized."); error = slave_interface_ptr->slave_reg_cb_discrete(pucRegBuffer, usAddress, usNDiscrete); diff --git a/components/freemodbus/common/include/esp_modbus_common.h b/components/freemodbus/common/include/esp_modbus_common.h index cd171c7557e..4081a865c53 100644 --- a/components/freemodbus/common/include/esp_modbus_common.h +++ b/components/freemodbus/common/include/esp_modbus_common.h @@ -18,6 +18,10 @@ #include "driver/uart.h" // for UART types +#ifdef __cplusplus +extern "C" { +#endif + #define MB_CONTROLLER_STACK_SIZE (CONFIG_FMB_CONTROLLER_STACK_SIZE) // Stack size for Modbus controller #define MB_CONTROLLER_PRIORITY (CONFIG_FMB_SERIAL_TASK_PRIO - 1) // priority of MB controller task @@ -64,7 +68,8 @@ typedef enum MB_PORT_SERIAL_SLAVE, /*!< Modbus port type serial slave. */ MB_PORT_TCP_MASTER, /*!< Modbus port type TCP master. */ MB_PORT_TCP_SLAVE, /*!< Modbus port type TCP slave. */ - MB_PORT_COUNT /*!< Modbus port count. */ + MB_PORT_COUNT, /*!< Modbus port count. */ + MB_PORT_INACTIVE = 0xFF } mb_port_type_t; /** @@ -114,7 +119,7 @@ typedef union { uart_port_t port; /*!< Modbus communication port (UART) number */ uint32_t baudrate; /*!< Modbus baudrate */ uart_parity_t parity; /*!< Modbus UART parity settings */ - uint16_t dummy_port; + uint16_t dummy_port; /*!< Dummy field, unused */ }; // Tcp communication structure struct { @@ -135,4 +140,8 @@ typedef esp_err_t (*iface_destroy)(void); /*!< Interface method typedef esp_err_t (*iface_setup)(void*); /*!< Interface method setup */ typedef esp_err_t (*iface_start)(void); /*!< Interface method start */ +#ifdef __cplusplus +} +#endif + #endif // _MB_IFACE_COMMON_H diff --git a/components/freemodbus/common/include/esp_modbus_master.h b/components/freemodbus/common/include/esp_modbus_master.h index 6fae9540be2..7b87ffaa52b 100644 --- a/components/freemodbus/common/include/esp_modbus_master.h +++ b/components/freemodbus/common/include/esp_modbus_master.h @@ -21,6 +21,10 @@ #include "soc/soc.h" // for BITN definitions #include "esp_modbus_common.h" // for common types +#ifdef __cplusplus +extern "C" { +#endif + /*! * \brief Modbus descriptor table parameter type defines. */ @@ -238,4 +242,8 @@ esp_err_t mbc_master_get_parameter(uint16_t cid, char* name, uint8_t* value, uin */ esp_err_t mbc_master_set_parameter(uint16_t cid, char* name, uint8_t* value, uint8_t *type); +#ifdef __cplusplus +} +#endif + #endif // _ESP_MB_MASTER_INTERFACE_H diff --git a/components/freemodbus/common/include/esp_modbus_slave.h b/components/freemodbus/common/include/esp_modbus_slave.h index dcaf6d94e56..1f911450a2b 100644 --- a/components/freemodbus/common/include/esp_modbus_slave.h +++ b/components/freemodbus/common/include/esp_modbus_slave.h @@ -24,6 +24,10 @@ #include "freertos/event_groups.h" // for event groups #include "esp_modbus_common.h" // for common types +#ifdef __cplusplus +extern "C" { +#endif + /** * @brief Parameter access event information type */ @@ -120,4 +124,8 @@ esp_err_t mbc_slave_get_param_info(mb_param_info_t* reg_info, uint32_t timeout); */ esp_err_t mbc_slave_set_descriptor(mb_register_area_descriptor_t descr_data); +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/freemodbus/common/include/mbcontroller.h b/components/freemodbus/common/include/mbcontroller.h index 73a50ca600b..08b3c183c8f 100644 --- a/components/freemodbus/common/include/mbcontroller.h +++ b/components/freemodbus/common/include/mbcontroller.h @@ -13,7 +13,7 @@ * limitations under the License. */ // mbcontroller.h -// mbcontroller common header +// mbcontroller - common Modbus controller header file #ifndef _MODBUS_CONTROLLER_COMMON #define _MODBUS_CONTROLLER_COMMON @@ -28,5 +28,5 @@ #include "esp_modbus_master.h" #include "esp_modbus_slave.h" -#endif +#endif diff --git a/components/freemodbus/modbus/ascii/mbascii.c b/components/freemodbus/modbus/ascii/mbascii.c index 3f0be31dff8..6850c9071fc 100644 --- a/components/freemodbus/modbus/ascii/mbascii.c +++ b/components/freemodbus/modbus/ascii/mbascii.c @@ -44,16 +44,7 @@ #include "mbcrc.h" #include "mbport.h" -#if MB_SLAVE_ASCII_ENABLED > 0 - -/* ----------------------- Defines ------------------------------------------*/ -#define MB_ASCII_DEFAULT_CR '\r' /*!< Default CR character for Modbus ASCII. */ -#define MB_ASCII_DEFAULT_LF '\n' /*!< Default LF character for Modbus ASCII. */ -#define MB_SER_PDU_SIZE_MIN 3 /*!< Minimum size of a Modbus ASCII frame. */ -#define MB_SER_PDU_SIZE_MAX 256 /*!< Maximum size of a Modbus ASCII frame. */ -#define MB_SER_PDU_SIZE_LRC 1 /*!< Size of LRC field in PDU. */ -#define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */ -#define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */ +#if MB_SLAVE_ASCII_ENABLED /* ----------------------- Type definitions ---------------------------------*/ typedef enum @@ -78,6 +69,10 @@ typedef enum BYTE_LOW_NIBBLE /*!< Character for low nibble of byte. */ } eMBBytePos; +/* ----------------------- Shared variables ---------------------------------*/ +/* We reuse the Modbus RTU buffer because only one driver is active */ +extern volatile UCHAR ucMbSlaveBuf[]; + /* ----------------------- Static functions ---------------------------------*/ static UCHAR prvucMBCHAR2BIN( UCHAR ucCharacter ); @@ -89,10 +84,7 @@ static UCHAR prvucMBLRC( UCHAR * pucFrame, USHORT usLen ); static volatile eMBSndState eSndState; static volatile eMBRcvState eRcvState; -/* We reuse the Modbus RTU buffer because only one buffer is needed and the - * RTU buffer is bigger. */ -extern volatile UCHAR ucRTUBuf[]; -static volatile UCHAR *ucASCIIBuf = ucRTUBuf; +static volatile UCHAR *ucASCIIBuf = ucMbSlaveBuf; static volatile USHORT usRcvBufferPos; static volatile eMBBytePos eBytePos; @@ -113,11 +105,11 @@ eMBASCIIInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eP ENTER_CRITICAL_SECTION( ); ucMBLFCharacter = MB_ASCII_DEFAULT_LF; - if( xMBPortSerialInit( ucPort, ulBaudRate, 7, eParity ) != TRUE ) + if( xMBPortSerialInit( ucPort, ulBaudRate, MB_ASCII_BITS_PER_SYMB, eParity ) != TRUE ) { eStatus = MB_EPORTERR; } - else if( xMBPortTimersInit( MB_ASCII_TIMEOUT_SEC * 20000UL ) != TRUE ) + else if( xMBPortTimersInit( MB_ASCII_TIMEOUT_MS * 20UL ) != TRUE ) { eStatus = MB_EPORTERR; } @@ -157,7 +149,7 @@ eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); /* Length and CRC check */ - if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN ) + if( ( usRcvBufferPos >= MB_ASCII_SER_PDU_SIZE_MIN ) && ( prvucMBLRC( ( UCHAR * ) ucASCIIBuf, usRcvBufferPos ) == 0 ) ) { /* Save the address field. All frames are passed to the upper layed @@ -227,7 +219,7 @@ xMBASCIIReceiveFSM( void ) assert( eSndState == STATE_TX_IDLE ); - ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte ); + xNeedPoll = xMBPortSerialGetByte( ( CHAR * ) & ucByte ); switch ( eRcvState ) { /* A new character is received. If the character is a ':' the input @@ -292,7 +284,7 @@ xMBASCIIReceiveFSM( void ) /* Notify the caller of eMBASCIIReceive that a new frame * was received. */ - xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED ); + (void)xMBPortEventPost( EV_FRAME_RECEIVED ); } else if( ucByte == ':' ) { @@ -317,7 +309,7 @@ xMBASCIIReceiveFSM( void ) /* Enable timer for character timeout. */ vMBPortTimersEnable( ); /* Reset the input buffers to store the frame. */ - usRcvBufferPos = 0;; + usRcvBufferPos = 0; eBytePos = BYTE_HIGH_NIBBLE; eRcvState = STATE_RX_RCV; } @@ -330,7 +322,7 @@ xMBASCIIReceiveFSM( void ) BOOL xMBASCIITransmitFSM( void ) { - BOOL xNeedPoll = FALSE; + BOOL xNeedPoll = TRUE; UCHAR ucByte; assert( eRcvState == STATE_RX_IDLE ); @@ -388,26 +380,20 @@ xMBASCIITransmitFSM( void ) * been sent. */ case STATE_TX_NOTIFY: eSndState = STATE_TX_IDLE; - xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); - - /* Disable transmitter. This prevents another transmit buffer - * empty interrupt. */ - vMBPortSerialEnable( TRUE, FALSE ); - eSndState = STATE_TX_IDLE; + xMBPortEventPost( EV_FRAME_TRANSMIT ); + xNeedPoll = FALSE; break; /* We should not get a transmitter event if the transmitter is in * idle state. */ case STATE_TX_IDLE: - /* enable receiver/disable transmitter. */ - vMBPortSerialEnable( TRUE, FALSE ); break; } return xNeedPoll; } -BOOL +BOOL MB_PORT_ISR_ATTR xMBASCIITimerT1SExpired( void ) { switch ( eRcvState ) @@ -421,7 +407,8 @@ xMBASCIITimerT1SExpired( void ) break; default: - assert( ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_WAIT_EOF ) ); + assert( ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_WAIT_EOF ) + || (eRcvState == STATE_RX_IDLE )); break; } vMBPortTimersDisable( ); diff --git a/components/freemodbus/modbus/ascii/mbascii.h b/components/freemodbus/modbus/ascii/mbascii.h index 0be1b3ce6f8..535701b6dd5 100644 --- a/components/freemodbus/modbus/ascii/mbascii.h +++ b/components/freemodbus/modbus/ascii/mbascii.h @@ -34,6 +34,14 @@ #ifdef __cplusplus PR_BEGIN_EXTERN_C #endif + +/* ----------------------- Defines ------------------------------------------*/ +#define MB_ASCII_DEFAULT_CR '\r' /*!< Default CR character for Modbus ASCII. */ +#define MB_ASCII_DEFAULT_LF '\n' /*!< Default LF character for Modbus ASCII. */ +#define MB_ASCII_SER_PDU_SIZE_MIN 3 /*!< Minimum size of a Modbus ASCII frame. */ + +/* ----------------------- Function declaration -----------------------------*/ + #if MB_SLAVE_ASCII_ENABLED > 0 eMBErrorCode eMBASCIIInit( UCHAR slaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ); @@ -49,6 +57,21 @@ BOOL xMBASCIITransmitFSM( void ); BOOL xMBASCIITimerT1SExpired( void ); #endif +#if MB_MASTER_ASCII_ENABLED > 0 +eMBErrorCode eMBMasterASCIIInit( UCHAR ucPort, + ULONG ulBaudRate, eMBParity eParity ); +void eMBMasterASCIIStart( void ); +void eMBMasterASCIIStop( void ); + +eMBErrorCode eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, + USHORT * pusLength ); +eMBErrorCode eMBMasterASCIISend( UCHAR slaveAddress, const UCHAR * pucFrame, + USHORT usLength ); +BOOL xMBMasterASCIIReceiveFSM( void ); +BOOL xMBMasterASCIITransmitFSM( void ); +BOOL xMBMasterASCIITimerT1SExpired( void ); +#endif + #ifdef __cplusplus PR_END_EXTERN_C #endif diff --git a/components/freemodbus/modbus/ascii/mbascii_m.c b/components/freemodbus/modbus/ascii/mbascii_m.c new file mode 100644 index 00000000000..9b97191defe --- /dev/null +++ b/components/freemodbus/modbus/ascii/mbascii_m.c @@ -0,0 +1,574 @@ +/* + * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. + * Copyright (c) 2006 Christian Walter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * File: $Id: mbascii.c,v 1.17 2010/06/06 13:47:07 wolti Exp $ + */ + +/* ----------------------- System includes ----------------------------------*/ +#include "stdlib.h" +#include "string.h" + +/* ----------------------- Platform includes --------------------------------*/ +#include "port.h" + +/* ----------------------- Modbus includes ----------------------------------*/ +#include "mb_m.h" +#include "mbconfig.h" +#include "mbascii.h" +#include "mbframe.h" + +#include "mbcrc.h" +#include "mbport.h" + +#if MB_MASTER_ASCII_ENABLED > 0 + +/* ----------------------- Defines ------------------------------------------*/ +#define MB_TIMER_TICS_PER_MS 20UL + +/* ----------------------- Type definitions ---------------------------------*/ +typedef enum +{ + STATE_M_RX_INIT, /*!< Receiver is in initial state. */ + STATE_M_RX_IDLE, /*!< Receiver is in idle state. */ + STATE_M_RX_RCV, /*!< Frame is beeing received. */ + STATE_M_RX_WAIT_EOF, /*!< Wait for End of Frame. */ + STATE_M_RX_ERROR, /*!< If the frame is invalid. */ +} eMBMasterAsciiRcvState; + +typedef enum +{ + STATE_M_TX_IDLE, /*!< Transmitter is in idle state. */ + STATE_M_TX_START, /*!< Starting transmission (':' sent). */ + STATE_M_TX_DATA, /*!< Sending of data (Address, Data, LRC). */ + STATE_M_TX_END, /*!< End of transmission. */ + STATE_M_TX_NOTIFY, /*!< Notify sender that the frame has been sent. */ + STATE_M_TX_XFWR, /*!< Transmitter is in transfer finish and wait receive state. */ +} eMBMasterAsciiSndState; + +typedef enum +{ + BYTE_HIGH_NIBBLE, /*!< Character for high nibble of byte. */ + BYTE_LOW_NIBBLE /*!< Character for low nibble of byte. */ +} eMBBytePos; + +/* ----------------------- Shared values -----------------------------------*/ +/* These Modbus values are shared in ASCII mode*/ +extern volatile UCHAR ucMasterRcvBuf[]; +extern volatile UCHAR ucMasterSndBuf[]; +extern volatile eMBMasterTimerMode eMasterCurTimerMode; + +/* ----------------------- Static functions ---------------------------------*/ +static UCHAR prvucMBCHAR2BIN( UCHAR ucCharacter ); + +static UCHAR prvucMBBIN2CHAR( UCHAR ucByte ); + +static UCHAR prvucMBLRC( UCHAR * pucFrame, USHORT usLen ); + +/* ----------------------- Static variables ---------------------------------*/ +static volatile eMBMasterAsciiSndState eSndState; +static volatile eMBMasterAsciiRcvState eRcvState; + +static volatile UCHAR *ucMasterASCIIRcvBuf = ucMasterRcvBuf; +static volatile UCHAR *ucMasterASCIISndBuf = ucMasterSndBuf; + +static volatile USHORT usMasterRcvBufferPos; +static volatile eMBBytePos eBytePos; + +static volatile UCHAR *pucMasterSndBufferCur; +static volatile USHORT usMasterSndBufferCount; + +static volatile UCHAR ucLRC; +static volatile UCHAR ucMBLFCharacter; + +/* ----------------------- Start implementation -----------------------------*/ +eMBErrorCode +eMBMasterASCIIInit( UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) +{ + eMBErrorCode eStatus = MB_ENOERR; + + ENTER_CRITICAL_SECTION( ); + ucMBLFCharacter = MB_ASCII_DEFAULT_LF; + + if( xMBMasterPortSerialInit( ucPort, ulBaudRate, MB_ASCII_BITS_PER_SYMB, eParity ) != TRUE ) + { + eStatus = MB_EPORTERR; + } + else if( xMBMasterPortTimersInit( MB_ASCII_TIMEOUT_MS * MB_TIMER_TICS_PER_MS ) != TRUE ) + { + eStatus = MB_EPORTERR; + } + + EXIT_CRITICAL_SECTION( ); + + return eStatus; +} + +void +eMBMasterASCIIStart( void ) +{ + ENTER_CRITICAL_SECTION( ); + eRcvState = STATE_M_RX_IDLE; + vMBMasterPortSerialEnable( TRUE, FALSE ); + vMBMasterPortTimersT35Enable( ); + EXIT_CRITICAL_SECTION( ); +} + +void +eMBMasterASCIIStop( void ) +{ + ENTER_CRITICAL_SECTION( ); + vMBMasterPortSerialEnable( FALSE, FALSE ); + vMBMasterPortTimersDisable( ); + EXIT_CRITICAL_SECTION( ); +} + +eMBErrorCode +eMBMasterASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) +{ + eMBErrorCode eStatus = MB_ENOERR; + + ENTER_CRITICAL_SECTION( ); + assert( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX ); + + /* Length and CRC check */ + if( ( usMasterRcvBufferPos >= MB_ASCII_SER_PDU_SIZE_MIN ) + && ( prvucMBLRC( ( UCHAR * ) ucMasterASCIIRcvBuf, usMasterRcvBufferPos ) == 0 ) ) + { + /* Save the address field. All frames are passed to the upper layed + * and the decision if a frame is used is done there. + */ + *pucRcvAddress = ucMasterASCIIRcvBuf[MB_SER_PDU_ADDR_OFF]; + + /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus + * size of address field and CRC checksum. + */ + *pusLength = ( USHORT )( usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC ); + + /* Return the start of the Modbus PDU to the caller. */ + *pucFrame = ( UCHAR * ) & ucMasterASCIIRcvBuf[MB_SER_PDU_PDU_OFF]; + } + else + { + eStatus = MB_EIO; + } + EXIT_CRITICAL_SECTION( ); + return eStatus; +} + +eMBErrorCode +eMBMasterASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) +{ + eMBErrorCode eStatus = MB_ENOERR; + UCHAR usLRC; + + if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL; + + ENTER_CRITICAL_SECTION( ); + /* Check if the receiver is still in idle state. If not we where too + * slow with processing the received frame and the master sent another + * frame on the network. We have to abort sending the frame. + */ + if(eRcvState == STATE_M_RX_IDLE) + { + /* First byte before the Modbus-PDU is the slave address. */ + pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1; + usMasterSndBufferCount = 1; + + /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */ + pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress; + usMasterSndBufferCount += usLength; + + /* Calculate LRC checksum for Modbus-Serial-Line-PDU. */ + usLRC = prvucMBLRC( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount ); + ucMasterASCIISndBuf[usMasterSndBufferCount++] = usLRC; + + /* Activate the transmitter. */ + eSndState = STATE_M_TX_START; + vMBMasterPortSerialEnable( FALSE, TRUE ); + } + else + { + eStatus = MB_EIO; + } + EXIT_CRITICAL_SECTION( ); + return eStatus; +} + +BOOL +xMBMasterASCIIReceiveFSM( void ) +{ + BOOL xNeedPoll = FALSE; + UCHAR ucByte; + UCHAR ucResult; + + assert(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR )); + + /* Always read the character. */ + xNeedPoll = xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte ); + + switch ( eRcvState ) + { + /* If we have received a character in the init state we have to + * wait until the frame is finished. + */ + case STATE_M_RX_INIT: + vMBMasterPortTimersT35Enable( ); + break; + + /* In the error state we wait until all characters in the + * damaged frame are transmitted. + */ + case STATE_M_RX_ERROR: + vMBMasterPortTimersRespondTimeoutEnable( ); + break; + + /* In the idle state we wait for a new character. If a character + * is received the t1.5 and t3.5 timers are started and the + * receiver is in the state STATE_RX_RECEIVE and disable early + * the timer of respond timeout . + */ + case STATE_M_RX_IDLE: + /* Waiting for the start of frame character during respond timeout */ + vMBMasterPortTimersRespondTimeoutEnable( ); + if( ucByte == ':' ) + { + /* Reset the input buffers to store the frame in receive state. */ + usMasterRcvBufferPos = 0; + eBytePos = BYTE_HIGH_NIBBLE; + eRcvState = STATE_M_RX_RCV; + } + eSndState = STATE_M_TX_IDLE; + break; + + /* A new character is received. If the character is a ':' the input + * buffer is cleared. A CR-character signals the end of the data + * block. Other characters are part of the data block and their + * ASCII value is converted back to a binary representation. + */ + case STATE_M_RX_RCV: + /* Enable timer timeout. */ + vMBMasterPortTimersT35Enable( ); + if( ucByte == ':' ) + { + /* Empty receive buffer. */ + eBytePos = BYTE_HIGH_NIBBLE; + usMasterRcvBufferPos = 0; + } + else if( ucByte == MB_ASCII_DEFAULT_CR ) + { + eRcvState = STATE_M_RX_WAIT_EOF; + } + else + { + ucResult = prvucMBCHAR2BIN( ucByte ); + switch ( eBytePos ) + { + /* High nibble of the byte comes first. We check for + * a buffer overflow here. */ + case BYTE_HIGH_NIBBLE: + if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX ) + { + ucMasterASCIIRcvBuf[usMasterRcvBufferPos] = ( UCHAR )( ucResult << 4 ); + eBytePos = BYTE_LOW_NIBBLE; + break; + } + else + { + /* not handled in Modbus specification but seems + * a resonable implementation. */ + eRcvState = STATE_M_RX_ERROR; + /* Disable previously activated timer because of error state. */ + vMBPortTimersDisable( ); + } + break; + + case BYTE_LOW_NIBBLE: + ucMasterASCIIRcvBuf[usMasterRcvBufferPos] |= ucResult; + usMasterRcvBufferPos++; + eBytePos = BYTE_HIGH_NIBBLE; + break; + } + } + break; + + case STATE_M_RX_WAIT_EOF: + if( ucByte == ucMBLFCharacter ) + { + /* Disable character timeout timer because all characters are + * received. */ + vMBMasterPortTimersDisable( ); + /* Receiver is again in idle state. */ + eRcvState = STATE_M_RX_IDLE; + + /* Notify the caller of eMBMasterASCIIReceive that a new frame + * was received. */ + (void)xMBMasterPortEventPost( EV_MASTER_FRAME_RECEIVED ); + xNeedPoll = FALSE; + } + else if( ucByte == ':' ) + { + /* Start of frame character received but last message is not completed. + * Empty receive buffer and back to receive state. */ + eBytePos = BYTE_HIGH_NIBBLE; + usMasterRcvBufferPos = 0; + eRcvState = STATE_M_RX_IDLE; + + /* Enable timer for respond timeout and wait for next frame. */ + vMBMasterPortTimersRespondTimeoutEnable( ); + } + else + { + /* Frame is not okay. Delete entire frame. */ + eRcvState = STATE_M_RX_IDLE; + } + break; + } + + return xNeedPoll; +} + +BOOL +xMBMasterASCIITransmitFSM( void ) +{ + BOOL xNeedPoll = TRUE; + UCHAR ucByte; + BOOL xFrameIsBroadcast = FALSE; + + assert( eRcvState == STATE_M_RX_IDLE ); + + switch ( eSndState ) + { + /* We should not get a transmitter event if the transmitter is in + * idle state. */ + case STATE_M_TX_XFWR: + break; + + /* We should not get a transmitter event if the transmitter is in + * idle state. */ + case STATE_M_TX_IDLE: + break; + + /* Start of transmission. The start of a frame is defined by sending + * the character ':'. */ + case STATE_M_TX_START: + ucByte = ':'; + xMBMasterPortSerialPutByte( ( CHAR )ucByte ); + eSndState = STATE_M_TX_DATA; + eBytePos = BYTE_HIGH_NIBBLE; + break; + + /* Send the data block. Each data byte is encoded as a character hex + * stream with the high nibble sent first and the low nibble sent + * last. If all data bytes are exhausted we send a '\r' character + * to end the transmission. */ + case STATE_M_TX_DATA: + if( usMasterSndBufferCount > 0 ) + { + switch ( eBytePos ) + { + case BYTE_HIGH_NIBBLE: + ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucMasterSndBufferCur >> 4 ) ); + xMBMasterPortSerialPutByte( ( CHAR ) ucByte ); + eBytePos = BYTE_LOW_NIBBLE; + break; + + case BYTE_LOW_NIBBLE: + ucByte = prvucMBBIN2CHAR( ( UCHAR )( *pucMasterSndBufferCur & 0x0F ) ); + xMBMasterPortSerialPutByte( ( CHAR )ucByte ); + pucMasterSndBufferCur++; + eBytePos = BYTE_HIGH_NIBBLE; + usMasterSndBufferCount--; + break; + } + } + else + { + xMBMasterPortSerialPutByte( MB_ASCII_DEFAULT_CR ); + eSndState = STATE_M_TX_END; + } + break; + + /* Finish the frame by sending a LF character. */ + case STATE_M_TX_END: + xMBMasterPortSerialPutByte( ( CHAR )ucMBLFCharacter ); + /* We need another state to make sure that the CR character has + * been sent. */ + eSndState = STATE_M_TX_NOTIFY; + break; + + /* Notify the task which called eMBMasterASCIISend that the frame has + * been sent. */ + case STATE_M_TX_NOTIFY: + xFrameIsBroadcast = ( ucMasterASCIISndBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE; + vMBMasterRequestSetType( xFrameIsBroadcast ); + eSndState = STATE_M_TX_XFWR; + /* If the frame is broadcast ,master will enable timer of convert delay, + * else master will enable timer of respond timeout. */ + if ( xFrameIsBroadcast == TRUE ) + { + vMBMasterPortTimersConvertDelayEnable( ); + } + else + { + vMBMasterPortTimersRespondTimeoutEnable( ); + } + xNeedPoll = FALSE; + break; + } + + return xNeedPoll; +} + +BOOL +xMBMasterASCIITimerT1SExpired( void ) +{ + BOOL xNeedPoll = FALSE; + + switch ( eRcvState ) + { + /* Timer t35 expired. Startup phase is finished. */ + case STATE_M_RX_INIT: + xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY); + ESP_EARLY_LOGI("xMBMasterASCIITimerT1SExpired", "RX_INIT_EXPIRED"); + break; + + /* Start of message is not received during respond timeout. + * Process error. */ + case STATE_M_RX_IDLE: + eRcvState = STATE_M_RX_ERROR; + break; + + /* A recieve timeout expired and no any new character received. + * Wait for respond time and go to error state to inform listener about error */ + case STATE_M_RX_RCV: + eRcvState = STATE_M_RX_ERROR; + break; + + /* An error occured while receiving the frame. */ + case STATE_M_RX_ERROR: + vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA); + xNeedPoll = xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS ); + break; + + /* If we have a timeout we go back to the idle state and wait for + * the next frame. + */ + case STATE_M_RX_WAIT_EOF: + eRcvState = STATE_M_RX_IDLE; + break; + + default: + assert( 0 ); + break; + } + eRcvState = STATE_M_RX_IDLE; + + switch (eSndState) + { + /* A frame was send finish and convert delay or respond timeout expired. + * If the frame is broadcast,The master will idle,and if the frame is not + * broadcast.*/ + case STATE_M_TX_XFWR: + if ( xMBMasterRequestIsBroadcast( ) == FALSE ) { + vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT); + xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS); + } + break; + + /* Function called in an illegal state. */ + default: + assert( ( eSndState == STATE_M_TX_START ) || ( eSndState == STATE_M_TX_IDLE ) + || ( eSndState == STATE_M_TX_DATA ) || ( eSndState == STATE_M_TX_END ) + || ( eSndState == STATE_M_TX_NOTIFY ) ); + break; + } + eSndState = STATE_M_TX_IDLE; + + vMBMasterPortTimersDisable( ); + /* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */ + if (xMBMasterGetCurTimerMode() == MB_TMODE_CONVERT_DELAY) { + xNeedPoll = xMBMasterPortEventPost( EV_MASTER_EXECUTE ); + } + + vMBMasterPortTimersDisable( ); + + /* no context switch required. */ + return xNeedPoll; +} + +static UCHAR +prvucMBCHAR2BIN( UCHAR ucCharacter ) +{ + if( ( ucCharacter >= '0' ) && ( ucCharacter <= '9' ) ) + { + return ( UCHAR )( ucCharacter - '0' ); + } + else if( ( ucCharacter >= 'A' ) && ( ucCharacter <= 'F' ) ) + { + return ( UCHAR )( ucCharacter - 'A' + 0x0A ); + } + else + { + return 0xFF; + } +} + +static UCHAR +prvucMBBIN2CHAR( UCHAR ucByte ) +{ + if( ucByte <= 0x09 ) + { + return ( UCHAR )( '0' + ucByte ); + } + else if( ( ucByte >= 0x0A ) && ( ucByte <= 0x0F ) ) + { + return ( UCHAR )( ucByte - 0x0A + 'A' ); + } + else + { + /* Programming error. */ + assert( 0 ); + } + return '0'; +} + +static UCHAR +prvucMBLRC( UCHAR * pucFrame, USHORT usLen ) +{ + UCHAR ucLRC = 0; /* LRC char initialized */ + + while( usLen-- ) + { + ucLRC += *pucFrame++; /* Add buffer byte without carry */ + } + + /* Return twos complement */ + ucLRC = ( UCHAR ) ( -( ( CHAR ) ucLRC ) ); + return ucLRC; +} + +#endif diff --git a/components/freemodbus/modbus/functions/mbfunccoils.c b/components/freemodbus/modbus/functions/mbfunccoils.c index 54227b335b8..eac8ef8871f 100644 --- a/components/freemodbus/modbus/functions/mbfunccoils.c +++ b/components/freemodbus/modbus/functions/mbfunccoils.c @@ -62,8 +62,9 @@ eMBException prveMBError2Exception( eMBErrorCode eErrorCode ); /* ----------------------- Start implementation -----------------------------*/ +#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED -#if MB_FUNC_READ_COILS_ENABLED > 0 +#if MB_FUNC_READ_COILS_ENABLED eMBException eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen ) @@ -268,3 +269,5 @@ eMBFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen ) #endif #endif + +#endif diff --git a/components/freemodbus/modbus/functions/mbfunccoils_m.c b/components/freemodbus/modbus/functions/mbfunccoils_m.c index 6749a6e67b3..ba3e28d7371 100644 --- a/components/freemodbus/modbus/functions/mbfunccoils_m.c +++ b/components/freemodbus/modbus/functions/mbfunccoils_m.c @@ -36,7 +36,6 @@ #include "port.h" /* ----------------------- Modbus includes ----------------------------------*/ -//#include "mb.h" #include "mb_m.h" #include "mbframe.h" #include "mbproto.h" @@ -72,8 +71,9 @@ eMBException prveMBError2Exception( eMBErrorCode eErrorCode ); /* ----------------------- Start implementation -----------------------------*/ -#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0 -#if MB_FUNC_READ_COILS_ENABLED > 0 +#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED + +#if MB_FUNC_READ_COILS_ENABLED /** * This function will request read coil. diff --git a/components/freemodbus/modbus/functions/mbfuncdisc.c b/components/freemodbus/modbus/functions/mbfuncdisc.c index fb378441b91..ef8b9971a4c 100644 --- a/components/freemodbus/modbus/functions/mbfuncdisc.c +++ b/components/freemodbus/modbus/functions/mbfuncdisc.c @@ -17,8 +17,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - - /* ----------------------- System includes ----------------------------------*/ #include "stdlib.h" #include "string.h" @@ -42,8 +40,9 @@ eMBException prveMBError2Exception( eMBErrorCode eErrorCode ); /* ----------------------- Start implementation -----------------------------*/ +#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED -#if MB_FUNC_READ_COILS_ENABLED > 0 +#if MB_FUNC_READ_COILS_ENABLED eMBException eMBFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen ) @@ -123,3 +122,6 @@ eMBFuncReadDiscreteInputs( UCHAR * pucFrame, USHORT * usLen ) } #endif + +#endif + diff --git a/components/freemodbus/modbus/functions/mbfuncdisc_m.c b/components/freemodbus/modbus/functions/mbfuncdisc_m.c index 6e9a8666b0d..bc6a39b7ea4 100644 --- a/components/freemodbus/modbus/functions/mbfuncdisc_m.c +++ b/components/freemodbus/modbus/functions/mbfuncdisc_m.c @@ -55,8 +55,9 @@ eMBException prveMBError2Exception( eMBErrorCode eErrorCode ); /* ----------------------- Start implementation -----------------------------*/ -#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0 -#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0 +#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED + +#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED /** * This function will request read discrete inputs. diff --git a/components/freemodbus/modbus/functions/mbfuncholding.c b/components/freemodbus/modbus/functions/mbfuncholding.c index e74a0624dc6..534c7b153a9 100644 --- a/components/freemodbus/modbus/functions/mbfuncholding.c +++ b/components/freemodbus/modbus/functions/mbfuncholding.c @@ -70,8 +70,9 @@ eMBException prveMBError2Exception( eMBErrorCode eErrorCode ); /* ----------------------- Start implementation -----------------------------*/ +#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED -#if MB_FUNC_WRITE_HOLDING_ENABLED > 0 +#if MB_FUNC_WRITE_HOLDING_ENABLED eMBException eMBFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) @@ -306,3 +307,5 @@ eMBFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) } #endif + +#endif diff --git a/components/freemodbus/modbus/functions/mbfuncholding_m.c b/components/freemodbus/modbus/functions/mbfuncholding_m.c index c2d7b94e624..5ab07341e1d 100644 --- a/components/freemodbus/modbus/functions/mbfuncholding_m.c +++ b/components/freemodbus/modbus/functions/mbfuncholding_m.c @@ -83,8 +83,9 @@ eMBException prveMBError2Exception( eMBErrorCode eErrorCode ); /* ----------------------- Start implementation -----------------------------*/ -#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0 -#if MB_FUNC_WRITE_HOLDING_ENABLED > 0 +#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED + +#if MB_FUNC_WRITE_HOLDING_ENABLED /** * This function will request write holding register. @@ -112,9 +113,9 @@ eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRe ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF] = usRegAddr >> 8; ucMBFrame[MB_PDU_REQ_WRITE_ADDR_OFF + 1] = usRegAddr; ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF] = usRegData >> 8; - ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usRegData ; + ucMBFrame[MB_PDU_REQ_WRITE_VALUE_OFF + 1] = usRegData; vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_SIZE ); - ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT ); + ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT ); eErrStatus = eMBMasterWaitRequestFinish( ); } return eErrStatus; @@ -183,16 +184,16 @@ eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr, ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF] = usRegAddr >> 8; ucMBFrame[MB_PDU_REQ_WRITE_MUL_ADDR_OFF + 1] = usRegAddr; ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF] = usNRegs >> 8; - ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF + 1] = usNRegs ; + ucMBFrame[MB_PDU_REQ_WRITE_MUL_REGCNT_OFF + 1] = usNRegs; ucMBFrame[MB_PDU_REQ_WRITE_MUL_BYTECNT_OFF] = usNRegs * 2; ucMBFrame += MB_PDU_REQ_WRITE_MUL_VALUES_OFF; while( usNRegs > usRegIndex) { *ucMBFrame++ = pusDataBuffer[usRegIndex] >> 8; - *ucMBFrame++ = pusDataBuffer[usRegIndex++] ; + *ucMBFrame++ = pusDataBuffer[usRegIndex++]; } vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_WRITE_MUL_SIZE_MIN + 2*usNRegs ); - ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT ); + ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT ); eErrStatus = eMBMasterWaitRequestFinish( ); } return eErrStatus; @@ -278,7 +279,7 @@ eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRe ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF] = usNRegs >> 8; ucMBFrame[MB_PDU_REQ_READ_REGCNT_OFF + 1] = usNRegs; vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READ_SIZE ); - ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT ); + ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT ); eErrStatus = eMBMasterWaitRequestFinish( ); } return eErrStatus; @@ -371,20 +372,20 @@ eMBMasterReqReadWriteMultipleHoldingRegister( UCHAR ucSndAddr, ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF] = usReadRegAddr >> 8; ucMBFrame[MB_PDU_REQ_READWRITE_READ_ADDR_OFF + 1] = usReadRegAddr; ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF] = usNReadRegs >> 8; - ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF + 1] = usNReadRegs ; + ucMBFrame[MB_PDU_REQ_READWRITE_READ_REGCNT_OFF + 1] = usNReadRegs; ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF] = usWriteRegAddr >> 8; ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_ADDR_OFF + 1] = usWriteRegAddr; ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF] = usNWriteRegs >> 8; - ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF + 1] = usNWriteRegs ; + ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_REGCNT_OFF + 1] = usNWriteRegs; ucMBFrame[MB_PDU_REQ_READWRITE_WRITE_BYTECNT_OFF] = usNWriteRegs * 2; ucMBFrame += MB_PDU_REQ_READWRITE_WRITE_VALUES_OFF; while( usNWriteRegs > usRegIndex) { *ucMBFrame++ = pusDataBuffer[usRegIndex] >> 8; - *ucMBFrame++ = pusDataBuffer[usRegIndex++] ; + *ucMBFrame++ = pusDataBuffer[usRegIndex++]; } vMBMasterSetPDUSndLength( MB_PDU_SIZE_MIN + MB_PDU_REQ_READWRITE_SIZE_MIN + 2*usNWriteRegs ); - ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT ); + ( void ) xMBMasterPortEventPost( EV_MASTER_FRAME_TRANSMIT ); eErrStatus = eMBMasterWaitRequestFinish( ); } return eErrStatus; diff --git a/components/freemodbus/modbus/functions/mbfuncinput.c b/components/freemodbus/modbus/functions/mbfuncinput.c index cceedbafea6..a54deaf4f5d 100644 --- a/components/freemodbus/modbus/functions/mbfuncinput.c +++ b/components/freemodbus/modbus/functions/mbfuncinput.c @@ -53,7 +53,9 @@ eMBException prveMBError2Exception( eMBErrorCode eErrorCode ); /* ----------------------- Start implementation -----------------------------*/ -#if MB_FUNC_READ_INPUT_ENABLED > 0 +#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED + +#if MB_FUNC_READ_INPUT_ENABLED eMBException eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen ) @@ -120,3 +122,5 @@ eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen ) } #endif + +#endif diff --git a/components/freemodbus/modbus/functions/mbfuncother.c b/components/freemodbus/modbus/functions/mbfuncother.c index fcee972bf8f..2efec46dd1a 100644 --- a/components/freemodbus/modbus/functions/mbfuncother.c +++ b/components/freemodbus/modbus/functions/mbfuncother.c @@ -41,7 +41,9 @@ #include "mbproto.h" #include "mbconfig.h" -#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0 +#if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED + +#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED /* ----------------------- Static variables ---------------------------------*/ static UCHAR ucMBSlaveID[MB_FUNC_OTHER_REP_SLAVEID_BUF]; @@ -86,3 +88,5 @@ eMBFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen ) } #endif + +#endif diff --git a/components/freemodbus/modbus/include/mb.h b/components/freemodbus/modbus/include/mb.h index 594dcbf7da7..8486bf1aa71 100644 --- a/components/freemodbus/modbus/include/mb.h +++ b/components/freemodbus/modbus/include/mb.h @@ -73,9 +73,8 @@ PR_BEGIN_EXTERN_C */ #define MB_TCP_PORT_USE_DEFAULT 0 +#define MB_FUNC_CODE_MAX 127 /* ----------------------- Type definitions ---------------------------------*/ -#ifndef _MB_M_H - /*! \ingroup modbus * \brief Modbus serial transmission modes (RTU/ASCII). * @@ -122,8 +121,6 @@ typedef enum MB_ETIMEDOUT /*!< timeout error occurred. */ } eMBErrorCode; -#endif - /* ----------------------- Function prototypes ------------------------------*/ /*! \ingroup modbus * \brief Initialize the Modbus protocol stack. diff --git a/components/freemodbus/modbus/include/mb_m.h b/components/freemodbus/modbus/include/mb_m.h index f25053282db..84be552eaef 100644 --- a/components/freemodbus/modbus/include/mb_m.h +++ b/components/freemodbus/modbus/include/mb_m.h @@ -33,6 +33,7 @@ #include "mbconfig.h" #include "port.h" +#include "mb.h" #ifdef __cplusplus PR_BEGIN_EXTERN_C @@ -54,7 +55,7 @@ PR_BEGIN_EXTERN_C * * \code * // Initialize protocol stack in RTU mode for a Master - * eMBMasterInit( MB_RTU, 38400, MB_PAR_EVEN ); + * eMBMasterSerialInit( MB_RTU, 38400, MB_PAR_EVEN ); * // Enable the Modbus Protocol Stack. * eMBMasterEnable( ); * for( ;; ) @@ -73,54 +74,6 @@ PR_BEGIN_EXTERN_C */ #define MB_MASTER_TCP_PORT_USE_DEFAULT 0 -#ifndef _MB_H - -/* ----------------------- Type definitions ---------------------------------*/ -/*! \ingroup modbus - * \brief Modbus serial transmission modes (RTU/ASCII). - * - * Modbus serial supports two transmission modes. Either ASCII or RTU. RTU - * is faster but has more hardware requirements and requires a network with - * a low jitter. ASCII is slower and more reliable on slower links (E.g. modems) - */ -typedef enum { - MB_RTU, /*!< RTU transmission mode. */ - MB_ASCII, /*!< ASCII transmission mode. */ - MB_TCP /*!< TCP mode. */ -} eMBMode; - -/*! \ingroup modbus - * \brief If register should be written or read. - * - * This value is passed to the callback functions which support either - * reading or writing register values. Writing means that the application - * registers should be updated and reading means that the modbus protocol - * stack needs to know the current register values. - * - * \see eMBRegHoldingCB( ), eMBRegCoilsCB( ), eMBRegDiscreteCB( ) and - * eMBRegInputCB( ). - */ -typedef enum { - MB_REG_READ, /*!< Read register values and pass to protocol stack. */ - MB_REG_WRITE /*!< Update register values. */ -} eMBRegisterMode; - -/*! \ingroup modbus - * \brief Errorcodes used by all function in the protocol stack. - */ -typedef enum { - MB_ENOERR, /*!< no error. */ - MB_ENOREG, /*!< illegal register address. */ - MB_EINVAL, /*!< illegal argument. */ - MB_EPORTERR, /*!< porting layer error. */ - MB_ENORES, /*!< insufficient resources. */ - MB_EIO, /*!< I/O error. */ - MB_EILLSTATE, /*!< protocol stack in illegal state. */ - MB_ETIMEDOUT /*!< timeout error occurred. */ -} eMBErrorCode; - -#endif - /*! \ingroup modbus * \brief Errorcodes used by all function in the Master request. */ @@ -140,9 +93,9 @@ typedef enum */ typedef enum { - MB_TMODE_T35, /*!< Master receive frame T3.5 timeout. */ - MB_TMODE_RESPOND_TIMEOUT, /*!< Master wait respond for slave. */ - MB_TMODE_CONVERT_DELAY /*!< Master sent broadcast ,then delay sometime.*/ + MB_TMODE_T35, /*!< Master receive frame T3.5 timeout. */ + MB_TMODE_RESPOND_TIMEOUT, /*!< Master wait respond for slave. */ + MB_TMODE_CONVERT_DELAY /*!< Master sent broadcast ,then delay sometime.*/ }eMBMasterTimerMode; /* ----------------------- Function prototypes ------------------------------*/ @@ -167,8 +120,8 @@ typedef enum * is returned: * - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error. */ -eMBErrorCode eMBMasterInit( eMBMode eMode, UCHAR ucPort, - ULONG ulBaudRate, eMBParity eParity ); +eMBErrorCode eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort, + ULONG ulBaudRate, eMBParity eParity ); /*! \ingroup modbus * \brief Initialize the Modbus Master protocol stack for Modbus TCP. @@ -301,7 +254,7 @@ eMBErrorCode eMBMasterRegisterCB( UCHAR ucFunctionCode, * ILLEGAL DATA ADDRESS is sent as a response. */ eMBErrorCode eMBMasterRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, - USHORT usNRegs ); + USHORT usNRegs ); /*! \ingroup modbus_registers * \brief Callback function used if a Holding Register value is @@ -330,7 +283,7 @@ eMBErrorCode eMBMasterRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, * ILLEGAL DATA ADDRESS is sent as a response. */ eMBErrorCode eMBMasterRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, - USHORT usNRegs, eMBRegisterMode eMode ); + USHORT usNRegs, eMBRegisterMode eMode ); /*! \ingroup modbus_registers * \brief Callback function used if a Coil Register value is @@ -359,7 +312,7 @@ eMBErrorCode eMBMasterRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, * ILLEGAL DATA ADDRESS is sent as a response. */ eMBErrorCode eMBMasterRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, - USHORT usNCoils, eMBRegisterMode eMode ); + USHORT usNCoils, eMBRegisterMode eMode ); /*! \ingroup modbus_registers * \brief Callback function used if a Input Discrete Register value is @@ -382,7 +335,7 @@ eMBErrorCode eMBMasterRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, * ILLEGAL DATA ADDRESS is sent as a response. */ eMBErrorCode eMBMasterRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, - USHORT usNDiscrete ); + USHORT usNDiscrete ); /*! \ingroup modbus *\brief These Modbus functions are called for user when Modbus run in Master Mode. @@ -393,20 +346,20 @@ eMBMasterReqErrCode eMBMasterReqWriteHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usRegData, LONG lTimeOut ); eMBMasterReqErrCode eMBMasterReqWriteMultipleHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, - USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut ); + USHORT usNRegs, USHORT * pusDataBuffer, LONG lTimeOut ); eMBMasterReqErrCode eMBMasterReqReadHoldingRegister( UCHAR ucSndAddr, USHORT usRegAddr, USHORT usNRegs, LONG lTimeOut ); eMBMasterReqErrCode eMBMasterReqReadWriteMultipleHoldingRegister( UCHAR ucSndAddr, - USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer, - USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut ); + USHORT usReadRegAddr, USHORT usNReadRegs, USHORT * pusDataBuffer, + USHORT usWriteRegAddr, USHORT usNWriteRegs, LONG lTimeOut ); eMBMasterReqErrCode eMBMasterReqReadCoils( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usNCoils, LONG lTimeOut ); eMBMasterReqErrCode eMBMasterReqWriteCoil( UCHAR ucSndAddr, USHORT usCoilAddr, USHORT usCoilData, LONG lTimeOut ); eMBMasterReqErrCode eMBMasterReqWriteMultipleCoils( UCHAR ucSndAddr, - USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut ); + USHORT usCoilAddr, USHORT usNCoils, UCHAR * pucDataBuffer, LONG lTimeOut ); eMBMasterReqErrCode eMBMasterReqReadDiscreteInputs( UCHAR ucSndAddr, USHORT usDiscreteAddr, USHORT usNDiscreteIn, LONG lTimeOut ); @@ -442,6 +395,8 @@ void vMBMasterSetCBRunInMasterMode( BOOL IsMasterMode ); USHORT usMBMasterGetPDUSndLength( void ); void vMBMasterSetPDUSndLength( USHORT SendPDULength ); void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode ); +void vMBMasterRequestSetType( BOOL xIsBroadcast ); +eMBMasterTimerMode xMBMasterGetCurTimerMode( void ); BOOL xMBMasterRequestIsBroadcast( void ); eMBMasterErrorEventType eMBMasterGetErrorType( void ); void vMBMasterSetErrorType( eMBMasterErrorEventType errorType ); diff --git a/components/freemodbus/modbus/include/mbconfig.h b/components/freemodbus/modbus/include/mbconfig.h index ce160d82710..be97b5268c8 100644 --- a/components/freemodbus/modbus/include/mbconfig.h +++ b/components/freemodbus/modbus/include/mbconfig.h @@ -50,24 +50,39 @@ PR_BEGIN_EXTERN_C * @{ */ /*! \brief If Modbus Master ASCII support is enabled. */ -#define MB_MASTER_ASCII_ENABLED ( 0 ) +#define MB_MASTER_ASCII_ENABLED ( CONFIG_FMB_COMM_MODE_ASCII_EN ) /*! \brief If Modbus Master RTU support is enabled. */ -#define MB_MASTER_RTU_ENABLED ( 1 ) +#define MB_MASTER_RTU_ENABLED ( CONFIG_FMB_COMM_MODE_RTU_EN ) /*! \brief If Modbus Master TCP support is enabled. */ #define MB_MASTER_TCP_ENABLED ( 0 ) /*! \brief If Modbus Slave ASCII support is enabled. */ -#define MB_SLAVE_ASCII_ENABLED ( 1 ) +#define MB_SLAVE_ASCII_ENABLED ( CONFIG_FMB_COMM_MODE_ASCII_EN ) /*! \brief If Modbus Slave RTU support is enabled. */ -#define MB_SLAVE_RTU_ENABLED ( 1 ) +#define MB_SLAVE_RTU_ENABLED ( CONFIG_FMB_COMM_MODE_RTU_EN ) /*! \brief If Modbus Slave TCP support is enabled. */ #define MB_TCP_ENABLED ( 1 ) +#if !CONFIG_FMB_COMM_MODE_ASCII_EN && !CONFIG_FMB_COMM_MODE_RTU_EN +#error "None of Modbus communication mode is enabled. Please enable one of ASCII or RTU mode in Kconfig." +#endif + +/*! \brief This option defines the number of data bits per ASCII character. + * + * A parity bit is added before the stop bit which keeps the actual byte size at 10 bits. + */ +#ifdef CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB +#define MB_ASCII_BITS_PER_SYMB ( CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB ) +#endif + /*! \brief The character timeout value for Modbus ASCII. * * The character timeout value is not fixed for Modbus ASCII and is therefore * a configuration option. It should be set to the maximum expected delay * time of the network. */ -#define MB_ASCII_TIMEOUT_SEC ( 1 ) +#ifdef CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS +#define MB_ASCII_TIMEOUT_MS ( CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS ) +#endif + /*! \brief Timeout to wait in ASCII prior to enabling transmitter. * * If defined the function calls vMBPortSerialDelay with the argument @@ -130,6 +145,9 @@ PR_BEGIN_EXTERN_C /*! \brief If the Read/Write Multiple Registers function should be enabled. */ #define MB_FUNC_READWRITE_HOLDING_ENABLED ( 1 ) +/*! \brief Check the option to place timer handler into IRAM */ +#define MB_PORT_TIMER_ISR_IN_IRAM ( CONFIG_FMB_TIMER_ISR_IN_IRAM ) + /*! @} */ #ifdef __cplusplus PR_END_EXTERN_C diff --git a/components/freemodbus/modbus/include/mbframe.h b/components/freemodbus/modbus/include/mbframe.h index 99d59c613e3..d6bb2ccb431 100644 --- a/components/freemodbus/modbus/include/mbframe.h +++ b/components/freemodbus/modbus/include/mbframe.h @@ -31,6 +31,8 @@ #ifndef _MB_FRAME_H #define _MB_FRAME_H +#include "port.h" + #ifdef __cplusplus PR_BEGIN_EXTERN_C #endif @@ -66,6 +68,12 @@ PR_BEGIN_EXTERN_C #define MB_PDU_FUNC_OFF 0 /*!< Offset of function code in PDU. */ #define MB_PDU_DATA_OFF 1 /*!< Offset for response data in PDU. */ +#define MB_SER_PDU_SIZE_MAX ( MB_SERIAL_BUF_SIZE ) /*!< Maximum size of a Modbus frame. */ +#define MB_SER_PDU_SIZE_LRC 1 /*!< Size of LRC field in PDU. */ +#define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */ +#define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */ +#define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */ + /* ----------------------- Prototypes 0-------------------------------------*/ typedef void ( *pvMBFrameStart ) ( void ); diff --git a/components/freemodbus/modbus/include/mbport.h b/components/freemodbus/modbus/include/mbport.h index ad064496001..ccd2ec52b1e 100644 --- a/components/freemodbus/modbus/include/mbport.h +++ b/components/freemodbus/modbus/include/mbport.h @@ -37,6 +37,20 @@ PR_BEGIN_EXTERN_C #endif +#if CONFIG_UART_ISR_IN_IRAM +#define MB_PORT_SERIAL_ISR_FLAG ESP_INTR_FLAG_IRAM +#else +#define MB_PORT_SERIAL_ISR_FLAG ESP_INTR_FLAG_LOWMED +#endif + +#if MB_PORT_TIMER_ISR_IN_IRAM +#define MB_PORT_ISR_ATTR IRAM_ATTR +#define MB_PORT_TIMER_ISR_FLAG ESP_INTR_FLAG_IRAM +#else +#define MB_PORT_ISR_ATTR +#define MB_PORT_TIMER_ISR_FLAG ESP_INTR_FLAG_LOWMED +#endif + /* ----------------------- Type definitions ---------------------------------*/ typedef enum @@ -44,7 +58,8 @@ typedef enum EV_READY = 0x01, /*!< Startup finished. */ EV_FRAME_RECEIVED = 0x02, /*!< Frame received. */ EV_EXECUTE = 0x04, /*!< Execute function. */ - EV_FRAME_SENT = 0x08 /*!< Frame sent. */ + EV_FRAME_SENT = 0x08, /*!< Frame sent. */ + EV_FRAME_TRANSMIT = 0x10 /*!< Frame transmit. */ } eMBEventType; #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED diff --git a/components/freemodbus/modbus/include/mbproto.h b/components/freemodbus/modbus/include/mbproto.h index 786aaf4030d..f84cb3edcf5 100644 --- a/components/freemodbus/modbus/include/mbproto.h +++ b/components/freemodbus/modbus/include/mbproto.h @@ -53,7 +53,7 @@ PR_BEGIN_EXTERN_C #define MB_FUNC_DIAG_GET_COM_EVENT_CNT ( 11 ) #define MB_FUNC_DIAG_GET_COM_EVENT_LOG ( 12 ) #define MB_FUNC_OTHER_REPORT_SLAVEID ( 17 ) -#define MB_FUNC_ERROR ( 128 ) +#define MB_FUNC_ERROR ( 128u ) /* ----------------------- Type definitions ---------------------------------*/ typedef enum { diff --git a/components/freemodbus/modbus/mb.c b/components/freemodbus/modbus/mb.c index f0d348f7ffc..d655043ab4e 100644 --- a/components/freemodbus/modbus/mb.c +++ b/components/freemodbus/modbus/mb.c @@ -43,13 +43,13 @@ #include "mbfunc.h" #include "mbport.h" -#if MB_SLAVE_RTU_ENABLED == 1 +#if MB_SLAVE_RTU_ENABLED #include "mbrtu.h" #endif -#if MB_SLAVE_ASCII_ENABLED == 1 +#if MB_SLAVE_ASCII_ENABLED #include "mbascii.h" #endif -#if MB_TCP_ENABLED == 1 +#if MB_TCP_ENABLED #include "mbtcp.h" #endif @@ -62,6 +62,8 @@ static UCHAR ucMBAddress; static eMBMode eMBCurrentMode; +volatile UCHAR ucMbSlaveBuf[MB_SERIAL_BUF_SIZE]; + static enum { STATE_ENABLED, @@ -227,7 +229,7 @@ eMBRegisterCB( UCHAR ucFunctionCode, pxMBFunctionHandler pxHandler ) int i; eMBErrorCode eStatus; - if( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= 127 ) ) + if( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= MB_FUNC_CODE_MAX ) ) { ENTER_CRITICAL_SECTION( ); if( pxHandler != NULL ) @@ -330,7 +332,7 @@ eMBDisable( void ) eMBErrorCode eMBPoll( void ) { - static UCHAR *ucMBFrame; + static UCHAR *ucMBFrame = NULL; static UCHAR ucRcvAddress; static UCHAR ucFunctionCode; static USHORT usLength; @@ -353,9 +355,11 @@ eMBPoll( void ) switch ( eEvent ) { case EV_READY: + ESP_LOGD(MB_PORT_TAG, "%s:EV_READY", __func__); break; case EV_FRAME_RECEIVED: + ESP_LOGD(MB_PORT_TAG, "EV_FRAME_RECEIVED"); eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); if( eStatus == MB_ENOERR ) { @@ -363,11 +367,16 @@ eMBPoll( void ) if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) { ( void )xMBPortEventPost( EV_EXECUTE ); + ESP_LOG_BUFFER_HEX_LEVEL(MB_PORT_TAG, &ucMBFrame[MB_PDU_FUNC_OFF], usLength, ESP_LOG_DEBUG); } } break; case EV_EXECUTE: + if ( !ucMBFrame ) { + return MB_EILLSTATE; + } + ESP_LOGD(MB_PORT_TAG, "%s:EV_EXECUTE", __func__); ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; eException = MB_EX_ILLEGAL_FUNCTION; for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) @@ -377,7 +386,7 @@ eMBPoll( void ) { break; } - else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) + if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) { eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength ); break; @@ -403,9 +412,17 @@ eMBPoll( void ) } break; + case EV_FRAME_TRANSMIT: + ESP_LOGD(MB_PORT_TAG, "%s:EV_FRAME_TRANSMIT", __func__); + break; + case EV_FRAME_SENT: + ESP_LOGD(MB_PORT_TAG, "%s:EV_FRAME_SENT", __func__); + break; + + default: break; } } - return MB_ENOERR; + return eStatus; } diff --git a/components/freemodbus/modbus/mb_m.c b/components/freemodbus/modbus/mb_m.c index a668243b2c5..98a65740313 100644 --- a/components/freemodbus/modbus/mb_m.c +++ b/components/freemodbus/modbus/mb_m.c @@ -44,17 +44,17 @@ #include "mbfunc.h" #include "mbport.h" -#if MB_MASTER_RTU_ENABLED == 1 +#if MB_MASTER_RTU_ENABLED #include "mbrtu.h" #endif -#if MB_MASTER_ASCII_ENABLED == 1 +#if MB_MASTER_ASCII_ENABLED #include "mbascii.h" #endif -#if MB_MASTER_TCP_ENABLED == 1 +#if MB_MASTER_TCP_ENABLED #include "mbtcp.h" #endif -#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0 +#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED #ifndef MB_PORT_HAS_CLOSE @@ -66,6 +66,15 @@ static UCHAR ucMBMasterDestAddress; static BOOL xMBRunInMasterMode = FALSE; static volatile eMBMasterErrorEventType eMBMasterCurErrorType; +static volatile USHORT usMasterSendPDULength; +static volatile eMBMode eMBMasterCurrentMode; + +/*------------------------ Shared variables ---------------------------------*/ + +volatile UCHAR ucMasterSndBuf[MB_SERIAL_BUF_SIZE]; +volatile UCHAR ucMasterRcvBuf[MB_SERIAL_BUF_SIZE]; +volatile eMBMasterTimerMode eMasterCurTimerMode; +volatile BOOL xFrameIsBroadcast = FALSE; static enum { @@ -137,7 +146,7 @@ static xMBFunctionHandler xMasterFuncHandlers[MB_FUNC_HANDLERS_MAX] = { /* ----------------------- Start implementation -----------------------------*/ eMBErrorCode -eMBMasterInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) +eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) { eMBErrorCode eStatus = MB_ENOERR; @@ -153,6 +162,7 @@ eMBMasterInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity pxMBMasterFrameCBByteReceived = xMBMasterRTUReceiveFSM; pxMBMasterFrameCBTransmitterEmpty = xMBMasterRTUTransmitFSM; pxMBMasterPortCBTimerExpired = xMBMasterRTUTimerExpired; + eMBMasterCurrentMode = MB_RTU; eStatus = eMBMasterRTUInit(ucPort, ulBaudRate, eParity); break; @@ -167,6 +177,7 @@ eMBMasterInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity pxMBMasterFrameCBByteReceived = xMBMasterASCIIReceiveFSM; pxMBMasterFrameCBTransmitterEmpty = xMBMasterASCIITransmitFSM; pxMBMasterPortCBTimerExpired = xMBMasterASCIITimerT1SExpired; + eMBMasterCurrentMode = MB_ASCII; eStatus = eMBMasterASCIIInit(ucPort, ulBaudRate, eParity ); break; @@ -440,15 +451,56 @@ void vMBMasterSetDestAddress( UCHAR Address ) } // Get Modbus Master current error event type. -eMBMasterErrorEventType eMBMasterGetErrorType( void ) +eMBMasterErrorEventType inline eMBMasterGetErrorType( void ) { return eMBMasterCurErrorType; } // Set Modbus Master current error event type. -void vMBMasterSetErrorType( eMBMasterErrorEventType errorType ) +void IRAM_ATTR vMBMasterSetErrorType( eMBMasterErrorEventType errorType ) { eMBMasterCurErrorType = errorType; } -#endif // MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0 +/* Get Modbus Master send PDU's buffer address pointer.*/ +void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame ) +{ + *pucFrame = ( UCHAR * ) &ucMasterSndBuf[MB_SER_PDU_PDU_OFF]; +} + +/* Set Modbus Master send PDU's buffer length.*/ +void vMBMasterSetPDUSndLength( USHORT SendPDULength ) +{ + usMasterSendPDULength = SendPDULength; +} + +/* Get Modbus Master send PDU's buffer length.*/ +USHORT usMBMasterGetPDUSndLength( void ) +{ + return usMasterSendPDULength; +} + +/* Set Modbus Master current timer mode.*/ +void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode ) +{ + eMasterCurTimerMode = eMBTimerMode; +} + +/* Get Modbus Master current timer mode.*/ +eMBMasterTimerMode MB_PORT_ISR_ATTR xMBMasterGetCurTimerMode( void ) +{ + return eMasterCurTimerMode; +} + +/* The master request is broadcast? */ +BOOL MB_PORT_ISR_ATTR xMBMasterRequestIsBroadcast( void ) +{ + return xFrameIsBroadcast; +} + +/* The master request is broadcast? */ +void vMBMasterRequestSetType( BOOL xIsBroadcast ){ + xFrameIsBroadcast = xIsBroadcast; +} + +#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED diff --git a/components/freemodbus/modbus/rtu/mbcrc.c b/components/freemodbus/modbus/rtu/mbcrc.c index 29b9ea765a9..1cd0a6ec8b0 100644 --- a/components/freemodbus/modbus/rtu/mbcrc.c +++ b/components/freemodbus/modbus/rtu/mbcrc.c @@ -30,6 +30,9 @@ /* ----------------------- Platform includes --------------------------------*/ #include "port.h" +#include "mbconfig.h" + +#if MB_MASTER_RTU_ENABLED || MB_SLAVE_RTU_ENABLED static const UCHAR aucCRCHi[] = { 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, @@ -96,3 +99,5 @@ usMBCRC16( UCHAR * pucFrame, USHORT usLen ) } return ( USHORT )( ucCRCHi << 8 | ucCRCLo ); } + +#endif diff --git a/components/freemodbus/modbus/rtu/mbrtu.c b/components/freemodbus/modbus/rtu/mbrtu.c index 6287ae9d9bf..867ad686147 100644 --- a/components/freemodbus/modbus/rtu/mbrtu.c +++ b/components/freemodbus/modbus/rtu/mbrtu.c @@ -43,12 +43,7 @@ #include "mbcrc.h" #include "mbport.h" -/* ----------------------- Defines ------------------------------------------*/ -#define MB_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */ -#define MB_SER_PDU_SIZE_MAX 256 /*!< Maximum size of a Modbus RTU frame. */ -#define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */ -#define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */ -#define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */ +#if MB_SLAVE_RTU_ENABLED /* ----------------------- Type definitions ---------------------------------*/ typedef enum @@ -65,16 +60,18 @@ typedef enum STATE_TX_XMIT /*!< Transmitter is in transfer state. */ } eMBSndState; +/* ----------------------- Shared variables ---------------------------------*/ +extern volatile UCHAR ucMbSlaveBuf[]; + /* ----------------------- Static variables ---------------------------------*/ static volatile eMBSndState eSndState; static volatile eMBRcvState eRcvState; -volatile UCHAR ucRTUBuf[MB_SER_PDU_SIZE_MAX]; - static volatile UCHAR *pucSndBufferCur; static volatile USHORT usSndBufferCount; static volatile USHORT usRcvBufferPos; +static volatile UCHAR *ucRTUBuf = ucMbSlaveBuf; /* ----------------------- Start implementation -----------------------------*/ eMBErrorCode @@ -223,13 +220,13 @@ eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) BOOL xMBRTUReceiveFSM( void ) { - BOOL xTaskNeedSwitch = FALSE; + BOOL xStatus = FALSE; UCHAR ucByte; assert( eSndState == STATE_TX_IDLE ); /* Always read the character. */ - ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte ); + xStatus = xMBPortSerialGetByte( ( CHAR * ) & ucByte ); switch ( eRcvState ) { @@ -249,7 +246,7 @@ xMBRTUReceiveFSM( void ) /* In the idle state we wait for a new character. If a character * is received the t1.5 and t3.5 timers are started and the - * receiver is in the state STATE_RX_RECEIVCE. + * receiver is in the state STATE_RX_RCV. */ case STATE_RX_IDLE: usRcvBufferPos = 0; @@ -268,7 +265,9 @@ xMBRTUReceiveFSM( void ) case STATE_RX_RCV: if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ) { - ucRTUBuf[usRcvBufferPos++] = ucByte; + if ( xStatus ) { + ucRTUBuf[usRcvBufferPos++] = ucByte; + } } else { @@ -277,13 +276,14 @@ xMBRTUReceiveFSM( void ) vMBPortTimersEnable( ); break; } - return xTaskNeedSwitch; + + return xStatus; } BOOL xMBRTUTransmitFSM( void ) { - BOOL xNeedPoll = FALSE; + BOOL xNeedPoll = TRUE; assert( eRcvState == STATE_RX_IDLE ); @@ -292,8 +292,6 @@ xMBRTUTransmitFSM( void ) /* We should not get a transmitter event if the transmitter is in * idle state. */ case STATE_TX_IDLE: - /* enable receiver/disable transmitter. */ - vMBPortSerialEnable( TRUE, FALSE ); break; case STATE_TX_XMIT: @@ -306,11 +304,10 @@ xMBRTUTransmitFSM( void ) } else { - xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); - /* Disable transmitter. This prevents another transmit buffer - * empty interrupt. */ - vMBPortSerialEnable( TRUE, FALSE ); + xMBPortEventPost( EV_FRAME_TRANSMIT ); + xNeedPoll = FALSE; eSndState = STATE_TX_IDLE; + vMBPortTimersEnable( ); } break; } @@ -318,7 +315,7 @@ xMBRTUTransmitFSM( void ) return xNeedPoll; } -BOOL +BOOL MB_PORT_ISR_ATTR xMBRTUTimerT35Expired( void ) { BOOL xNeedPoll = FALSE; @@ -350,3 +347,4 @@ xMBRTUTimerT35Expired( void ) return xNeedPoll; } +#endif diff --git a/components/freemodbus/modbus/rtu/mbrtu.h b/components/freemodbus/modbus/rtu/mbrtu.h index 223c6354709..63c3cc46b73 100644 --- a/components/freemodbus/modbus/rtu/mbrtu.h +++ b/components/freemodbus/modbus/rtu/mbrtu.h @@ -35,6 +35,11 @@ #ifdef __cplusplus PR_BEGIN_EXTERN_C #endif + +/* ----------------------- Defines ------------------------------------------*/ +#define MB_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */ + +#if MB_SLAVE_RTU_ENABLED eMBErrorCode eMBRTUInit( UCHAR slaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ); void eMBRTUStart( void ); @@ -45,8 +50,9 @@ BOOL xMBRTUReceiveFSM( void ); BOOL xMBRTUTransmitFSM( void ); BOOL xMBRTUTimerT15Expired( void ); BOOL xMBRTUTimerT35Expired( void ); +#endif -#if MB_MASTER_RTU_ENABLED > 0 +#if MB_MASTER_RTU_ENABLED eMBErrorCode eMBMasterRTUInit( UCHAR ucPort, ULONG ulBaudRate,eMBParity eParity ); void eMBMasterRTUStart( void ); void eMBMasterRTUStop( void ); diff --git a/components/freemodbus/modbus/rtu/mbrtu_m.c b/components/freemodbus/modbus/rtu/mbrtu_m.c index 315e5c69fbb..41610350972 100644 --- a/components/freemodbus/modbus/rtu/mbrtu_m.c +++ b/components/freemodbus/modbus/rtu/mbrtu_m.c @@ -45,13 +45,8 @@ #include "mbcrc.h" #include "mbport.h" -#if MB_MASTER_RTU_ENABLED > 0 /* ----------------------- Defines ------------------------------------------*/ -#define MB_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */ -#define MB_SER_PDU_SIZE_MAX 256 /*!< Maximum size of a Modbus RTU frame. */ -#define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */ -#define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */ -#define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */ +#define MB_RTU_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */ /* ----------------------- Type definitions ---------------------------------*/ typedef enum @@ -69,21 +64,21 @@ typedef enum STATE_M_TX_XFWR, /*!< Transmitter is in transfer finish and wait receive state. */ } eMBMasterSndState; +#if MB_MASTER_RTU_ENABLED +/*------------------------ Shared variables ---------------------------------*/ +extern volatile UCHAR ucMasterRcvBuf[]; +extern volatile UCHAR ucMasterSndBuf[]; + /* ----------------------- Static variables ---------------------------------*/ static volatile eMBMasterSndState eSndState; static volatile eMBMasterRcvState eRcvState; -static volatile UCHAR ucMasterRTUSndBuf[MB_PDU_SIZE_MAX]; -static volatile UCHAR ucMasterRTURcvBuf[MB_SER_PDU_SIZE_MAX]; -static volatile USHORT usMasterSendPDULength; - static volatile UCHAR *pucMasterSndBufferCur; static volatile USHORT usMasterSndBufferCount; - static volatile USHORT usMasterRcvBufferPos; -static volatile BOOL xFrameIsBroadcast = FALSE; -static volatile eMBMasterTimerMode eMasterCurTimerMode; +static volatile UCHAR *ucMasterRTURcvBuf = ucMasterRcvBuf; +static volatile UCHAR *ucMasterRTUSndBuf = ucMasterSndBuf; /* ----------------------- Start implementation -----------------------------*/ eMBErrorCode @@ -164,10 +159,10 @@ eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLengt assert( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX ); /* Length and CRC check */ - if( ( usMasterRcvBufferPos >= MB_SER_PDU_SIZE_MIN ) + if( ( usMasterRcvBufferPos >= MB_RTU_SER_PDU_SIZE_MIN ) && ( usMBCRC16( ( UCHAR * ) ucMasterRTURcvBuf, usMasterRcvBufferPos ) == 0 ) ) { - /* Save the address field. All frames are passed to the upper layed + /* Save the address field. All frames are passed to the upper layer * and the decision if a frame is used is done there. */ *pucRcvAddress = ucMasterRTURcvBuf[MB_SER_PDU_ADDR_OFF]; @@ -234,13 +229,13 @@ eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength BOOL xMBMasterRTUReceiveFSM( void ) { - BOOL xTaskNeedSwitch = FALSE; + BOOL xStatus = FALSE; UCHAR ucByte; assert(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR )); /* Always read the character. */ - ( void )xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte ); + xStatus = xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte ); switch ( eRcvState ) { @@ -260,7 +255,7 @@ xMBMasterRTUReceiveFSM( void ) /* In the idle state we wait for a new character. If a character * is received the t1.5 and t3.5 timers are started and the - * receiver is in the state STATE_RX_RECEIVCE and disable early + * receiver is in the state STATE_M_RX_RCV and disable early * the timer of respond timeout . */ case STATE_M_RX_IDLE: @@ -271,11 +266,16 @@ xMBMasterRTUReceiveFSM( void ) eSndState = STATE_M_TX_IDLE; usMasterRcvBufferPos = 0; - ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte; - eRcvState = STATE_M_RX_RCV; + // Skip zero byte at first position (address in response != 0) + if ( xStatus && ucByte ) { + ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte; + eRcvState = STATE_M_RX_RCV; + } /* Enable t3.5 timers. */ +#if CONFIG_FMB_TIMER_PORT_ENABLED vMBMasterPortTimersT35Enable( ); +#endif break; /* We are currently receiving a frame. Reset the timer after @@ -286,22 +286,27 @@ xMBMasterRTUReceiveFSM( void ) case STATE_M_RX_RCV: if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX ) { - ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte; + if ( xStatus ) { + ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte; + } } else { eRcvState = STATE_M_RX_ERROR; } +#if CONFIG_FMB_TIMER_PORT_ENABLED vMBMasterPortTimersT35Enable( ); +#endif break; } - return xTaskNeedSwitch; + return xStatus; } BOOL xMBMasterRTUTransmitFSM( void ) { - BOOL xNeedPoll = FALSE; + BOOL xNeedPoll = TRUE; + BOOL xFrameIsBroadcast = FALSE; assert( eRcvState == STATE_M_RX_IDLE ); @@ -310,11 +315,10 @@ xMBMasterRTUTransmitFSM( void ) /* We should not get a transmitter event if the transmitter is in * idle state. */ case STATE_M_TX_XFWR: + xNeedPoll = FALSE; break; case STATE_M_TX_IDLE: - /* enable receiver/disable transmitter. */ - vMBMasterPortSerialEnable( TRUE, FALSE ); break; case STATE_M_TX_XMIT: @@ -328,9 +332,7 @@ xMBMasterRTUTransmitFSM( void ) else { xFrameIsBroadcast = ( ucMasterRTUSndBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE; - /* Disable transmitter. This prevents another transmit buffer - * empty interrupt. */ - vMBMasterPortSerialEnable( TRUE, FALSE ); + vMBMasterRequestSetType( xFrameIsBroadcast ); eSndState = STATE_M_TX_XFWR; /* If the frame is broadcast ,master will enable timer of convert delay, * else master will enable timer of respond timeout. */ @@ -342,7 +344,6 @@ xMBMasterRTUTransmitFSM( void ) { vMBMasterPortTimersRespondTimeoutEnable( ); } - xNeedPoll = TRUE; } break; } @@ -350,7 +351,7 @@ xMBMasterRTUTransmitFSM( void ) return xNeedPoll; } -BOOL +BOOL MB_PORT_ISR_ATTR xMBMasterRTUTimerExpired(void) { BOOL xNeedPoll = FALSE; @@ -368,10 +369,10 @@ xMBMasterRTUTimerExpired(void) xNeedPoll = xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED); break; - /* An error occured while receiving the frame. */ + /* An error occurred while receiving the frame. */ case STATE_M_RX_ERROR: vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA); - xNeedPoll = xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS ); + xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS); break; /* Function called in an illegal state. */ @@ -386,62 +387,30 @@ xMBMasterRTUTimerExpired(void) { /* A frame was send finish and convert delay or respond timeout expired. * If the frame is broadcast,The master will idle,and if the frame is not - * broadcast.Notify the listener process error.*/ + * broadcast. Notify the listener process error.*/ case STATE_M_TX_XFWR: - if ( xFrameIsBroadcast == FALSE ) { + if ( xMBMasterRequestIsBroadcast( ) == FALSE ) { vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT); xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS); } break; /* Function called in an illegal state. */ default: - assert( ( eSndState == STATE_M_TX_XMIT ) || ( eSndState == STATE_M_TX_IDLE )); + assert( ( eSndState == STATE_M_TX_XMIT ) || ( eSndState == STATE_M_TX_IDLE )); break; } eSndState = STATE_M_TX_IDLE; vMBMasterPortTimersDisable( ); /* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */ - if (eMasterCurTimerMode == MB_TMODE_CONVERT_DELAY) { - xNeedPoll = xMBMasterPortEventPost( EV_MASTER_EXECUTE ); + if (xMBMasterGetCurTimerMode() == MB_TMODE_CONVERT_DELAY) { + xNeedPoll = xMBMasterPortEventPost(EV_MASTER_EXECUTE); } return xNeedPoll; } -/* Get Modbus Master send RTU's buffer address pointer.*/ -void vMBMasterGetRTUSndBuf( UCHAR ** pucFrame ) -{ - *pucFrame = ( UCHAR * ) ucMasterRTUSndBuf; -} -/* Get Modbus Master send PDU's buffer address pointer.*/ -void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame ) -{ - *pucFrame = ( UCHAR * ) &ucMasterRTUSndBuf[MB_SER_PDU_PDU_OFF]; -} - -/* Set Modbus Master send PDU's buffer length.*/ -void vMBMasterSetPDUSndLength( USHORT SendPDULength ) -{ - usMasterSendPDULength = SendPDULength; -} - -/* Get Modbus Master send PDU's buffer length.*/ -USHORT usMBMasterGetPDUSndLength( void ) -{ - return usMasterSendPDULength; -} - -/* Set Modbus Master current timer mode.*/ -void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode ) -{ - eMasterCurTimerMode = eMBTimerMode; -} -/* The master request is broadcast? */ -BOOL xMBMasterRequestIsBroadcast( void ){ - return xFrameIsBroadcast; -} -#endif +#endif \ No newline at end of file diff --git a/components/freemodbus/port/port.c b/components/freemodbus/port/port.c index 484128ae7f5..e7f641111d1 100644 --- a/components/freemodbus/port/port.c +++ b/components/freemodbus/port/port.c @@ -37,22 +37,36 @@ /* ----------------------- Modbus includes ----------------------------------*/ #include "freertos/FreeRTOS.h" -#include "freertos/portmacro.h" #include "sys/lock.h" #include "port.h" /* ----------------------- Variables ----------------------------------------*/ static _lock_t s_port_lock; +static UCHAR ucPortMode = 0; /* ----------------------- Start implementation -----------------------------*/ inline void -vMBPortEnterCritical( ) +vMBPortEnterCritical(void) { _lock_acquire(&s_port_lock); } inline void -vMBPortExitCritical( ) +vMBPortExitCritical(void) { _lock_release(&s_port_lock); } + +UCHAR +ucMBPortGetMode( void ) +{ + return ucPortMode; +} + +void +vMBPortSetMode( UCHAR ucMode ) +{ + ENTER_CRITICAL_SECTION(); + ucPortMode = ucMode; + EXIT_CRITICAL_SECTION(); +} diff --git a/components/freemodbus/port/port.h b/components/freemodbus/port/port.h index f0070f1d2cf..cc866fd8039 100644 --- a/components/freemodbus/port/port.h +++ b/components/freemodbus/port/port.h @@ -18,7 +18,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" -#include "freertos/portmacro.h" #include "esp_log.h" // for ESP_LOGE macro #include "mbconfig.h" @@ -28,6 +27,24 @@ #define MB_PORT_TAG "MB_PORT_COMMON" +#define MB_BAUD_RATE_DEFAULT (115200) +#define MB_QUEUE_LENGTH (CONFIG_FMB_QUEUE_LENGTH) + +#define MB_SERIAL_TASK_PRIO (CONFIG_FMB_SERIAL_TASK_PRIO) +#define MB_SERIAL_TASK_STACK_SIZE (CONFIG_FMB_SERIAL_TASK_STACK_SIZE) +#define MB_SERIAL_TOUT (3) // 3.5*8 = 28 ticks, TOUT=3 -> ~24..33 ticks + +// Set buffer size for transmission +#define MB_SERIAL_BUF_SIZE (CONFIG_FMB_SERIAL_BUF_SIZE) + +// common definitions for serial port implementations +#define MB_SERIAL_TX_TOUT_MS (CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND) +#define MB_SERIAL_TX_TOUT_TICKS pdMS_TO_TICKS(MB_SERIAL_TX_TOUT_MS) // timeout for transmission +#define MB_SERIAL_RX_TOUT_MS (1) +#define MB_SERIAL_RX_TOUT_TICKS pdMS_TO_TICKS(MB_SERIAL_RX_TOUT_MS) // timeout for receive + +#define MB_SERIAL_RESP_LEN_MIN (4) + #define MB_PORT_CHECK(a, ret_val, str, ...) \ if (!(a)) { \ ESP_LOGE(MB_PORT_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ @@ -57,20 +74,22 @@ typedef short SHORT; typedef unsigned long ULONG; typedef long LONG; -void vMBPortEnterCritical( ); -void vMBPortExitCritical( ); +void vMBPortEnterCritical(void); +void vMBPortExitCritical(void); -#define ENTER_CRITICAL_SECTION( ) { ESP_LOGD(MB_PORT_TAG,"%s: Port enter critical.", __func__); \ +#define ENTER_CRITICAL_SECTION( ) { ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port enter critical.", __func__); \ vMBPortEnterCritical(); } #define EXIT_CRITICAL_SECTION( ) { vMBPortExitCritical(); \ - ESP_LOGD(MB_PORT_TAG,"%s: Port exit critical", __func__); } + ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port exit critical", __func__); } #define MB_PORT_PARITY_GET(parity) ((parity != UART_PARITY_DISABLE) ? \ ((parity == UART_PARITY_ODD) ? MB_PAR_ODD : MB_PAR_EVEN) : MB_PAR_NONE) #define MB_PORT_CHECK_EVENT( event, mask ) ( event & mask ) #define MB_PORT_CLEAR_EVENT( event, mask ) do { event &= ~mask; } while(0) +void vMBPortSetMode( UCHAR ucMode ); + #ifdef __cplusplus PR_END_EXTERN_C #endif /* __cplusplus */ diff --git a/components/freemodbus/port/portevent.c b/components/freemodbus/port/portevent.c index 2c6597eef7e..42dde249e4d 100644 --- a/components/freemodbus/port/portevent.c +++ b/components/freemodbus/port/portevent.c @@ -56,7 +56,7 @@ /* ----------------------- Variables ----------------------------------------*/ static xQueueHandle xQueueHdl; -#define MB_EVENT_QUEUE_SIZE (1) +#define MB_EVENT_QUEUE_SIZE (6) #define MB_EVENT_QUEUE_TIMEOUT (pdMS_TO_TICKS(CONFIG_FMB_EVENT_QUEUE_TIMEOUT)) /* ----------------------- Start implementation -----------------------------*/ @@ -82,21 +82,30 @@ vMBPortEventClose( void ) } } -BOOL +BOOL MB_PORT_ISR_ATTR xMBPortEventPost( eMBEventType eEvent ) { - BOOL bStatus = TRUE; + BaseType_t xStatus, xHigherPriorityTaskWoken = pdFALSE; assert(xQueueHdl != NULL); if( (BOOL)xPortInIsrContext() == TRUE ) { - xQueueSendFromISR(xQueueHdl, (const void*)&eEvent, pdFALSE); + xStatus = xQueueSendFromISR(xQueueHdl, (const void*)&eEvent, &xHigherPriorityTaskWoken); + if ( xHigherPriorityTaskWoken ) + { + portYIELD_FROM_ISR(); + } + if (xStatus != pdTRUE) { + ESP_EARLY_LOGV(MB_PORT_TAG, "%s: Post message failure = %d.", __func__, xStatus); + return FALSE; + } } else { - xQueueSend(xQueueHdl, (const void*)&eEvent, MB_EVENT_QUEUE_TIMEOUT); + xStatus = xQueueSend(xQueueHdl, (const void*)&eEvent, MB_EVENT_QUEUE_TIMEOUT); + MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "%s: Post message failure.", __func__); } - return bStatus; + return TRUE; } BOOL @@ -114,7 +123,7 @@ xMBPortEventGet(eMBEventType * peEvent) xQueueHandle xMBPortEventGetHandle(void) { - if(xQueueHdl != NULL) // + if(xQueueHdl != NULL) { return xQueueHdl; } diff --git a/components/freemodbus/port/portevent_m.c b/components/freemodbus/port/portevent_m.c index 1c990005f9e..77f4780dca8 100644 --- a/components/freemodbus/port/portevent_m.c +++ b/components/freemodbus/port/portevent_m.c @@ -78,7 +78,7 @@ xMBMasterPortEventInit( void ) return TRUE; } -BOOL +BOOL MB_PORT_ISR_ATTR xMBMasterPortEventPost( eMBMasterEventType eEvent ) { BOOL bStatus = FALSE; @@ -117,8 +117,7 @@ xMBMasterPortEventGet( eMBMasterEventType * eEvent) { EventBits_t uxBits; BOOL xEventHappened = FALSE; - uxBits = xEventGroupWaitBits( - xEventGroupMasterHdl, // The event group being tested. + uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested. MB_EVENT_POLL_MASK, // The bits within the event group to wait for. pdTRUE, // Masked bits should be cleared before returning. pdFALSE, // Don't wait for both bits, either bit will do. diff --git a/components/freemodbus/port/portother.c b/components/freemodbus/port/portother.c index 6997677fce6..43b6e967f84 100644 --- a/components/freemodbus/port/portother.c +++ b/components/freemodbus/port/portother.c @@ -50,12 +50,10 @@ /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" -#include "port_serial_slave.h" /* ----------------------- Modbus includes ----------------------------------*/ /* ----------------------- Variables ----------------------------------------*/ -static UCHAR ucPortMode = 0; /* ----------------------- Start implementation -----------------------------*/ @@ -66,22 +64,6 @@ bMBPortIsWithinException( void ) return bIsWithinException; } -/* ----------------------- Start implementation -----------------------------*/ - -UCHAR -ucMBPortGetMode( void ) -{ - return ucPortMode; -} - -void -vMBPortSetMode( UCHAR ucMode ) -{ - ENTER_CRITICAL_SECTION(); - ucPortMode = ucMode; - EXIT_CRITICAL_SECTION(); -} - void vMBPortClose( void ) { diff --git a/components/freemodbus/port/portother_m.c b/components/freemodbus/port/portother_m.c index ffb02380875..15b727a4417 100644 --- a/components/freemodbus/port/portother_m.c +++ b/components/freemodbus/port/portother_m.c @@ -55,25 +55,9 @@ /* ----------------------- Modbus includes ----------------------------------*/ /* ----------------------- Variables ----------------------------------------*/ -static UCHAR ucPortMode = 0; /* ----------------------- Start implementation -----------------------------*/ -UCHAR -ucMBPortGetMode( void ) -{ - return ucPortMode; -} - -void -vMBPortSetMode( UCHAR ucMode ) -{ - ENTER_CRITICAL_SECTION(); - ucPortMode = ucMode; - EXIT_CRITICAL_SECTION(); -} - - void vMBMasterPortClose( void ) { diff --git a/components/freemodbus/port/portserial.c b/components/freemodbus/port/portserial.c index 2f3029bda71..6667427e2ca 100644 --- a/components/freemodbus/port/portserial.c +++ b/components/freemodbus/port/portserial.c @@ -55,23 +55,6 @@ #include "sdkconfig.h" // for KConfig options #include "port_serial_slave.h" -// Definitions of UART default pin numbers -#define MB_UART_RXD (CONFIG_MB_UART_RXD) -#define MB_UART_TXD (CONFIG_MB_UART_TXD) -#define MB_UART_RTS (CONFIG_MB_UART_RTS) - -#define MB_BAUD_RATE_DEFAULT (115200) -#define MB_QUEUE_LENGTH (CONFIG_FMB_QUEUE_LENGTH) - -#define MB_SERIAL_TASK_PRIO (CONFIG_FMB_SERIAL_TASK_PRIO) -#define MB_SERIAL_TASK_STACK_SIZE (CONFIG_FMB_SERIAL_TASK_STACK_SIZE) -#define MB_SERIAL_TOUT (3) // 3.5*8 = 28 ticks, TOUT=3 -> ~24..33 ticks - -// Set buffer size for transmission -#define MB_SERIAL_BUF_SIZE (CONFIG_FMB_SERIAL_BUF_SIZE) - -// Note: This code uses mixed coding standard from legacy IDF code and used freemodbus stack - // A queue to handle UART event. static QueueHandle_t xMbUartQueue; static TaskHandle_t xMbTaskHandle; @@ -83,12 +66,14 @@ static UCHAR ucUartNumber = UART_NUM_MAX - 1; static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag -static UCHAR ucBuffer[MB_SERIAL_BUF_SIZE]; // Temporary buffer to transfer received data to modbus stack -static USHORT uiRxBufferPos = 0; // position in the receiver buffer - void vMBPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable) { // This function can be called from xMBRTUTransmitFSM() of different task + if (bTxEnable) { + bTxStateEnabled = TRUE; + } else { + bTxStateEnabled = FALSE; + } if (bRxEnable) { //uart_enable_rx_intr(ucUartNumber); bRxStateEnabled = TRUE; @@ -97,70 +82,69 @@ void vMBPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable) vTaskSuspend(xMbTaskHandle); // Block receiver task bRxStateEnabled = FALSE; } - if (bTxEnable) { - bTxStateEnabled = TRUE; - } else { - bTxStateEnabled = FALSE; - } } -static void vMBPortSerialRxPoll(size_t xEventSize) +static USHORT usMBPortSerialRxPoll(size_t xEventSize) { - USHORT usLength; + BOOL xReadStatus = TRUE; + USHORT usCnt = 0; if (bRxStateEnabled) { - if (xEventSize > 0) { - xEventSize = (xEventSize > MB_SERIAL_BUF_SIZE) ? MB_SERIAL_BUF_SIZE : xEventSize; - uiRxBufferPos = ((uiRxBufferPos + xEventSize) >= MB_SERIAL_BUF_SIZE) ? 0 : uiRxBufferPos; - // Get received packet into Rx buffer - usLength = uart_read_bytes(ucUartNumber, &ucBuffer[uiRxBufferPos], xEventSize, portMAX_DELAY); - for(USHORT usCnt = 0; usCnt < usLength; usCnt++ ) { - // Call the Modbus stack callback function and let it fill the buffers. - ( void )pxMBFrameCBByteReceived(); // calls callback xMBRTUReceiveFSM() to execute MB state machine - } - // The buffer is transferred into Modbus stack and is not needed here any more - uart_flush_input(ucUartNumber); - // Send event EV_FRAME_RECEIVED to allow stack process packet -#ifndef MB_TIMER_PORT_ENABLED - // Let the stack know that T3.5 time is expired and data is received - (void)pxMBPortCBTimerExpired(); // calls callback xMBRTUTimerT35Expired(); -#endif - ESP_LOGD(TAG, "RX_T35_timeout: %d(bytes in buffer)\n", (uint32_t)usLength); + // Get received packet into Rx buffer + while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) { + // Call the Modbus stack callback function and let it fill the buffers. + xReadStatus = pxMBFrameCBByteReceived(); // callback to execute receive FSM } + uart_flush_input(ucUartNumber); + // Send event EV_FRAME_RECEIVED to allow stack process packet +#if !CONFIG_FMB_TIMER_PORT_ENABLED + // Let the stack know that T3.5 time is expired and data is received + (void)pxMBPortCBTimerExpired(); // calls callback xMBRTUTimerT35Expired(); +#endif + ESP_LOGD(TAG, "RX: %d bytes\n", usCnt); } + return usCnt; } -BOOL xMBPortSerialTxPoll() +BOOL xMBPortSerialTxPoll(void) { - BOOL bStatus = FALSE; USHORT usCount = 0; - BOOL bNeedPoll = FALSE; + BOOL bNeedPoll = TRUE; if( bTxStateEnabled ) { // Continue while all response bytes put in buffer or out of buffer - while((bNeedPoll == FALSE) && (usCount++ < MB_SERIAL_BUF_SIZE)) { + while((bNeedPoll) && (usCount++ < MB_SERIAL_BUF_SIZE)) { // Calls the modbus stack callback function to let it fill the UART transmit buffer. - bNeedPoll = pxMBFrameCBTransmitterEmpty( ); // calls callback xMBRTUTransmitFSM(); + bNeedPoll = pxMBFrameCBTransmitterEmpty( ); // callback to transmit FSM } - ESP_LOGD(TAG, "MB_TX_buffer sent: (%d) bytes\n", (uint16_t)usCount); - bStatus = TRUE; + ESP_LOGD(TAG, "MB_TX_buffer send: (%d) bytes\n", (uint16_t)usCount); + // Waits while UART sending the packet + esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS); + vMBPortSerialEnable(TRUE, FALSE); + MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure."); + return TRUE; } - return bStatus; + return FALSE; } static void vUartTask(void *pvParameters) { uart_event_t xEvent; + USHORT usResult = 0; for(;;) { if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) { ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber); - //vMBPortTimersEnable(); switch(xEvent.type) { //Event of UART receving data case UART_DATA: - ESP_LOGD(TAG,"Receive data, len: %d", xEvent.size); - // Read received data and send it to modbus stack - vMBPortSerialRxPoll(xEvent.size); + ESP_LOGD(TAG,"Data event, length: %d", xEvent.size); + // This flag set in the event means that no more + // data received during configured timeout and UART TOUT feature is triggered + if (xEvent.timeout_flag) { + // Read received data and send it to modbus stack + usResult = usMBPortSerialRxPoll(xEvent.size); + ESP_LOGD(TAG,"Timeout occured, processed: %d bytes", usResult); + } break; //Event of HW FIFO overflow detected case UART_FIFO_OVF: @@ -245,18 +229,22 @@ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, // Set UART config xErr = uart_param_config(ucUartNumber, &xUartConfig); MB_PORT_CHECK((xErr == ESP_OK), - FALSE, "mb config failure, uart_param_config() returned (0x%x).", (uint32_t)xErr); + FALSE, "mb config failure, uart_param_config() returned (0x%x).", xErr); // Install UART driver, and get the queue. xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE, - MB_QUEUE_LENGTH, &xMbUartQueue, ESP_INTR_FLAG_LEVEL3); + MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG); MB_PORT_CHECK((xErr == ESP_OK), FALSE, - "mb serial driver failure, uart_driver_install() returned (0x%x).", (uint32_t)xErr); -#ifndef MB_TIMER_PORT_ENABLED + "mb serial driver failure, uart_driver_install() returned (0x%x).", xErr); +#if !CONFIG_FMB_TIMER_PORT_ENABLED // Set timeout for TOUT interrupt (T3.5 modbus time) xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT); MB_PORT_CHECK((xErr == ESP_OK), FALSE, - "mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (uint32_t)xErr); + "mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr); #endif + + // Set always timeout flag to trigger timeout interrupt even after rx fifo full + uart_set_always_rx_timeout(ucUartNumber, true); + // Create a task to handle UART events BaseType_t xStatus = xTaskCreate(vUartTask, "uart_queue_task", MB_SERIAL_TASK_STACK_SIZE, NULL, MB_SERIAL_TASK_PRIO, &xMbTaskHandle); @@ -265,15 +253,14 @@ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, // Force exit from function with failure MB_PORT_CHECK(FALSE, FALSE, "mb stack serial task creation error. xTaskCreate() returned (0x%x).", - (uint32_t)xStatus); + xStatus); } else { vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started } - uiRxBufferPos = 0; return TRUE; } -void vMBPortSerialClose() +void vMBPortSerialClose(void) { (void)vTaskSuspend(xMbTaskHandle); (void)vTaskDelete(xMbTaskHandle); @@ -292,10 +279,8 @@ BOOL xMBPortSerialPutByte(CHAR ucByte) BOOL xMBPortSerialGetByte(CHAR* pucByte) { assert(pucByte != NULL); - MB_PORT_CHECK((uiRxBufferPos < MB_SERIAL_BUF_SIZE), - FALSE, "mb stack serial get byte failure."); - *pucByte = ucBuffer[uiRxBufferPos]; - uiRxBufferPos++; - return TRUE; + USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS); + return (usLength == 1); } + diff --git a/components/freemodbus/port/portserial_m.c b/components/freemodbus/port/portserial_m.c index 2beb50d2830..92e78b04bfe 100644 --- a/components/freemodbus/port/portserial_m.c +++ b/components/freemodbus/port/portserial_m.c @@ -50,20 +50,9 @@ #include "esp_log.h" #include "sdkconfig.h" #include "port_serial_master.h" - /* ----------------------- Defines ------------------------------------------*/ - -#define MB_BAUD_RATE_DEFAULT (115200) -#define MB_QUEUE_LENGTH (CONFIG_FMB_QUEUE_LENGTH) - -#define MB_SERIAL_TASK_PRIO (CONFIG_FMB_SERIAL_TASK_PRIO) -#define MB_SERIAL_TASK_STACK_SIZE (CONFIG_FMB_SERIAL_TASK_STACK_SIZE) -#define MB_SERIAL_TOUT (3) // 3.5*8 = 28 ticks, TOUT=3 -> ~24..33 ticks - -// Set buffer size for transmission -#define MB_SERIAL_BUF_SIZE (CONFIG_FMB_SERIAL_BUF_SIZE) -#define MB_SERIAL_TX_TOUT_MS (100) -#define MB_SERIAL_TX_TOUT_TICKS pdMS_TO_TICKS(MB_SERIAL_TX_TOUT_MS) // timeout for transmission +#define MB_SERIAL_RX_SEMA_TOUT_MS (1000) +#define MB_SERIAL_RX_SEMA_TOUT (pdMS_TO_TICKS(MB_SERIAL_RX_SEMA_TOUT_MS)) /* ----------------------- Static variables ---------------------------------*/ static const CHAR *TAG = "MB_MASTER_SERIAL"; @@ -74,12 +63,42 @@ static TaskHandle_t xMbTaskHandle; // The UART hardware port number static UCHAR ucUartNumber = UART_NUM_MAX - 1; - static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag -static UCHAR ucBuffer[MB_SERIAL_BUF_SIZE]; // Temporary buffer to transfer received data to modbus stack -static USHORT uiRxBufferPos = 0; // position in the receiver buffer +static SemaphoreHandle_t xMasterSemaRxHandle; // Rx blocking semaphore handle + +static BOOL xMBMasterPortRxSemaInit( void ) +{ + xMasterSemaRxHandle = xSemaphoreCreateBinary(); + MB_PORT_CHECK((xMasterSemaRxHandle != NULL), FALSE , "%s: RX semaphore create failure.", __func__); + return TRUE; +} + +static BOOL xMBMasterPortRxSemaTake( LONG lTimeOut ) +{ + BaseType_t xStatus = pdTRUE; + xStatus = xSemaphoreTake(xMasterSemaRxHandle, lTimeOut ); + MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s: RX semaphore take failure.", __func__); + ESP_LOGV(MB_PORT_TAG,"%s:Take RX semaphore (%lu ticks).", __func__, lTimeOut); + return TRUE; +} + +static void vMBMasterRxSemaRelease( void ) +{ + BaseType_t xStatus = pdFALSE; + xStatus = xSemaphoreGive(xMasterSemaRxHandle); + if (xStatus != pdTRUE) { + ESP_LOGD(MB_PORT_TAG,"%s:RX semaphore is free.", __func__); + } +} + +static BOOL vMBMasterRxSemaIsBusy( void ) +{ + BaseType_t xStatus = pdFALSE; + xStatus = (uxSemaphoreGetCount(xMasterSemaRxHandle) == 0) ? TRUE : FALSE; + return xStatus; +} void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable) { @@ -91,6 +110,7 @@ void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable) } if (bRxEnable) { bRxStateEnabled = TRUE; + vMBMasterRxSemaRelease(); vTaskResume(xMbTaskHandle); // Resume receiver task } else { vTaskSuspend(xMbTaskHandle); // Block receiver task @@ -98,64 +118,83 @@ void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable) } } -static void vMBMasterPortSerialRxPoll(size_t xEventSize) +static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize) { - USHORT usLength; + BOOL xReadStatus = TRUE; + USHORT usCnt = 0; - if (bRxStateEnabled) { - if (xEventSize > 0) { - xEventSize = (xEventSize > MB_SERIAL_BUF_SIZE) ? MB_SERIAL_BUF_SIZE : xEventSize; - // Get received packet into Rx buffer - usLength = uart_read_bytes(ucUartNumber, &ucBuffer[0], xEventSize, portMAX_DELAY); - uiRxBufferPos = 0; - for(USHORT usCnt = 0; usCnt < usLength; usCnt++ ) { - // Call the Modbus stack callback function and let it fill the stack buffers. - ( void )pxMBMasterFrameCBByteReceived(); // calls callback xMBRTUReceiveFSM() - } - // The buffer is transferred into Modbus stack and is not needed here any more - uart_flush_input(ucUartNumber); - ESP_LOGD(TAG, "RX_T35_timeout: %d(bytes in buffer)\n", (uint32_t)usLength); + xReadStatus = xMBMasterPortRxSemaTake(MB_SERIAL_RX_SEMA_TOUT); + if (xReadStatus) { + while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) { + // Call the Modbus stack callback function and let it fill the stack buffers. + xReadStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM } +#if !CONFIG_FMB_TIMER_PORT_ENABLED + pxMBMasterPortCBTimerExpired(); +#endif + // The buffer is transferred into Modbus stack and is not needed here any more + uart_flush_input(ucUartNumber); + ESP_LOGD(TAG, "Received data: %d(bytes in buffer)\n", (uint32_t)usCnt); } else { - ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%d bytes) received. ", __func__, (uint16_t)xEventSize); + ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%d bytes) received. ", __func__, xEventSize); } + return usCnt; } -BOOL xMBMasterPortSerialTxPoll() +BOOL xMBMasterPortSerialTxPoll(void) { - BOOL bStatus = FALSE; USHORT usCount = 0; - BOOL bNeedPoll = FALSE; + BOOL bNeedPoll = TRUE; if( bTxStateEnabled ) { // Continue while all response bytes put in buffer or out of buffer - while((bNeedPoll == FALSE) && (usCount++ < MB_SERIAL_BUF_SIZE)) { + while(bNeedPoll && (usCount++ < MB_SERIAL_BUF_SIZE)) { // Calls the modbus stack callback function to let it fill the UART transmit buffer. - bNeedPoll = pxMBMasterFrameCBTransmitterEmpty( ); // calls callback xMBRTUTransmitFSM(); + bNeedPoll = pxMBMasterFrameCBTransmitterEmpty( ); // callback to transmit FSM } ESP_LOGD(TAG, "MB_TX_buffer sent: (%d) bytes.", (uint16_t)(usCount - 1)); // Waits while UART sending the packet esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS); - bTxStateEnabled = FALSE; + vMBMasterPortSerialEnable( TRUE, FALSE ); MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure."); - bStatus = TRUE; + return TRUE; } - return bStatus; + return FALSE; } // UART receive event task static void vUartTask(void* pvParameters) { uart_event_t xEvent; + USHORT usResult = 0; for(;;) { - if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) { // portMAX_DELAY + if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) { ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber); switch(xEvent.type) { //Event of UART receiving data case UART_DATA: - ESP_LOGD(TAG,"Receive data, len: %d.", xEvent.size); - // Read received data and send it to modbus stack - vMBMasterPortSerialRxPoll(xEvent.size); + ESP_LOGD(TAG,"Data event, len: %d.", xEvent.size); + // This flag set in the event means that no more + // data received during configured timeout and UART TOUT feature is triggered + if (xEvent.timeout_flag) { + // Workaround: The UART driver can cut mb frame + // (generate timeouts for incomplete frame) when wifi is active + if (xEvent.size <= MB_SER_PDU_SIZE_MIN) { + break; + } + // Response is received but previous packet processing is pending + // Do not wait completion of processing and just discard received data + // as incorrect (fragmentation of response). + if (vMBMasterRxSemaIsBusy()) { + uart_flush_input(ucUartNumber); + break; + } + // Read received data and send it to modbus stack + usResult = usMBMasterPortSerialRxPoll(xEvent.size); + ESP_LOGD(TAG,"Timeout occurred, processed: %d bytes", usResult); + // Block receiver task until data is not processed + vTaskSuspend(NULL); + } break; //Event of HW FIFO overflow detected case UART_FIFO_OVF: @@ -240,16 +279,20 @@ BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, // Set UART config xErr = uart_param_config(ucUartNumber, &xUartConfig); MB_PORT_CHECK((xErr == ESP_OK), - FALSE, "mb config failure, uart_param_config() returned (0x%x).", (uint32_t)xErr); + FALSE, "mb config failure, uart_param_config() returned (0x%x).", xErr); // Install UART driver, and get the queue. xErr = uart_driver_install(ucUartNumber, MB_SERIAL_BUF_SIZE, MB_SERIAL_BUF_SIZE, - MB_QUEUE_LENGTH, &xMbUartQueue, ESP_INTR_FLAG_LEVEL3); + MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG); MB_PORT_CHECK((xErr == ESP_OK), FALSE, - "mb serial driver failure, uart_driver_install() returned (0x%x).", (uint32_t)xErr); + "mb serial driver failure, uart_driver_install() returned (0x%x).", xErr); // Set timeout for TOUT interrupt (T3.5 modbus time) xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT); MB_PORT_CHECK((xErr == ESP_OK), FALSE, - "mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (uint32_t)xErr); + "mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr); + // Set always timeout flag to trigger timeout interrupt even after rx fifo full + uart_set_always_rx_timeout(ucUartNumber, true); + MB_PORT_CHECK((xMBMasterPortRxSemaInit()), FALSE, + "mb serial RX semaphore create fail."); // Create a task to handle UART events BaseType_t xStatus = xTaskCreate(vUartTask, "uart_queue_task", MB_SERIAL_TASK_STACK_SIZE, NULL, MB_SERIAL_TASK_PRIO, &xMbTaskHandle); @@ -258,16 +301,15 @@ BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, // Force exit from function with failure MB_PORT_CHECK(FALSE, FALSE, "mb stack serial task creation error. xTaskCreate() returned (0x%x).", - (uint32_t)xStatus); + xStatus); } else { vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started } - uiRxBufferPos = 0; ESP_LOGD(MB_PORT_TAG,"%s Init serial.", __func__); return TRUE; } -void vMBMasterPortSerialClose() +void vMBMasterPortSerialClose(void) { (void)vTaskDelete(xMbTaskHandle); ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber)); @@ -285,9 +327,7 @@ BOOL xMBMasterPortSerialPutByte(CHAR ucByte) BOOL xMBMasterPortSerialGetByte(CHAR* pucByte) { assert(pucByte != NULL); - MB_PORT_CHECK((uiRxBufferPos < MB_SERIAL_BUF_SIZE), - FALSE, "mb stack serial get byte failure."); - *pucByte = ucBuffer[uiRxBufferPos]; - uiRxBufferPos++; - return TRUE; + USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS); + return (usLength == 1); } + diff --git a/components/freemodbus/port/porttimer.c b/components/freemodbus/port/porttimer.c index 2859aa01669..22d5ce902bb 100644 --- a/components/freemodbus/port/porttimer.c +++ b/components/freemodbus/port/porttimer.c @@ -45,13 +45,14 @@ #include "port.h" /* ----------------------- Modbus includes ----------------------------------*/ +#include "sdkconfig.h" #include "mb.h" #include "mbport.h" #include "driver/timer.h" -#include "sdkconfig.h" #include "port_serial_slave.h" -#ifdef CONFIG_FMB_TIMER_PORT_ENABLED + +#if CONFIG_FMB_TIMER_PORT_ENABLED #define MB_US50_FREQ (20000) // 20kHz 1/20000 = 50mks #define MB_DISCR_TIME_US (50) // 50uS = one discreet for timer @@ -61,12 +62,18 @@ #define MB_TIMER_DIVIDER ((TIMER_BASE_CLK / 1000000UL) * MB_DISCR_TIME_US - 1) // divider for 50uS #define MB_TIMER_WITH_RELOAD (1) -static const USHORT usTimerIndex = CONFIG_FMB_TIMER_INDEX; // Modbus Timer index used by stack +static const USHORT usTimerIndex = CONFIG_FMB_TIMER_INDEX; // Modbus Timer index used by stack static const USHORT usTimerGroupIndex = CONFIG_FMB_TIMER_GROUP; // Modbus Timer group index used by stack - -static timg_dev_t *MB_TG[2] = {&TIMERG0, &TIMERG1}; +static timer_isr_handle_t xTimerIntHandle; // Timer interrupt handle +static timg_dev_t *MB_TG[2] = { &TIMERG0, &TIMERG1 }; /* ----------------------- Start implementation -----------------------------*/ + +static inline void vTimerGroupSetCounterEnableInISR(USHORT usTimerGroup, USHORT usTimer, BOOL xTimerState) +{ + MB_TG[usTimerGroup]->hw_timer[usTimer].config.enable = xTimerState; +} + static void IRAM_ATTR vTimerGroupIsr(void *param) { // Retrieve the interrupt status and the counter value @@ -82,7 +89,7 @@ static void IRAM_ATTR vTimerGroupIsr(void *param) BOOL xMBPortTimersInit(USHORT usTim1Timerout50us) { -#ifdef CONFIG_FMB_TIMER_PORT_ENABLED +#if CONFIG_FMB_TIMER_PORT_ENABLED MB_PORT_CHECK((usTim1Timerout50us > 0), FALSE, "Modbus timeout discreet is incorrect."); esp_err_t xErr; @@ -96,16 +103,15 @@ BOOL xMBPortTimersInit(USHORT usTim1Timerout50us) // Configure timer xErr = timer_init(usTimerGroupIndex, usTimerIndex, &config); MB_PORT_CHECK((xErr == ESP_OK), FALSE, - "timer init failure, timer_init() returned (0x%x).", (uint32_t)xErr); + "timer init failure, timer_init() returned (0x%x).", xErr); // Stop timer counter xErr = timer_pause(usTimerGroupIndex, usTimerIndex); MB_PORT_CHECK((xErr == ESP_OK), FALSE, - "stop timer failure, timer_pause() returned (0x%x).", (uint32_t)xErr); + "stop timer failure, timer_pause() returned (0x%x).", xErr); // Reset counter value xErr = timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0x00000000ULL); MB_PORT_CHECK((xErr == ESP_OK), FALSE, - "timer set value failure, timer_set_counter_value() returned (0x%x).", - (uint32_t)xErr); + "timer set value failure, timer_set_counter_value() returned (0x%x).", xErr); // wait3T5_us = 35 * 11 * 100000 / baud; // the 3.5T symbol time for baudrate // Set alarm value for usTim1Timerout50us * 50uS xErr = timer_set_alarm_value(usTimerGroupIndex, usTimerIndex, (uint32_t)(usTim1Timerout50us)); @@ -113,7 +119,8 @@ BOOL xMBPortTimersInit(USHORT usTim1Timerout50us) "failure to set alarm failure, timer_set_alarm_value() returned (0x%x).", (uint32_t)xErr); // Register ISR for timer - xErr = timer_isr_register(usTimerGroupIndex, usTimerIndex, vTimerGroupIsr, NULL, ESP_INTR_FLAG_IRAM, NULL); + xErr = timer_isr_register(usTimerGroupIndex, usTimerIndex, vTimerGroupIsr, + (void*)(uint32_t)usTimerIndex, MB_PORT_TIMER_ISR_FLAG, &xTimerIntHandle); MB_PORT_CHECK((xErr == ESP_OK), FALSE, "timer set value failure, timer_isr_register() returned (0x%x).", (uint32_t)xErr); @@ -121,9 +128,9 @@ BOOL xMBPortTimersInit(USHORT usTim1Timerout50us) return TRUE; } -void vMBPortTimersEnable() +void vMBPortTimersEnable(void) { -#ifdef CONFIG_FMB_TIMER_PORT_ENABLED +#if CONFIG_FMB_TIMER_PORT_ENABLED ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex)); ESP_ERROR_CHECK(timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0ULL)); ESP_ERROR_CHECK(timer_enable_intr(usTimerGroupIndex, usTimerIndex)); @@ -131,21 +138,26 @@ void vMBPortTimersEnable() #endif } -void vMBPortTimersDisable() +void MB_PORT_ISR_ATTR +vMBPortTimersDisable(void) { -#ifdef CONFIG_FMB_TIMER_PORT_ENABLED - ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex)); - ESP_ERROR_CHECK(timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0ULL)); - // Disable timer interrupt - ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex)); +#if CONFIG_FMB_TIMER_PORT_ENABLED + if( (BOOL)xPortInIsrContext() ) { + vTimerGroupSetCounterEnableInISR(usTimerGroupIndex, usTimerIndex, TIMER_PAUSE); + } else { + ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex)); + ESP_ERROR_CHECK(timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0ULL)); + // Disable timer interrupt + ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex)); + } #endif } -void vMBPortTimerClose() +void vMBPortTimerClose(void) { #ifdef CONFIG_FMB_TIMER_PORT_ENABLED ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex)); ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex)); + ESP_ERROR_CHECK(esp_intr_free(xTimerIntHandle)); #endif } - diff --git a/components/freemodbus/port/porttimer_m.c b/components/freemodbus/port/porttimer_m.c index c2b5b423c51..1a8a1fb6603 100644 --- a/components/freemodbus/port/porttimer_m.c +++ b/components/freemodbus/port/porttimer_m.c @@ -40,6 +40,7 @@ #include "mb_m.h" #include "mbport.h" #include "port_serial_master.h" +#include "sdkconfig.h" #define MB_US50_FREQ (20000) // 20kHz 1/20000 = 50mks #define MB_TICK_TIME_US (50) // 50uS = one tick for timer @@ -50,21 +51,25 @@ #define MB_TIMER_WITH_RELOAD (1) // Timer group and timer number to measure time (configurable in KConfig) -#define MB_TIMER_INDEX CONFIG_FMB_TIMER_INDEX -#define MB_TIMER_GROUP CONFIG_FMB_TIMER_GROUP +#define MB_TIMER_INDEX (CONFIG_FMB_TIMER_INDEX + 1) // Master uses the second timer in group +#define MB_TIMER_GROUP (CONFIG_FMB_TIMER_GROUP) -#define MB_TIMER_IO_LED 0 /* ----------------------- Variables ----------------------------------------*/ static USHORT usT35TimeOut50us; static const USHORT usTimerIndex = MB_TIMER_INDEX; // Initialize Modbus Timer index used by stack, static const USHORT usTimerGroupIndex = MB_TIMER_GROUP; // Timer group index used by stack - +static timer_isr_handle_t xTimerIntHandle; // Timer interrupt handle static timg_dev_t *MB_TG[2] = { &TIMERG0, &TIMERG1 }; /* ----------------------- static functions ---------------------------------*/ +static inline void vTimerGroupSetCounterEnableInISR(USHORT usTimerGroup, USHORT usTimer, BOOL xTimerState) +{ + MB_TG[usTimerGroup]->hw_timer[usTimer].config.enable = xTimerState; +} + static void IRAM_ATTR vTimerGroupIsr(void *param) { // Retrieve the interrupt status and the counter value @@ -115,7 +120,7 @@ BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us) (uint32_t)xErr); // Register ISR for timer xErr = timer_isr_register(usTimerGroupIndex, usTimerIndex, - vTimerGroupIsr, NULL, ESP_INTR_FLAG_IRAM, NULL); + vTimerGroupIsr, (void*)(uint32_t)usTimerIndex, MB_PORT_TIMER_ISR_FLAG, &xTimerIntHandle); MB_PORT_CHECK((xErr == ESP_OK), FALSE, "timer set value failure, timer_isr_register() returned (0x%x).", (uint32_t)xErr); @@ -151,11 +156,10 @@ static BOOL xMBMasterPortTimersEnable(USHORT usTimerTics50us) MB_PORT_CHECK((xErr == ESP_OK), FALSE, "timer start failure, timer_start() returned (0x%x).", (uint32_t)xErr); - //ESP_LOGD(MB_PORT_TAG,"%s Init timer.", __func__); return TRUE; } -void vMBMasterPortTimersT35Enable() +void vMBMasterPortTimersT35Enable(void) { USHORT usTimerTicks = usT35TimeOut50us; @@ -165,7 +169,7 @@ void vMBMasterPortTimersT35Enable() (void)xMBMasterPortTimersEnable(usTimerTicks); } -void vMBMasterPortTimersConvertDelayEnable() +void vMBMasterPortTimersConvertDelayEnable(void) { // Covert time in milliseconds into ticks USHORT usTimerTicks = ((MB_MASTER_DELAY_MS_CONVERT * 1000) / MB_TICK_TIME_US); @@ -176,7 +180,7 @@ void vMBMasterPortTimersConvertDelayEnable() (void)xMBMasterPortTimersEnable(usTimerTicks); } -void vMBMasterPortTimersRespondTimeoutEnable() +void vMBMasterPortTimersRespondTimeoutEnable(void) { USHORT usTimerTicks = (MB_MASTER_TIMEOUT_MS_RESPOND * 1000 / MB_TICK_TIME_US); @@ -185,17 +189,23 @@ void vMBMasterPortTimersRespondTimeoutEnable() (void)xMBMasterPortTimersEnable(usTimerTicks); } -void vMBMasterPortTimersDisable() +void MB_PORT_ISR_ATTR +vMBMasterPortTimersDisable() { - // Stop timer and then reload timer counter value - ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex)); - ESP_ERROR_CHECK(timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0ULL)); - // Disable timer interrupt - ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex)); + if( (BOOL)xPortInIsrContext() ) { + vTimerGroupSetCounterEnableInISR(usTimerGroupIndex, usTimerIndex, TIMER_PAUSE); + } else { + // Stop timer and then reload timer counter value + ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex)); + ESP_ERROR_CHECK(timer_set_counter_value(usTimerGroupIndex, usTimerIndex, 0ULL)); + // Disable timer interrupt + ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex)); + } } -void vMBMasterPortTimerClose() +void vMBMasterPortTimerClose(void) { ESP_ERROR_CHECK(timer_pause(usTimerGroupIndex, usTimerIndex)); ESP_ERROR_CHECK(timer_disable_intr(usTimerGroupIndex, usTimerIndex)); + ESP_ERROR_CHECK(esp_intr_free(xTimerIntHandle)); } diff --git a/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c b/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c index 1cd9bdcd204..dcb04bb0fbf 100644 --- a/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c +++ b/components/freemodbus/serial_master/modbus_controller/mbc_serial_master.c @@ -23,10 +23,10 @@ #include "freertos/task.h" // for task api access #include "freertos/event_groups.h" // for event groups #include "freertos/queue.h" // for queue api access -#include "mb_m.h" // for modbus stack master types definition +#include "sdkconfig.h" // for KConfig values #include "port.h" // for port callback functions #include "mbutils.h" // for mbutils functions definition for stack callback -#include "sdkconfig.h" // for KConfig values +#include "mb_m.h" // for modbus stack master types definition #include "esp_modbus_common.h" // for common types #include "esp_modbus_master.h" // for public master types #include "mbc_master.h" // for private master types @@ -36,10 +36,8 @@ extern BOOL xMBMasterPortSerialTxPoll(void); /*-----------------------Master mode use these variables----------------------*/ +#define MB_RESPONSE_TICS pdMS_TO_TICKS(CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND) -// The response time is average processing time + data transmission (higher on lower speeds) -// ~resp_time_ms = min_pcocessing_time_ms + ((2 packets * (header_size + packet_bytes)) * 11 bits in byte * 1000 ms_in_sec) / transmit_speed)) -#define MB_RESPONSE_TIMEOUT(size) pdMS_TO_TICKS(30 + (2 * ((size << 1) + 8) * 11 * 1000 / mb_speed)) static mb_master_interface_t* mbm_interface_ptr = NULL; //&default_interface_inst; @@ -103,7 +101,7 @@ static esp_err_t mbc_serial_master_start(void) const mb_communication_info_t* comm_info = (mb_communication_info_t*)&mbm_opts->mbm_comm; // Initialize Modbus stack using mbcontroller parameters - status = eMBMasterInit((eMBMode)comm_info->mode, (UCHAR)comm_info->port, + status = eMBMasterSerialInit((eMBMode)comm_info->mode, (UCHAR)comm_info->port, (ULONG)comm_info->baudrate, MB_PORT_PARITY_GET(comm_info->parity)); MB_MASTER_CHECK((status == MB_ENOERR), ESP_ERR_INVALID_STATE, @@ -141,6 +139,8 @@ static esp_err_t mbc_serial_master_destroy(void) MB_MASTER_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack close failure returned (0x%x).", (uint32_t)mb_error); free(mbm_interface_ptr); // free the memory allocated for options + vMBPortSetMode((UCHAR)MB_PORT_INACTIVE); + mbm_interface_ptr = NULL; return ESP_OK; } @@ -188,11 +188,6 @@ static esp_err_t mbc_serial_master_send_request(mb_param_request_t* request, voi uint8_t mb_command = request->command; uint16_t mb_offset = request->reg_start; uint16_t mb_size = request->reg_size; - uint32_t mb_speed = mbm_opts->mbm_comm.baudrate; - - // Timeout value for packet processing - uint32_t timeout = 0; - size_t pack_length = 0; // Set the buffer for callback function processing of received data mbm_opts->mbm_reg_buffer_ptr = (uint8_t*)data_ptr; @@ -202,56 +197,44 @@ static esp_err_t mbc_serial_master_send_request(mb_param_request_t* request, voi switch(mb_command) { case MB_FUNC_READ_COILS: - pack_length = (mb_size >= 8) ? (mb_size >> 3) : 1; - timeout = MB_RESPONSE_TIMEOUT(pack_length); mb_error = eMBMasterReqReadCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset, - (USHORT)mb_size , (LONG)timeout ); + (USHORT)mb_size , (LONG)MB_RESPONSE_TICS ); break; case MB_FUNC_WRITE_SINGLE_COIL: - timeout = MB_RESPONSE_TIMEOUT(1); mb_error = eMBMasterReqWriteCoil((UCHAR)mb_slave_addr, (USHORT)mb_offset, - *(USHORT*)data_ptr, (LONG)timeout ); + *(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS ); break; case MB_FUNC_WRITE_MULTIPLE_COILS: - pack_length = (mb_size >= 8) ? (mb_size >> 3) : 1; - timeout = MB_RESPONSE_TIMEOUT(pack_length); mb_error = eMBMasterReqWriteMultipleCoils((UCHAR)mb_slave_addr, (USHORT)mb_offset, - (USHORT)mb_size, (UCHAR*)data_ptr, (LONG)timeout); + (USHORT)mb_size, (UCHAR*)data_ptr, (LONG)MB_RESPONSE_TICS); break; case MB_FUNC_READ_DISCRETE_INPUTS: - pack_length = (mb_size >= 8) ? (mb_size >> 3) : 1; - timeout = MB_RESPONSE_TIMEOUT(pack_length); mb_error = eMBMasterReqReadDiscreteInputs((UCHAR)mb_slave_addr, (USHORT)mb_offset, - (USHORT)mb_size, (LONG)timeout ); + (USHORT)mb_size, (LONG)MB_RESPONSE_TICS ); break; case MB_FUNC_READ_HOLDING_REGISTER: - timeout = MB_RESPONSE_TIMEOUT(mb_size); mb_error = eMBMasterReqReadHoldingRegister((UCHAR)mb_slave_addr, (USHORT)mb_offset, - (USHORT)mb_size, (LONG)timeout ); + (USHORT)mb_size, (LONG)MB_RESPONSE_TICS ); break; case MB_FUNC_WRITE_REGISTER: - timeout = MB_RESPONSE_TIMEOUT(1); mb_error = eMBMasterReqWriteHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset, - *(USHORT*)data_ptr, (LONG)timeout ); + *(USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS ); break; case MB_FUNC_WRITE_MULTIPLE_REGISTERS: - timeout = MB_RESPONSE_TIMEOUT(mb_size); mb_error = eMBMasterReqWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset, (USHORT)mb_size, - (USHORT*)data_ptr, (LONG)timeout ); + (USHORT*)data_ptr, (LONG)MB_RESPONSE_TICS ); break; case MB_FUNC_READWRITE_MULTIPLE_REGISTERS: - timeout = MB_RESPONSE_TIMEOUT(mb_size << 1); mb_error = eMBMasterReqReadWriteMultipleHoldingRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset, (USHORT)mb_size, (USHORT*)data_ptr, (USHORT)mb_offset, (USHORT)mb_size, - (LONG)timeout ); + (LONG)MB_RESPONSE_TICS ); break; case MB_FUNC_READ_INPUT_REGISTER: - timeout = MB_RESPONSE_TIMEOUT(mb_size); mb_error = eMBMasterReqReadInputRegister( (UCHAR)mb_slave_addr, (USHORT)mb_offset, - (USHORT)mb_size, (LONG) timeout ); + (USHORT)mb_size, (LONG) MB_RESPONSE_TICS ); break; default: ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect function in request (%u) ", @@ -268,16 +251,16 @@ static esp_err_t mbc_serial_master_send_request(mb_param_request_t* request, voi break; case MB_MRE_NO_REG: - error = ESP_ERR_NOT_SUPPORTED; + error = ESP_ERR_NOT_SUPPORTED; // Invalid register request break; case MB_MRE_TIMEDOUT: - error = ESP_ERR_TIMEOUT; + error = ESP_ERR_TIMEOUT; // Slave did not send response break; case MB_MRE_EXE_FUN: case MB_MRE_REV_DATA: - error = ESP_ERR_INVALID_RESPONSE; + error = ESP_ERR_INVALID_RESPONSE; // Invalid response from slave break; case MB_MRE_MASTER_BUSY: @@ -351,41 +334,6 @@ static uint8_t mbc_serial_master_get_command(mb_param_type_t param_type, mb_para return command; } -// Helper function to set parameter buffer according to its type -static esp_err_t mbc_serial_master_set_param_data(void* dest, void* src, mb_descr_type_t param_type, size_t param_size) -{ - esp_err_t err = ESP_OK; - MB_MASTER_CHECK((dest != NULL), - ESP_ERR_INVALID_ARG, "incorrect parameter pointer."); - MB_MASTER_CHECK((src != NULL), - ESP_ERR_INVALID_ARG, "incorrect parameter pointer."); - // Transfer parameter data into value of characteristic - switch(param_type) - { - case PARAM_TYPE_U8: - *((uint8_t*)dest) = *((uint8_t*)src); - break; - case PARAM_TYPE_U16: - *((uint16_t*)dest) = *((uint16_t*)src); - break; - case PARAM_TYPE_U32: - *((uint32_t*)dest) = *((uint32_t*)src); - break; - case PARAM_TYPE_FLOAT: - *((float*)dest) = *(float*)src; - break; - case PARAM_TYPE_ASCII: - memcpy((void*)dest, (void*)src, (size_t)param_size); - break; - default: - ESP_LOGE(MB_MASTER_TAG, "%s: Incorrect param type (%u).", - __FUNCTION__, (uint16_t)param_type); - err = ESP_ERR_NOT_SUPPORTED; - break; - } - return err; -} - // Helper to search parameter by name in the parameter description table // and fills Modbus request fields accordingly static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode, @@ -414,7 +362,7 @@ static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode, continue; // The length of strings is different then check next record in the table } // Compare the name of parameter with parameter key from table - uint8_t comp_result = memcmp((const char*)name, (const char*)reg_ptr->param_key, (size_t)param_key_len); + int comp_result = memcmp((const void*)name, (const void*)reg_ptr->param_key, (size_t)param_key_len); if (comp_result == 0) { // The correct line is found in the table and reg_ptr points to the found parameter description request->slave_addr = reg_ptr->mb_slave_addr; @@ -436,7 +384,7 @@ static esp_err_t mbc_serial_master_set_request(char* name, mb_param_mode_t mode, // Get parameter data for corresponding characteristic static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name, - uint8_t* value, uint8_t *type) + uint8_t* value_ptr, uint8_t *type) { MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor."); @@ -445,19 +393,12 @@ static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name, esp_err_t error = ESP_ERR_INVALID_RESPONSE; mb_param_request_t request ; mb_parameter_descriptor_t reg_info = { 0 }; - uint8_t param_buffer[PARAM_MAX_SIZE] = { 0 }; error = mbc_serial_master_set_request(name, MB_PARAM_READ, &request, ®_info); if ((error == ESP_OK) && (cid == reg_info.cid)) { - error = mbc_serial_master_send_request(&request, ¶m_buffer[0]); + // Send request to read characteristic data + error = mbc_serial_master_send_request(&request, value_ptr); if (error == ESP_OK) { - // If data pointer is NULL then we don't need to set value - // (it is still in the cache of cid) - if (value != NULL) { - error = mbc_serial_master_set_param_data((void*)value, (void*)¶m_buffer[0], - reg_info.param_type, reg_info.param_size); - MB_MASTER_CHECK((error == ESP_OK), ESP_ERR_INVALID_STATE, "fail to set parameter data."); - } ESP_LOGD(MB_MASTER_TAG, "%s: Good response for get cid(%u) = %s", __FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error)); } else { @@ -475,28 +416,22 @@ static esp_err_t mbc_serial_master_get_parameter(uint16_t cid, char* name, // Set parameter value for characteristic selected by name and cid static esp_err_t mbc_serial_master_set_parameter(uint16_t cid, char* name, - uint8_t* value, uint8_t *type) + uint8_t* value_ptr, uint8_t *type) { MB_MASTER_CHECK((name != NULL), ESP_ERR_INVALID_ARG, "mb incorrect descriptor."); - MB_MASTER_CHECK((value != NULL), + MB_MASTER_CHECK((value_ptr != NULL), ESP_ERR_INVALID_ARG, "value pointer is incorrect."); MB_MASTER_CHECK((type != NULL), ESP_ERR_INVALID_ARG, "type pointer is incorrect."); esp_err_t error = ESP_ERR_INVALID_RESPONSE; mb_param_request_t request ; mb_parameter_descriptor_t reg_info = { 0 }; - uint8_t param_buffer[PARAM_MAX_SIZE] = { 0 }; error = mbc_serial_master_set_request(name, MB_PARAM_WRITE, &request, ®_info); if ((error == ESP_OK) && (cid == reg_info.cid)) { - // Transfer value of characteristic into parameter buffer - error = mbc_serial_master_set_param_data((void*)¶m_buffer[0], (void*)value, - reg_info.param_type, reg_info.param_size); - MB_MASTER_CHECK((error == ESP_OK), - ESP_ERR_INVALID_STATE, "failure to set parameter data."); // Send request to write characteristic data - error = mbc_serial_master_send_request(&request, ¶m_buffer[0]); + error = mbc_serial_master_send_request(&request, value_ptr); if (error == ESP_OK) { ESP_LOGD(MB_MASTER_TAG, "%s: Good response for set cid(%u) = %s", __FUNCTION__, (int)reg_info.cid, (char*)esp_err_to_name(error)); @@ -723,6 +658,7 @@ esp_err_t mbc_serial_master_create(mb_port_type_t port_type, void** handler) mb_master_options_t* mbm_opts = &mbm_interface_ptr->opts; mbm_opts->port_type = MB_PORT_SERIAL_MASTER; + vMBPortSetMode((UCHAR)MB_PORT_SERIAL_MASTER); mbm_opts->mbm_comm.mode = MB_MODE_RTU; mbm_opts->mbm_comm.port = MB_UART_PORT; mbm_opts->mbm_comm.baudrate = MB_DEVICE_SPEED; diff --git a/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c b/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c index c1687bc72e3..3c6f03aa06b 100644 --- a/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c +++ b/components/freemodbus/serial_slave/modbus_controller/mbc_serial_slave.c @@ -28,7 +28,7 @@ #include "port_serial_slave.h" // Shared pointer to interface structure -static mb_slave_interface_t* mbs_interface_ptr = NULL; // &default_interface_inst; +static mb_slave_interface_t* mbs_interface_ptr = NULL; // Modbus task function static void modbus_slave_task(void *pvParameters) @@ -48,7 +48,11 @@ static void modbus_slave_task(void *pvParameters) // Check if stack started then poll for data if (status & MB_EVENT_STACK_STARTED) { (void)eMBPoll(); // allow stack to process data - (void)xMBPortSerialTxPoll(); // Send response buffer if ready + // Send response buffer + BOOL xSentState = xMBPortSerialTxPoll(); + if (xSentState) { + (void)xMBPortEventPost( EV_FRAME_SENT ); + } } } } @@ -129,7 +133,8 @@ static esp_err_t mbc_serial_slave_destroy(void) MB_SLAVE_CHECK((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, "mb stack close failure returned (0x%x).", (uint32_t)mb_error); free(mbs_interface_ptr); - + vMBPortSetMode((UCHAR)MB_PORT_INACTIVE); + mbs_interface_ptr = NULL; return ESP_OK; } @@ -152,7 +157,7 @@ esp_err_t mbc_serial_slave_set_descriptor(const mb_register_area_descriptor_t de } // The helper function to get time stamp in microseconds -static uint64_t get_time_stamp() +static uint64_t get_time_stamp(void) { uint64_t time_stamp = esp_timer_get_time(); return time_stamp; @@ -457,6 +462,7 @@ esp_err_t mbc_serial_slave_create(mb_port_type_t port_type, void** handler) mbs_interface_ptr = malloc(sizeof(mb_slave_interface_t)); } MB_SLAVE_ASSERT(mbs_interface_ptr != NULL); + vMBPortSetMode((UCHAR)port_type); mb_slave_options_t* mbs_opts = &mbs_interface_ptr->opts; mbs_opts->port_type = MB_PORT_SERIAL_SLAVE; // set interface port type diff --git a/examples/protocols/modbus_master/main/sense_modbus.c b/examples/protocols/modbus_master/main/sense_modbus.c index f8b77539f78..67ac00bd96c 100644 --- a/examples/protocols/modbus_master/main/sense_modbus.c +++ b/examples/protocols/modbus_master/main/sense_modbus.c @@ -59,16 +59,16 @@ static void* sense_modbus_get_param_data(const mb_parameter_descriptor_t* param_ switch(param_descriptor->mb_param_type) { case MB_PARAM_HOLDING: - instance_ptr = (void*)(&holding_reg_params + param_descriptor->param_offset - 1); + instance_ptr = ((void*)&holding_reg_params + param_descriptor->param_offset - 1); break; case MB_PARAM_INPUT: - instance_ptr = (void*)(&input_reg_params + param_descriptor->param_offset - 1); + instance_ptr = ((void*)&input_reg_params + param_descriptor->param_offset - 1); break; case MB_PARAM_COIL: - instance_ptr = (void*)(&coil_reg_params + param_descriptor->param_offset - 1); + instance_ptr = ((void*)&coil_reg_params + param_descriptor->param_offset - 1); break; case MB_PARAM_DISCRETE: - instance_ptr = (void*)(&discrete_reg_params + param_descriptor->param_offset - 1); + instance_ptr = ((void*)&discrete_reg_params + param_descriptor->param_offset - 1); break; default: instance_ptr = NULL; -- GitLab From ce2f54afaf1bee67e01c893e004b4091acdc598b Mon Sep 17 00:00:00 2001 From: aleks Date: Fri, 21 Aug 2020 12:58:46 +0200 Subject: [PATCH 2/5] driver: uart add esp_err_t uart_wait_tx_idle_polling() --- components/driver/include/driver/uart.h | 12 ++++++++++++ components/driver/uart.c | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index ff202cf6d58..5b7b470b675 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -854,6 +854,18 @@ esp_err_t uart_set_wakeup_threshold(uart_port_t uart_num, int wakeup_threshold); */ esp_err_t uart_get_wakeup_threshold(uart_port_t uart_num, int* out_wakeup_threshold); +/** + * @brief Wait until UART tx memory empty and the last char send ok (polling mode). + * + * @param uart_num UART number + * + * * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_FAIL Driver not installed + */ +esp_err_t uart_wait_tx_idle_polling(uart_port_t uart_num); + /** * @brief Configure behavior of UART RX timeout interrupt. * diff --git a/components/driver/uart.c b/components/driver/uart.c index fc592146276..b6395196554 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -1732,3 +1732,10 @@ void uart_set_always_rx_timeout(uart_port_t uart_num, bool always_rx_timeout) p_uart_obj[uart_num]->rx_always_timeout_flg = false; } } + +esp_err_t uart_wait_tx_idle_polling(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + while(!uart_is_tx_idle(uart_num)); + return ESP_OK; +} -- GitLab From 3e682862709f4fbbcc55517d73faf3ec555abb51 Mon Sep 17 00:00:00 2001 From: aleks Date: Fri, 21 Aug 2020 13:00:02 +0200 Subject: [PATCH 3/5] freemodbus: fix uart serial handling issues --- components/freemodbus/port/portserial.c | 6 ++++-- components/freemodbus/port/portserial_m.c | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/components/freemodbus/port/portserial.c b/components/freemodbus/port/portserial.c index 6667427e2ca..29553d55c27 100644 --- a/components/freemodbus/port/portserial.c +++ b/components/freemodbus/port/portserial.c @@ -91,7 +91,7 @@ static USHORT usMBPortSerialRxPoll(size_t xEventSize) if (bRxStateEnabled) { // Get received packet into Rx buffer - while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) { + while(xReadStatus && (usCnt++ <= xEventSize)) { // Call the Modbus stack callback function and let it fill the buffers. xReadStatus = pxMBFrameCBByteReceived(); // callback to execute receive FSM } @@ -119,7 +119,7 @@ BOOL xMBPortSerialTxPoll(void) } ESP_LOGD(TAG, "MB_TX_buffer send: (%d) bytes\n", (uint16_t)usCount); // Waits while UART sending the packet - esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS); + esp_err_t xTxStatus = uart_wait_tx_idle_polling(ucUartNumber); vMBPortSerialEnable(TRUE, FALSE); MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure."); return TRUE; @@ -141,6 +141,8 @@ static void vUartTask(void *pvParameters) // This flag set in the event means that no more // data received during configured timeout and UART TOUT feature is triggered if (xEvent.timeout_flag) { + // Get buffered data length + ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size)); // Read received data and send it to modbus stack usResult = usMBPortSerialRxPoll(xEvent.size); ESP_LOGD(TAG,"Timeout occured, processed: %d bytes", usResult); diff --git a/components/freemodbus/port/portserial_m.c b/components/freemodbus/port/portserial_m.c index 92e78b04bfe..92ba5335b67 100644 --- a/components/freemodbus/port/portserial_m.c +++ b/components/freemodbus/port/portserial_m.c @@ -33,14 +33,6 @@ * File: $Id: portserial.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions $ */ -#include "port.h" - -/* ----------------------- Modbus includes ----------------------------------*/ -#include "mb_m.h" -#include "mbport.h" -#include "mbrtu.h" -#include "mbconfig.h" - #include #include "driver/uart.h" #include "soc/dport_access.h" @@ -49,6 +41,12 @@ #include "freertos/queue.h" #include "esp_log.h" #include "sdkconfig.h" +/* ----------------------- Modbus includes ----------------------------------*/ +#include "port.h" +#include "mbport.h" +#include "mb_m.h" +#include "mbrtu.h" +#include "mbconfig.h" #include "port_serial_master.h" /* ----------------------- Defines ------------------------------------------*/ #define MB_SERIAL_RX_SEMA_TOUT_MS (1000) @@ -125,7 +123,7 @@ static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize) xReadStatus = xMBMasterPortRxSemaTake(MB_SERIAL_RX_SEMA_TOUT); if (xReadStatus) { - while(xReadStatus && (usCnt++ <= MB_SERIAL_BUF_SIZE)) { + while(xReadStatus && (usCnt++ <= xEventSize)) { // Call the Modbus stack callback function and let it fill the stack buffers. xReadStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM } @@ -154,8 +152,8 @@ BOOL xMBMasterPortSerialTxPoll(void) } ESP_LOGD(TAG, "MB_TX_buffer sent: (%d) bytes.", (uint16_t)(usCount - 1)); // Waits while UART sending the packet - esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS); - vMBMasterPortSerialEnable( TRUE, FALSE ); + esp_err_t xTxStatus = uart_wait_tx_idle_polling(ucUartNumber); + vMBMasterPortSerialEnable(TRUE, FALSE); MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure."); return TRUE; } @@ -189,6 +187,8 @@ static void vUartTask(void* pvParameters) uart_flush_input(ucUartNumber); break; } + // Get buffered data length + ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size)); // Read received data and send it to modbus stack usResult = usMBMasterPortSerialRxPoll(xEvent.size); ESP_LOGD(TAG,"Timeout occurred, processed: %d bytes", usResult); -- GitLab From c236b02e6b8b7fa528b9cc107ca870ddc28e286d Mon Sep 17 00:00:00 2001 From: aleks Date: Fri, 25 Sep 2020 16:22:08 +0200 Subject: [PATCH 4/5] freemodbus: change default placement of irqs and add troubleshooting section place irq handlers and related functions into flash by default add the troubleshooting section in the readme files of examples remove constraints related to buffer size --- components/freemodbus/Kconfig | 2 +- components/freemodbus/README.rst | 6 ++--- examples/protocols/modbus_master/README.md | 23 +++++++++++++++++++ .../modbus_master/sdkconfig.defaults | 5 ++-- examples/protocols/modbus_slave/README.md | 10 +++++++- .../protocols/modbus_slave/sdkconfig.defaults | 4 ++++ 6 files changed, 42 insertions(+), 8 deletions(-) diff --git a/components/freemodbus/Kconfig b/components/freemodbus/Kconfig index c257cc66dba..c176ee16af2 100644 --- a/components/freemodbus/Kconfig +++ b/components/freemodbus/Kconfig @@ -151,7 +151,7 @@ menu "Modbus configuration" config FMB_TIMER_ISR_IN_IRAM bool "Place timer interrupt handler into IRAM" - default y + default n select UART_ISR_IN_IRAM help This option places Modbus timer IRQ handler into IRAM. diff --git a/components/freemodbus/README.rst b/components/freemodbus/README.rst index 0c9d8f41b70..ab88ae55b8e 100644 --- a/components/freemodbus/README.rst +++ b/components/freemodbus/README.rst @@ -1,6 +1,4 @@ # Modbus assumptions, dependencies and constraints -1. Current implementation of Modbus has limitation: maximum frame size should be less than 120 bytes (default UART receive FIFO full threshold value). -This limitation is removed in ESP-IDF v4.2. - -2. The only one instance of Modbus port can be initialized at the same time. +1. The only one Modbus master and slave port can be initialized at the same time. +2. The maximum number of registers to read/write in one transaction accordingly is 125/120 registers (standard Modbus limitation). \ No newline at end of file diff --git a/examples/protocols/modbus_master/README.md b/examples/protocols/modbus_master/README.md index c700f4b29bb..aff9ad574cb 100644 --- a/examples/protocols/modbus_master/README.md +++ b/examples/protocols/modbus_master/README.md @@ -131,4 +131,27 @@ I (73143) SENSE_MAIN: cid: 5, (RelayP1) = ON ``` The example refreshes the characteristics from devices every 10 seconds, verifies if they exceeded limits and sets alarm accordingly. The output line describes Timestamp, Cid of characteristic, Characteristic name(Units), Characteristic value. +## Troubleshooting +If the examples does not work as expected and slave and master boards are not able to communicate correctly it is possible to find the reason for errors. +The most important errors are described in master example output and formatted as below: + +``` +E (209733) SENSE_MAIN: Update failed for cid: 3, Humidity_2(%rH) = 0, ESP_ERR_TIMEOUT +E (210143) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT). +``` + +ESP_ERR_TIMEOUT (0x107) - Modbus slave device does not respond during configured timeout. Check the connection and ability for communication using uart_echo_rs485 example or increase +Kconfig value CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND (CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS). + +ESP_ERR_NOT_SUPPORTED (0x106), ESP_ERR_INVALID_RESPONSE (0x108) - Modbus slave device does not support requested command or register and sent exeption response. + +ESP_ERR_INVALID_STATE (0x103) - Modbus stack is not configured correctly or can't work correctly due to critical failure. + +The CONFIG_FMB_SERIAL_TASK_PRIO value shall be configured to be higher than the highest priority of application tasks. If the application includes high priority WiFi or NVS read/write tasks it is recommended to disable CONFIG_FMB_TIMER_PORT_ENABLED. + +The enabled CONFIG_FMB_TIMER_ISR_IN_IRAM option allows to avoid delays related to the processing of non-IRAM-safe interrupts if your application intensively performs Flash read/write operations and resolve some Modbus errors. + +Note: Refer to file below for more information about current implementation of Modbus: + +* `components/freemodbus/README.rst` diff --git a/examples/protocols/modbus_master/sdkconfig.defaults b/examples/protocols/modbus_master/sdkconfig.defaults index 98e7ac19fe6..39f585a261a 100644 --- a/examples/protocols/modbus_master/sdkconfig.defaults +++ b/examples/protocols/modbus_master/sdkconfig.defaults @@ -1,11 +1,12 @@ # # Modbus configuration # -CONFIG_FMB_TIMER_PORT_ENABLED=y +CONFIG_FMB_TIMER_PORT_ENABLED=n +CONFIG_FMB_TIMER_ISR_IN_IRAM=n CONFIG_FMB_TIMER_GROUP=0 CONFIG_FMB_TIMER_INDEX=0 CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 -CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150 +CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=400 CONFIG_MB_UART_RXD=22 CONFIG_MB_UART_TXD=23 CONFIG_MB_UART_RTS=18 diff --git a/examples/protocols/modbus_slave/README.md b/examples/protocols/modbus_slave/README.md index 8efdb58be05..a3ca2cb1345 100644 --- a/examples/protocols/modbus_slave/README.md +++ b/examples/protocols/modbus_slave/README.md @@ -69,6 +69,14 @@ HOLDING READ/WRITE: time_stamp(us):12104081, mb_addr:1, type:2, st_address:0x3ff ``` The output lines describe type of operation, its timestamp, modbus address, access type, storage address in parameter structure and number of registers accordingly. -Note: Refer to file below for more information about current implementation of Modbus: +## Troubleshooting + +The Kconfig values below allow to solve some known communication issues: + +The CONFIG_FMB_SERIAL_TASK_PRIO value shall be configured to be higher than the highest priority of application tasks. If the application includes high priority WiFi or NVS read/write tasks it is recommended to disable CONFIG_FMB_TIMER_PORT_ENABLED. + +The enabled CONFIG_FMB_TIMER_ISR_IN_IRAM option allows to avoid delays related to the processing of non-IRAM-safe interrupts if your application intensively performs Flash read/write operations and resolve some Modbus errors. + +Note: Refer to file below for more information about the current implementation of Modbus: * `components/freemodbus/README.rst` diff --git a/examples/protocols/modbus_slave/sdkconfig.defaults b/examples/protocols/modbus_slave/sdkconfig.defaults index e79d7fa6244..484c68af6bd 100644 --- a/examples/protocols/modbus_slave/sdkconfig.defaults +++ b/examples/protocols/modbus_slave/sdkconfig.defaults @@ -1,6 +1,10 @@ # # Modbus configuration # +CONFIG_FMB_TIMER_PORT_ENABLED=n +CONFIG_FMB_TIMER_ISR_IN_IRAM=n +CONFIG_FMB_TIMER_GROUP=0 +CONFIG_FMB_TIMER_INDEX=0 CONFIG_MB_UART_RXD=22 CONFIG_MB_UART_TXD=23 CONFIG_MB_UART_RTS=18 -- GitLab From cdc7ff2913dce7923e4dc1fb64b9bedf98b242f4 Mon Sep 17 00:00:00 2001 From: aleks Date: Tue, 29 Sep 2020 12:04:10 +0200 Subject: [PATCH 5/5] examples: update test, examples examples: update link to freemodbus readme file examples: move baud, port, address settigs into kconfig, change defaults driver: apply test tag to rs485 test --- components/freemodbus/Kconfig | 4 +- .../freemodbus/modbus/ascii/mbascii_m.c | 3 +- .../freemodbus/modbus/functions/mbfunccoils.c | 8 +- .../modbus/functions/mbfunccoils_m.c | 14 +- .../modbus/functions/mbfuncholding.c | 16 +- .../modbus/functions/mbfuncholding_m.c | 16 +- .../freemodbus/modbus/functions/mbfuncinput.c | 4 +- .../modbus/functions/mbfuncinput_m.c | 4 +- .../freemodbus/modbus/functions/mbfuncother.c | 4 +- examples/protocols/modbus/serial/README.md | 84 ---------- .../modbus/serial/mb_master/README.md | 154 ------------------ examples/protocols/modbus_master/README.md | 2 +- .../modbus_master/main/Kconfig.projbuild | 29 ++++ .../modbus_master/main/sense_modbus.c | 5 +- .../modbus_master/sdkconfig.defaults | 2 + examples/protocols/modbus_slave/README.md | 4 +- .../modbus_slave/main/Kconfig.projbuild | 38 +++++ .../protocols/modbus_slave/main/freemodbus.c | 6 +- .../protocols/modbus_slave/sdkconfig.defaults | 3 + 19 files changed, 120 insertions(+), 280 deletions(-) delete mode 100644 examples/protocols/modbus/serial/README.md delete mode 100644 examples/protocols/modbus/serial/mb_master/README.md diff --git a/components/freemodbus/Kconfig b/components/freemodbus/Kconfig index c176ee16af2..98f28b8c6dc 100644 --- a/components/freemodbus/Kconfig +++ b/components/freemodbus/Kconfig @@ -14,8 +14,8 @@ menu "Modbus configuration" config FMB_MASTER_TIMEOUT_MS_RESPOND int "Slave respond timeout (Milliseconds)" - default 150 - range 50 400 + default 400 + range 50 2000 help If master sends a frame which is not broadcast, it has to wait sometime for slave response. if slave is not respond in this time, the master will process timeout error. diff --git a/components/freemodbus/modbus/ascii/mbascii_m.c b/components/freemodbus/modbus/ascii/mbascii_m.c index 9b97191defe..4eb96494b42 100644 --- a/components/freemodbus/modbus/ascii/mbascii_m.c +++ b/components/freemodbus/modbus/ascii/mbascii_m.c @@ -443,7 +443,7 @@ xMBMasterASCIITransmitFSM( void ) return xNeedPoll; } -BOOL +BOOL MB_PORT_ISR_ATTR xMBMasterASCIITimerT1SExpired( void ) { BOOL xNeedPoll = FALSE; @@ -453,7 +453,6 @@ xMBMasterASCIITimerT1SExpired( void ) /* Timer t35 expired. Startup phase is finished. */ case STATE_M_RX_INIT: xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY); - ESP_EARLY_LOGI("xMBMasterASCIITimerT1SExpired", "RX_INIT_EXPIRED"); break; /* Start of message is not received during respond timeout. diff --git a/components/freemodbus/modbus/functions/mbfunccoils.c b/components/freemodbus/modbus/functions/mbfunccoils.c index eac8ef8871f..40996602bfa 100644 --- a/components/freemodbus/modbus/functions/mbfunccoils.c +++ b/components/freemodbus/modbus/functions/mbfunccoils.c @@ -196,7 +196,7 @@ eMBFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen ) return eStatus; } -#endif +#endif // #if MB_FUNC_WRITE_COIL_ENABLED #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0 eMBException @@ -266,8 +266,8 @@ eMBFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen ) return eStatus; } -#endif +#endif // #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED -#endif +#endif // #if MB_FUNC_READ_COILS_ENABLED -#endif +#endif // #if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED diff --git a/components/freemodbus/modbus/functions/mbfunccoils_m.c b/components/freemodbus/modbus/functions/mbfunccoils_m.c index ba3e28d7371..a797981928e 100644 --- a/components/freemodbus/modbus/functions/mbfunccoils_m.c +++ b/components/freemodbus/modbus/functions/mbfunccoils_m.c @@ -175,9 +175,10 @@ eMBMasterFuncReadCoils( UCHAR * pucFrame, USHORT * usLen ) } return eStatus; } -#endif -#if MB_FUNC_WRITE_COIL_ENABLED > 0 +#endif // #if MB_FUNC_READ_COILS_ENABLED + +#if MB_FUNC_WRITE_COIL_ENABLED /** * This function will request write one coil. @@ -267,9 +268,9 @@ eMBMasterFuncWriteCoil( UCHAR * pucFrame, USHORT * usLen ) return eStatus; } -#endif // #if MB_FUNC_WRITE_COIL_ENABLED > 0 +#endif // #if MB_FUNC_WRITE_COIL_ENABLED -#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0 +#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED /** * This function will request write multiple coils. @@ -387,5 +388,6 @@ eMBMasterFuncWriteMultipleCoils( UCHAR * pucFrame, USHORT * usLen ) return eStatus; } -#endif // #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0 -#endif // #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0 +#endif // #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED + +#endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED diff --git a/components/freemodbus/modbus/functions/mbfuncholding.c b/components/freemodbus/modbus/functions/mbfuncholding.c index 534c7b153a9..00115f39cfd 100644 --- a/components/freemodbus/modbus/functions/mbfuncholding.c +++ b/components/freemodbus/modbus/functions/mbfuncholding.c @@ -104,9 +104,9 @@ eMBFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) } return eStatus; } -#endif +#endif // #if MB_FUNC_WRITE_HOLDING_ENABLED -#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0 +#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED eMBException eMBFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) { @@ -163,9 +163,9 @@ eMBFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) } return eStatus; } -#endif +#endif // #if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED -#if MB_FUNC_READ_HOLDING_ENABLED > 0 +#if MB_FUNC_READ_HOLDING_ENABLED eMBException eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) @@ -228,9 +228,9 @@ eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) return eStatus; } -#endif +#endif // #if MB_FUNC_READ_HOLDING_ENABLED -#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0 +#if MB_FUNC_READWRITE_HOLDING_ENABLED eMBException eMBFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) @@ -306,6 +306,6 @@ eMBFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) return eStatus; } -#endif +#endif // #if MB_FUNC_READWRITE_HOLDING_ENABLED -#endif +#endif // #if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED diff --git a/components/freemodbus/modbus/functions/mbfuncholding_m.c b/components/freemodbus/modbus/functions/mbfuncholding_m.c index 5ab07341e1d..ed74889733b 100644 --- a/components/freemodbus/modbus/functions/mbfuncholding_m.c +++ b/components/freemodbus/modbus/functions/mbfuncholding_m.c @@ -36,7 +36,6 @@ #include "port.h" /* ----------------------- Modbus includes ----------------------------------*/ -//#include "mb.h" #include "mb_m.h" #include "mbframe.h" #include "mbproto.h" @@ -151,9 +150,10 @@ eMBMasterFuncWriteHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) } return eStatus; } -#endif -#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0 +#endif // #if MB_FUNC_WRITE_HOLDING_ENABLED + +#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED /** * This function will request write multiple holding register. @@ -247,9 +247,10 @@ eMBMasterFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) } return eStatus; } -#endif -#if MB_FUNC_READ_HOLDING_ENABLED > 0 +#endif // #if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED + +#if MB_FUNC_READ_HOLDING_ENABLED /** * This function will request read holding register. @@ -338,7 +339,7 @@ eMBMasterFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen ) #endif -#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0 +#if MB_FUNC_READWRITE_HOLDING_ENABLED /** * This function will request read and write holding register. @@ -450,6 +451,7 @@ eMBMasterFuncReadWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen return eStatus; } -#endif +#endif // #if MB_FUNC_READWRITE_HOLDING_ENABLED + #endif // #if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0 diff --git a/components/freemodbus/modbus/functions/mbfuncinput.c b/components/freemodbus/modbus/functions/mbfuncinput.c index a54deaf4f5d..f1a03b2752e 100644 --- a/components/freemodbus/modbus/functions/mbfuncinput.c +++ b/components/freemodbus/modbus/functions/mbfuncinput.c @@ -121,6 +121,6 @@ eMBFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen ) return eStatus; } -#endif +#endif // #if MB_FUNC_READ_INPUT_ENABLED -#endif +#endif // #if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED diff --git a/components/freemodbus/modbus/functions/mbfuncinput_m.c b/components/freemodbus/modbus/functions/mbfuncinput_m.c index 9a6b5677b67..1681762844d 100644 --- a/components/freemodbus/modbus/functions/mbfuncinput_m.c +++ b/components/freemodbus/modbus/functions/mbfuncinput_m.c @@ -56,6 +56,7 @@ eMBException prveMBError2Exception( eMBErrorCode eErrorCode ); /* ----------------------- Start implementation -----------------------------*/ #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED + #if MB_FUNC_READ_INPUT_ENABLED /** @@ -143,5 +144,6 @@ eMBMasterFuncReadInputRegister( UCHAR * pucFrame, USHORT * usLen ) return eStatus; } -#endif +#endif // #if MB_FUNC_READ_INPUT_ENABLED + #endif // #if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED diff --git a/components/freemodbus/modbus/functions/mbfuncother.c b/components/freemodbus/modbus/functions/mbfuncother.c index 2efec46dd1a..e77fdd6175b 100644 --- a/components/freemodbus/modbus/functions/mbfuncother.c +++ b/components/freemodbus/modbus/functions/mbfuncother.c @@ -87,6 +87,6 @@ eMBFuncReportSlaveID( UCHAR * pucFrame, USHORT * usLen ) return MB_EX_NONE; } -#endif +#endif // #if MB_FUNC_OTHER_REP_SLAVEID_ENABLED -#endif +#endif // #if MB_SLAVE_RTU_ENABLED || MB_SLAVE_ASCII_ENABLED diff --git a/examples/protocols/modbus/serial/README.md b/examples/protocols/modbus/serial/README.md deleted file mode 100644 index a1e2b5ef265..00000000000 --- a/examples/protocols/modbus/serial/README.md +++ /dev/null @@ -1,84 +0,0 @@ -# Modbus Master-Slave Example - -## Overview - -These two projects illustrate the communication between Modbus master and slave device in the segment. -Master initializes Modbus interface driver and then reads parameters from slave device in the segment. -After several successful read attempts slave sets the alarm relay (end of test condition). -Once master reads the alarm it stops communication and destroy driver. - -The examples: - -* `examples/protocols/modbus/serial/mb_master` - Modbus serial master ASCII/RTU -* `examples/protocols/modbus/serial/mb_slave` - Modbus serial slave ASCII/RTU - -See README.md for each individual project for more information. - -## How to use example - -### Hardware Required - -This example can be run on any commonly available ESP32 development board. -The master and slave boards should be connected to each other through the RS485 interface line driver. -See the connection schematic in README.md files of each example. - -### Configure the project - -This example test requires communication mode setting for master and slave be the same and slave address set to 1. -Please refer to README.md files of each example project for more information. - -## About common_component in this example - -The folder "mb_example_common" includes definitions of parameter structures for master and slave device (both projects share the same parameters). -However, currently it is for example purpose only and can be modified for particular application. - -## Example Output - -Example of Slave output: - -``` -I (343) SLAVE_TEST: Modbus slave stack initialized. -I (343) SLAVE_TEST: Start modbus test... -I (81463) SLAVE_TEST: HOLDING READ (81150420 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -I (82463) SLAVE_TEST: HOLDING READ (82150720 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -I (83573) SLAVE_TEST: HOLDING READ (83260630 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -I (84603) SLAVE_TEST: HOLDING READ (84290530 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -I (85703) SLAVE_TEST: HOLDING READ (85396692 us), ADDR:1, TYPE:2, INST_ADDR:0x3ffb2868, SIZE:6 -``` - -Example of Modbus Master output: - -``` -I (399) MASTER_TEST: Modbus master stack initialized... -I (499) MASTER_TEST: Start modbus test... -I (549) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.230000 (0x3f9d70a4) read successful. -I (629) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 12.100000 (0x4141999a) read successful. -I (709) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 3.560000 (0x4063d70a) read successful. -I (769) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 23.400000 (0x41bb3333) read successful. -I (829) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 5.890000 (0x40bc7ae1) read successful. -I (889) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 34.500000 (0x420a0000) read successful. -E (949) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x108) (ESP_ERR_INVALID_RESPONSE). -E (949) MASTER_TEST: Characteristic #6 (RelayP1) read fail, err = 264 (ESP_ERR_INVALID_RESPONSE). -E (1029) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x108) (ESP_ERR_INVALID_RESPONSE). -E (1029) MASTER_TEST: Characteristic #7 (RelayP2) read fail, err = 264 (ESP_ERR_INVALID_RESPONSE). -``` - -## Troubleshooting - -If the examples do not work as expected and slave and master boards are not able to communicate correctly it is possible to find the reason for errors. -The most important errors are described in master example output and formatted as below: - -``` -E (1692332) MB_CONTROLLER_MASTER: mbc_master_get_parameter(111): SERIAL master get parameter failure error=(0x107) (ESP_ERR_TIMEOUT). -``` - -ESP_ERR_TIMEOUT (0x107) - Modbus slave device does not respond during configured timeout. Check the connection and ability for communication using uart_echo_rs485 example or increase -Kconfig value CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND (CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS). - -ESP_ERR_NOT_SUPPORTED (0x106), ESP_ERR_INVALID_RESPONSE (0x108) - Modbus slave device does not support requested command or register and sent exeption response. - -ESP_ERR_INVALID_STATE (0x103) - Modbus stack is not configured correctly or can't work correctly due to critical failure. - -Note: Refer to file below for more information about current implementation of Modbus: - -* `components/freemodbus/README.rst` \ No newline at end of file diff --git a/examples/protocols/modbus/serial/mb_master/README.md b/examples/protocols/modbus/serial/mb_master/README.md deleted file mode 100644 index 6132676d582..00000000000 --- a/examples/protocols/modbus/serial/mb_master/README.md +++ /dev/null @@ -1,154 +0,0 @@ -# Modbus Master Example - -This example demonstrates using of FreeModbus stack port implementation for ESP32 as a master device. -This implementation is able to read/write values of slave devices connected into Modbus segment. All parameters to be accessed are defined in data dictionary of the modbus master example source file. -The values represented as characteristics with its name and characteristic CID which are linked into registers of slave devices connected into Modbus segment. -The example implements simple control algorithm and checks parameters from slave device and gets alarm (relay in the slave device) when value of holding_data0 parameter exceeded limit. -The instances for the modbus parameters are common for master and slave examples and located in examples\protocols\modbus\serial\common_components folder. - -Example parameters definition: --------------------------------------------------------------------------------------------------- -| Slave Address | Characteristic ID | Characteristic name | Description | -|---------------------|----------------------|----------------------|----------------------------| -| MB_DEVICE_ADDR1 | CID_INP_DATA_0, | Data_channel_0 | Data channel 1 | -| MB_DEVICE_ADDR1 | CID_HOLD_DATA_0, | Humidity_1 | Humidity 1 | -| MB_DEVICE_ADDR1 | CID_INP_DATA_1 | Temperature_1 | Sensor temperature | -| MB_DEVICE_ADDR1 | CID_HOLD_DATA_1, | Humidity_2 | Humidity 2 | -| MB_DEVICE_ADDR1 | CID_INP_DATA_2 | Temperature_2 | Ambient temperature | -| MB_DEVICE_ADDR1 | CID_HOLD_DATA_2 | Humidity_3 | Humidity 3 | -| MB_DEVICE_ADDR1 | CID_RELAY_P1 | RelayP1 | Alarm Relay outputs on/off | -| MB_DEVICE_ADDR1 | CID_RELAY_P2 | RelayP2 | Alarm Relay outputs on/off | --------------------------------------------------------------------------------------------------- -Note: The Slave Address is the same for all parameters for example test but it can be changed in the ```Example Data (Object) Dictionary``` table of master example to address parameters from other slaves. -The Kconfig ```Modbus slave address``` - CONFIG_MB_SLAVE_ADDR parameter in slave example can be configured to create Modbus multi slave segment. - -Simplified Modbus connection schematic for example test: - ``` - MB_DEVICE_ADDR1 - ------------- ------------- - | | RS485 network | | - | Slave 1 |---<>--+---<>---| Master | - | | | | - ------------- ------------- -``` -Modbus multi slave segment connection schematic: -``` - MB_DEVICE_ADDR1 - ------------- - | | - | Slave 1 |---<>--+ - | | | - ------------- | - MB_DEVICE_ADDR2 | - ------------- | ------------- - | | | | | - | Slave 2 |---<>--+---<>---| Master | - | | | | | - ------------- | ------------- - MB_DEVICE_ADDR3 | - ------------- RS485 network - | | | - | Slave 3 |---<>--+ - | | - ------------- -``` - -## Hardware required : -Option 1: -PC (Modbus Slave app) + USB Serial adapter connected to USB port + RS485 line drivers + ESP32 WROVER-KIT board. - -Option 2: -Several ESP32 WROVER-KIT board flashed with modbus_slave example software to represent slave device with specific slave address (See CONFIG_MB_SLAVE_ADDR). The slave addresses for each board have to be configured as defined in "connection schematic" above. -One ESP32 WROVER-KIT board flashed with modbus_master example. All the boards require connection of RS485 line drivers (see below). - -The MAX485 line driver is used as an example below but other similar chips can be used as well. -RS485 example circuit schematic for connection of master and slave devices into segment: -``` - VCC ---------------+ +--------------- VCC - | | - +-------x-------+ +-------x-------+ - RXD <------| RO | DIFFERENTIAL | RO|-----> RXD - | B|---------------|B | - TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD -ESP32 WROVER KIT 1 | | RS-485 side | | External PC (emulator) with USB to serial or - RTS --+--->| DE | / \ | DE|---+ ESP32 WROVER KIT 2 (slave) - | | A|---------------|A | | - +----| /RE | PAIR | /RE|---+-- RTS - +-------x-------+ +-------x-------+ - | | - --- --- - Modbus Master device Modbus Slave device - -``` - -## How to setup and use an example: - -### Configure the application -Start the command below to setup configuration: -``` -idf.py menuconfig -``` -Configure the UART pins used for modbus communication using and table below. -Define the communication mode parameter for master and slave in Kconfig - CONFIG_MB_COMM_MODE (must be the same for master and slave devices in one segment). -Configure the slave address for each slave in the Modbus segment (the CONFIG_MB_SLAVE_ADDR in Kconfig). - -``` - ------------------------------------------------------------------------------------------------ - | ESP32 Interface | #define | Default ESP32 Pin | External RS485 Pin| - | ----------------------|------------------------------|-------------------|-------------------| - | Transmit Data (TxD) | CONFIG_MB_UART_TXD | GPIO23 | DI | - | Receive Data (RxD) | CONFIG_MB_UART_RXD | GPIO22 | RO | - | Request To Send (RTS) | CONFIG_MB_UART_RTS | GPIO18 | ~RE/DE | - | | | | | - | Ground | n/a | GND | GND | - ------------------------------------------------------------------------------------------------ -``` -The communication parameters of Modbus stack allow to configure it appropriately but usually it is enough to use default settings. -See the help string of parameters for more information. - -### Setup external Modbus slave devices or emulator -Option 1: -Configure the external Modbus master software according to port configuration parameters used in the example. The Modbus Slave application can be used with this example to emulate slave devices with its parameters. Use official documentation for software to setup emulation of slave devices. - -Option 2: -Other option is to have the modbus_slave example application flashed into ESP32 WROVER KIT board and connect boards together as showed on the Modbus connection schematic above. See the Modbus slave API documentation to configure communication parameters and slave addresses as defined in "Example parameters definition" table above. - -### Build and flash software of master device -Build the project and flash it to the board, then run monitor tool to view serial output: -``` -idf.py -p PORT flash monitor -``` - -(To exit the serial monitor, type ``Ctrl-]``.) - -See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. - -## Example Output -Example output of the application: -``` -I (9035) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful. -I (9045) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 5.539999 (0x40b147ac) read successful. -I (9045) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful. -I (9055) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful. -I (9065) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful. -I (9075) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful. -I (9085) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful. -I (9095) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = OFF (0xaa) read successful. -I (9605) MASTER_TEST: Characteristic #0 Data_channel_0 (Volts) value = 1.120000 (0x3f8f5c29) read successful. -I (9615) MASTER_TEST: Characteristic #1 Humidity_1 (%rH) value = 5.739999 (0x40b7ae12) read successful. -I (9615) MASTER_TEST: Characteristic #2 Temperature_1 (C) value = 2.340000 (0x4015c28f) read successful. -I (9625) MASTER_TEST: Characteristic #3 Humidity_2 (%rH) value = 2.560000 (0x4023d70a) read successful. -I (9635) MASTER_TEST: Characteristic #4 Temperature_2 (C) value = 3.560000 (0x4063d70a) read successful. -I (9645) MASTER_TEST: Characteristic #5 Humidity_3 (%rH) value = 3.780000 (0x4071eb85) read successful. -I (9655) MASTER_TEST: Characteristic #6 RelayP1 (on/off) value = OFF (0x55) read successful. -I (9665) MASTER_TEST: Characteristic #7 RelayP2 (on/off) value = ON (0xff) read successful. -I (10175) MASTER_TEST: Alarm triggered by cid #7. -I (10175) MASTER_TEST: Destroy master... - -``` -The example reads the characteristics from slave device(s), while alarm is not triggered in the slave device (See the "Example parameters definition"). The output line describes Timestamp, Cid of characteristic, Characteristic name (Units), Characteristic value (Hex). - -Note: Refer to file below for more information about current implementation of Modbus: - -* `components/freemodbus/README.rst` - diff --git a/examples/protocols/modbus_master/README.md b/examples/protocols/modbus_master/README.md index aff9ad574cb..c3923ffb019 100644 --- a/examples/protocols/modbus_master/README.md +++ b/examples/protocols/modbus_master/README.md @@ -154,4 +154,4 @@ The enabled CONFIG_FMB_TIMER_ISR_IN_IRAM option allows to avoid delays related t Note: Refer to file below for more information about current implementation of Modbus: -* `components/freemodbus/README.rst` +[freemodbus readme file](../../../components/freemodbus/README.rst) diff --git a/examples/protocols/modbus_master/main/Kconfig.projbuild b/examples/protocols/modbus_master/main/Kconfig.projbuild index 544610001ef..44fa8e9eb06 100644 --- a/examples/protocols/modbus_master/main/Kconfig.projbuild +++ b/examples/protocols/modbus_master/main/Kconfig.projbuild @@ -1,5 +1,19 @@ menu "Modbus Example Configuration" + config MB_UART_PORT_NUM + int "UART port number" + range 0 2 + default 2 + help + UART communication port number for Modbus example. + + config MB_UART_BAUD_RATE + int "UART communication speed" + range 1200 115200 + default 115200 + help + UART communication speed for Modbus example. + config MB_UART_RXD int "UART RXD pin number" range 0 34 @@ -24,4 +38,19 @@ menu "Modbus Example Configuration" GPIO number for UART RTS pin. This pin is connected to ~RE/DE pin of RS485 transceiver to switch direction. + choice MB_COMM_MODE + prompt "Modbus communication mode" + default MB_COMM_MODE_RTU if CONFIG_FMB_COMM_MODE_RTU_EN + help + Selection of Modbus communication mode option for Modbus. + + config MB_COMM_MODE_RTU + bool "RTU mode" + depends on FMB_COMM_MODE_RTU_EN + + config MB_COMM_MODE_ASCII + bool "ASCII mode" + depends on FMB_COMM_MODE_ASCII_EN + + endchoice endmenu diff --git a/examples/protocols/modbus_master/main/sense_modbus.c b/examples/protocols/modbus_master/main/sense_modbus.c index 67ac00bd96c..399c055bba5 100644 --- a/examples/protocols/modbus_master/main/sense_modbus.c +++ b/examples/protocols/modbus_master/main/sense_modbus.c @@ -29,6 +29,7 @@ #include "esp_err.h" #include "esp_log.h" #include "sense_modbus.h" // for Modbus defines +#include "sdkconfig.h" // This module provide an easy way to work with characteristics instead of // Modbus parameters @@ -42,8 +43,8 @@ static const char* TAG = "SENSE_MB"; } // Define port options for the master application -#define MB_BAUDRATE 115200 -#define MB_PORTNUM 2 +#define MB_BAUDRATE CONFIG_MB_UART_BAUD_RATE +#define MB_PORTNUM CONFIG_MB_UART_PORT_NUM #define MB_PARITY UART_PARITY_DISABLE // Keep the pointer to active characteristic table and its size diff --git a/examples/protocols/modbus_master/sdkconfig.defaults b/examples/protocols/modbus_master/sdkconfig.defaults index 39f585a261a..fee01725d03 100644 --- a/examples/protocols/modbus_master/sdkconfig.defaults +++ b/examples/protocols/modbus_master/sdkconfig.defaults @@ -1,6 +1,8 @@ # # Modbus configuration # +CONFIG_MB_COMM_MODE_RTU=y +CONFIG_MB_COMM_MODE_ASCII=n CONFIG_FMB_TIMER_PORT_ENABLED=n CONFIG_FMB_TIMER_ISR_IN_IRAM=n CONFIG_FMB_TIMER_GROUP=0 diff --git a/examples/protocols/modbus_slave/README.md b/examples/protocols/modbus_slave/README.md index a3ca2cb1345..00d73bc57d0 100644 --- a/examples/protocols/modbus_slave/README.md +++ b/examples/protocols/modbus_slave/README.md @@ -77,6 +77,6 @@ The CONFIG_FMB_SERIAL_TASK_PRIO value shall be configured to be higher than the The enabled CONFIG_FMB_TIMER_ISR_IN_IRAM option allows to avoid delays related to the processing of non-IRAM-safe interrupts if your application intensively performs Flash read/write operations and resolve some Modbus errors. -Note: Refer to file below for more information about the current implementation of Modbus: +Note: Refer to file below for more information about current implementation of Modbus: -* `components/freemodbus/README.rst` +[freemodbus readme file](../../../components/freemodbus/README.rst) diff --git a/examples/protocols/modbus_slave/main/Kconfig.projbuild b/examples/protocols/modbus_slave/main/Kconfig.projbuild index 7a438335810..42a76bc5071 100644 --- a/examples/protocols/modbus_slave/main/Kconfig.projbuild +++ b/examples/protocols/modbus_slave/main/Kconfig.projbuild @@ -1,5 +1,27 @@ menu "Modbus Slave Example Configuration" + config MB_SLAVE_ADDR + int "Modbus slave address" + range 1 127 + default 1 + help + This is the Modbus slave address in the network. + The address is used as an index to resolve slave ip address. + + config MB_UART_PORT_NUM + int "UART port number" + range 0 2 + default 2 + help + UART communication port number for Modbus example. + + config MB_UART_BAUD_RATE + int "UART communication speed" + range 1200 115200 + default 115200 + help + UART communication speed for Modbus example. + config MB_UART_RXD int "UART RXD pin number" range 0 34 @@ -24,4 +46,20 @@ menu "Modbus Slave Example Configuration" GPIO number for UART RTS pin. This pin is connected to ~RE/DE pin of RS485 transceiver to switch direction. + choice MB_COMM_MODE + prompt "Modbus communication mode" + default MB_COMM_MODE_RTU if CONFIG_FMB_COMM_MODE_RTU_EN + help + Selection of Modbus communication mode option for Modbus. + + config MB_COMM_MODE_RTU + bool "RTU mode" + depends on FMB_COMM_MODE_RTU_EN + + config MB_COMM_MODE_ASCII + bool "ASCII mode" + depends on FMB_COMM_MODE_ASCII_EN + + endchoice + endmenu diff --git a/examples/protocols/modbus_slave/main/freemodbus.c b/examples/protocols/modbus_slave/main/freemodbus.c index e29492c506b..dc163234853 100644 --- a/examples/protocols/modbus_slave/main/freemodbus.c +++ b/examples/protocols/modbus_slave/main/freemodbus.c @@ -11,9 +11,9 @@ #include "deviceparams.h" // for device parameters structures #include "esp_log.h" // for log_write -#define MB_PORT_NUM (2) // Number of UART port used for Modbus connection -#define MB_DEV_ADDR (1) // The address of device in Modbus network -#define MB_DEV_SPEED (115200) // The communication speed of the UART +#define MB_PORT_NUM (CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection +#define MB_DEV_ADDR (CONFIG_MB_SLAVE_ADDR) // The address of device in Modbus network +#define MB_DEV_SPEED (CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART // Defines below are used to define register start address for each type of Modbus registers #define MB_REG_DISCRETE_INPUT_START (0x0000) diff --git a/examples/protocols/modbus_slave/sdkconfig.defaults b/examples/protocols/modbus_slave/sdkconfig.defaults index 484c68af6bd..93699a1e110 100644 --- a/examples/protocols/modbus_slave/sdkconfig.defaults +++ b/examples/protocols/modbus_slave/sdkconfig.defaults @@ -1,6 +1,9 @@ # # Modbus configuration # +CONFIG_MB_SLAVE_ADDR=1 +CONFIG_MB_COMM_MODE_RTU=y +CONFIG_MB_COMM_MODE_ASCII=n CONFIG_FMB_TIMER_PORT_ENABLED=n CONFIG_FMB_TIMER_ISR_IN_IRAM=n CONFIG_FMB_TIMER_GROUP=0 -- GitLab