1

I'm using an esp32 s3 (dual core) and I'd like to transmit DMX signals. The thing is that I have set the resolution as such that each tick is 4us (should be) but what I am getting is bits with way less time duration.

bit duration

I am using esp-idf v5.5. I know that for now the overhead in the task is too much but that should not have an influence of the timing on the signals, right?

Any clue what might be causing this effect on the signals? The expected bits are there actually but timing is not correct.

larger picture of dmx signal

Does anyone have a clue on why my timing might not be correct and what I could do to solve this?

My code (i know bit much but rather all than lack of information)

main.c

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_cpp.h"

void app_main(void)
{
    printf("Main, Starting\n");
    esp_cpp();
    printf("Restarting now.\n");
    fflush(stdout);
    esp_restart();
}

esp_cpp.cpp

#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "driver/uart.h"
#include "esp_cpp.h"
#include "esp_dmx_transmitter.h"

extern "C" {  

    
    void esp_cpp() {
        uart_port_t dmx_uart_num = UART_NUM_1;
        ESP_Dmx_Transmitter *p_dmx_transmitter = new ESP_Dmx_Transmitter(dmx_uart_num);

        p_dmx_transmitter->init();

        while(true) {
            p_dmx_transmitter->transmit();
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
    }
}

esp_cpp.h

#ifndef _ESP_CPP_H_
#define _ESP_CPP_H_


#ifdef __cplusplus
    extern "C" {
#endif
    

void esp_cpp();

#ifdef __cplusplus
    } 
#endif


#endif //_ESP_CPP_H

esp_dmx_transmitter.h

#ifndef _ESP_DMX_TRANSMITTER_H
#define _ESP_DMX_TRANSMITTER_H

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include <stdint.h>
#include "driver/rmt_tx.h"

class ESP_Dmx_Transmitter {
    public:
        ESP_Dmx_Transmitter(uart_port_t uart_num) : _dmx_uart_num(uart_num) {};
        ~ESP_Dmx_Transmitter() {};
        
        void init();
        void transmit();

    private:
        const uart_port_t _dmx_uart_num;
        friend void _esp_dmx_transmitter_task(void*);
        uint8_t _dmx_buffer[513];
        bool _flag_transmit = false;

        rmt_copy_encoder_config_t _copy_encoder_config = {};
        rmt_encoder_handle_t _copy_encoder = NULL;

        rmt_transmit_config_t _tx_config = {
            .loop_count = 0,
            .flags = {
                .eot_level = 1,
                .queue_nonblocking = 1,
            },
        };

        rmt_symbol_word_t _dmx_break_mab_symbol;
        rmt_channel_handle_t _tx_channel = NULL;
};

#endif

esp_dmx_transmitter.cpp

#include "esp_dmx_transmitter.h"
#include "driver/gpio.h"
#include <rom/ets_sys.h>
#include <esp_log.h>
#include "soc/gpio_reg.h"

#define TAG "ESP_DMX_TRANSMITTER"

void _esp_dmx_transmitter_task(void *pvParameters) {
    ESP_Dmx_Transmitter* p_dmx_transmitter = static_cast<ESP_Dmx_Transmitter*>(pvParameters);

    while(true) {
        vTaskDelay(pdMS_TO_TICKS(25)); // 20ms delay between packets
        if(!p_dmx_transmitter->_flag_transmit) continue;
        p_dmx_transmitter->_flag_transmit = false;
        uart_port_t dmx_uart_num = p_dmx_transmitter->_dmx_uart_num;

        size_t num_symbols = 1 + 513 * 5;
        rmt_symbol_word_t *items = (rmt_symbol_word_t*)malloc(num_symbols * sizeof(rmt_symbol_word_t));

        //  first >=88us -> 0
        //  then >= 8us -> 1

        items[0].duration0 = 22;
        items[0].level0 = 0;
        items[0].duration1 = 2;
        items[0].level1 = 1;

        for(int i = 0; i < 513; i++) {
            uint16_t base_word = i * 5 + 1;
            //  each bit is 4us
            //  each message has 1 start bit (low)
            //  each message has 2 stop bits (high)
            //  each message (8 bits) goes from LSB to MSB

            //  first word has start bit and bit 7 - 0
            //  2nd word has bit 7 - 1 and bit 7 - 2
            //  3rd word has bit 7 - 3 and bit 7 - 4
            //  4th word has bit 7 - 5 and bit 7 - 6
            //  8th word has bit 7 - 7 and two stop bits
            //  5 words
            uint8_t byte = p_dmx_transmitter->_dmx_buffer[i];
            items[base_word].duration0 = 1;
            items[base_word].level0 = 0;
            items[base_word].duration1 = 1;
            items[base_word].level1 = (byte & 0x01) ? 1 : 0;

            for(int i = 1; i < 5; i++) {
                byte >>= 1;
                items[base_word + i].duration0 = 1;
                items[base_word + i].level0 = (byte & 0x01) ? 1 : 0;
                byte >>= 1;
                items[base_word + i].duration1 = 1;
                items[base_word + i].level1 = (byte & 0x01) ? 1 : 0;
            }

            byte >>= 1;
            items[base_word + 4].duration0 = 1;
            items[base_word + 4].level0 = (byte & 0x01) ? 1 : 0;
            items[base_word + 4].duration1 = 2;
            items[base_word + 4].level1 = 1;

        }

        ESP_ERROR_CHECK(
            rmt_transmit(
                p_dmx_transmitter->_tx_channel,
                p_dmx_transmitter->_copy_encoder,
                items,
                num_symbols,
                &p_dmx_transmitter->_tx_config
            )
        );
        rmt_tx_wait_all_done(p_dmx_transmitter->_tx_channel, portMAX_DELAY);
        free(items);
    }
}

void ESP_Dmx_Transmitter::init() {

    rmt_tx_channel_config_t tx_chan_config = {
        .gpio_num = GPIO_NUM_16,
        .clk_src = RMT_CLK_SRC_APB, // Use the default clock source (usually APB)
        .resolution_hz = 250000, // 1 MHz resolution, 1 tick = 1 microsecond
        .mem_block_symbols = 512, // Memory block size
        .trans_queue_depth = 4,          // set the number of transactions that can pend in the background
        .intr_priority = 0,
        .flags = {
            .invert_out = false,
            .with_dma = true,  
            .io_loop_back = false,
            .io_od_mode = true,
            .allow_pd = false
        }
    };

    ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &_tx_channel));

    ESP_ERROR_CHECK(rmt_enable(_tx_channel));

    ESP_ERROR_CHECK(rmt_new_copy_encoder(&_copy_encoder_config, &_copy_encoder));

    for(int i = 0; i < 513; i++) {
        _dmx_buffer[i] = i; // or fill with channel values
    }

    xTaskCreate(
        _esp_dmx_transmitter_task, 
        "_esp_dmx_transmitter_task", 
        4096, 
        this,
        10,
        NULL
    );
    
}

void ESP_Dmx_Transmitter::transmit() {
    while(_flag_transmit == true) {
        vTaskDelay(pdMS_TO_TICKS(1));
    }
    _flag_transmit = true;
}
1
  • Seems there are very few people with knowledge on how RMT works. Anyway, this old post claims they fixed something by adding carrier modulation: esp32.com/viewtopic.php?t=40711#p134463 I guess you can always read ESP IDF source code if you're desperate :) Commented Oct 5 at 16:01

1 Answer 1

1

The solution was found in the config;

Original

rmt_tx_channel_config_t tx_chan_config = {
        .gpio_num = GPIO_NUM_16,
        .clk_src = RMT_CLK_SRC_APB, 
        .resolution_hz = 250000, 
        .mem_block_symbols = 512, 
        .trans_queue_depth = 4,
        .intr_priority = 0,
        .flags = {
            .invert_out = false,
            .with_dma = true,  
            .io_loop_back = false,
            .io_od_mode = true,
            .allow_pd = false
        }
    };

the value .io_od_mode is set to true which indicates that the pin is set as open drain. I found this after noticing that 0 values where durated too long and 1 values too short. Setting io_od_mode to false solved the issue.

New config:

rmt_tx_channel_config_t tx_chan_config = {
        .gpio_num = GPIO_NUM_16,
        .clk_src = RMT_CLK_SRC_APB, 
        .resolution_hz = 250000, 
        .mem_block_symbols = 512, 
        .trans_queue_depth = 4,
        .intr_priority = 0,
        .flags = {
            .invert_out = false,
            .with_dma = true,  
            .io_loop_back = false,
            .io_od_mode = false,
            .allow_pd = false
        }
    };

(I also set the clk_src to RMT_CLK_SRC_DEFAULT but I think that was not the main issue here)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.