Page 1 of 1

How to setup RMT timings for neopixel LED's

Posted: Thu May 06, 2021 5:32 am
by zazas321
Hello. I am learning about the RMT and neopixels. I have found adafruit neopixel library :
https://github.com/adafruit/Adafruit_NeoPixel

The library is using RMT to drive the neopixels.


I have also read espressif documentation on rmt and found this great example project:
https://github.com/adafruit/Adafruit_NeoPixel


Both projects are very simmilar in terms of RMT, however, it is still not fully clear to me how RMT timings are setup. In the espressif RMT example, the rmt_item_t is setup as following :

Code: Select all

 const rmt_item32_t bit0 = {{{ 32767, 1, 15000, 0 }}}; //Logical 0
 const rmt_item32_t bit1 = {{{ 32767, 1, 32767, 0 }}}; //Logical 1
I want t understand how these values correspond to actual time . What is the formula behind it?


In the neopixel adafruit library the things get even more confusing , please see the following functions

Code: Select all


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 = {{{t0h_ticks, 1, t0l_ticks, 0}}}; //Logical 0
    const rmt_item32_t bit1 = {{{t1h_ticks, 1, 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;
}

void espShow(uint8_t pin, uint8_t *pixels, uint32_t numBytes)
{
    // Reserve channel
    rmt_channel_t channel = ADAFRUIT_RMT_CHANNEL_MAX;
    for (size_t i = 0; i < ADAFRUIT_RMT_CHANNEL_MAX; i++)
    {
        if (!rmt_reserved_channels[i])
        {
            rmt_reserved_channels[i] = true;
            channel = i;
        }
    }
    if (channel == ADAFRUIT_RMT_CHANNEL_MAX)
    {
        // Ran out of channels!
        return;
    }

    // Match default TX config from ESP-IDF version 3.4
    rmt_config_t config = {
        .rmt_mode = RMT_MODE_TX,
        .channel = channel,
        .gpio_num = pin,
        .clk_div = 2,
        .mem_block_num = 1,
        .tx_config = {
            //.carrier_freq_hz = 38000,
            //.carrier_level = RMT_CARRIER_LEVEL_HIGH,
            .idle_level = RMT_IDLE_LEVEL_LOW,
            //.carrier_duty_percent = 33,
            .carrier_en = false,
            .loop_en = false,
            .idle_output_en = true,
        }};
    rmt_config(&config);
    rmt_driver_install(config.channel, 0, 0);

    // Convert NS timings to ticks
    uint32_t counter_clk_hz = 0;

    // this emulates the rmt_get_counter_clock() function from ESP-IDF 3.4
    if (RMT_LL_HW_BASE->conf_ch[config.channel].conf1.ref_always_on == RMT_BASECLK_REF)
    {
        cout << "hello, world ref" << endl; // Say Hello
        //Serial.println("hello mate ref");
        uint32_t div_cnt = RMT_LL_HW_BASE->conf_ch[config.channel].conf0.div_cnt;
        uint32_t div = div_cnt == 0 ? 256 : div_cnt;
        counter_clk_hz = REF_CLK_FREQ / (div);
    }
    else
    {
        cout << "hello, world apb" << endl; // Say Hello
        //std:cout<<"hello mate apb"<<std<<endl;
        uint32_t div_cnt = RMT_LL_HW_BASE->conf_ch[config.channel].conf0.div_cnt;
        uint32_t div = div_cnt == 0 ? 256 : div_cnt;
        counter_clk_hz = APB_CLK_FREQ / (div);
    }

    // NS to tick converter
    float ratio = (float)counter_clk_hz / 1e9;

    t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
    t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
    t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
    t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);

    // Initialize automatic timing translator
    rmt_translator_init(config.channel, ws2812_rmt_adapter);

    // Write and wait to finish
    rmt_write_sample(config.channel, pixels, (size_t)numBytes, true);
    rmt_wait_tx_done(config.channel, pdMS_TO_TICKS(100));

    // Free channel again
    rmt_driver_uninstall(config.channel);
    rmt_reserved_channels[channel] = false;

    gpio_set_direction(pin, GPIO_MODE_OUTPUT);
}

In the esp_show function, the timings for t0h,t1h,t0l,t1l are being setup, but I cannot grasp how are they doing it...
Could someone please clarify this. Any help is appreciated