summaryrefslogtreecommitdiff
path: root/hw/bsp/espressif/components/led_strip/src/led_strip_rmt_dev_idf4.c
blob: a1067cd7c413dde43bc022ffdd6ebb98b6eef6d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
 * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include "esp_log.h"
#include "esp_check.h"
#include "driver/rmt.h"
#include "led_strip.h"
#include "led_strip_interface.h"

static const char *TAG = "led_strip_rmt";

#define WS2812_T0H_NS   (300)
#define WS2812_T0L_NS   (900)
#define WS2812_T1H_NS   (900)
#define WS2812_T1L_NS   (300)

#define SK6812_T0H_NS   (300)
#define SK6812_T0L_NS   (900)
#define SK6812_T1H_NS   (600)
#define SK6812_T1L_NS   (600)

#define LED_STRIP_RESET_MS (10)

// the memory size of each RMT channel, in words (4 bytes)
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
#else
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
#endif

static uint32_t led_t0h_ticks = 0;
static uint32_t led_t1h_ticks = 0;
static uint32_t led_t0l_ticks = 0;
static uint32_t led_t1l_ticks = 0;

typedef struct {
    led_strip_t base;
    rmt_channel_t rmt_channel;
    uint32_t strip_len;
    uint8_t bytes_per_pixel;
    uint8_t buffer[0];
} led_strip_rmt_obj;

static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
        size_t wanted_num, size_t *translated_size, size_t *item_num)
{
    if (src == NULL || dest == NULL) {
        *translated_size = 0;
        *item_num = 0;
        return;
    }
    const rmt_item32_t bit0 = {{{ led_t0h_ticks, 1, led_t0l_ticks, 0 }}}; //Logical 0
    const rmt_item32_t bit1 = {{{ led_t1h_ticks, 1, led_t1l_ticks, 0 }}}; //Logical 1
    size_t size = 0;
    size_t num = 0;
    uint8_t *psrc = (uint8_t *)src;
    rmt_item32_t *pdest = dest;
    while (size < src_size && num < wanted_num) {
        for (int i = 0; i < 8; i++) {
            // MSB first
            if (*psrc & (1 << (7 - i))) {
                pdest->val =  bit1.val;
            } else {
                pdest->val =  bit0.val;
            }
            num++;
            pdest++;
        }
        size++;
        psrc++;
    }
    *translated_size = size;
    *item_num = num;
}

static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
{
    led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
    ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of the maximum number of leds");
    uint32_t start = index * rmt_strip->bytes_per_pixel;
    // In thr order of GRB
    rmt_strip->buffer[start + 0] = green & 0xFF;
    rmt_strip->buffer[start + 1] = red & 0xFF;
    rmt_strip->buffer[start + 2] = blue & 0xFF;
    if (rmt_strip->bytes_per_pixel > 3) {
        rmt_strip->buffer[start + 3] = 0;
    }
    return ESP_OK;
}

static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
{
    led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
    ESP_RETURN_ON_ERROR(rmt_write_sample(rmt_strip->rmt_channel, rmt_strip->buffer, rmt_strip->strip_len * rmt_strip->bytes_per_pixel, true), TAG,
                        "transmit RMT samples failed");
    vTaskDelay(pdMS_TO_TICKS(LED_STRIP_RESET_MS));
    return ESP_OK;
}

static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
{
    led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
    // Write zero to turn off all LEDs
    memset(rmt_strip->buffer, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
    return led_strip_rmt_refresh(strip);
}

static esp_err_t led_strip_rmt_del(led_strip_t *strip)
{
    led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
    ESP_RETURN_ON_ERROR(rmt_driver_uninstall(rmt_strip->rmt_channel), TAG, "uninstall RMT driver failed");
    free(rmt_strip);
    return ESP_OK;
}

esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *dev_config, led_strip_handle_t *ret_strip)
{
    led_strip_rmt_obj *rmt_strip = NULL;
    esp_err_t ret = ESP_OK;
    ESP_RETURN_ON_FALSE(led_config && dev_config && ret_strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
    ESP_RETURN_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, TAG, "invalid led_pixel_format");
    ESP_RETURN_ON_FALSE(dev_config->flags.with_dma == 0, ESP_ERR_NOT_SUPPORTED, TAG, "DMA is not supported");

    uint8_t bytes_per_pixel = 3;
    if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
        bytes_per_pixel = 4;
    } else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
        bytes_per_pixel = 3;
    } else {
        assert(false);
    }

    // allocate memory for led_strip object
    rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
    ESP_RETURN_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, TAG, "request memory for les_strip failed");

    // install RMT channel driver
    rmt_config_t config = RMT_DEFAULT_CONFIG_TX(led_config->strip_gpio_num, dev_config->rmt_channel);
    // set the minimal clock division because the LED strip needs a high clock resolution
    config.clk_div = 2;

    uint8_t mem_block_num = 2;
    // override the default value if the user specify the mem block size
    if (dev_config->mem_block_symbols) {
        mem_block_num = (dev_config->mem_block_symbols + LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS / 2) / LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
    }
    config.mem_block_num = mem_block_num;

    ESP_GOTO_ON_ERROR(rmt_config(&config), err, TAG, "RMT config failed");
    ESP_GOTO_ON_ERROR(rmt_driver_install(config.channel, 0, 0), err, TAG, "RMT install failed");

    uint32_t counter_clk_hz = 0;
    rmt_get_counter_clock((rmt_channel_t)dev_config->rmt_channel, &counter_clk_hz);
    // ns -> ticks
    float ratio = (float)counter_clk_hz / 1e9;
    if (led_config->led_model == LED_MODEL_WS2812) {
        led_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
        led_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
        led_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
        led_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
    } else if (led_config->led_model == LED_MODEL_SK6812) {
        led_t0h_ticks = (uint32_t)(ratio * SK6812_T0H_NS);
        led_t0l_ticks = (uint32_t)(ratio * SK6812_T0L_NS);
        led_t1h_ticks = (uint32_t)(ratio * SK6812_T1H_NS);
        led_t1l_ticks = (uint32_t)(ratio * SK6812_T1L_NS);
    } else {
        assert(false);
    }

    // adapter to translates the LES strip date frame into RMT symbols
    rmt_translator_init((rmt_channel_t)dev_config->rmt_channel, ws2812_rmt_adapter);

    rmt_strip->bytes_per_pixel = bytes_per_pixel;
    rmt_strip->rmt_channel = (rmt_channel_t)dev_config->rmt_channel;
    rmt_strip->strip_len = led_config->max_leds;
    rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
    rmt_strip->base.refresh = led_strip_rmt_refresh;
    rmt_strip->base.clear = led_strip_rmt_clear;
    rmt_strip->base.del = led_strip_rmt_del;

    *ret_strip = &rmt_strip->base;
    return ESP_OK;

err:
    if (rmt_strip) {
        free(rmt_strip);
    }
    return ret;
}