├── CMakeLists.txt ├── LICENSE ├── README.md ├── rmt_uart.c └── rmt_uart.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "rmt_uart.c" 2 | INCLUDE_DIRS ".") 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 naffej 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32 RMT UART 2 | 3 | Component for Espressif ESP32 ESP-IDF framework. 4 | 5 | This components uses ESP32's RMT peripheral as an UART port. It can send and receive UART frames as well. 6 | 7 | ## Supported versions of frameworks and devices 8 | 9 | | Chip | Framework | Versions | Number of UART 10 | |----------------|--------------------|------------|---------- 11 | | ESP32-S2 | ESP-IDF | v4.3 and above | 2 12 | | ESP32-C3 | ESP-IDF | v4.3 and above | 2 13 | | ESP32-S3 | ESP-IDF | v4.3 and above | 4 14 | 15 | ## How to Use 16 | Clone this repository to your project components directory. 17 | 18 | ## Configuration 19 | 20 | ```c 21 | typedef struct { 22 | int baud_rate; /*!< UART baud rate*/ 23 | rmt_uart_mode_t mode; /*!< UART mode*/ 24 | rmt_uart_word_length_t data_bits; /*!< UART byte size*/ 25 | rmt_uart_parity_t parity; /*!< UART parity mode*/ 26 | rmt_uart_stop_bits_t stop_bits; /*!< UART stop bits*/ 27 | gpio_num_t tx_io_num; /*!< UART TX GPIO num*/ 28 | gpio_num_t rx_io_num; /*!< UART RX GPIO num*/ 29 | size_t buffer_size; /*!< UART buffer size/*> 30 | } rmt_uart_config_t; 31 | ``` 32 | 33 | Mode can be TX only, RX only or both TX and RX. 34 | Buffer size must be 20 times of the length of transmit/receive data. 35 | If you want to send 10 bytes maximum then buffer_size = 200. 36 | 37 | 38 | ## Restrictions 39 | Due to hardware limitations ESP32-S2 can only receive 12 bytes at once. In RX only mode this limit is 24 bytes. Transmit has no restriction. 40 | -------------------------------------------------------------------------------- /rmt_uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rmt_uart.h" 3 | #include "esp_log.h" 4 | #include "esp_check.h" 5 | #include "driver/rmt.h" 6 | #include "driver/uart.h" 7 | #include "driver/gpio.h" 8 | 9 | static const char *TAG = "rmt-uart"; 10 | 11 | typedef struct { 12 | rmt_item32_t* items; 13 | int item_index; 14 | } rmt_uart_contex_tx_t; 15 | 16 | typedef struct { 17 | uint8_t* bytes; 18 | int byte_num; 19 | int bit_num; 20 | uint16_t raw_data; 21 | RingbufHandle_t rb; 22 | } rmt_uart_contex_rx_t; 23 | 24 | typedef struct { 25 | rmt_config_t rmt_config_tx; 26 | rmt_config_t rmt_config_rx; 27 | rmt_uart_config_t rmt_uart_config; 28 | rmt_uart_contex_tx_t rmt_uart_contex_tx; 29 | rmt_uart_contex_rx_t rmt_uart_contex_rx; 30 | uint16_t rmt_bit_len; 31 | bool configured; 32 | } rmt_uart_contex_t; 33 | 34 | static rmt_uart_contex_t rmt_uart_contex[RMT_UART_NUM_MAX] = {0}; 35 | 36 | static int convert_byte_to_items(rmt_uart_contex_t* ctx, uint8_t byte) 37 | { 38 | rmt_uart_contex_tx_t* rtc = &ctx->rmt_uart_contex_tx; 39 | uint16_t data = (byte << 1) | (1 << 9); 40 | 41 | for (int i = 0; i < 9; i += 2) 42 | { 43 | rmt_item32_t* item = &rtc->items[rtc->item_index]; 44 | item->duration0 = ctx->rmt_bit_len; 45 | item->duration1 = ctx->rmt_bit_len; 46 | item->level0 = (data >> i) ^ 1; 47 | item->level1 = (data >> (i + 1)) ^ 1; 48 | rtc->item_index++; 49 | if (rtc->item_index >= (ctx->rmt_uart_config.buffer_size / sizeof(rmt_item32_t))) 50 | { 51 | ESP_LOGE(TAG, "DATA TOO LONG"); 52 | return -1; 53 | } 54 | ESP_LOGD(TAG, "\trmt tx item %02d: duration0: %d level0: %d duration1: %d level1: %d", rtc->item_index, item->duration0, item->level0, item->duration1, item->level1); 55 | } 56 | return 0; 57 | } 58 | 59 | static int convert_data_to_items(rmt_uart_contex_t* ctx, const uint8_t* data, uint16_t len) 60 | { 61 | rmt_uart_contex_tx_t* rtc = &ctx->rmt_uart_contex_tx; 62 | rtc->item_index = 0; 63 | 64 | for (int i = 0; i < len; ++i) 65 | { 66 | if (convert_byte_to_items(ctx, data[i])) return -1; 67 | } 68 | return rtc->item_index; 69 | } 70 | 71 | static unsigned int round_closest(unsigned int dividend, unsigned int divisor) 72 | { 73 | return (dividend + (divisor / 2)) / divisor; 74 | } 75 | 76 | static void fill_bits(rmt_uart_contex_t* ctx, uint32_t duration, uint32_t level) 77 | { 78 | rmt_uart_contex_rx_t* rrc = &ctx->rmt_uart_contex_rx; 79 | int rmt_bit_numbers = round_closest(duration, ctx->rmt_bit_len); 80 | // all remaining bits are high 81 | if (rmt_bit_numbers == 0 && level) 82 | { 83 | rmt_bit_numbers = 10 - rrc->bit_num; 84 | } 85 | 86 | int from = rrc->bit_num; 87 | int to = rrc->bit_num + rmt_bit_numbers; 88 | for (int j = from; j < to; ++j) 89 | { 90 | if (rrc->bit_num == 0 && level) 91 | { 92 | ESP_LOGW(TAG, "not a start bit, skip"); 93 | return; 94 | } 95 | rrc->bit_num++; 96 | if (rrc->bit_num == 10 && !level) 97 | { 98 | ESP_LOGE(TAG, "not a stop bit byte_num=%d", rrc->byte_num); 99 | return; 100 | } 101 | rrc->raw_data |= (level << j); 102 | if (rrc->bit_num == 10) 103 | { 104 | rrc->raw_data >>= 1; 105 | rrc->raw_data &= 0x00FF; 106 | rrc->bytes[rrc->byte_num] = rrc->raw_data; 107 | ESP_LOGD(TAG, "\trmt rx data=%d", rrc->raw_data); 108 | rrc->byte_num++; 109 | rrc->bit_num = 0; 110 | rrc->raw_data = 0; 111 | } 112 | } 113 | } 114 | 115 | esp_err_t rmt_uart_init(rmt_uart_port_t uart_num, const rmt_uart_config_t* uart_config) 116 | { 117 | const int RMT_DIV = APB_CLK_FREQ / 50 / uart_config->baud_rate; 118 | const int RMT_TICK = APB_CLK_FREQ / RMT_DIV; 119 | ESP_LOGI(TAG, "baud=%d rmt_div=%d rmt_tick=%d", uart_config->baud_rate, RMT_DIV, RMT_TICK); 120 | 121 | ESP_RETURN_ON_FALSE((uart_num < RMT_UART_NUM_MAX), ESP_FAIL, TAG, "uart_num error"); 122 | ESP_RETURN_ON_FALSE((uart_config), ESP_FAIL, TAG, "uart_config error"); 123 | ESP_RETURN_ON_FALSE(((10UL * RMT_TICK / uart_config->baud_rate) < 0xFFFF), ESP_FAIL, TAG, "rmt tick too long, reconfigure 'RMT_DIV'"); 124 | ESP_RETURN_ON_FALSE(((RMT_TICK / uart_config->baud_rate) > 49), ESP_FAIL, TAG, "rmt tick too long, reconfigure 'RMT_DIV'"); 125 | ESP_RETURN_ON_FALSE(((RMT_TICK / uart_config->baud_rate / 2) < 0xFF), ESP_FAIL, TAG, "baud rate too slow, reconfigure 'RMT_DIV'"); 126 | 127 | uint16_t bit_len = RMT_TICK / uart_config->baud_rate; 128 | rmt_uart_contex[uart_num].rmt_bit_len = bit_len; 129 | memcpy(&rmt_uart_contex[uart_num].rmt_uart_config, uart_config, sizeof(rmt_uart_config_t)); 130 | 131 | if (uart_config->mode == RMT_UART_MODE_RX_ONLY || uart_config->mode == RMT_UART_MODE_TX_RX) 132 | { 133 | rmt_config_t rmt_config_rx = RMT_DEFAULT_CONFIG_RX(uart_config->rx_io_num, uart_num); 134 | rmt_config_rx.clk_div = RMT_DIV; 135 | rmt_config_rx.mem_block_num = (uart_config->mode == RMT_UART_MODE_RX_ONLY) ? 2 : 1; 136 | rmt_config_rx.rx_config.idle_threshold = 10 * bit_len; 137 | rmt_config_rx.rx_config.filter_en = true; 138 | rmt_config_rx.rx_config.filter_ticks_thresh = bit_len / 2; 139 | ESP_ERROR_CHECK(rmt_config(&rmt_config_rx)); 140 | ESP_ERROR_CHECK(rmt_driver_install(rmt_config_rx.channel, uart_config->buffer_size, 0)); 141 | rmt_uart_contex[uart_num].rmt_config_rx = rmt_config_rx; 142 | rmt_get_ringbuf_handle(rmt_config_rx.channel, &rmt_uart_contex[uart_num].rmt_uart_contex_rx.rb); 143 | ESP_RETURN_ON_FALSE((rmt_uart_contex[uart_num].rmt_uart_contex_rx.rb), ESP_FAIL, TAG, "rmt ringbuffer is null"); 144 | #if CONFIG_SPIRAM_USE_MALLOC 145 | rmt_uart_contex[uart_num].rmt_uart_contex_tx.items = heap_caps_calloc(1, uart_config->buffer_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); 146 | #else 147 | rmt_uart_contex[uart_num].rmt_uart_contex_tx.items = calloc(1, uart_config->buffer_size); 148 | #endif 149 | rmt_rx_start(rmt_uart_contex[uart_num].rmt_config_rx.channel, true); 150 | } 151 | 152 | if (uart_config->mode == RMT_UART_MODE_TX_ONLY || uart_config->mode == RMT_UART_MODE_TX_RX) 153 | { 154 | rmt_config_t rmt_config_tx = RMT_DEFAULT_CONFIG_TX(uart_config->tx_io_num, uart_num + 1); 155 | rmt_config_tx.tx_config.carrier_en = false; 156 | rmt_config_tx.clk_div = RMT_DIV; 157 | rmt_config_tx.flags = RMT_CHANNEL_FLAGS_INVERT_SIG; 158 | ESP_ERROR_CHECK(rmt_config(&rmt_config_tx)); 159 | ESP_ERROR_CHECK(rmt_driver_install(rmt_config_tx.channel, uart_config->buffer_size, 0)); 160 | rmt_uart_contex[uart_num].rmt_config_tx = rmt_config_tx; 161 | } 162 | 163 | rmt_uart_contex[uart_num].configured = true; 164 | 165 | return ESP_OK; 166 | } 167 | 168 | esp_err_t rmt_uart_write_bytes(rmt_uart_port_t uart_num, const uint8_t* data, size_t size) 169 | { 170 | rmt_uart_contex_t* ctx = &rmt_uart_contex[uart_num]; 171 | ESP_RETURN_ON_FALSE((ctx->configured), ESP_FAIL, TAG, "uart not configured"); 172 | ESP_RETURN_ON_FALSE((ctx->rmt_uart_config.mode != RMT_UART_MODE_RX_ONLY), ESP_FAIL, TAG, "uart RX only"); 173 | rmt_uart_contex_tx_t* rtc = &ctx->rmt_uart_contex_tx; 174 | if (convert_data_to_items(ctx, data, size) < 0) return ESP_FAIL; 175 | // printf("rmt tx "); 176 | // for (int i = 0; i < size; ++i) 177 | // printf("%d ", data[i]); 178 | // printf("\n"); 179 | return rmt_write_items(ctx->rmt_config_tx.channel, ctx->rmt_uart_contex_tx.items, rtc->item_index, true); 180 | } 181 | 182 | int rmt_uart_read_bytes(rmt_uart_port_t uart_num, uint8_t* buf, size_t size, TickType_t ticks_to_wait) 183 | { 184 | rmt_uart_contex_t* ctx = &rmt_uart_contex[uart_num]; 185 | ESP_RETURN_ON_FALSE((ctx->configured), ESP_FAIL, TAG, "uart not configured"); 186 | ESP_RETURN_ON_FALSE((ctx->rmt_uart_config.mode != RMT_UART_MODE_TX_ONLY), ESP_FAIL, TAG, "uart TX only"); 187 | rmt_uart_contex_rx_t* rrc = &ctx->rmt_uart_contex_rx; 188 | rrc->bytes = buf; 189 | rrc->byte_num = 0; 190 | rrc->bit_num = 0; 191 | size_t length = 0; 192 | rmt_item32_t* items = NULL; 193 | 194 | items = (rmt_item32_t*)xRingbufferReceive(rrc->rb, &length, ticks_to_wait); 195 | if (items) { 196 | for (size_t i = 0; i < length / 4; ++i) 197 | { 198 | ESP_LOGD(TAG, "\trmt rx item %02d: duration0: %d level0: %d duration1: %d level1: %d", i, items[i].duration0, items[i].level0, items[i].duration1, items[i].level1); 199 | fill_bits(ctx, items[i].duration0, items[i].level0); 200 | fill_bits(ctx, items[i].duration1, items[i].level1); 201 | } 202 | vRingbufferReturnItem(rrc->rb, (void*)items); 203 | 204 | ESP_LOGD(TAG, "\trx is complete byte_num=%d", rrc->byte_num); 205 | // printf("rmt rx "); 206 | // for (int i = 0; i < rrc->byte_num; ++i) 207 | // printf("%d ", rrc->bytes[i]); 208 | // printf("\n\n"); 209 | } 210 | return rrc->byte_num; 211 | } 212 | 213 | esp_err_t rmt_uart_deinit(rmt_uart_port_t uart_num) 214 | { 215 | rmt_uart_contex_t* ctx = &rmt_uart_contex[uart_num]; 216 | ESP_RETURN_ON_FALSE((ctx->configured), ESP_FAIL, TAG, "uart not configured"); 217 | esp_err_t ret = ESP_OK; 218 | 219 | if (ctx->rmt_uart_config.mode != RMT_UART_MODE_RX_ONLY) 220 | { 221 | rmt_uart_contex_tx_t* rtc = &ctx->rmt_uart_contex_tx; 222 | #if CONFIG_SPIRAM_USE_MALLOC 223 | heap_caps_free(rtc->items); 224 | #else 225 | free(rtc->items); 226 | #endif 227 | ret = rmt_driver_uninstall(uart_num + 1); 228 | if (ret != ESP_OK) return ret; 229 | } 230 | 231 | if (ctx->rmt_uart_config.mode != RMT_UART_MODE_TX_ONLY) 232 | { 233 | ret = rmt_driver_uninstall(uart_num); 234 | if (ret != ESP_OK) return ret; 235 | } 236 | 237 | ctx->configured = false; 238 | return ret; 239 | } 240 | -------------------------------------------------------------------------------- /rmt_uart.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include 8 | #include 9 | #include "esp_err.h" 10 | #include "freertos/FreeRTOS.h" 11 | #include "soc/soc_caps.h" 12 | #include "hal/gpio_types.h" 13 | 14 | #define RMT_UART_NUM_0 (0) /*!< RMT UART port 0 */ 15 | #define RMT_UART_NUM_1 (1) /*!< RMT UART port 1 */ 16 | #if SOC_RMT_CHANNELS_PER_GROUP > 4 17 | #define RMT_UART_NUM_2 (2) /*!< RMT UART port 2 */ 18 | #define RMT_UART_NUM_3 (3) /*!< RMT UART port 2 */ 19 | #define RMT_UART_NUM_MAX (4) 20 | #else 21 | #define RMT_UART_NUM_MAX (2) 22 | #endif 23 | 24 | typedef int rmt_uart_port_t; 25 | 26 | typedef enum { 27 | RMT_UART_MODE_TX_ONLY = 0x0, 28 | RMT_UART_MODE_RX_ONLY = 0x1, 29 | RMT_UART_MODE_TX_RX = 0x2, 30 | } rmt_uart_mode_t; 31 | 32 | typedef enum { 33 | RMT_UART_DATA_8_BITS = 0x0, /*!< word length: 8bits*/ 34 | RMT_UART_DATA_9_BITS = 0x1, /*!< word length: 9bits*/ 35 | RMT_UART_DATA_BITS_MAX = 0x2, 36 | } rmt_uart_word_length_t; 37 | 38 | typedef enum { 39 | RMT_UART_STOP_BITS_1 = 0x1, /*!< stop bit: 1bit*/ 40 | RMT_UART_STOP_BITS_MAX = 0x2, 41 | } rmt_uart_stop_bits_t; 42 | 43 | typedef enum { 44 | RMT_UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/ 45 | } rmt_uart_parity_t; 46 | 47 | typedef struct { 48 | int baud_rate; /*!< UART baud rate*/ 49 | rmt_uart_mode_t mode; /*!< UART mode*/ 50 | rmt_uart_word_length_t data_bits; /*!< UART byte size*/ 51 | rmt_uart_parity_t parity; /*!< UART parity mode*/ 52 | rmt_uart_stop_bits_t stop_bits; /*!< UART stop bits*/ 53 | gpio_num_t tx_io_num; 54 | gpio_num_t rx_io_num; 55 | size_t buffer_size; 56 | } rmt_uart_config_t; 57 | 58 | esp_err_t rmt_uart_init(rmt_uart_port_t uart_num, const rmt_uart_config_t* uart_config); 59 | esp_err_t rmt_uart_write_bytes(rmt_uart_port_t uart_num, const uint8_t* data, size_t size); 60 | esp_err_t rmt_uart_read_bytes(rmt_uart_port_t uart_num, uint8_t* buf, size_t size, TickType_t ticks_to_wait); 61 | esp_err_t rmt_uart_deinit(rmt_uart_port_t uart_num); 62 | 63 | 64 | #ifdef __cplusplus 65 | } 66 | #endif 67 | --------------------------------------------------------------------------------