diff --git a/.gitignore b/.gitignore index a8e10db2..86e34e78 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ .cproject .project +# vscode setting +.vscode + # Example project files examples/**/sdkconfig examples/**/sdkconfig.old diff --git a/components/esp8266/driver/uart.c b/components/esp8266/driver/uart.c new file mode 100644 index 00000000..f7d88f98 --- /dev/null +++ b/components/esp8266/driver/uart.c @@ -0,0 +1,1022 @@ +// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "freertos/ringbuf.h" + +#include "esp_err.h" +#include "esp_log.h" +#include "esp_attr.h" + +#include "esp8266/uart_struct.h" +#include "esp8266/uart_register.h" +#include "esp8266/pin_mux_register.h" +#include "esp8266/eagle_soc.h" +#include "esp8266/rom_functions.h" + +#include "rom/ets_sys.h" + +#include "driver/uart.h" + +#define portYIELD_FROM_ISR() taskYIELD() + +#define UART_ENTER_CRITICAL() portENTER_CRITICAL() +#define UART_EXIT_CRITICAL() portEXIT_CRITICAL() + +static const char *UART_TAG = "uart"; +#define UART_CHECK(a, str, ret_val) \ + if (!(a)) { \ + ESP_LOGE(UART_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \ + return (ret_val); \ + } + +#define UART_EMPTY_THRESH_DEFAULT (10) +#define UART_FULL_THRESH_DEFAULT (120) +#define UART_TOUT_THRESH_DEFAULT (10) + +typedef struct { + uart_event_type_t type; /*!< UART TX data type */ + struct { + size_t size; + uint8_t data[0]; + } tx_data; +} uart_tx_data_t; + +typedef struct { + uart_port_t uart_num; /*!< UART port number*/ + int queue_size; /*!< UART event queue size*/ + QueueHandle_t xQueueUart; /*!< UART queue handler*/ + uart_mode_t uart_mode; /*!< UART controller actual mode set by uart_set_mode() */ + + // rx parameters + int rx_buffered_len; /*!< UART cached data length */ + SemaphoreHandle_t rx_mux; /*!< UART RX data mutex*/ + int rx_buf_size; /*!< RX ring buffer size */ + RingbufHandle_t rx_ring_buf; /*!< RX ring buffer handler*/ + bool rx_buffer_full_flg; /*!< RX ring buffer full flag. */ + int rx_cur_remain; /*!< Data number that waiting to be read out in ring buffer item*/ + uint8_t *rx_ptr; /*!< pointer to the current data in ring buffer*/ + uint8_t *rx_head_ptr; /*!< pointer to the head of RX item*/ + uint8_t rx_data_buf[UART_FIFO_LEN]; /*!< Data buffer to stash FIFO data*/ + uint8_t rx_stash_len; /*!< stashed data length.(When using flow control, after reading out FIFO data, if we fail to push to buffer, we can just stash them.) */ + + // tx parameters + SemaphoreHandle_t tx_fifo_sem; /*!< UART TX FIFO semaphore*/ + SemaphoreHandle_t tx_mux; /*!< UART TX mutex*/ + int tx_buf_size; /*!< TX ring buffer size */ + RingbufHandle_t tx_ring_buf; /*!< TX ring buffer handler*/ + bool tx_waiting_fifo; /*!< this flag indicates that some task is waiting for FIFO empty interrupt, used to send all data without any data buffer*/ + uint8_t *tx_ptr; /*!< TX data pointer to push to FIFO in TX buffer mode*/ + uart_tx_data_t *tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/ + uint32_t tx_len_tot; /*!< Total length of current item in ring buffer*/ + uint32_t tx_len_cur; +} uart_obj_t; + +static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; +// DRAM_ATTR is required to avoid UART array placed in flash, due to accessed from ISR +static DRAM_ATTR uart_dev_t *const UART[UART_NUM_MAX] = {&uart0, &uart1}; + +typedef void (*uart_isr_t)(void *); +typedef struct { + uart_isr_t fn; /*!< isr function */ + void *args; /*!< isr function args */ +} uart_isr_func_t; + +static uart_isr_func_t uart_isr_func[UART_NUM_MAX]; + + +esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((data_bit < UART_DATA_BITS_MAX), "data bit error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->conf0.bit_num = data_bit; + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t *data_bit) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((data_bit), "empty pointer", ESP_FAIL); + + *(data_bit) = UART[uart_num]->conf0.bit_num; + return ESP_OK; +} + +esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((stop_bit < UART_STOP_BITS_MAX), "stop bit error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->conf0.stop_bit_num = stop_bit; + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t *stop_bit) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((stop_bit), "empty pointer", ESP_FAIL); + + (*stop_bit) = UART[uart_num]->conf0.stop_bit_num; + return ESP_OK; +} + +esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK(((parity_mode == UART_PARITY_DISABLE) || (parity_mode == UART_PARITY_EVEN) || (parity_mode == UART_PARITY_ODD)), + "parity_mode error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->conf0.parity = (parity_mode & 0x1); + UART[uart_num]->conf0.parity_en = ((parity_mode >> 1) & 0x1); + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t *parity_mode) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((parity_mode), "empty pointer", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + + if (UART[uart_num]->conf0.parity_en) { + if (UART[uart_num]->conf0.parity) { + (*parity_mode) = UART_PARITY_ODD; + } else { + (*parity_mode) = UART_PARITY_EVEN; + } + } else { + (*parity_mode) = UART_PARITY_DISABLE; + } + + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->clk_div.val = (uint32_t)(UART_CLK_FREQ / baud_rate) & 0xFFFFF; + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t *baudrate) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((baudrate), "empty pointer", ESP_ERR_INVALID_ARG); + + (*baudrate) = (UART_CLK_FREQ / (UART[uart_num]->clk_div.val & 0xFFFFF)); + return ESP_OK; +} + +esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((((inverse_mask & ~UART_LINE_INV_MASK) == 0) || (inverse_mask == 0)), "inverse_mask error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->conf0.val &= ~UART_LINE_INV_MASK; + UART[uart_num]->conf0.val |= inverse_mask; + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((flow_ctrl < UART_HW_FLOWCTRL_MAX), "uart_flow ctrl error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + + if (flow_ctrl & UART_HW_FLOWCTRL_RTS) { + UART[uart_num]->conf1.rx_flow_thrhd = rx_thresh; + UART[uart_num]->conf1.rx_flow_en = 1; + } else { + UART[uart_num]->conf1.rx_flow_en = 0; + } + + if (flow_ctrl & UART_HW_FLOWCTRL_CTS) { + UART[uart_num]->conf0.tx_flow_en = 1; + } else { + UART[uart_num]->conf0.tx_flow_en = 0; + } + + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t *flow_ctrl) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + uart_hw_flowcontrol_t val = UART_HW_FLOWCTRL_DISABLE; + + if (UART[uart_num]->conf1.rx_flow_en) { + val |= UART_HW_FLOWCTRL_RTS; + } + + if (UART[uart_num]->conf0.tx_flow_en) { + val |= UART_HW_FLOWCTRL_CTS; + } + + (*flow_ctrl) = val; + return ESP_OK; +} + +static esp_err_t uart_wait_tx_done(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + uint32_t byte_delay_us = 0; + uart_get_baudrate(uart_num, &byte_delay_us); + byte_delay_us = (uint32_t)(10000000/byte_delay_us); // (1/baudrate)*10*1000_000 us + + while (1) { + // wait for tx done. + if (UART[uart_num]->status.txfifo_cnt == 0) { + ets_delay_us(byte_delay_us); // Delay one byte time to guarantee transmission completion + return ESP_OK; + } + } +} + +esp_err_t uart_enable_swap(void) +{ + // wait for tx done. + uart_wait_tx_done(UART_NUM_0); + + UART_ENTER_CRITICAL(); + // MTCK -> UART0_CTS -> U0RXD + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_UART0_CTS); + // MTD0 -> UART0_RTS -> U0TXD + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_UART0_RTS); + // enable swap U0TXD <-> UART0_RTS and U0RXD <-> UART0_CTS + SET_PERI_REG_MASK(UART_SWAP_REG, 0x4); + UART_EXIT_CRITICAL(); + + return ESP_OK; +} + +esp_err_t uart_disable_swap(void) +{ + // wait for tx done. + uart_wait_tx_done(UART_NUM_0); + + UART_ENTER_CRITICAL(); + // disable swap U0TXD <-> UART0_RTS and U0RXD <-> UART0_CTS + CLEAR_PERI_REG_MASK(UART_SWAP_REG, 0x4); + UART_EXIT_CRITICAL(); + + return ESP_OK; +} + +static esp_err_t uart_reset_rx_fifo(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->conf0.rxfifo_rst = 0x1; + UART[uart_num]->conf0.rxfifo_rst = 0x0; + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->int_clr.val |= mask; + UART_EXIT_CRITICAL(); + return ESP_OK; +} + + +esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->int_ena.val |= enable_mask; + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->int_ena.val &= ~disable_mask; + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t 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); +} + +esp_err_t uart_disable_rx_intr(uart_port_t uart_num) +{ + return uart_disable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); +} + +esp_err_t uart_disable_tx_intr(uart_port_t uart_num) +{ + return uart_disable_intr_mask(uart_num, UART_TXFIFO_EMPTY_INT_ENA); +} + +esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((thresh < UART_FIFO_LEN), "empty intr threshold error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + UART[uart_num]->int_clr.txfifo_empty = 1; + UART[uart_num]->conf1.txfifo_empty_thrhd = thresh & 0x7f; + UART[uart_num]->int_ena.txfifo_empty = enable & 0x1; + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +static void uart_intr_service(void *arg) +{ + // UART intr process + uint32_t uart_num = 0; + // read status to get interrupt status for UART0-1 + uint32_t uart_intr_status = UART[uart_num]->int_st.val; + + if (uart_isr_func == NULL) { + return; + } + + do { + if (uart_intr_status != 0) { + if (uart_isr_func[uart_num].fn != NULL) { + uart_isr_func[uart_num].fn(uart_isr_func[uart_num].args); + } + } + } while (++uart_num < UART_NUM_MAX); +} + + +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void *), void *arg) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + UART_ENTER_CRITICAL(); + _xt_isr_mask(1 << ETS_UART_INUM); + _xt_isr_attach(ETS_UART_INUM, uart_intr_service, NULL); + uart_isr_func[uart_num].fn = fn; + uart_isr_func[uart_num].args = arg; + _xt_isr_unmask(1 << ETS_UART_INUM); + UART_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_conf) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + if (uart_num == UART_NUM_1) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); + } else { + PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); + + if (uart_conf->flow_ctrl & UART_HW_FLOWCTRL_RTS) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS); + } + + if (uart_conf->flow_ctrl & UART_HW_FLOWCTRL_CTS) { + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_UART0_CTS); + } + + uart_set_hw_flow_ctrl(uart_num, uart_conf->flow_ctrl, uart_conf->rx_flow_ctrl_thresh); + } + + uart_set_baudrate(uart_num, uart_conf->baud_rate); + uart_set_word_length(uart_num, uart_conf->data_bits); + uart_set_stop_bits(uart_num, uart_conf->stop_bits); + uart_set_parity(uart_num, uart_conf->parity); + uart_reset_rx_fifo(uart_num); + + return ESP_OK; +} + +esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *intr_conf) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + uart_clear_intr_status(uart_num, UART_INTR_MASK); + UART_ENTER_CRITICAL(); + UART[uart_num]->int_clr.val = UART_INTR_MASK; + + if (intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { + UART[uart_num]->conf1.rx_tout_thrhd = ((intr_conf->rx_timeout_thresh) & 0x7f); + UART[uart_num]->conf1.rx_tout_en = 1; + } else { + UART[uart_num]->conf1.rx_tout_en = 0; + } + + if (intr_conf->intr_enable_mask & UART_RXFIFO_FULL_INT_ENA_M) { + UART[uart_num]->conf1.rxfifo_full_thrhd = intr_conf->rxfifo_full_thresh; + } + + if (intr_conf->intr_enable_mask & UART_TXFIFO_EMPTY_INT_ENA_M) { + UART[uart_num]->conf1.txfifo_empty_thrhd = intr_conf->txfifo_empty_intr_thresh; + } + + // uart_clear_intr_status(UART[uart_num], mask); + UART[uart_num]->int_ena.val = intr_conf->intr_enable_mask; + _xt_isr_unmask(0x1 << ETS_UART_INUM); + UART_EXIT_CRITICAL(); + + return ESP_OK; +} + +// internal isr handler for default driver code. +static void uart_rx_intr_handler_default(void *param) +{ + uart_obj_t *p_uart = (uart_obj_t *) param; + uint8_t uart_num = p_uart->uart_num; + uart_dev_t *uart_reg = UART[uart_num]; + int rx_fifo_len = uart_reg->status.rxfifo_cnt; + uint8_t buf_idx = 0; + uint32_t uart_intr_status = UART[uart_num]->int_st.val; + uart_event_t uart_event; + BaseType_t task_woken = 0; + + while (uart_intr_status != 0x0) { + + buf_idx = 0; + uart_event.type = UART_EVENT_MAX; + + if (uart_intr_status & UART_TXFIFO_EMPTY_INT_ST_M) { + uart_clear_intr_status(uart_num, UART_TXFIFO_EMPTY_INT_CLR_M); + uart_disable_intr_mask(uart_num, UART_TXFIFO_EMPTY_INT_ENA_M); + + // TX semaphore will only be used when tx_buf_size is zero. + if (p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size == 0) { + p_uart->tx_waiting_fifo = false; + xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, &task_woken); + + if (task_woken == pdTRUE) { + portYIELD_FROM_ISR(); + } + } else { + // We don't use TX ring buffer, because the size is zero. + if (p_uart->tx_buf_size == 0) { + continue; + } + + int tx_fifo_rem = UART_FIFO_LEN - UART[uart_num]->status.txfifo_cnt; + bool en_tx_flg = false; + + // We need to put a loop here, in case all the buffer items are very short. + // That would cause a watch_dog reset because empty interrupt happens so often. + // Although this is a loop in ISR, this loop will execute at most 128 turns. + while (tx_fifo_rem) { + if (p_uart->tx_len_tot == 0 || p_uart->tx_ptr == NULL || p_uart->tx_len_cur == 0) { + size_t size; + p_uart->tx_head = (uart_tx_data_t *) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); + + if (p_uart->tx_head) { + // The first item is the data description + // Get the first item to get the data information + if (p_uart->tx_len_tot == 0) { + p_uart->tx_ptr = NULL; + p_uart->tx_len_tot = p_uart->tx_head->tx_data.size; + // We have saved the data description from the 1st item, return buffer. + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &task_woken); + + if (task_woken == pdTRUE) { + portYIELD_FROM_ISR(); + } + } else if (p_uart->tx_ptr == NULL) { + // Update the TX item pointer, we will need this to return item to buffer. + p_uart->tx_ptr = (uint8_t *) p_uart->tx_head; + en_tx_flg = true; + p_uart->tx_len_cur = size; + } + } else { + // Can not get data from ring buffer, return; + break; + } + } + + if (p_uart->tx_len_tot > 0 && p_uart->tx_ptr && p_uart->tx_len_cur > 0) { + // To fill the TX FIFO. + int send_len = p_uart->tx_len_cur > tx_fifo_rem ? tx_fifo_rem : p_uart->tx_len_cur; + + for (buf_idx = 0; buf_idx < send_len; buf_idx++) { + UART[uart_num]->fifo.rw_byte = *(p_uart->tx_ptr++) & 0xff; + } + + p_uart->tx_len_tot -= send_len; + p_uart->tx_len_cur -= send_len; + tx_fifo_rem -= send_len; + + if (p_uart->tx_len_cur == 0) { + // Return item to ring buffer. + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &task_woken); + + if (task_woken == pdTRUE) { + portYIELD_FROM_ISR(); + } + + p_uart->tx_head = NULL; + p_uart->tx_ptr = NULL; + } + + if (p_uart->tx_len_tot == 0) { + en_tx_flg = false; + } else { + en_tx_flg = true; + } + } + } + + if (en_tx_flg) { + uart_clear_intr_status(uart_num, UART_TXFIFO_EMPTY_INT_CLR_M); + uart_enable_intr_mask(uart_num, UART_TXFIFO_EMPTY_INT_ENA_M); + } + } + } else if ((uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M) + || (uart_intr_status & UART_RXFIFO_FULL_INT_ST_M) + ) { + rx_fifo_len = uart_reg->status.rxfifo_cnt; + + if (p_uart->rx_buffer_full_flg == false) { + // We have to read out all data in RX FIFO to clear the interrupt signal + while (buf_idx < rx_fifo_len) { + p_uart->rx_data_buf[buf_idx++] = uart_reg->fifo.rw_byte; + } + + // Get the buffer from the FIFO + // After Copying the Data From FIFO ,Clear intr_status + 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; + p_uart->rx_stash_len = rx_fifo_len; + + // If we fail to push data to ring buffer, we will have to stash the data, and send next time. + // Mainly for applications that uses flow control or small ring buffer. + if (pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->rx_data_buf, p_uart->rx_stash_len, &task_woken)) { + uart_disable_intr_mask(uart_num, UART_RXFIFO_TOUT_INT_ENA_M | UART_RXFIFO_FULL_INT_ENA_M); + uart_event.type = UART_BUFFER_FULL; + p_uart->rx_buffer_full_flg = true; + } else { + p_uart->rx_buffered_len += p_uart->rx_stash_len; + } + + if (task_woken == pdTRUE) { + portYIELD_FROM_ISR(); + } + } else { + uart_disable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA_M | UART_RXFIFO_TOUT_INT_ENA_M); + uart_clear_intr_status(uart_num, UART_RXFIFO_FULL_INT_CLR_M | UART_RXFIFO_TOUT_INT_CLR_M); + } + } else if (uart_intr_status & UART_RXFIFO_OVF_INT_ST_M) { + // When fifo overflows, we reset the fifo. + uart_reset_rx_fifo(uart_num); + uart_reg->int_clr.rxfifo_ovf = 1; + uart_event.type = UART_FIFO_OVF; + } else if (uart_intr_status & UART_FRM_ERR_INT_ST_M) { + uart_reg->int_clr.frm_err = 1; + uart_event.type = UART_FRAME_ERR; + } else if (uart_intr_status & UART_PARITY_ERR_INT_ST_M) { + uart_reg->int_clr.parity_err = 1; + uart_event.type = UART_PARITY_ERR; + } else { + uart_reg->int_clr.val = uart_intr_status; // simply clear all other intr status + uart_event.type = UART_EVENT_MAX; + } + + if (uart_event.type != UART_EVENT_MAX && p_uart->xQueueUart) { + if (pdFALSE == xQueueSendFromISR(p_uart->xQueueUart, (void *)&uart_event, &task_woken)) { + ESP_EARLY_LOGV(UART_TAG, "UART event queue full"); + } + + if (task_woken == pdTRUE) { + portYIELD_FROM_ISR(); + } + } + + uart_intr_status = uart_reg->int_st.val; + } +} + +// Fill UART tx_fifo and return a number, +// This function by itself is not thread-safe, always call from within a muxed section. +static int uart_fill_fifo(uart_port_t uart_num, const char *buffer, uint32_t len) +{ + uint8_t i = 0; + uint8_t tx_fifo_cnt = UART[uart_num]->status.txfifo_cnt; + uint8_t tx_remain_fifo_cnt = (UART_FIFO_LEN - tx_fifo_cnt); + uint8_t copy_cnt = (len >= tx_remain_fifo_cnt ? tx_remain_fifo_cnt : len); + + for (i = 0; i < copy_cnt; i++) { + UART[uart_num]->fifo.rw_byte = buffer[i]; + } + + return copy_cnt; +} + +int uart_tx_chars(uart_port_t uart_num, const char *buffer, uint32_t len) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); + UART_CHECK(buffer, "buffer null", (-1)); + + if (len == 0) { + return 0; + } + + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); + int tx_len = uart_fill_fifo(uart_num, (const char *) buffer, len); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); + return tx_len; +} + +static int uart_tx_all(uart_port_t uart_num, const char *src, size_t size) +{ + if (size == 0) { + return 0; + } + + size_t original_size = size; + + // lock for uart_tx + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); + + if (p_uart_obj[uart_num]->tx_buf_size > 0) { + int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); + int offset = 0; + uart_tx_data_t evt; + evt.tx_data.size = size; + evt.type = UART_DATA; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void *) &evt, sizeof(uart_tx_data_t), portMAX_DELAY); + + while (size > 0) { + int send_size = size > max_size / 2 ? max_size / 2 : size; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void *)(src + offset), send_size, portMAX_DELAY); + size -= send_size; + offset += send_size; + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); + } + } else { + while (size) { + // semaphore for tx_fifo available + if (pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_fifo_sem, (portTickType)portMAX_DELAY)) { + size_t sent = uart_fill_fifo(uart_num, (char *) src, size); + + if (sent < size) { + p_uart_obj[uart_num]->tx_waiting_fifo = true; + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); + } + + size -= sent; + src += sent; + } + } + + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); + } + uart_wait_tx_done(uart_num); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); + return original_size; +} + +int uart_write_bytes(uart_port_t uart_num, const char *src, size_t size) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); + UART_CHECK(src, "buffer null", (-1)); + + return uart_tx_all(uart_num, src, size); +} + +int uart_read_bytes(uart_port_t uart_num, uint8_t *buf, uint32_t length, TickType_t ticks_to_wait) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((buf), "uart data null", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); + + uint8_t *data = NULL; + size_t size; + size_t copy_len = 0; + int len_tmp; + + if (xSemaphoreTake(p_uart_obj[uart_num]->rx_mux, (portTickType)ticks_to_wait) != pdTRUE) { + return -1; + } + + while (length) { + if (p_uart_obj[uart_num]->rx_cur_remain == 0) { + data = (uint8_t *) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); + + if (data) { + p_uart_obj[uart_num]->rx_head_ptr = data; + p_uart_obj[uart_num]->rx_ptr = data; + p_uart_obj[uart_num]->rx_cur_remain = size; + } else { + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); + return copy_len; + } + } + + if (p_uart_obj[uart_num]->rx_cur_remain > length) { + len_tmp = length; + } else { + len_tmp = p_uart_obj[uart_num]->rx_cur_remain; + } + + memcpy(buf + copy_len, p_uart_obj[uart_num]->rx_ptr, len_tmp); + UART_ENTER_CRITICAL(); + p_uart_obj[uart_num]->rx_buffered_len -= len_tmp; + p_uart_obj[uart_num]->rx_ptr += len_tmp; + UART_EXIT_CRITICAL(); + p_uart_obj[uart_num]->rx_cur_remain -= len_tmp; + copy_len += len_tmp; + length -= len_tmp; + + if (p_uart_obj[uart_num]->rx_cur_remain == 0) { + vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_head_ptr); + p_uart_obj[uart_num]->rx_head_ptr = NULL; + p_uart_obj[uart_num]->rx_ptr = NULL; + + if (p_uart_obj[uart_num]->rx_buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); + + if (res == pdTRUE) { + UART_ENTER_CRITICAL(); + p_uart_obj[uart_num]->rx_buffered_len += p_uart_obj[uart_num]->rx_stash_len; + p_uart_obj[uart_num]->rx_buffer_full_flg = false; + UART_EXIT_CRITICAL(); + uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); + } + } + } + } + + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); + return copy_len; +} + +esp_err_t uart_get_buffered_data_len(uart_port_t uart_num, size_t *size) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", ESP_ERR_INVALID_ARG); + + *size = p_uart_obj[uart_num]->rx_buffered_len; + return ESP_OK; +} + +esp_err_t uart_flush(uart_port_t uart_num) __attribute__((alias("uart_flush_input"))); + +esp_err_t uart_flush_input(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", ESP_ERR_INVALID_ARG); + uart_obj_t *p_uart = p_uart_obj[uart_num]; + uint8_t *data; + size_t size; + + // rx sem protect the ring buffer read related functions + xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); + uart_disable_rx_intr(p_uart_obj[uart_num]->uart_num); + + while (true) { + if (p_uart->rx_head_ptr) { + vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr); + UART_ENTER_CRITICAL(); + p_uart_obj[uart_num]->rx_buffered_len -= p_uart->rx_cur_remain; + UART_EXIT_CRITICAL(); + p_uart->rx_ptr = NULL; + p_uart->rx_cur_remain = 0; + p_uart->rx_head_ptr = NULL; + } + + data = (uint8_t *) xRingbufferReceive(p_uart->rx_ring_buf, &size, (portTickType) 0); + + if (data == NULL) { + if (p_uart_obj[uart_num]->rx_buffered_len != 0) { + ESP_LOGE(UART_TAG, "rx_buffered_len error"); + p_uart_obj[uart_num]->rx_buffered_len = 0; + } + + // We also need to clear the `rx_buffer_full_flg` here. + UART_ENTER_CRITICAL(); + p_uart_obj[uart_num]->rx_buffer_full_flg = false; + UART_EXIT_CRITICAL(); + break; + } + + UART_ENTER_CRITICAL(); + p_uart_obj[uart_num]->rx_buffered_len -= size; + UART_EXIT_CRITICAL(); + vRingbufferReturnItem(p_uart->rx_ring_buf, data); + + if (p_uart_obj[uart_num]->rx_buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); + + if (res == pdTRUE) { + UART_ENTER_CRITICAL(); + p_uart_obj[uart_num]->rx_buffered_len += p_uart_obj[uart_num]->rx_stash_len; + p_uart_obj[uart_num]->rx_buffer_full_flg = false; + UART_EXIT_CRITICAL(); + } + } + } + + p_uart->rx_ptr = NULL; + p_uart->rx_cur_remain = 0; + p_uart->rx_head_ptr = NULL; + uart_reset_rx_fifo(uart_num); + uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); + xSemaphoreGive(p_uart->rx_mux); + return ESP_OK; +} + +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t *uart_queue) +{ + esp_err_t r; + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + UART_CHECK((rx_buffer_size > UART_FIFO_LEN) || ((uart_num == UART_NUM_1) && (rx_buffer_size == 0)), "uart rx buffer length error(>128)", ESP_ERR_INVALID_ARG); + UART_CHECK((tx_buffer_size > UART_FIFO_LEN) || (tx_buffer_size == 0), "uart tx buffer length error(>128 or 0)", ESP_ERR_INVALID_ARG); + UART_CHECK((queue_size >= 0), "queue_size error(>=0)", ESP_ERR_INVALID_ARG); + + if (p_uart_obj[uart_num] == NULL) { + p_uart_obj[uart_num] = (uart_obj_t *) calloc(1, sizeof(uart_obj_t)); + + if (p_uart_obj[uart_num] == NULL) { + ESP_LOGE(UART_TAG, "UART driver malloc error"); + return ESP_FAIL; + } + + p_uart_obj[uart_num]->uart_num = uart_num; + p_uart_obj[uart_num]->uart_mode = UART_MODE_UART; + p_uart_obj[uart_num]->tx_fifo_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_mux = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->rx_mux = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->queue_size = queue_size; + p_uart_obj[uart_num]->tx_ptr = NULL; + p_uart_obj[uart_num]->tx_head = NULL; + p_uart_obj[uart_num]->tx_len_tot = 0; + p_uart_obj[uart_num]->rx_buffered_len = 0; + + if (uart_queue) { + p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); + *uart_queue = p_uart_obj[uart_num]->xQueueUart; + ESP_LOGI(UART_TAG, "queue free spaces: %d", (int)uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); + } else { + p_uart_obj[uart_num]->xQueueUart = NULL; + } + + p_uart_obj[uart_num]->rx_buffer_full_flg = false; + p_uart_obj[uart_num]->tx_waiting_fifo = false; + p_uart_obj[uart_num]->rx_ptr = NULL; + p_uart_obj[uart_num]->rx_cur_remain = 0; + p_uart_obj[uart_num]->rx_head_ptr = NULL; + p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, RINGBUF_TYPE_BYTEBUF); + p_uart_obj[uart_num]->rx_buf_size = rx_buffer_size; + + if (tx_buffer_size > 0) { + p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT); + p_uart_obj[uart_num]->tx_buf_size = tx_buffer_size; + } else { + p_uart_obj[uart_num]->tx_ring_buf = NULL; + p_uart_obj[uart_num]->tx_buf_size = 0; + } + } else { + ESP_LOGE(UART_TAG, "UART driver already installed"); + return ESP_FAIL; + } + + r = uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); + + 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 + | UART_FRM_ERR_INT_ENA_M + | UART_RXFIFO_OVF_INT_ENA_M, + .rxfifo_full_thresh = UART_FULL_THRESH_DEFAULT, + .rx_timeout_thresh = UART_TOUT_THRESH_DEFAULT, + .txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT + }; + r = uart_intr_config(uart_num, &uart_intr); + + if (r != ESP_OK) { + goto err; + } + + return r; + +err: + ESP_LOGE(UART_TAG, "driver install error"); + uart_driver_delete(uart_num); + return r; +} + +// Make sure no other tasks are still using UART before you call this function +esp_err_t uart_driver_delete(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG); + + if (p_uart_obj[uart_num] == NULL) { + ESP_LOGI(UART_TAG, "ALREADY NULL"); + return ESP_OK; + } + + uart_disable_rx_intr(uart_num); + uart_disable_tx_intr(uart_num); + _xt_isr_mask(0x1 << ETS_UART_INUM); + + if (p_uart_obj[uart_num]->tx_fifo_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_fifo_sem = NULL; + } + + if (p_uart_obj[uart_num]->tx_mux) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_mux); + p_uart_obj[uart_num]->tx_mux = NULL; + } + + if (p_uart_obj[uart_num]->rx_mux) { + vSemaphoreDelete(p_uart_obj[uart_num]->rx_mux); + p_uart_obj[uart_num]->rx_mux = NULL; + } + + if (p_uart_obj[uart_num]->xQueueUart) { + vQueueDelete(p_uart_obj[uart_num]->xQueueUart); + p_uart_obj[uart_num]->xQueueUart = NULL; + } + + if (p_uart_obj[uart_num]->rx_ring_buf) { + vRingbufferDelete(p_uart_obj[uart_num]->rx_ring_buf); + p_uart_obj[uart_num]->rx_ring_buf = NULL; + } + + if (p_uart_obj[uart_num]->tx_ring_buf) { + vRingbufferDelete(p_uart_obj[uart_num]->tx_ring_buf); + p_uart_obj[uart_num]->tx_ring_buf = NULL; + } + + free(p_uart_obj[uart_num]); + p_uart_obj[uart_num] = NULL; + + 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(); + + // The tout_thresh = 1, defines TOUT interrupt timeout equal to + // transmission time of one symbol (~11 bit) on current baudrate + if (tout_thresh > 0) { + UART[uart_num]->conf1.rx_tout_thrhd = (tout_thresh & 0x7f); + UART[uart_num]->conf1.rx_tout_en = 1; + } else { + UART[uart_num]->conf1.rx_tout_en = 0; + } + + UART_EXIT_CRITICAL(); + return ESP_OK; +} diff --git a/components/esp8266/include/driver/uart.h b/components/esp8266/include/driver/uart.h new file mode 100644 index 00000000..1532a73d --- /dev/null +++ b/components/esp8266/include/driver/uart.h @@ -0,0 +1,547 @@ +// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#ifndef _DRIVER_UART_H_ +#define _DRIVER_UART_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" +#include "esp_log.h" +#include "freertos/queue.h" + +#define UART_FIFO_LEN (128) /*!< Length of the hardware FIFO buffers */ +#define UART_INTR_MASK 0x1ff /*!< Mask of all UART interrupts */ +#define UART_LINE_INV_MASK (0x3f << 19) /*!< TBD */ + +#define UART_INVERSE_DISABLE (0x0) /*!< Disable UART signal inverse*/ +#define UART_INVERSE_RXD (BIT(19)) /*!< UART RXD input inverse*/ +#define UART_INVERSE_CTS (BIT(20)) /*!< UART CTS input inverse*/ +#define UART_INVERSE_TXD (BIT(22)) /*!< UART TXD output inverse*/ +#define UART_INVERSE_RTS (BIT(23)) /*!< UART RTS output inverse*/ + +/** + * @brief UART mode selection + */ +typedef enum { + UART_MODE_UART = 0x00, /*!< mode: regular UART mode*/ +} uart_mode_t; + +/** + * @brief UART word length constants + */ +typedef enum { + UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/ + UART_DATA_6_BITS = 0x1, /*!< word length: 6bits*/ + UART_DATA_7_BITS = 0x2, /*!< word length: 7bits*/ + UART_DATA_8_BITS = 0x3, /*!< word length: 8bits*/ + UART_DATA_BITS_MAX = 0x4, +} uart_word_length_t; + +/** + * @brief UART stop bits number + */ +typedef enum { + UART_STOP_BITS_1 = 0x1, /*!< stop bit: 1bit*/ + UART_STOP_BITS_1_5 = 0x2, /*!< stop bit: 1.5bits*/ + UART_STOP_BITS_2 = 0x3, /*!< stop bit: 2bits*/ + UART_STOP_BITS_MAX = 0x4, +} uart_stop_bits_t; + +/** + * @brief UART peripheral number + */ +typedef enum { + UART_NUM_0 = 0x0, + UART_NUM_1 = 0x1, + UART_NUM_MAX, +} uart_port_t; + +/** + * @brief UART parity constants + */ +typedef enum { + UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/ + UART_PARITY_EVEN = 0x2, /*!< Enable UART even parity*/ + UART_PARITY_ODD = 0x3 /*!< Enable UART odd parity*/ +} uart_parity_t; + +/** + * @brief UART hardware flow control modes + */ +typedef enum { + UART_HW_FLOWCTRL_DISABLE = 0x0, /*!< disable hardware flow control*/ + UART_HW_FLOWCTRL_RTS = 0x1, /*!< enable RX hardware flow control (rts)*/ + UART_HW_FLOWCTRL_CTS = 0x2, /*!< enable TX hardware flow control (cts)*/ + UART_HW_FLOWCTRL_CTS_RTS = 0x3, /*!< enable hardware flow control*/ + UART_HW_FLOWCTRL_MAX = 0x4, +} uart_hw_flowcontrol_t; + +/** + * @brief UART configuration parameters for uart_param_config function + */ +typedef struct { + int baud_rate; /*!< UART baud rate*/ + uart_word_length_t data_bits; /*!< UART byte size*/ + uart_parity_t parity; /*!< UART parity mode*/ + uart_stop_bits_t stop_bits; /*!< UART stop bits*/ + uart_hw_flowcontrol_t flow_ctrl; /*!< UART HW flow control mode (cts/rts)*/ + uint8_t rx_flow_ctrl_thresh; /*!< UART HW RTS threshold*/ +} uart_config_t; + +/** + * @brief UART interrupt configuration parameters for uart_intr_config function + */ +typedef struct { + uint32_t intr_enable_mask; /*!< UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator*/ + uint8_t rx_timeout_thresh; /*!< UART timeout interrupt threshold (unit: time of sending one byte)*/ + uint8_t txfifo_empty_intr_thresh; /*!< UART TX empty interrupt threshold.*/ + uint8_t rxfifo_full_thresh; /*!< UART RX full interrupt threshold.*/ +} uart_intr_config_t; + +/** + * @brief UART event types used in the ring buffer + */ +typedef enum { + UART_DATA, /*!< UART data event*/ + UART_BUFFER_FULL, /*!< UART RX buffer full event*/ + UART_FIFO_OVF, /*!< UART FIFO overflow event*/ + UART_FRAME_ERR, /*!< UART RX frame error event*/ + UART_PARITY_ERR, /*!< UART RX parity event*/ + UART_EVENT_MAX, /*!< UART event max index*/ +} uart_event_type_t; + +/** + * @brief Event structure used in UART event queue + */ +typedef struct { + uart_event_type_t type; /*!< UART event type */ + size_t size; /*!< UART data size for UART_DATA event*/ +} uart_event_t; + + +/** + * @brief Set UART data bits. + * + * @param uart_num Uart port number. + * @param data_bit Uart data bits. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit); + +/** + * @brief Get UART data bits. + * + * @param uart_num Uart port number. + * @param data_bit Pointer to accept value of UART data bits. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t *data_bit); + +/** + * @brief Set UART stop bits. + * + * @param uart_num Uart port number + * @param stop_bits Uart stop bits + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bits); + +/** + * @brief Get UART stop bits. + * + * @param uart_num Uart port number. + * @param stop_bits Pointer to accept value of UART stop bits. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t *stop_bits); + +/** + * @brief Set UART parity mode. + * + * @param uart_num Uart port number. + * @param parity_mode The enum of uart parity configuration. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode); + +/** + * @brief Get UART parity mode. + * + * @param uart_num Uart port number + * @param parity_mode Pointer to accept value of UART parity mode. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t *parity_mode); + +/** + * @brief Set UART baud rate. + * + * @param uart_num Uart port number + * @param baudrate UART baud rate. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baudrate); + +/** + * @brief Get UART baud rate. + * + * @param uart_num Uart port number. + * @param baudrate Pointer to accept value of Uart baud rate. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t *baudrate); + +/** + * @brief Set UART line inverse mode + * + * @param uart_num UART_NUM_0 + * @param inverse_mask Choose the wires that need to be inverted. + * Inverse_mask should be chosen from + * UART_INVERSE_RXD / UART_INVERSE_TXD / UART_INVERSE_RTS / UART_INVERSE_CTS, + * combined with OR operation. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask); + +/** + * @brief Configure Hardware flow control. + * + * @param uart_num Uart port number. + * @param flow_ctrl Hardware flow control mode. + * @param rx_thresh Threshold of Hardware flow control. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh); + +/** + * @brief Get hardware flow control mode + * + * @param uart_num Uart port number. + * @param flow_ctrl Option for different flow control mode. + * + * @return + * - ESP_OK Success, result will be put in (*flow_ctrl) + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t *flow_ctrl); + +/** + * @brief UART0 swap. + * Use MTCK as UART0 RX, MTDO as UART0 TX, so ROM log will not output from + * this new UART0. We also need to use MTDO (U0RTS) and MTCK (U0CTS) as UART0 in hardware. + * + * @return + * - ESP_OK Success + */ +esp_err_t uart_enable_swap(void); + +/** + * @brief Disable UART0 swap. + * Use the original UART0, not MTCK and MTDO. + * + * @return + * - ESP_OK Success + */ +esp_err_t uart_disable_swap(void); + +/** + * @brief Clear uart interrupts status. + * + * @param uart_num Uart port number. + * @param mask Uart interrupt bits mask. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t mask); + +/** + * @brief Set UART interrupt enable + * + * @param uart_num Uart port number + * @param enable_mask Bit mask of the enable bits. + * The bit mask should be composed from the fields of register UART_INT_ENA_REG. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); + +/** + * @brief Clear UART interrupt enable bits + * + * @param uart_num Uart port number + * @param disable_mask Bit mask of the disable bits. + * The bit mask should be composed from the fields of register UART_INT_ENA_REG. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); + +/** + * @brief Enable UART RX interrupt (RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_num UART_NUM_0 + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_enable_rx_intr(uart_port_t uart_num); + +/** + * @brief Disable UART RX interrupt (RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_num UART_NUM_0 + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_disable_rx_intr(uart_port_t uart_num); + +/** + * @brief Disable UART TX interrupt (TX_FULL & TX_TIMEOUT INTERRUPT) + * + * @param uart_num UART_NUM_0 + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_disable_tx_intr(uart_port_t uart_num); + +/** + * @brief Enable UART TX interrupt (TX_FULL & TX_TIMEOUT INTERRUPT) + * + * @param uart_num UART_NUM_0 + * @param enable 1: enable; 0: disable + * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); + +/** + * @brief Register UART interrupt handler (ISR). + * + * @param uart_num UART_NUM_0 + * @param fn Interrupt handler function. + * @param arg parameter for handler function + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void *), void *arg); + +/** + * @brief Config Common parameters of serial ports. + * + * @param uart_num Uart port number. + * @param uart_conf Uart config parameters. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_conf); + +/** + * @brief Config types of uarts. + * + * @param uart_num Uart port number. + * @param uart_intr_conf Uart interrupt config parameters. + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *uart_intr_conf); + +/** + * @brief Install UART driver. + * + * @note Rx_buffer_size should be greater than UART_FIFO_LEN. Tx_buffer_size should be either zero or greater than UART_FIFO_LEN. + * + * @param uart_num Uart port number. + * @param rx_buffer_size UART RX ring buffer size. + * @param tx_buffer_size UART TX ring buffer size. + * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out. + * @param queue_size UART event queue size/depth. + * @param uart_queue UART event queue handle (out param). On success, a new queue handle is written here to provide + * access to UART events. If set to NULL, driver will not use an event queue. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t *uart_queue); + +/** + * @brief Uninstall UART driver. + * + * @param uart_num Uart port number. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_driver_delete(uart_port_t uart_num); + +/** + * @brief Send data to the UART port from a given buffer and length. + * + * This function will not wait for enough space in TX FIFO. It will just fill the available TX FIFO and return when the FIFO is full. + * @note This function should only be used when UART TX buffer is not enabled. + * + * @param uart_num Uart port number. + * @param buffer data buffer address + * @param len data length to send + * + * @return + * - (-1) Parameter error + * - OTHERS (>=0) The number of bytes pushed to the TX FIFO + */ +int uart_tx_chars(uart_port_t uart_num, const char *buffer, uint32_t len); + +/** + * @brief Send data to the UART port from a given buffer and length, + * + * If the UART driver's parameter 'tx_buffer_size' is set to zero: + * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. + * + * Otherwise, if the 'tx_buffer_size' > 0, this function will return after copying all the data to tx ring buffer, + * UART ISR will then move data from the ring buffer to TX FIFO gradually. + * + * @param uart_num Uart port number. + * @param src data buffer address + * @param size data length to send + * + * @return + * - (-1) Parameter error + * - OTHERS (>=0) The number of bytes pushed to the TX FIFO + */ +int uart_write_bytes(uart_port_t uart_num, const char *src, size_t size); + +/** + * @brief UART read bytes from UART buffer + * + * @param uart_num Uart port number. + * @param buf pointer to the buffer. + * @param length data length + * @param ticks_to_wait sTimeout, count in RTOS ticks + * + * @return + * - (-1) Error + * - OTHERS (>=0) The number of bytes read from UART FIFO + */ +int uart_read_bytes(uart_port_t uart_num, uint8_t *buf, uint32_t length, TickType_t ticks_to_wait); + +/** + * @brief Alias of uart_flush_input. + * UART ring buffer flush. This will discard all data in the UART RX buffer. + * @note Instead of waiting the data sent out, this function will clear UART rx buffer. + * In order to send all the data in tx FIFO, we can use uart_wait_tx_done function. + * @param uart_num UART port number. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_flush(uart_port_t uart_num); + +/** + * @brief Clear input buffer, discard all the data is in the ring-buffer. + * @note In order to send all the data in tx FIFO, we can use uart_wait_tx_done function. + * @param uart_num UART port number. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_flush_input(uart_port_t uart_num); + +/** + * @brief UART get RX ring buffer cached data length + * + * @param uart_num UART port number. + * @param size Pointer of size_t to accept cached data length + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_get_buffered_data_len(uart_port_t uart_num, size_t *size); + +/** + * @brief UART set threshold timeout for TOUT feature + * + * @param uart_num Uart number to configure + * @param tout_thresh This parameter defines timeout threshold in uart symbol periods. The maximum value of threshold is 126. + * tout_thresh = 1, defines TOUT interrupt timeout equal to transmission time of one symbol (~11 bit) on current baudrate. + * If the time is expired the UART_RXFIFO_TOUT_INT interrupt is triggered. If tout_thresh == 0, + * the TOUT feature is disabled. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh); + +#ifdef __cplusplus +} +#endif + +#endif // _DRIVER_UART_H_ diff --git a/components/esp8266/include/esp8266/uart_register.h b/components/esp8266/include/esp8266/uart_register.h index 68a90b69..2da0291d 100644 --- a/components/esp8266/include/esp8266/uart_register.h +++ b/components/esp8266/include/esp8266/uart_register.h @@ -1,31 +1,25 @@ -/* - * ESPRSSIF MIT License - * - * Copyright (c) 2015 - * - * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ +// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef UART_REGISTER_H_ #define UART_REGISTER_H_ -#include "eagle_soc.h" +#include "esp8266/eagle_soc.h" + +#ifdef __cplusplus +extern "C" { +#endif #define REG_UART_BASE(i) (0x60000000 + (i)*0xf00) //version value:32'h062000 @@ -78,6 +72,36 @@ #define UART_TXFIFO_EMPTY_INT_CLR (BIT(1)) #define UART_RXFIFO_FULL_INT_CLR (BIT(0)) +#define UART_RXFIFO_FULL_INT_ENA (BIT(0)) +#define UART_RXFIFO_FULL_INT_ENA_M (BIT(0)) +#define UART_RXFIFO_FULL_INT_ST_M (BIT(0)) +#define UART_RXFIFO_FULL_INT_CLR_M (BIT(0)) + +#define UART_TXFIFO_EMPTY_INT_ENA (BIT(1)) +#define UART_TXFIFO_EMPTY_INT_ENA_M (BIT(1)) +#define UART_TXFIFO_EMPTY_INT_ST_M (BIT(1)) +#define UART_TXFIFO_EMPTY_INT_CLR_M (BIT(1)) + +#define UART_PARITY_ERR_INT_ENA (BIT(2)) +#define UART_PARITY_ERR_INT_ENA_M (BIT(2)) +#define UART_PARITY_ERR_INT_ST_M (BIT(2)) +#define UART_PARITY_ERR_INT_CLR_M (BIT(2)) + +#define UART_FRM_ERR_INT_ENA (BIT(3)) +#define UART_FRM_ERR_INT_ENA_M (BIT(3)) +#define UART_FRM_ERR_INT_ST_M (BIT(3)) +#define UART_FRM_ERR_INT_CLR_M (BIT(3)) + +#define UART_RXFIFO_OVF_INT_ENA (BIT(4)) +#define UART_RXFIFO_OVF_INT_ENA_M (BIT(4)) +#define UART_RXFIFO_OVF_INT_ST_M (BIT(4)) +#define UART_RXFIFO_OVF_INT_CLR_M (BIT(4)) + +#define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) +#define UART_RXFIFO_TOUT_INT_ENA_M (BIT(8)) +#define UART_RXFIFO_TOUT_INT_ST_M (BIT(8)) +#define UART_RXFIFO_TOUT_INT_CLR_M (BIT(8)) + #define UART_CLKDIV(i) (REG_UART_BASE(i) + 0x14) #define UART_CLKDIV_CNT 0x000FFFFF #define UART_CLKDIV_S 0 @@ -124,7 +148,11 @@ #define UART_BIT_NUM 0x00000003 #define UART_BIT_NUM_S 2 #define UART_PARITY_EN (BIT(1)) +#define UART_PARITY_EN_M 0x00000001 +#define UART_PARITY_EN_S 1 #define UART_PARITY (BIT(0)) +#define UART_PARITY_M 0x00000001 +#define UART_PARITY_S 0 #define UART_CONF1(i) (REG_UART_BASE(i) + 0x24) #define UART_RX_TOUT_EN (BIT(31)) @@ -153,4 +181,11 @@ #define UART_DATE(i) (REG_UART_BASE(i) + 0x78) #define UART_ID(i) (REG_UART_BASE(i) + 0x7C) -#endif // UART_REGISTER_H_INCLUDED +#define UART_SWAP_REG 0x3FF00028 + +#ifdef __cplusplus +} +#endif /* end of __cplusplus */ + +#endif /* _UART_REGISTER_H_ */ + diff --git a/components/esp8266/include/esp8266/uart_struct.h b/components/esp8266/include/esp8266/uart_struct.h new file mode 100644 index 00000000..10eddc3f --- /dev/null +++ b/components/esp8266/include/esp8266/uart_struct.h @@ -0,0 +1,204 @@ +/* + * Copyright 2018 Espressif Systems (Shanghai) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _UART_STRUCT_H_ +#define _UART_STRUCT_H_ + +#include +#include "esp8266/eagle_soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ESP8266 UART Register Definitions */ + +typedef volatile struct { + union { + struct { + uint8_t rw_byte; /*This register stores one byte data read by rx fifo.*/ + uint8_t reserved[3]; + }; + __RO__ uint32_t val; + } fifo; + union { + struct { + uint32_t rxfifo_full: 1; /*This interrupt raw bit turns to high level when receiver receives more data than (rx_flow_thrhd_h3 rx_flow_thrhd).*/ + uint32_t txfifo_empty: 1; /*This interrupt raw bit turns to high level when the amount of data in transmitter's fifo is less than ((tx_mem_cnttxfifo_cnt) .*/ + uint32_t parity_err: 1; /*This interrupt raw bit turns to high level when receiver detects the parity error of data.*/ + uint32_t frm_err: 1; /*This interrupt raw bit turns to high level when receiver detects data's frame error .*/ + uint32_t rxfifo_ovf: 1; /*This interrupt raw bit turns to high level when receiver receives more data than the fifo can store.*/ + uint32_t dsr_chg: 1; /*This interrupt raw bit turns to high level when receiver detects the edge change of dsrn signal.*/ + uint32_t cts_chg: 1; /*This interrupt raw bit turns to high level when receiver detects the edge change of ctsn signal.*/ + uint32_t brk_det: 1; /*This interrupt raw bit turns to high level when receiver detects the 0 after the stop bit.*/ + uint32_t rxfifo_tout: 1; /*This interrupt raw bit turns to high level when receiver takes more time than rx_tout_thrhd to receive a byte.*/ + uint32_t reserved9: 23; + }; + __RO__ uint32_t val; + } int_raw; + union { + struct { + uint32_t rxfifo_full: 1; /*This is the status bit for rxfifo_full_int_raw when rxfifo_full_int_ena is set to 1.*/ + uint32_t txfifo_empty: 1; /*This is the status bit for txfifo_empty_int_raw when txfifo_empty_int_ena is set to 1.*/ + uint32_t parity_err: 1; /*This is the status bit for parity_err_int_raw when parity_err_int_ena is set to 1.*/ + uint32_t frm_err: 1; /*This is the status bit for frm_err_int_raw when fm_err_int_ena is set to 1.*/ + uint32_t rxfifo_ovf: 1; /*This is the status bit for rxfifo_ovf_int_raw when rxfifo_ovf_int_ena is set to 1.*/ + uint32_t dsr_chg: 1; /*This is the status bit for dsr_chg_int_raw when dsr_chg_int_ena is set to 1.*/ + uint32_t cts_chg: 1; /*This is the status bit for cts_chg_int_raw when cts_chg_int_ena is set to 1.*/ + uint32_t brk_det: 1; /*This is the status bit for brk_det_int_raw when brk_det_int_ena is set to 1.*/ + uint32_t rxfifo_tout: 1; /*This is the status bit for rxfifo_tout_int_raw when rxfifo_tout_int_ena is set to 1.*/ + uint32_t reserved9: 23; + }; + __RO__ uint32_t val; + } int_st; + union { + struct { + uint32_t rxfifo_full: 1; /*This is the enable bit for rxfifo_full_int_st register.*/ + uint32_t txfifo_empty: 1; /*This is the enable bit for rxfifo_full_int_st register.*/ + uint32_t parity_err: 1; /*This is the enable bit for parity_err_int_st register.*/ + uint32_t frm_err: 1; /*This is the enable bit for frm_err_int_st register.*/ + uint32_t rxfifo_ovf: 1; /*This is the enable bit for rxfifo_ovf_int_st register.*/ + uint32_t dsr_chg: 1; /*This is the enable bit for dsr_chg_int_st register.*/ + uint32_t cts_chg: 1; /*This is the enable bit for cts_chg_int_st register.*/ + uint32_t brk_det: 1; /*This is the enable bit for brk_det_int_st register.*/ + uint32_t rxfifo_tout: 1; /*This is the enable bit for rxfifo_tout_int_st register.*/ + uint32_t reserved9: 23; + }; + __RW__ uint32_t val; + } int_ena; + union { + struct { + uint32_t rxfifo_full: 1; /*Set this bit to clear the rxfifo_full_int_raw interrupt.*/ + uint32_t txfifo_empty: 1; /*Set this bit to clear txfifo_empty_int_raw interrupt.*/ + uint32_t parity_err: 1; /*Set this bit to clear parity_err_int_raw interrupt.*/ + uint32_t frm_err: 1; /*Set this bit to clear frm_err_int_raw interrupt.*/ + uint32_t rxfifo_ovf: 1; /*Set this bit to clear rxfifo_ovf_int_raw interrupt.*/ + uint32_t dsr_chg: 1; /*Set this bit to clear the dsr_chg_int_raw interrupt.*/ + uint32_t cts_chg: 1; /*Set this bit to clear the cts_chg_int_raw interrupt.*/ + uint32_t brk_det: 1; /*Set this bit to clear the brk_det_int_raw interrupt.*/ + uint32_t rxfifo_tout: 1; /*Set this bit to clear the rxfifo_tout_int_raw interrupt.*/ + uint32_t reserved9: 23; + }; + __WO__ uint32_t val; + } int_clr; + union { + struct { + uint32_t div_int: 20; /*The register value is the integer part of the frequency divider's factor.*/ + uint32_t reserved20: 12; + }; + __RW__ uint32_t val; + } clk_div; + union { + struct { + uint32_t en: 1; /*This is the enable bit for detecting baudrate.*/ + uint32_t reserved1: 7; + uint32_t glitch_filt: 8; /*when input pulse width is lower then this value ignore this pulse.this register is used in auto-baud detect process.*/ + uint32_t reserved16: 16; + }; + __RW__ uint32_t val; + } auto_baud; + union { + struct { + uint32_t rxfifo_cnt: 8; /*(rx_mem_cnt rxfifo_cnt) stores the byte number of valid data in receiver's fifo. rx_mem_cnt register stores the 3 most significant bits rxfifo_cnt stores the 8 least significant bits.*/ + uint32_t reserved8: 5; + uint32_t dsrn: 1; /*This register stores the level value of the internal uart dsr signal.*/ + uint32_t ctsn: 1; /*This register stores the level value of the internal uart cts signal.*/ + uint32_t rxd: 1; /*This register stores the level value of the internal uart rxd signal.*/ + uint32_t txfifo_cnt: 8; /*(tx_mem_cnt txfifo_cnt) stores the byte number of valid data in transmitter's fifo.tx_mem_cnt stores the 3 most significant bits txfifo_cnt stores the 8 least significant bits.*/ + uint32_t reserved24: 5; + uint32_t dtrn: 1; /*The register represent the level value of the internal uart dsr signal.*/ + uint32_t rtsn: 1; /*This register represent the level value of the internal uart cts signal.*/ + uint32_t txd: 1; /*This register represent the level value of the internal uart rxd signal.*/ + }; + __RO__ uint32_t val; + } status; + union { + struct { + uint32_t parity: 1; /*This register is used to configure the parity check mode. 0:even 1:odd*/ + uint32_t parity_en: 1; /*Set this bit to enable uart parity check.*/ + uint32_t bit_num: 2; /*This register is used to set the length of data: 0:5bits 1:6bits 2:7bits 3:8bits*/ + uint32_t stop_bit_num: 2; /*This register is used to set the length of stop bit. 1:1bit 2:1.5bits 3:2bits*/ + uint32_t sw_rts: 1; /*This register is used to configure the software rts signal which is used in software flow control.*/ + uint32_t sw_dtr: 1; /*This register is used to configure the software dtr signal which is used in software flow control..*/ + uint32_t txd_brk: 1; /*Set this bit to enable transmitter to send 0 when the process of sending data is done.*/ + uint32_t irda_dplx: 1; /*Set this bit to enable irda loop-back mode.*/ + uint32_t irda_tx_en: 1; /*This is the start enable bit for irda transmitter.*/ + uint32_t irda_wctl: 1; /*1:the irda transmitter's 11th bit is the same to the 10th bit. 0:set irda transmitter's 11th bit to 0.*/ + uint32_t irda_tx_inv: 1; /*Set this bit to inverse the level value of irda transmitter's level.*/ + uint32_t irda_rx_inv: 1; /*Set this bit to inverse the level value of irda receiver's level.*/ + uint32_t loopback: 1; /*Set this bit to enable uart loop-back test mode.*/ + uint32_t tx_flow_en: 1; /*Set this bit to enable transmitter's flow control function.*/ + uint32_t irda_en: 1; /*Set this bit to enable irda protocol.*/ + uint32_t rxfifo_rst: 1; /*Set this bit to reset uart receiver's fifo.*/ + uint32_t txfifo_rst: 1; /*Set this bit to reset uart transmitter's fifo.*/ + uint32_t rxd_inv: 1; /*Set this bit to inverse the level value of uart rxd signal.*/ + uint32_t cts_inv: 1; /*Set this bit to inverse the level value of uart cts signal.*/ + uint32_t dsr_inv: 1; /*Set this bit to inverse the level value of uart dsr signal.*/ + uint32_t txd_inv: 1; /*Set this bit to inverse the level value of uart txd signal.*/ + uint32_t rts_inv: 1; /*Set this bit to inverse the level value of uart rts signal.*/ + uint32_t dtr_inv: 1; /*Set this bit to inverse the level value of uart dtr signal.*/ + uint32_t reserved25: 7; + }; + __RW__ uint32_t val; + } conf0; + union { + struct { + uint32_t rxfifo_full_thrhd: 7; /*When receiver receives more data than its threshold value,receiver will produce rxfifo_full_int_raw interrupt.the threshold value is (rx_flow_thrhd_h3 rxfifo_full_thrhd).*/ + uint32_t reserved7: 1; + uint32_t txfifo_empty_thrhd: 7; /*when the data amount in transmitter fifo is less than its threshold value, it will produce txfifo_empty_int_raw interrupt. the threshold value is (tx_mem_empty_thresh txfifo_empty_thrhd)*/ + uint32_t reserved15: 1; + uint32_t rx_flow_thrhd: 7; /*when receiver receives more data than its threshold value, receiver produce signal to tell the transmitter stop transferring data. the threshold value is (rx_flow_thrhd_h3 rx_flow_thrhd).*/ + uint32_t rx_flow_en: 1; /*This is the flow enable bit for uart receiver. 1:choose software flow control with configuring sw_rts signal*/ + uint32_t rx_tout_thrhd: 7; /*This register is used to configure the timeout value for uart receiver receiving a byte.*/ + uint32_t rx_tout_en: 1; /*This is the enable bit for uart receiver's timeout function.*/ + }; + __RW__ uint32_t val; + } conf1; + union { + struct { + uint32_t min_cnt: 20; /*This register stores the value of the minimum duration time for the low level pulse, it is used in baudrate-detect process.*/ + uint32_t reserved20: 12; + }; + __RO__ uint32_t val; + } lowpulse; + union { + struct { + uint32_t min_cnt: 20; /*This register stores the value of the maximum duration time for the high level pulse, it is used in baudrate-detect process.*/ + uint32_t reserved20: 12; + }; + __RO__ uint32_t val; + } highpulse; + union { + struct { + uint32_t edge_cnt: 10; /*This register stores the count of rxd edge change, it is used in baudrate-detect process.*/ + uint32_t reserved10: 22; + }; + __RO__ uint32_t val; + } rxd_cnt; + uint32_t reserved[18]; + __RW__ uint32_t date; /**/ + __RW__ uint32_t id; /**/ +} uart_dev_t; + +extern uart_dev_t uart0; +extern uart_dev_t uart1; + +#ifdef __cplusplus +} +#endif /* end of __cplusplus */ + +#endif /* _UART_STRUCT_H_ */ diff --git a/components/esp8266/ld/esp8266.peripherals.ld b/components/esp8266/ld/esp8266.peripherals.ld index 7f6e6809..57329602 100644 --- a/components/esp8266/ld/esp8266.peripherals.ld +++ b/components/esp8266/ld/esp8266.peripherals.ld @@ -1 +1,4 @@ -PROVIDE ( GPIO = 0x60000300); \ No newline at end of file +PROVIDE ( GPIO = 0x60000300); + +PROVIDE ( uart0 = 0x60000000 ); +PROVIDE ( uart1 = 0x60000f00 ); \ No newline at end of file diff --git a/components/freertos/freertos/ringbuf.c b/components/freertos/freertos/ringbuf.c new file mode 100644 index 00000000..fb6bc07e --- /dev/null +++ b/components/freertos/freertos/ringbuf.c @@ -0,0 +1,1184 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "ringbuf.h" + +//32-bit alignment macros +#define rbALIGN_SIZE( xSize ) ( ( xSize + portBYTE_ALIGNMENT_MASK ) & ~portBYTE_ALIGNMENT_MASK ) +#define rbCHECK_ALIGNED( pvPtr ) ( ( ( UBaseType_t ) pvPtr & portBYTE_ALIGNMENT_MASK ) == 0 ) + +//Ring buffer flags +#define rbALLOW_SPLIT_FLAG ( ( UBaseType_t ) 1 ) //The ring buffer allows items to be split +#define rbBYTE_BUFFER_FLAG ( ( UBaseType_t ) 2 ) //The ring buffer is a byte buffer +#define rbBUFFER_FULL_FLAG ( ( UBaseType_t ) 4 ) //The ring buffer is currently full (write pointer == free pointer) + +//Item flags +#define rbITEM_FREE_FLAG ( ( UBaseType_t ) 1 ) //Item has been retrieved and returned by application, free to overwrite +#define rbITEM_DUMMY_DATA_FLAG ( ( UBaseType_t ) 2 ) //Data from here to end of the ring buffer is dummy data. Restart reading at start of head of the buffer +#define rbITEM_SPLIT_FLAG ( ( UBaseType_t ) 4 ) //Valid for RINGBUF_TYPE_ALLOWSPLIT, indicating that rest of the data is wrapped around + +typedef struct { + //This size of this structure must be 32-bit aligned + size_t xItemLen; + UBaseType_t uxItemFlags; +} ItemHeader_t; + +#define rbHEADER_SIZE sizeof(ItemHeader_t) +typedef struct Ringbuffer_t Ringbuffer_t; +typedef BaseType_t (*CheckItemFitsFunction_t)(Ringbuffer_t *pxRingbuffer, size_t xItemSize); +typedef void (*CopyItemFunction_t)(Ringbuffer_t *pxRingbuffer, const uint8_t *pcItem, size_t xItemSize); +typedef BaseType_t (*CheckItemAvailFunction_t) (Ringbuffer_t *pxRingbuffer); +typedef void *(*GetItemFunction_t)(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xMaxSize, size_t *pxItemSize); +typedef void (*ReturnItemFunction_t)(Ringbuffer_t *pxRingbuffer, uint8_t *pvItem); +typedef size_t (*GetCurMaxSizeFunction_t)(Ringbuffer_t *pxRingbuffer); + +struct Ringbuffer_t { + size_t xSize; //Size of the data storage + UBaseType_t uxRingbufferFlags; //Flags to indicate the type and status of ring buffer + size_t xMaxItemSize; //Maximum item size + + CheckItemFitsFunction_t xCheckItemFits; //Function to check if item can currently fit in ring buffer + CopyItemFunction_t vCopyItem; //Function to copy item to ring buffer + GetItemFunction_t pvGetItem; //Function to get item from ring buffer + ReturnItemFunction_t vReturnItem; //Function to return item to ring buffer + GetCurMaxSizeFunction_t xGetCurMaxSize; //Function to get current free size + + uint8_t *pucWrite; //Write Pointer. Points to where the next item should be written + uint8_t *pucRead; //Read Pointer. Points to where the next item should be read from + uint8_t *pucFree; //Free Pointer. Points to the last item that has yet to be returned to the ring buffer + uint8_t *pucHead; //Pointer to the start of the ring buffer storage area + uint8_t *pucTail; //Pointer to the end of the ring buffer storage area + + BaseType_t xItemsWaiting; //Number of items/bytes(for byte buffers) currently in ring buffer that have not yet been read + SemaphoreHandle_t xFreeSpaceSemaphore; //Binary semaphore, wakes up writing threads when more free space becomes available or when another thread times out attempting to write + SemaphoreHandle_t xItemsBufferedSemaphore; //Binary semaphore, indicates there are new packets in the circular buffer. See remark. +}; + +/* +Remark: A counting semaphore for items_buffered_sem would be more logical, but counting semaphores in +FreeRTOS need a maximum count, and allocate more memory the larger the maximum count is. Here, we +would need to set the maximum to the maximum amount of times a null-byte unit first in the buffer, +which is quite high and so would waste a fair amount of memory. +*/ + +/* ------------------------------------------------ Static Declarations ------------------------------------------ */ +/* + * WARNING: All of the following static functions (except generic functions) + * ARE NOT THREAD SAFE. Therefore they should only be called within a critical + * section (using spin locks) + */ + +//Calculate current amount of free space (in bytes) in the ring buffer +static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer); + +//Checks if an item/data is currently available for retrieval +static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer); + +//Checks if an item will currently fit in a no-split/allow-split ring buffer +static BaseType_t prvCheckItemFitsDefault( Ringbuffer_t *pxRingbuffer, size_t xItemSize); + +//Checks if an item will currently fit in a byte buffer +static BaseType_t prvCheckItemFitsByteBuffer( Ringbuffer_t *pxRingbuffer, size_t xItemSize); + +//Copies an item to a no-split ring buffer. Only call this function after calling prvCheckItemFitsDefault() +static void prvCopyItemNoSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize); + +//Copies an item to a allow-split ring buffer. Only call this function after calling prvCheckItemFitsDefault() +static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize); + +//Copies an item to a byte buffer. Only call this function after calling prvCheckItemFitsByteBuffer() +static void prvCopyItemByteBuf(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize); + +//Retrieve item from no-split/allow-split ring buffer. *pxIsSplit is set to pdTRUE if the retrieved item is split +static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xUnusedParam, size_t *pxItemSize); + +//Retrieve data from byte buffer. If xMaxSize is 0, all continuous data is retrieved +static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer, BaseType_t *pxUnusedParam ,size_t xMaxSize, size_t *pxItemSize); + +//Return an item to a split/no-split ring buffer +static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem); + +//Return data to a byte buffer +static void prvReturnItemByteBuf(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem); + +//Get the maximum size an item that can currently have if sent to a no-split ring buffer +static size_t prvGetCurMaxSizeNoSplit(Ringbuffer_t *pxRingbuffer); + +//Get the maximum size an item that can currently have if sent to a allow-split ring buffer +static size_t prvGetCurMaxSizeAllowSplit(Ringbuffer_t *pxRingbuffer); + +//Get the maximum size an item that can currently have if sent to a byte buffer +static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer); + +/** + * Generic function used to retrieve an item/data from ring buffers. If called on + * an allow-split buffer, and pvItem2 and xItemSize2 are not NULL, both parts of + * a split item will be retrieved. xMaxSize will only take effect if called on + * byte buffers. + */ +static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize, TickType_t xTicksToWait); + +//Generic function used to retrieve an item/data from ring buffers in an ISR +static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize); + +/* ------------------------------------------------ Static Definitions ------------------------------------------- */ + +static size_t prvGetFreeSize(Ringbuffer_t *pxRingbuffer) +{ + size_t xReturn; + if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) { + xReturn = 0; + } else { + BaseType_t xFreeSize = pxRingbuffer->pucFree - pxRingbuffer->pucWrite; + //Check if xFreeSize has underflowed + if (xFreeSize <= 0) { + xFreeSize += pxRingbuffer->xSize; + } + xReturn = xFreeSize; + } + configASSERT(xReturn <= pxRingbuffer->xSize); + return xReturn; +} + +static BaseType_t prvCheckItemFitsDefault( Ringbuffer_t *pxRingbuffer, size_t xItemSize) +{ + //Check arguments and buffer state + configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucWrite)); //pucWrite is always aligned in no-split ring buffers + configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail); //Check write pointer is within bounds + + size_t xTotalItemSize = rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE; //Rounded up aligned item size with header + if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) { + //Buffer is either complete empty or completely full + return (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) ? pdFALSE : pdTRUE; + } + if (pxRingbuffer->pucFree > pxRingbuffer->pucWrite) { + //Free space does not wrap around + return (xTotalItemSize <= pxRingbuffer->pucFree - pxRingbuffer->pucWrite) ? pdTRUE : pdFALSE; + } + //Free space wraps around + if (xTotalItemSize <= pxRingbuffer->pucTail - pxRingbuffer->pucWrite) { + return pdTRUE; //Item fits without wrapping around + } + //Check if item fits by wrapping + if (pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) { + //Allow split wrapping incurs an extra header + return (xTotalItemSize + rbHEADER_SIZE <= pxRingbuffer->xSize - (pxRingbuffer->pucWrite - pxRingbuffer->pucFree)) ? pdTRUE : pdFALSE; + } else { + return (xTotalItemSize <= pxRingbuffer->pucFree - pxRingbuffer->pucHead) ? pdTRUE : pdFALSE; + } +} + +static BaseType_t prvCheckItemFitsByteBuffer( Ringbuffer_t *pxRingbuffer, size_t xItemSize) +{ + //Check arguments and buffer state + configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail); //Check write pointer is within bounds + + if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) { + //Buffer is either complete empty or completely full + return (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) ? pdFALSE : pdTRUE; + } + if (pxRingbuffer->pucFree > pxRingbuffer->pucWrite) { + //Free space does not wrap around + return (xItemSize <= pxRingbuffer->pucFree - pxRingbuffer->pucWrite) ? pdTRUE : pdFALSE; + } + //Free space wraps around + return (xItemSize <= pxRingbuffer->xSize - (pxRingbuffer->pucWrite - pxRingbuffer->pucFree)) ? pdTRUE : pdFALSE; +} + +static void prvCopyItemNoSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize) +{ + //Check arguments and buffer state + size_t xAlignedItemSize = rbALIGN_SIZE(xItemSize); //Rounded up aligned item size + size_t xRemLen = pxRingbuffer->pucTail - pxRingbuffer->pucWrite; //Length from pucWrite until end of buffer + configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucWrite)); //pucWrite is always aligned in no-split ring buffers + configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail); //Check write pointer is within bounds + configASSERT(xRemLen >= rbHEADER_SIZE); //Remaining length must be able to at least fit an item header + + //If remaining length can't fit item, set as dummy data and wrap around + if (xRemLen < xAlignedItemSize + rbHEADER_SIZE) { + ItemHeader_t *pxDummy = (ItemHeader_t *)pxRingbuffer->pucWrite; + pxDummy->uxItemFlags = rbITEM_DUMMY_DATA_FLAG; //Set remaining length as dummy data + pxDummy->xItemLen = 0; //Dummy data should have no length + pxRingbuffer->pucWrite = pxRingbuffer->pucHead; //Reset write pointer to wrap around + } + + //Item should be guaranteed to fit at this point. Set item header and copy data + ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucWrite; + pxHeader->xItemLen = xItemSize; + pxHeader->uxItemFlags = 0; + pxRingbuffer->pucWrite += rbHEADER_SIZE; //Advance pucWrite past header + memcpy(pxRingbuffer->pucWrite, pucItem, xItemSize); + pxRingbuffer->xItemsWaiting++; + pxRingbuffer->pucWrite += xAlignedItemSize; //Advance pucWrite past item to next aligned address + + //If current remaining length can't fit a header, wrap around write pointer + if (pxRingbuffer->pucTail - pxRingbuffer->pucWrite < rbHEADER_SIZE) { + pxRingbuffer->pucWrite = pxRingbuffer->pucHead; //Wrap around pucWrite + } + //Check if buffer is full + if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) { + //Mark the buffer as full to distinguish with an empty buffer + pxRingbuffer->uxRingbufferFlags |= rbBUFFER_FULL_FLAG; + } +} + +static void prvCopyItemAllowSplit(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize) +{ + //Check arguments and buffer state + size_t xAlignedItemSize = rbALIGN_SIZE(xItemSize); //Rounded up aligned item size + size_t xRemLen = pxRingbuffer->pucTail - pxRingbuffer->pucWrite; //Length from pucWrite until end of buffer + configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucWrite)); //pucWrite is always aligned in split ring buffers + configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail); //Check write pointer is within bounds + configASSERT(xRemLen >= rbHEADER_SIZE); //Remaining length must be able to at least fit an item header + + //Split item if necessary + if (xRemLen < xAlignedItemSize + rbHEADER_SIZE) { + //Write first part of the item + ItemHeader_t *pxFirstHeader = (ItemHeader_t *)pxRingbuffer->pucWrite; + pxFirstHeader->uxItemFlags = 0; + pxFirstHeader->xItemLen = xRemLen - rbHEADER_SIZE; //Fill remaining length with first part + pxRingbuffer->pucWrite += rbHEADER_SIZE; //Advance pucWrite past header + xRemLen -= rbHEADER_SIZE; + if (xRemLen > 0) { + memcpy(pxRingbuffer->pucWrite, pucItem, xRemLen); + pxRingbuffer->xItemsWaiting++; + //Update item arguments to account for data already copied + pucItem += xRemLen; + xItemSize -= xRemLen; + xAlignedItemSize -= xRemLen; + pxFirstHeader->uxItemFlags |= rbITEM_SPLIT_FLAG; //There must be more data + } else { + //Remaining length was only large enough to fit header + pxFirstHeader->uxItemFlags |= rbITEM_DUMMY_DATA_FLAG; //Item will completely be stored in 2nd part + } + pxRingbuffer->pucWrite = pxRingbuffer->pucHead; //Reset write pointer to start of buffer + } + + //Item (whole or second part) should be guaranteed to fit at this point + ItemHeader_t *pxSecondHeader = (ItemHeader_t *)pxRingbuffer->pucWrite; + pxSecondHeader->xItemLen = xItemSize; + pxSecondHeader->uxItemFlags = 0; + pxRingbuffer->pucWrite += rbHEADER_SIZE; //Advance write pointer past header + memcpy(pxRingbuffer->pucWrite, pucItem, xItemSize); + pxRingbuffer->xItemsWaiting++; + pxRingbuffer->pucWrite += xAlignedItemSize; //Advance pucWrite past item to next aligned address + + //If current remaining length can't fit a header, wrap around write pointer + if (pxRingbuffer->pucTail - pxRingbuffer->pucWrite < rbHEADER_SIZE) { + pxRingbuffer->pucWrite = pxRingbuffer->pucHead; //Wrap around pucWrite + } + //Check if buffer is full + if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) { + //Mark the buffer as full to distinguish with an empty buffer + pxRingbuffer->uxRingbufferFlags |= rbBUFFER_FULL_FLAG; + } +} + +static void prvCopyItemByteBuf(Ringbuffer_t *pxRingbuffer, const uint8_t *pucItem, size_t xItemSize) +{ + //Check arguments and buffer state + configASSERT(pxRingbuffer->pucWrite >= pxRingbuffer->pucHead && pxRingbuffer->pucWrite < pxRingbuffer->pucTail); //Check write pointer is within bounds + + size_t xRemLen = pxRingbuffer->pucTail - pxRingbuffer->pucWrite; //Length from pucWrite until end of buffer + if (xRemLen < xItemSize) { + //Copy as much as possible into remaining length + memcpy(pxRingbuffer->pucWrite, pucItem, xRemLen); + pxRingbuffer->xItemsWaiting += xRemLen; + //Update item arguments to account for data already written + pucItem += xRemLen; + xItemSize -= xRemLen; + pxRingbuffer->pucWrite = pxRingbuffer->pucHead; //Reset write pointer to start of buffer + } + //Copy all or remaining portion of the item + memcpy(pxRingbuffer->pucWrite, pucItem, xItemSize); + pxRingbuffer->xItemsWaiting += xItemSize; + pxRingbuffer->pucWrite += xItemSize; + + //Wrap around pucWrite if it reaches the end + if (pxRingbuffer->pucWrite == pxRingbuffer->pucTail) { + pxRingbuffer->pucWrite = pxRingbuffer->pucHead; + } + //Check if buffer is full + if (pxRingbuffer->pucWrite == pxRingbuffer->pucFree) { + pxRingbuffer->uxRingbufferFlags |= rbBUFFER_FULL_FLAG; //Mark the buffer as full to avoid confusion with an empty buffer + } +} + +static BaseType_t prvCheckItemAvail(Ringbuffer_t *pxRingbuffer) +{ + if ((pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) && pxRingbuffer->pucRead != pxRingbuffer->pucFree) { + return pdFALSE; //Byte buffers do not allow multiple retrievals before return + } + if ((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))) { + return pdTRUE; //Items/data available for retrieval + } else { + return pdFALSE; //No items/data available for retrieval + } +} + +static void *prvGetItemDefault(Ringbuffer_t *pxRingbuffer, BaseType_t *pxIsSplit, size_t xUnusedParam, size_t *pxItemSize) +{ + //Check arguments and buffer state + ItemHeader_t *pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead; + configASSERT(pxIsSplit != NULL); + configASSERT((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))); //Check there are items to be read + configASSERT(rbCHECK_ALIGNED(pxRingbuffer->pucRead)); //pucRead is always aligned in split ring buffers + configASSERT(pxRingbuffer->pucRead >= pxRingbuffer->pucHead && pxRingbuffer->pucRead < pxRingbuffer->pucTail); //Check read pointer is within bounds + configASSERT((pxHeader->xItemLen <= pxRingbuffer->xMaxItemSize) || (pxHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG)); + + uint8_t *pcReturn; + //Wrap around if dummy data (dummy data indicates wrap around in no-split buffers) + if (pxHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) { + pxRingbuffer->pucRead = pxRingbuffer->pucHead; + //Check for errors with the next item + pxHeader = (ItemHeader_t *)pxRingbuffer->pucRead; + configASSERT(pxHeader->xItemLen <= pxRingbuffer->xMaxItemSize); + } + pcReturn = pxRingbuffer->pucRead + rbHEADER_SIZE; //Get pointer to part of item containing data (point past the header) + if (pxHeader->xItemLen == 0) { + //Inclusive of pucTail for special case where item of zero length just fits at the end of the buffer + configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn <= pxRingbuffer->pucTail); + } else { + //Exclusive of pucTali if length is larger than zero, pcReturn should never point to pucTail + configASSERT(pcReturn >= pxRingbuffer->pucHead && pcReturn < pxRingbuffer->pucTail); + } + *pxItemSize = pxHeader->xItemLen; //Get length of item + pxRingbuffer->xItemsWaiting --; //Update item count + *pxIsSplit = (pxHeader->uxItemFlags & rbITEM_SPLIT_FLAG) ? pdTRUE : pdFALSE; + + pxRingbuffer->pucRead += rbHEADER_SIZE + rbALIGN_SIZE(pxHeader->xItemLen); //Update pucRead + //Check if pucRead requires wrap around + if ((pxRingbuffer->pucTail - pxRingbuffer->pucRead) < rbHEADER_SIZE) { + pxRingbuffer->pucRead = pxRingbuffer->pucHead; + } + return (void *)pcReturn; +} + +static void *prvGetItemByteBuf(Ringbuffer_t *pxRingbuffer, BaseType_t *pxUnusedParam ,size_t xMaxSize, size_t *pxItemSize) +{ + //Check arguments and buffer state + configASSERT((pxRingbuffer->xItemsWaiting > 0) && ((pxRingbuffer->pucRead != pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG))); //Check there are items to be read + configASSERT(pxRingbuffer->pucRead >= pxRingbuffer->pucHead && pxRingbuffer->pucRead < pxRingbuffer->pucTail); //Check read pointer is within bounds + configASSERT(pxRingbuffer->pucRead == pxRingbuffer->pucFree); + + uint8_t *ret = pxRingbuffer->pucRead; + if ((pxRingbuffer->pucRead > pxRingbuffer->pucWrite) || (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG)) { //Available data wraps around + //Return contiguous piece from read pointer until buffer tail, or xMaxSize + if (xMaxSize == 0 || pxRingbuffer->pucTail - pxRingbuffer->pucRead <= xMaxSize) { + //All contiguous data from read pointer to tail + *pxItemSize = pxRingbuffer->pucTail - pxRingbuffer->pucRead; + pxRingbuffer->xItemsWaiting -= pxRingbuffer->pucTail - pxRingbuffer->pucRead; + pxRingbuffer->pucRead = pxRingbuffer->pucHead; //Wrap around read pointer + } else { + //Return xMaxSize amount of data + *pxItemSize = xMaxSize; + pxRingbuffer->xItemsWaiting -= xMaxSize; + pxRingbuffer->pucRead += xMaxSize; //Advance read pointer past retrieved data + } + } else { //Available data is contiguous between read and write pointer + if (xMaxSize == 0 || pxRingbuffer->pucWrite - pxRingbuffer->pucRead <= xMaxSize) { + //Return all contiguous data from read to write pointer + *pxItemSize = pxRingbuffer->pucWrite - pxRingbuffer->pucRead; + pxRingbuffer->xItemsWaiting -= pxRingbuffer->pucWrite - pxRingbuffer->pucRead; + pxRingbuffer->pucRead = pxRingbuffer->pucWrite; + } else { + //Return xMaxSize data from read pointer + *pxItemSize = xMaxSize; + pxRingbuffer->xItemsWaiting -= xMaxSize; + pxRingbuffer->pucRead += xMaxSize; //Advance read pointer past retrieved data + + } + } + return (void *)ret; +} + +static void prvReturnItemDefault(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem) +{ + //Check arguments and buffer state + configASSERT(rbCHECK_ALIGNED(pucItem)); + configASSERT(pucItem >= pxRingbuffer->pucHead); + configASSERT(pucItem <= pxRingbuffer->pucTail); //Inclusive of pucTail in the case of zero length item at the very end + + //Get and check header of the item + ItemHeader_t *pxCurHeader = (ItemHeader_t *)(pucItem - rbHEADER_SIZE); + configASSERT(pxCurHeader->xItemLen <= pxRingbuffer->xMaxItemSize); + configASSERT((pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) == 0); //Dummy items should never have been read + configASSERT((pxCurHeader->uxItemFlags & rbITEM_FREE_FLAG) == 0); //Indicates item has already been returned before + pxCurHeader->uxItemFlags &= ~rbITEM_SPLIT_FLAG; //Clear wrap flag if set (not strictly necessary) + pxCurHeader->uxItemFlags |= rbITEM_FREE_FLAG; //Mark as free + + /* + * Items might not be returned in the order they were retrieved. Move the free pointer + * up to the next item that has not been marked as free (by free flag) or up + * till the read pointer. When advancing the free pointer, items that have already been + * freed or items with dummy data should be skipped over + */ + pxCurHeader = (ItemHeader_t *)pxRingbuffer->pucFree; + //Skip over Items that have already been freed or are dummy items + while (((pxCurHeader->uxItemFlags & rbITEM_FREE_FLAG) || (pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG)) && pxRingbuffer->pucFree != pxRingbuffer->pucRead) { + if (pxCurHeader->uxItemFlags & rbITEM_DUMMY_DATA_FLAG) { + pxCurHeader->uxItemFlags |= rbITEM_FREE_FLAG; //Mark as freed (not strictly necessary but adds redundancy) + pxRingbuffer->pucFree = pxRingbuffer->pucHead; //Wrap around due to dummy data + } else { + //Item with data that has already been freed, advance free pointer past this item + size_t xAlignedItemSize = rbALIGN_SIZE(pxCurHeader->xItemLen); + pxRingbuffer->pucFree += xAlignedItemSize + rbHEADER_SIZE; + //Redundancy check to ensure free pointer has not overshot buffer bounds + configASSERT(pxRingbuffer->pucFree <= pxRingbuffer->pucHead + pxRingbuffer->xSize); + } + //Check if pucRead requires wrap around + if ((pxRingbuffer->pucTail - pxRingbuffer->pucFree) < rbHEADER_SIZE) { + pxRingbuffer->pucFree = pxRingbuffer->pucHead; + } + pxCurHeader = (ItemHeader_t *)pxRingbuffer->pucFree; //Update header to point to item + } + + //Check if the buffer full flag should be reset + if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) { + if (pxRingbuffer->pucFree != pxRingbuffer->pucWrite) { + pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG; + } else if (pxRingbuffer->pucFree == pxRingbuffer->pucWrite && pxRingbuffer->pucFree == pxRingbuffer->pucRead) { + //Special case where a full buffer is completely freed in one go + pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG; + } + } +} + +static void prvReturnItemByteBuf(Ringbuffer_t *pxRingbuffer, uint8_t *pucItem) +{ + //Check pointer points to address inside buffer + configASSERT((uint8_t *)pucItem >= pxRingbuffer->pucHead); + configASSERT((uint8_t *)pucItem < pxRingbuffer->pucTail); + //Free the read memory. Simply moves free pointer to read pointer as byte buffers do not allow multiple outstanding reads + pxRingbuffer->pucFree = pxRingbuffer->pucRead; + //If buffer was full before, reset full flag as free pointer has moved + if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) { + pxRingbuffer->uxRingbufferFlags &= ~rbBUFFER_FULL_FLAG; + } +} + +static size_t prvGetCurMaxSizeNoSplit(Ringbuffer_t *pxRingbuffer) +{ + BaseType_t xFreeSize; + //Check if buffer is full + if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) { + return 0; + } + if (pxRingbuffer->pucWrite < pxRingbuffer->pucFree) { + //Free space is contiguous between pucWrite and pucFree + xFreeSize = pxRingbuffer->pucFree - pxRingbuffer->pucWrite; + } else { + //Free space wraps around (or overlapped at pucHead), select largest + //contiguous free space as no-split items require contiguous space + size_t xSize1 = pxRingbuffer->pucTail - pxRingbuffer->pucWrite; + size_t xSize2 = pxRingbuffer->pucFree - pxRingbuffer->pucHead; + xFreeSize = (xSize1 > xSize2) ? xSize1 : xSize2; + } + + //No-split ring buffer items need space for a header + xFreeSize -= rbHEADER_SIZE; + //Limit free size to be within bounds + if (xFreeSize > pxRingbuffer->xMaxItemSize) { + xFreeSize = pxRingbuffer->xMaxItemSize; + } else if (xFreeSize < 0) { + //Occurs when free space is less than header size + xFreeSize = 0; + } + return xFreeSize; +} + +static size_t prvGetCurMaxSizeAllowSplit(Ringbuffer_t *pxRingbuffer) +{ + BaseType_t xFreeSize; + //Check if buffer is full + if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) { + return 0; + } + if (pxRingbuffer->pucWrite == pxRingbuffer->pucHead && pxRingbuffer->pucFree == pxRingbuffer->pucHead) { + //Check for special case where pucWrite and pucFree are both at pucHead + xFreeSize = pxRingbuffer->xSize - rbHEADER_SIZE; + } else if (pxRingbuffer->pucWrite < pxRingbuffer->pucFree) { + //Free space is contiguous between pucWrite and pucFree, requires single header + xFreeSize = (pxRingbuffer->pucFree - pxRingbuffer->pucWrite) - rbHEADER_SIZE; + } else { + //Free space wraps around, requires two headers + xFreeSize = (pxRingbuffer->pucFree - pxRingbuffer->pucHead) + + (pxRingbuffer->pucTail - pxRingbuffer->pucWrite) - + (rbHEADER_SIZE * 2); + } + + //Limit free size to be within bounds + if (xFreeSize > pxRingbuffer->xMaxItemSize) { + xFreeSize = pxRingbuffer->xMaxItemSize; + } else if (xFreeSize < 0) { + xFreeSize = 0; + } + return xFreeSize; +} + +static size_t prvGetCurMaxSizeByteBuf(Ringbuffer_t *pxRingbuffer) +{ + BaseType_t xFreeSize; + //Check if buffer is full + if (pxRingbuffer->uxRingbufferFlags & rbBUFFER_FULL_FLAG) { + return 0; + } + + /* + * Return whatever space is available depending on relative positions of the free + * pointer and write pointer. There is no overhead of headers in this mode + */ + xFreeSize = pxRingbuffer->pucFree - pxRingbuffer->pucWrite; + if (xFreeSize <= 0) { + xFreeSize += pxRingbuffer->xSize; + } + return xFreeSize; +} + +static BaseType_t prvReceiveGeneric(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize, TickType_t xTicksToWait) +{ + BaseType_t xReturn = pdFALSE; + BaseType_t xReturnSemaphore = pdFALSE; + TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait; + TickType_t xTicksRemaining = xTicksToWait; + while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end + //Block until more free space becomes available or timeout + if (xSemaphoreTake(pxRingbuffer->xItemsBufferedSemaphore, xTicksRemaining) != pdTRUE) { + xReturn = pdFALSE; //Timed out attempting to get semaphore + break; + } + + //Semaphore obtained, check if item can be retrieved + taskENTER_CRITICAL(); + if (prvCheckItemAvail(pxRingbuffer) == pdTRUE) { + //Item is available for retrieval + BaseType_t xIsSplit; + if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) { + //Second argument (pxIsSplit) is unused for byte buffers + *pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1); + } else { + //Third argument (xMaxSize) is unused for no-split/allow-split buffers + *pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1); + } + //Check for item split if configured to do so + if ((pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) && (pvItem2 != NULL) && (xItemSize2 != NULL)) { + if (xIsSplit == pdTRUE) { + *pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2); + configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred + configASSERT(xIsSplit == pdFALSE); //Second part should not have wrapped flag + } else { + *pvItem2 = NULL; + } + } + xReturn = pdTRUE; + if (pxRingbuffer->xItemsWaiting > 0) { + xReturnSemaphore = pdTRUE; + } + taskEXIT_CRITICAL(); + break; + } + //No item available for retrieval, adjust ticks and take the semaphore again + if (xTicksToWait != portMAX_DELAY) { + xTicksRemaining = xTicksEnd - xTaskGetTickCount(); + } + taskEXIT_CRITICAL(); + /* + * Gap between critical section and re-acquiring of the semaphore. If + * semaphore is given now, priority inversion might occur (see docs) + */ + } + + if (xReturnSemaphore == pdTRUE) { + xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore); //Give semaphore back so other tasks can retrieve + } + return xReturn; +} + +static BaseType_t prvReceiveGenericFromISR(Ringbuffer_t *pxRingbuffer, void **pvItem1, void **pvItem2, size_t *xItemSize1, size_t *xItemSize2, size_t xMaxSize) +{ + BaseType_t xReturn = pdFALSE; + BaseType_t xReturnSemaphore = pdFALSE; + + taskENTER_CRITICAL(); + if(prvCheckItemAvail(pxRingbuffer) == pdTRUE) { + BaseType_t xIsSplit; + if (pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) { + //Second argument (pxIsSplit) is unused for byte buffers + *pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, NULL, xMaxSize, xItemSize1); + } else { + //Third argument (xMaxSize) is unused for no-split/allow-split buffers + *pvItem1 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize1); + } + //Check for item split if configured to do so + if ((pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG) && pvItem2 != NULL && xItemSize2 != NULL) { + if (xIsSplit == pdTRUE) { + *pvItem2 = pxRingbuffer->pvGetItem(pxRingbuffer, &xIsSplit, 0, xItemSize2); + configASSERT(*pvItem2 < *pvItem1); //Check wrap around has occurred + configASSERT(xIsSplit == pdFALSE); //Second part should not have wrapped flag + } else { + *pvItem2 = NULL; + } + } + xReturn = pdTRUE; + if (pxRingbuffer->xItemsWaiting > 0) { + xReturnSemaphore = pdTRUE; + } + } + + taskEXIT_CRITICAL(); + + if (xReturnSemaphore == pdTRUE) { + xSemaphoreGiveFromISR(pxRingbuffer->xItemsBufferedSemaphore, NULL); //Give semaphore back so other tasks can retrieve + } + return xReturn; +} + +/* ------------------------------------------------- Public Definitions -------------------------------------------- */ + +RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType) +{ + //Allocate memory + Ringbuffer_t *pxRingbuffer = calloc(1, sizeof(Ringbuffer_t)); + if (pxRingbuffer == NULL) { + goto err; + } + if (xBufferType != RINGBUF_TYPE_BYTEBUF) { + xBufferSize = rbALIGN_SIZE(xBufferSize); //xBufferSize is rounded up for no-split/allow-split buffers + } + pxRingbuffer->pucHead = malloc(xBufferSize); + if (pxRingbuffer->pucHead == NULL) { + goto err; + } + + //Initialize values + pxRingbuffer->xSize = xBufferSize; + pxRingbuffer->pucTail = pxRingbuffer->pucHead + xBufferSize; + pxRingbuffer->pucFree = pxRingbuffer->pucHead; + pxRingbuffer->pucRead = pxRingbuffer->pucHead; + pxRingbuffer->pucWrite = pxRingbuffer->pucHead; + pxRingbuffer->xItemsWaiting = 0; + pxRingbuffer->xFreeSpaceSemaphore = xSemaphoreCreateBinary(); + pxRingbuffer->xItemsBufferedSemaphore = xSemaphoreCreateBinary(); + pxRingbuffer->uxRingbufferFlags = 0; + + //Initialize type dependent values and function pointers + if (xBufferType == RINGBUF_TYPE_NOSPLIT) { + pxRingbuffer->xCheckItemFits = prvCheckItemFitsDefault; + pxRingbuffer->vCopyItem = prvCopyItemNoSplit; + pxRingbuffer->pvGetItem = prvGetItemDefault; + pxRingbuffer->vReturnItem = prvReturnItemDefault; + /* + * Buffer lengths are always aligned. No-split buffer (read/write/free) + * pointers are also always aligned. Therefore worse case scenario is + * the write pointer is at the most aligned halfway point. + */ + pxRingbuffer->xMaxItemSize = rbALIGN_SIZE(pxRingbuffer->xSize / 2) - rbHEADER_SIZE; + pxRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeNoSplit; + } else if (xBufferType == RINGBUF_TYPE_ALLOWSPLIT) { + pxRingbuffer->uxRingbufferFlags |= rbALLOW_SPLIT_FLAG; + pxRingbuffer->xCheckItemFits = prvCheckItemFitsDefault; + pxRingbuffer->vCopyItem = prvCopyItemAllowSplit; + pxRingbuffer->pvGetItem = prvGetItemDefault; + pxRingbuffer->vReturnItem = prvReturnItemDefault; + //Worst case an item is split into two, incurring two headers of overhead + pxRingbuffer->xMaxItemSize = pxRingbuffer->xSize - (sizeof(ItemHeader_t) * 2); + pxRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeAllowSplit; + } else if (xBufferType == RINGBUF_TYPE_BYTEBUF) { + pxRingbuffer->uxRingbufferFlags |= rbBYTE_BUFFER_FLAG; + pxRingbuffer->xCheckItemFits = prvCheckItemFitsByteBuffer; + pxRingbuffer->vCopyItem = prvCopyItemByteBuf; + pxRingbuffer->pvGetItem = prvGetItemByteBuf; + pxRingbuffer->vReturnItem = prvReturnItemByteBuf; + //Byte buffers do not incur any overhead + pxRingbuffer->xMaxItemSize = pxRingbuffer->xSize; + pxRingbuffer->xGetCurMaxSize = prvGetCurMaxSizeByteBuf; + } else { + //Unsupported type + configASSERT(0); + } + + if (pxRingbuffer->xFreeSpaceSemaphore == NULL || pxRingbuffer->xItemsBufferedSemaphore == NULL) { + goto err; + } + xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore); + + return (RingbufHandle_t)pxRingbuffer; + +err: + //Some error has happened. Free/destroy all allocated things and return NULL. + if (pxRingbuffer) { + free(pxRingbuffer->pucHead); + if (pxRingbuffer->xFreeSpaceSemaphore) { + vSemaphoreDelete(pxRingbuffer->xFreeSpaceSemaphore); + } + if (pxRingbuffer->xItemsBufferedSemaphore) { + vSemaphoreDelete(pxRingbuffer->xItemsBufferedSemaphore); + } + } + free(pxRingbuffer); + return NULL; +} + +RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum) +{ + return xRingbufferCreate((rbALIGN_SIZE(xItemSize) + rbHEADER_SIZE) * xItemNum, RINGBUF_TYPE_NOSPLIT); +} + +BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, TickType_t xTicksToWait) +{ + //Check arguments + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + configASSERT(pvItem != NULL || xItemSize == 0); + if (xItemSize > pxRingbuffer->xMaxItemSize) { + return pdFALSE; //Data will never ever fit in the queue. + } + if ((pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) && xItemSize == 0) { + return pdTRUE; //Sending 0 bytes to byte buffer has no effect + } + + //Attempt to send an item + BaseType_t xReturn = pdFALSE; + BaseType_t xReturnSemaphore = pdFALSE; + TickType_t xTicksEnd = xTaskGetTickCount() + xTicksToWait; + TickType_t xTicksRemaining = xTicksToWait; + while (xTicksRemaining <= xTicksToWait) { //xTicksToWait will underflow once xTaskGetTickCount() > ticks_end + //Block until more free space becomes available or timeout + if (xSemaphoreTake(pxRingbuffer->xFreeSpaceSemaphore, xTicksRemaining) != pdTRUE) { + xReturn = pdFALSE; + break; + } + //Semaphore obtained, check if item can fit + taskENTER_CRITICAL(); + if(pxRingbuffer->xCheckItemFits(pxRingbuffer, xItemSize) == pdTRUE) { + //Item will fit, copy item + pxRingbuffer->vCopyItem(pxRingbuffer, pvItem, xItemSize); + xReturn = pdTRUE; + //Check if the free semaphore should be returned to allow other tasks to send + if (prvGetFreeSize(pxRingbuffer) > 0) { + xReturnSemaphore = pdTRUE; + } + taskEXIT_CRITICAL(); + break; + } + //Item doesn't fit, adjust ticks and take the semaphore again + if (xTicksToWait != portMAX_DELAY) { + xTicksRemaining = xTicksEnd - xTaskGetTickCount(); + } + taskEXIT_CRITICAL(); + /* + * Gap between critical section and re-acquiring of the semaphore. If + * semaphore is given now, priority inversion might occur (see docs) + */ + } + + if (xReturn == pdTRUE) { + //Indicate item was successfully sent + xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore); + } + if (xReturnSemaphore == pdTRUE) { + xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore); //Give back semaphore so other tasks can send + } + return xReturn; +} + +BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, BaseType_t *pxHigherPriorityTaskWoken) +{ + //Check arguments + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + configASSERT(pvItem != NULL || xItemSize == 0); + if (xItemSize > pxRingbuffer->xMaxItemSize) { + return pdFALSE; //Data will never ever fit in the queue. + } + if ((pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG) && xItemSize == 0) { + return pdTRUE; //Sending 0 bytes to byte buffer has no effect + } + + //Attempt to send an item + BaseType_t xReturn; + BaseType_t xReturnSemaphore = pdFALSE; + taskENTER_CRITICAL(); + if (pxRingbuffer->xCheckItemFits(xRingbuffer, xItemSize) == pdTRUE) { + pxRingbuffer->vCopyItem(xRingbuffer, pvItem, xItemSize); + xReturn = pdTRUE; + //Check if the free semaphore should be returned to allow other tasks to send + if (prvGetFreeSize(pxRingbuffer) > 0) { + xReturnSemaphore = pdTRUE; + } + } else { + xReturn = pdFALSE; + } + taskEXIT_CRITICAL(); + + if (xReturn == pdTRUE) { + //Indicate item was successfully sent + xSemaphoreGiveFromISR(pxRingbuffer->xItemsBufferedSemaphore, pxHigherPriorityTaskWoken); + } + if (xReturnSemaphore == pdTRUE) { + xSemaphoreGiveFromISR(pxRingbuffer->xFreeSpaceSemaphore, pxHigherPriorityTaskWoken); //Give back semaphore so other tasks can send + } + return xReturn; +} + +void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait) +{ + //Check arguments + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + //Attempt to retrieve an item + void *pvTempItem; + size_t xTempSize; + if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, 0, xTicksToWait) == pdTRUE) { + if (pxItemSize != NULL) { + *pxItemSize = xTempSize; + } + return pvTempItem; + } else { + return NULL; + } +} + +void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize) +{ + //Check arguments + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + //Attempt to retrieve an item + void *pvTempItem; + size_t xTempSize; + if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, 0) == pdTRUE) { + if (pxItemSize != NULL) { + *pxItemSize = xTempSize; + } + return pvTempItem; + } else { + return NULL; + } +} + +BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize, TickType_t xTicksToWait) +{ + //Check arguments + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG); + configASSERT(ppvHeadItem != NULL && ppvTailItem != NULL); + + //Attempt to retrieve multiple items + void *pvTempHeadItem, *pvTempTailItem; + size_t xTempHeadSize, xTempTailSize; + if (prvReceiveGeneric(pxRingbuffer, &pvTempHeadItem, &pvTempTailItem, &xTempHeadSize, &xTempTailSize, 0, xTicksToWait) == pdTRUE) { + //At least one item was retrieved + *ppvHeadItem = pvTempHeadItem; + if(pxHeadItemSize != NULL){ + *pxHeadItemSize = xTempHeadSize; + } + //Check to see if a second item was also retrieved + if (pvTempTailItem != NULL) { + *ppvTailItem = pvTempTailItem; + if (pxTailItemSize != NULL) { + *pxTailItemSize = xTempTailSize; + } + } else { + *ppvTailItem = NULL; + } + return pdTRUE; + } else { + //No items retrieved + *ppvHeadItem = NULL; + *ppvTailItem = NULL; + return pdFALSE; + } +} + +BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize) +{ + //Check arguments + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + configASSERT(pxRingbuffer->uxRingbufferFlags & rbALLOW_SPLIT_FLAG); + configASSERT(ppvHeadItem != NULL && ppvTailItem != NULL); + + //Attempt to retrieve multiple items + void *pvTempHeadItem, *pvTempTailItem; + size_t xTempHeadSize, xTempTailSize; + if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempHeadItem, &pvTempTailItem, &xTempHeadSize, &xTempTailSize, 0) == pdTRUE) { + //At least one item was received + *ppvHeadItem = pvTempHeadItem; + if (pxHeadItemSize != NULL) { + *pxHeadItemSize = xTempHeadSize; + } + //Check to see if a second item was also retrieved + if (pvTempTailItem != NULL) { + *ppvTailItem = pvTempTailItem; + if (pxTailItemSize != NULL) { + *pxTailItemSize = xTempTailSize; + } + } else { + *ppvTailItem = NULL; + } + return pdTRUE; + } else { + *ppvHeadItem = NULL; + *ppvTailItem = NULL; + return pdFALSE; + } +} + +void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait, size_t xMaxSize) +{ + //Check arguments + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG); //This function should only be called for byte buffers + if (xMaxSize == 0) { + return NULL; + } + + //Attempt to retrieve up to xMaxSize bytes + void *pvTempItem; + size_t xTempSize; + if (prvReceiveGeneric(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, xMaxSize, xTicksToWait) == pdTRUE) { + if (pxItemSize != NULL) { + *pxItemSize = xTempSize; + } + return pvTempItem; + } else { + return NULL; + } +} + +void *xRingbufferReceiveUpToFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize, size_t xMaxSize) +{ + //Check arguments + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + configASSERT(pxRingbuffer->uxRingbufferFlags & rbBYTE_BUFFER_FLAG); //This function should only be called for byte buffers + if (xMaxSize == 0) { + return NULL; + } + + //Attempt to retrieve up to xMaxSize bytes + void *pvTempItem; + size_t xTempSize; + if (prvReceiveGenericFromISR(pxRingbuffer, &pvTempItem, NULL, &xTempSize, NULL, xMaxSize) == pdTRUE) { + if (pxItemSize != NULL) { + *pxItemSize = xTempSize; + } + return pvTempItem; + } else { + return NULL; + } +} + +void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + configASSERT(pvItem != NULL); + + taskENTER_CRITICAL(); + pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem); + taskEXIT_CRITICAL(); + xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore); +} + +void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + configASSERT(pvItem != NULL); + + taskENTER_CRITICAL(); + pxRingbuffer->vReturnItem(pxRingbuffer, (uint8_t *)pvItem); + taskEXIT_CRITICAL(); + xSemaphoreGiveFromISR(pxRingbuffer->xFreeSpaceSemaphore, pxHigherPriorityTaskWoken); +} + +void vRingbufferDelete(RingbufHandle_t xRingbuffer) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + if (pxRingbuffer) { + free(pxRingbuffer->pucHead); + if (pxRingbuffer->xFreeSpaceSemaphore) { + vSemaphoreDelete(pxRingbuffer->xFreeSpaceSemaphore); + } + if (pxRingbuffer->xItemsBufferedSemaphore) { + vSemaphoreDelete(pxRingbuffer->xItemsBufferedSemaphore); + } + } + free(pxRingbuffer); +} + +size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + return pxRingbuffer->xMaxItemSize; +} + +size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + size_t xFreeSize; + taskENTER_CRITICAL(); + xFreeSize = pxRingbuffer->xGetCurMaxSize(pxRingbuffer); + taskEXIT_CRITICAL(); + return xFreeSize; +} + +BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + BaseType_t xReturn; + taskENTER_CRITICAL(); + //Cannot add semaphore to queue set if semaphore is not empty. Temporarily hold semaphore + BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xItemsBufferedSemaphore, 0); + xReturn = xQueueAddToSet(pxRingbuffer->xItemsBufferedSemaphore, xQueueSet); + if (xHoldSemaphore == pdTRUE) { + //Return semaphore if temporarily held + configASSERT(xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore) == pdTRUE); + } + taskEXIT_CRITICAL(); + return xReturn; +} + +BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember) +{ + //Check if the selected queue set member is the ring buffer's read semaphore + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + return (pxRingbuffer->xItemsBufferedSemaphore == xMember) ? pdTRUE : pdFALSE; +} + +BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + BaseType_t xReturn; + taskENTER_CRITICAL(); + //Cannot remove semaphore from queue set if semaphore is not empty. Temporarily hold semaphore + BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xItemsBufferedSemaphore, 0); + xReturn = xQueueRemoveFromSet(pxRingbuffer->xItemsBufferedSemaphore, xQueueSet); + if (xHoldSemaphore == pdTRUE) { + //Return semaphore if temporarily held + configASSERT(xSemaphoreGive(pxRingbuffer->xItemsBufferedSemaphore) == pdTRUE); + } + taskEXIT_CRITICAL(); + return xReturn; +} + +void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, UBaseType_t *uxFree, UBaseType_t *uxRead, UBaseType_t *uxWrite, UBaseType_t *uxItemsWaiting) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + taskENTER_CRITICAL(); + if (uxFree != NULL) { + *uxFree = (UBaseType_t)(pxRingbuffer->pucFree - pxRingbuffer->pucHead); + } + if (uxRead != NULL) { + *uxRead = (UBaseType_t)(pxRingbuffer->pucRead - pxRingbuffer->pucHead); + } + if (uxWrite != NULL) { + *uxWrite = (UBaseType_t)(pxRingbuffer->pucWrite - pxRingbuffer->pucHead); + } + if (uxItemsWaiting != NULL) { + *uxItemsWaiting = (UBaseType_t)(pxRingbuffer->xItemsWaiting); + } + taskEXIT_CRITICAL(); +} + +void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer) +{ + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + printf("Rb size:%d\tfree: %d\trptr: %d\tfreeptr: %d\twptr: %d\n", + pxRingbuffer->xSize, prvGetFreeSize(pxRingbuffer), + pxRingbuffer->pucRead - pxRingbuffer->pucHead, + pxRingbuffer->pucFree - pxRingbuffer->pucHead, + pxRingbuffer->pucWrite - pxRingbuffer->pucHead); +} + +/* --------------------------------- Deprecated Functions ------------------------------ */ +//Todo: Remove the following deprecated functions in next release + +bool xRingbufferIsNextItemWrapped(RingbufHandle_t xRingbuffer) +{ + //This function is deprecated, use xRingbufferReceiveSplit() instead + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + bool is_wrapped; + + portENTER_CRITICAL(); + ItemHeader_t *xHeader = (ItemHeader_t *)pxRingbuffer->pucRead; + is_wrapped = xHeader->uxItemFlags & rbITEM_SPLIT_FLAG; + portEXIT_CRITICAL(); + return is_wrapped; +} + +BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) +{ + //This function is deprecated. QueueSetWrite no longer supported + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + BaseType_t xReturn; + portENTER_CRITICAL(); + //Cannot add semaphore to queue set if semaphore is not empty. Temporary hold semaphore + BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xFreeSpaceSemaphore, 0); + xReturn = xQueueAddToSet(pxRingbuffer->xFreeSpaceSemaphore, xQueueSet); + if (xHoldSemaphore == pdTRUE) { + //Return semaphore is temporarily held + configASSERT(xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore) == pdTRUE); + } + portEXIT_CRITICAL(); + return xReturn; +} + +BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) +{ + //This function is deprecated. QueueSetWrite no longer supported + Ringbuffer_t *pxRingbuffer = (Ringbuffer_t *)xRingbuffer; + configASSERT(pxRingbuffer); + + BaseType_t xReturn; + portENTER_CRITICAL(); + //Cannot remove semaphore from queue set if semaphore is not empty. Temporary hold semaphore + BaseType_t xHoldSemaphore = xSemaphoreTake(pxRingbuffer->xFreeSpaceSemaphore, 0); + xReturn = xQueueRemoveFromSet(pxRingbuffer->xFreeSpaceSemaphore, xQueueSet); + if (xHoldSemaphore == pdTRUE) { + //Return semaphore is temporarily held + configASSERT(xSemaphoreGive(pxRingbuffer->xFreeSpaceSemaphore) == pdTRUE); + } + portEXIT_CRITICAL(); + return xReturn; +} + diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h new file mode 100644 index 00000000..de8a3690 --- /dev/null +++ b/components/freertos/include/freertos/ringbuf.h @@ -0,0 +1,415 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FREERTOS_RINGBUF_H +#define FREERTOS_RINGBUF_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h" must appear in source files before "include ringbuf.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * Type by which ring buffers are referenced. For example, a call to xRingbufferCreate() + * returns a RingbufHandle_t variable that can then be used as a parameter to + * xRingbufferSend(), xRingbufferReceive(), etc. + */ +typedef void * RingbufHandle_t; + +typedef enum { + /** + * No-split buffers will only store an item in contiguous memory and will + * never split an item. Each item requires an 8 byte overhead for a header + * and will always internally occupy a 32-bit aligned size of space. + */ + RINGBUF_TYPE_NOSPLIT = 0, + /** + * Allow-split buffers will split an item into two parts if necessary in + * order to store it. Each item requires an 8 byte overhead for a header, + * splitting incurs an extra header. Each item will always internally occupy + * a 32-bit aligned size of space. + */ + RINGBUF_TYPE_ALLOWSPLIT, + /** + * Byte buffers store data as a sequence of bytes and do not maintain separate + * items, therefore byte buffers have no overhead. All data is stored as a + * sequence of byte and any number of bytes can be sent or retrieved each + * time. + */ + RINGBUF_TYPE_BYTEBUF +} ringbuf_type_t; + +/** + * @brief Create a ring buffer + * + * @param[in] xBufferSize Size of the buffer in bytes. Note that items require + * space for overhead in no-split/allow-split buffers + * @param[in] xBufferType Type of ring buffer, see documentation. + * + * @note xBufferSize of no-split/allow-split buffers will be rounded up to the nearest 32-bit aligned size. + * + * @return A handle to the created ring buffer, or NULL in case of error. + */ +RingbufHandle_t xRingbufferCreate(size_t xBufferSize, ringbuf_type_t xBufferType); + +/** + * @brief Create a ring buffer of type RINGBUF_TYPE_NOSPLIT for a fixed item_size + * + * This API is similar to xRingbufferCreate(), but it will internally allocate + * additional space for the headers. + * + * @param[in] xItemSize Size of each item to be put into the ring buffer + * @param[in] xItemNum Maximum number of items the buffer needs to hold simultaneously + * + * @return A RingbufHandle_t handle to the created ring buffer, or NULL in case of error. + */ +RingbufHandle_t xRingbufferCreateNoSplit(size_t xItemSize, size_t xItemNum); + +/** + * @brief Insert an item into the ring buffer + * + * Attempt to insert an item into the ring buffer. This function will block until + * enough free space is available or until it timesout. + * + * @param[in] xRingbuffer Ring buffer to insert the item into + * @param[in] pvItem Pointer to data to insert. NULL is allowed if xItemSize is 0. + * @param[in] xItemSize Size of data to insert. + * @param[in] xTicksToWait Ticks to wait for room in the ring buffer. + * + * @note For no-split/allow-split ring buffers, the actual size of memory that + * the item will occupy will be rounded up to the nearest 32-bit aligned + * size. This is done to ensure all items are always stored in 32-bit + * aligned fashion. + * + * @return + * - pdTRUE if succeeded + * - pdFALSE on time-out or when the data is larger than the maximum permissible size of the buffer + */ +BaseType_t xRingbufferSend(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, TickType_t xTicksToWait); + +/** + * @brief Insert an item into the ring buffer in an ISR + * + * Attempt to insert an item into the ring buffer from an ISR. This function + * will return immediately if there is insufficient free space in the buffer. + * + * @param[in] xRingbuffer Ring buffer to insert the item into + * @param[in] pvItem Pointer to data to insert. NULL is allowed if xItemSize is 0. + * @param[in] xItemSize Size of data to insert. + * @param[out] pxHigherPriorityTaskWoken Value pointed to will be set to pdTRUE if the function woke up a higher priority task. + * + * @note For no-split/allow-split ring buffers, the actual size of memory that + * the item will occupy will be rounded up to the nearest 32-bit aligned + * size. This is done to ensure all items are always stored in 32-bit + * aligned fashion. + * + * @return + * - pdTRUE if succeeded + * - pdFALSE when the ring buffer does not have space. + */ +BaseType_t xRingbufferSendFromISR(RingbufHandle_t xRingbuffer, const void *pvItem, size_t xItemSize, BaseType_t *pxHigherPriorityTaskWoken); + +/** + * @brief Retrieve an item from the ring buffer + * + * Attempt to retrieve an item from the ring buffer. This function will block + * until an item is available or until it timesout. + * + * @param[in] xRingbuffer Ring buffer to retrieve the item from + * @param[out] pxItemSize Pointer to a variable to which the size of the retrieved item will be written. + * @param[in] xTicksToWait Ticks to wait for items in the ring buffer. + * + * @note A call to vRingbufferReturnItem() is required after this to free the item retrieved. + * + * @return + * - Pointer to the retrieved item on success; *pxItemSize filled with the length of the item. + * - NULL on timeout, *pxItemSize is untouched in that case. + */ +void *xRingbufferReceive(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait); + +/** + * @brief Retrieve an item from the ring buffer in an ISR + * + * Attempt to retrieve an item from the ring buffer. This function returns immediately + * if there are no items available for retrieval + * + * @param[in] xRingbuffer Ring buffer to retrieve the item from + * @param[out] pxItemSize Pointer to a variable to which the size of the + * retrieved item will be written. + * + * @note A call to vRingbufferReturnItemFromISR() is required after this to free the item retrieved. + * @note Byte buffers do not allow multiple retrievals before returning an item + * + * @return + * - Pointer to the retrieved item on success; *pxItemSize filled with the length of the item. + * - NULL when the ring buffer is empty, *pxItemSize is untouched in that case. + */ +void *xRingbufferReceiveFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize); + +/** + * @brief Retrieve a split item from an allow-split ring buffer + * + * Attempt to retrieve a split item from an allow-split ring buffer. If the item + * is not split, only a single item is retried. If the item is split, both parts + * will be retrieved. This function will block until an item is available or + * until it timesout. + * + * @param[in] xRingbuffer Ring buffer to retrieve the item from + * @param[out] ppvHeadItem Double pointer to first part (set to NULL if no items were retrieved) + * @param[out] ppvTailItem Double pointer to second part (set to NULL if item is not split) + * @param[out] pxHeadItemSize Pointer to size of first part (unmodified if no items were retrieved) + * @param[out] pxTailItemSize Pointer to size of second part (unmodified if item is not split) + * @param[in] xTicksToWait Ticks to wait for items in the ring buffer. + * + * @note Call(s) to vRingbufferReturnItem() is required after this to free up the item(s) retrieved. + * @note This function should only be called on allow-split buffers + * + * @return + * - pdTRUE if an item (split or unsplit) was retrieved + * - pdFALSE when no item was retrieved + */ +BaseType_t xRingbufferReceiveSplit(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize, TickType_t xTicksToWait); + +/** + * @brief Retrieve a split item from an allow-split ring buffer in an ISR + * + * Attempt to retrieve a split item from an allow-split ring buffer. If the item + * is not split, only a single item is retried. If the item is split, both parts + * will be retrieved. This function returns immediately if there are no items + * available for retrieval + * + * @param[in] xRingbuffer Ring buffer to retrieve the item from + * @param[out] ppvHeadItem Double pointer to first part (set to NULL if no items were retrieved) + * @param[out] ppvTailItem Double pointer to second part (set to NULL if item is not split) + * @param[out] pxHeadItemSize Pointer to size of first part (unmodified if no items were retrieved) + * @param[out] pxTailItemSize Pointer to size of second part (unmodified if item is not split) + * + * @note Calls to vRingbufferReturnItemFromISR() is required after this to free up the item(s) retrieved. + * @note This function should only be called on allow-split buffers + * + * @return + * - pdTRUE if an item (split or unsplit) was retrieved + * - pdFALSE when no item was retrieved + */ +BaseType_t xRingbufferReceiveSplitFromISR(RingbufHandle_t xRingbuffer, void **ppvHeadItem, void **ppvTailItem, size_t *pxHeadItemSize, size_t *pxTailItemSize); + +/** + * @brief Retrieve bytes from a byte buffer, specifying the maximum amount of bytes to retrieve + * + * Attempt to retrieve data from a byte buffer whilst specifying a maximum number + * of bytes to retrieve. This function will block until there is data available + * for retrieval or until it timesout. + * + * @param[in] xRingbuffer Ring buffer to retrieve the item from + * @param[out] pxItemSize Pointer to a variable to which the size of the retrieved item will be written. + * @param[in] xTicksToWait Ticks to wait for items in the ring buffer. + * @param[in] xMaxSize Maximum number of bytes to return. + * + * @note A call to vRingbufferReturnItem() is required after this to free up the data retrieved. + * @note This function should only be called on byte buffers + * @note Byte buffers do not allow multiple retrievals before returning an item + * + * @return + * - Pointer to the retrieved item on success; *pxItemSize filled with + * the length of the item. + * - NULL on timeout, *pxItemSize is untouched in that case. + */ +void *xRingbufferReceiveUpTo(RingbufHandle_t xRingbuffer, size_t *pxItemSize, TickType_t xTicksToWait, size_t xMaxSize); + +/** + * @brief Retrieve bytes from a byte buffer, specifying the maximum amount of + * bytes to retrieve. Call this from an ISR. + * + * Attempt to retrieve bytes from a byte buffer whilst specifying a maximum number + * of bytes to retrieve. This function will return immediately if there is no data + * available for retrieval. + * + * @param[in] xRingbuffer Ring buffer to retrieve the item from + * @param[out] pxItemSize Pointer to a variable to which the size of the retrieved item will be written. + * @param[in] xMaxSize Maximum number of bytes to return. + * + * @note A call to vRingbufferReturnItemFromISR() is required after this to free up the data received. + * @note This function should only be called on byte buffers + * @note Byte buffers do not allow multiple retrievals before returning an item + * + * @return + * - Pointer to the retrieved item on success; *pxItemSize filled with + * the length of the item. + * - NULL when the ring buffer is empty, *pxItemSize is untouched in that case. + */ +void *xRingbufferReceiveUpToFromISR(RingbufHandle_t xRingbuffer, size_t *pxItemSize, size_t xMaxSize); + +/** + * @brief Return a previously-retrieved item to the ring buffer + * + * @param[in] xRingbuffer Ring buffer the item was retrieved from + * @param[in] pvItem Item that was received earlier + * + * @note If a split item is retrieved, both parts should be returned by calling this function twice + */ +void vRingbufferReturnItem(RingbufHandle_t xRingbuffer, void *pvItem); + +/** + * @brief Return a previously-retrieved item to the ring buffer from an ISR + * + * @param[in] xRingbuffer Ring buffer the item was retrieved from + * @param[in] pvItem Item that was received earlier + * @param[out] pxHigherPriorityTaskWoken Value pointed to will be set to pdTRUE + * if the function woke up a higher priority task. + * + * @note If a split item is retrieved, both parts should be returned by calling this function twice + */ +void vRingbufferReturnItemFromISR(RingbufHandle_t xRingbuffer, void *pvItem, BaseType_t *pxHigherPriorityTaskWoken); + +/** + * @brief Delete a ring buffer + * + * @param[in] xRingbuffer Ring buffer to delete + */ +void vRingbufferDelete(RingbufHandle_t xRingbuffer); + +/** + * @brief Get maximum size of an item that can be placed in the ring buffer + * + * This function returns the maximum size an item can have if it was placed in + * an empty ring buffer. + * + * @param[in] xRingbuffer Ring buffer to query + * + * @return Maximum size, in bytes, of an item that can be placed in a ring buffer. + */ +size_t xRingbufferGetMaxItemSize(RingbufHandle_t xRingbuffer); + +/** + * @brief Get current free size available for an item/data in the buffer + * + * This gives the real time free space available for an item/data in the ring + * buffer. This represents the maximum size an item/data can have if it was + * currently sent to the ring buffer. + * + * @warning This API is not thread safe. So, if multiple threads are accessing + * the same ring buffer, it is the application's responsibility to + * ensure atomic access to this API and the subsequent Send + * + * @param[in] xRingbuffer Ring buffer to query + * + * @return Current free size, in bytes, available for an entry + */ +size_t xRingbufferGetCurFreeSize(RingbufHandle_t xRingbuffer); + +/** + * @brief Add the ring buffer's read semaphore to a queue set. + * + * The ring buffer's read semaphore indicates that data has been written + * to the ring buffer. This function adds the ring buffer's read semaphore to + * a queue set. + * + * @param[in] xRingbuffer Ring buffer to add to the queue set + * @param[in] xQueueSet Queue set to add the ring buffer's read semaphore to + * + * @return + * - pdTRUE on success, pdFALSE otherwise + */ +BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet); + + +/** + * @brief Check if the selected queue set member is the ring buffer's read semaphore + * + * This API checks if queue set member returned from xQueueSelectFromSet() + * is the read semaphore of this ring buffer. If so, this indicates the ring buffer + * has items waiting to be retrieved. + * + * @param[in] xRingbuffer Ring buffer which should be checked + * @param[in] xMember Member returned from xQueueSelectFromSet + * + * @return + * - pdTRUE when semaphore belongs to ring buffer + * - pdFALSE otherwise. + */ +BaseType_t xRingbufferCanRead(RingbufHandle_t xRingbuffer, QueueSetMemberHandle_t xMember); + +/** + * @brief Remove the ring buffer's read semaphore from a queue set. + * + * This specifically removes a ring buffer's read semaphore from a queue set. The + * read semaphore is used to indicate when data has been written to the ring buffer + * + * @param[in] xRingbuffer Ring buffer to remove from the queue set + * @param[in] xQueueSet Queue set to remove the ring buffer's read semaphore from + * + * @return + * - pdTRUE on success + * - pdFALSE otherwise + */ +BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet); + +/** + * @brief Get information about ring buffer status + * + * Get information of the a ring buffer's current status such as + * free/read/write pointer positions, and number of items waiting to be retrieved. + * Arguments can be set to NULL if they are not required. + * + * @param[in] xRingbuffer Ring buffer to remove from the queue set + * @param[out] uxFree Pointer use to store free pointer position + * @param[out] uxRead Pointer use to store read pointer position + * @param[out] uxWrite Pointer use to store write pointer position + * @param[out] uxItemsWaiting Pointer use to store number of items (bytes for byte buffer) waiting to be retrieved + */ +void vRingbufferGetInfo(RingbufHandle_t xRingbuffer, UBaseType_t *uxFree, UBaseType_t *uxRead, UBaseType_t *uxWrite, UBaseType_t *uxItemsWaiting); + +/** + * @brief Debugging function to print the internal pointers in the ring buffer + * + * @param xRingbuffer Ring buffer to show + */ +void xRingbufferPrintInfo(RingbufHandle_t xRingbuffer); + +/* -------------------------------- Deprecated Functions --------------------------- */ + +/** @cond */ //Doxygen command to hide deprecated function from API Reference +/* + * Deprecated as function is not thread safe and does not check if an item is + * actually available for retrieval. Use xRingbufferReceiveSplit() instead for + * thread safe method of retrieve a split item. + */ +bool xRingbufferIsNextItemWrapped(RingbufHandle_t xRingbuffer) __attribute__((deprecated)); + +/* + * Deprecated as queue sets are not meant to be used for writing to buffers. Adding + * the ring buffer write semaphore to a queue set will break queue set usage rules, + * as every read of a semaphore must be preceded by a call to xQueueSelectFromSet(). + * QueueSetWrite no longer supported. + */ +BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) __attribute__((deprecated)); + +/* + * Deprecated as queue sets are not meant to be used for writing to buffers. + * QueueSetWrite no longer supported. + */ +BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t xRingbuffer, QueueSetHandle_t xQueueSet) __attribute__((deprecated)); +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* FREERTOS_RINGBUF_H */ + diff --git a/examples/peripherals/uart_echo/Makefile b/examples/peripherals/uart_echo/Makefile new file mode 100644 index 00000000..e4714e57 --- /dev/null +++ b/examples/peripherals/uart_echo/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := uart_echo + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/peripherals/uart_echo/README.md b/examples/peripherals/uart_echo/README.md new file mode 100644 index 00000000..8916f987 --- /dev/null +++ b/examples/peripherals/uart_echo/README.md @@ -0,0 +1,61 @@ +# _UART Echo Example_ + +_This is an example which echoes any data it receives on UART0 back to the sender._ + +## How to use example + +### Hardware Required + +1. Connect an external serial interface to an ESP8266 board. The external interface should have 3.3V outputs. You may use e.g. 3.3V compatible USB-to-serial dongle: + + | ESP8266 Interface | #define | ESP8266 Pin | External UART Pin | + | --- | --- | --- | --- | + | Transmit Data (TxD) | ECHO_TEST_TXD | GPIO26 | RxD | + | Receive Data (RxD) | ECHO_TEST_RXD | GPIO25 | TxD | + | Ground | n/a | GND | GND | + +2. Verify if echo indeed comes from ESP8266 by disconnecting either 'TxD' or 'RxD' pin. There should be no any echo once any pin is disconnected. + +* Using a hardware flow control + + This is an optional check to verify if the hardware flow control works. To set it up you need an external serial interface that has RTS and CTS signals. + + 1. Connect the extra RTS/CTS signals as below + + | ESP8266 Interface | #define | ESP8266 Pin | External UART Pin | + | --- | --- | --- | --- | + | Request to Send (RTS) | ECHO_TEST_RTS | GPIO13 | CTS | + | Clear to Send (CTS) | ECHO_TEST_CTS | GPIO12 | RTS | + + 2. Configure UART0 driver to use the hardware flow control by setting `.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS` and adding `.rx_flow_ctrl_thresh = 122` + +### Configure the project + +``` +make menuconfig +``` + +* Set serial port under Serial Flasher Options. +* `make monitor` baud rate set to what you set in the example. + + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +make -j4 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 + +``` +I (180) boot: Loaded app from partition at offset 0x10000 +I (0) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (0) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +0123456789 +``` \ No newline at end of file diff --git a/examples/peripherals/uart_echo/main/component.mk b/examples/peripherals/uart_echo/main/component.mk new file mode 100644 index 00000000..44bd2b52 --- /dev/null +++ b/examples/peripherals/uart_echo/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/peripherals/uart_echo/main/uart_echo_example_main.c b/examples/peripherals/uart_echo/main/uart_echo_example_main.c new file mode 100644 index 00000000..d503a28e --- /dev/null +++ b/examples/peripherals/uart_echo/main/uart_echo_example_main.c @@ -0,0 +1,65 @@ +// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "driver/uart.h" + +/** + * This is an example which echos any data it receives on UART0 back to the sender, + * with hardware flow control turned off. It does not use UART driver event queue. + * + * - Port: UART0 + * - Receive (Rx) buffer: on + * - Transmit (Tx) buffer: off + * - Flow control: off + * - Event queue: off + */ + +#define BUF_SIZE (1024) + +static void echo_task() +{ + // Configure parameters of an UART driver, + // communication pins and install the driver + uart_config_t uart_config = { + .baud_rate = 74880, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE + }; + uart_param_config(UART_NUM_0, &uart_config); + uart_driver_install(UART_NUM_0, BUF_SIZE * 2, 0, 0, NULL); + + // Configure a temporary buffer for the incoming data + uint8_t *data = (uint8_t *) malloc(BUF_SIZE); + + while (1) { + // Read data from the UART + int len = uart_read_bytes(UART_NUM_0, data, BUF_SIZE, 20 / portTICK_RATE_MS); + // Write data back to the UART + uart_write_bytes(UART_NUM_0, (const char *) data, len); + } +} + +void app_main() +{ + xTaskCreate(echo_task, "uart_echo_task", 1024, NULL, 10, NULL); +} diff --git a/examples/peripherals/uart_events/Makefile b/examples/peripherals/uart_events/Makefile new file mode 100644 index 00000000..49694172 --- /dev/null +++ b/examples/peripherals/uart_events/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := uart_events + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/peripherals/uart_events/README.md b/examples/peripherals/uart_events/README.md new file mode 100644 index 00000000..681d079e --- /dev/null +++ b/examples/peripherals/uart_events/README.md @@ -0,0 +1,45 @@ +# _UART Events Example_ + +_This example shows how to use the UART driver to handle special UART events. It also reads data from UART0 directly, and echoes it to console._ + +* Compile and load example from terminl running `make flash monitor` +* Being in 'monotor' type samething to see the `UART_DATA` events and the typed data displayed. + +## How to use example + +### Configure the project + +``` +make menuconfig +``` + +* Set serial port under Serial Flasher Options. +* `make monitor` baud rate set to what you set in the example. + + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +make -j4 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 + +* Paste `0123456789` to monitor, the `UART DATA` event will be received. + +``` +I (185) boot: Loaded app from partition at offset 0x10000 +I (0) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (0) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE +I (0) uart: queue free spaces: 100 +I (15) uart_events: uart[0] event: +I (15) uart_events: [UART DATA]: 10 +I (15) uart_events: [DATA EVT]: +0123456789 +``` \ No newline at end of file diff --git a/examples/peripherals/uart_events/main/component.mk b/examples/peripherals/uart_events/main/component.mk new file mode 100644 index 00000000..44bd2b52 --- /dev/null +++ b/examples/peripherals/uart_events/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/peripherals/uart_events/main/uart_events_example_main.c b/examples/peripherals/uart_events/main/uart_events_example_main.c new file mode 100644 index 00000000..4717169a --- /dev/null +++ b/examples/peripherals/uart_events/main/uart_events_example_main.c @@ -0,0 +1,128 @@ +// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/uart.h" +#include "esp_log.h" + + +static const char *TAG = "uart_events"; + +/** + * This example shows how to use the UART driver to handle special UART events. + * + * It also reads data from UART0 directly, and echoes it to console. + * + * - Port: UART0 + * - Receive (Rx) buffer: on + * - Transmit (Tx) buffer: on + * - Flow control: off + * - Event queue: on + * - Pin assignment: TxD (default), RxD (default) + */ + +#define EX_UART_NUM UART_NUM_0 + +#define BUF_SIZE (1024) +#define RD_BUF_SIZE (BUF_SIZE) +static QueueHandle_t uart0_queue; + +static void uart_event_task(void *pvParameters) +{ + uart_event_t event; + size_t buffered_size; + uint8_t *dtmp = (uint8_t *) malloc(RD_BUF_SIZE); + + for (;;) { + // Waiting for UART event. + if (xQueueReceive(uart0_queue, (void *)&event, (portTickType)portMAX_DELAY)) { + bzero(dtmp, RD_BUF_SIZE); + ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM); + + switch (event.type) { + // Event of UART receving data + // We'd better handler data event fast, there would be much more data events than + // other types of events. If we take too much time on data event, the queue might be full. + case UART_DATA: + ESP_LOGI(TAG, "[UART DATA]: %d", event.size); + uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY); + ESP_LOGI(TAG, "[DATA EVT]:"); + uart_write_bytes(EX_UART_NUM, (const char *) dtmp, event.size); + break; + + // Event of HW FIFO overflow detected + case UART_FIFO_OVF: + ESP_LOGI(TAG, "hw fifo overflow"); + // If fifo overflow happened, you should consider adding flow control for your application. + // The ISR has already reset the rx FIFO, + // As an example, we directly flush the rx buffer here in order to read more data. + uart_flush_input(EX_UART_NUM); + xQueueReset(uart0_queue); + break; + + // Event of UART ring buffer full + case UART_BUFFER_FULL: + ESP_LOGI(TAG, "ring buffer full"); + // If buffer full happened, you should consider encreasing your buffer size + // As an example, we directly flush the rx buffer here in order to read more data. + uart_flush_input(EX_UART_NUM); + xQueueReset(uart0_queue); + break; + + case UART_PARITY_ERR: + ESP_LOGI(TAG, "uart parity error"); + break; + + // Event of UART frame error + case UART_FRAME_ERR: + ESP_LOGI(TAG, "uart frame error"); + break; + + // Others + default: + ESP_LOGI(TAG, "uart event type: %d", event.type); + break; + } + } + } + + free(dtmp); + dtmp = NULL; + vTaskDelete(NULL); +} + +void app_main() +{ + // Configure parameters of an UART driver, + // communication pins and install the driver + uart_config_t uart_config = { + .baud_rate = 74880, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE + }; + uart_param_config(EX_UART_NUM, &uart_config); + + // Install UART driver, and get the queue. + uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 100, &uart0_queue); + + // Create a task to handler UART event from ISR + xTaskCreate(uart_event_task, "uart_event_task", 2048, NULL, 12, NULL); +} \ No newline at end of file