help with RMT synchronised pulse sequence generator

Yossarian
Posts: 3
Joined: Sun Oct 16, 2022 10:31 am

help with RMT synchronised pulse sequence generator

Postby Yossarian » Tue Mar 28, 2023 6:39 pm

Hi Guys

I'm trying to make a twin channel synchronised pulse sequence generator. I'm using an esp32-C3 and espressif ide v5.0.0. I've modified the musical buzzer in the RMT examples. It's almost working. Pulses are synchronised beautifully, but it seems to be duplicating the first of the two pulses. Pulses are defined by a array of typedef structs consisting of on and off ticks (microseconds).

Code: Select all

pulse_sequence_t channelA_pulses[] = { {15,3}, {7,2}, };
pulse_sequence_t channelB_pulses[] = { {5,4}, {15,3}, };
Here's an oscilloscope shot from the code. The first pulse on both channels is duplicated. If I comment out the rmt_sync_reset(synchro) line and replace it with a vTaskDelay then the messed up duplicated pulse vanishes.
pulses.jpg
pulses.jpg (157.33 KiB) Viewed 666 times
I've pasted the full code below, and you can also get it from my gitlab page. Any help or suggestions much appreciated ;)

https://gitlab.com/patrick_oconnor/esp3 ... _generator

Thanks
Patrick O'Connor

Code: Select all

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/rmt_tx.h"
#include "pulse_sequence_encoder.h"

#define RMT_PULSE_RESOLUTION_HZ 1000000 // 1MHz resolution

static const char *TAG = "Two Channel Synchronised Pulse Generator";

void app_main(void)
{
    /*Custom Pulse sequence, {ticks high, ticks low}
    */
pulse_sequence_t channelA_pulses[] = { {15,3}, {7,2}, };
pulse_sequence_t channelB_pulses[] = { {5,4}, {15,3}, };

ESP_LOGI(TAG, "Create RMT TX channels");
int tx_gpio_number[2] = {9,10};                     // output pins
rmt_channel_handle_t tx_channels[2] = {NULL};       // declare two channels

// install channels one by one
for (int i = 0; i < 2; i++) {
    rmt_tx_channel_config_t tx_chan_config = {
        .clk_src = RMT_CLK_SRC_DEFAULT,             // select source clock
        .gpio_num = tx_gpio_number[i],              // GPIO number
        .mem_block_symbols = 48,                    // memory block size, 48 * 4 = 192 Bytes
        .resolution_hz = RMT_PULSE_RESOLUTION_HZ,  // 1MHz resolution
        .trans_queue_depth = 1,                     // Number of transactions that can pend in the background. 32 Bytes per pulse so 128 Bytes for 2 x twin pulses
    };
    ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &tx_channels[i]));
}

//Start both channels
for (int i = 0; i<2; i++) {
    ESP_ERROR_CHECK(rmt_enable(tx_channels[i]));
}

// install sync manager
ESP_LOGI(TAG, "Enable RMT synchronous TX channels");
rmt_sync_manager_handle_t synchro = NULL;
rmt_sync_manager_config_t synchro_config = {
    .tx_channel_array = tx_channels,
    .array_size = sizeof(tx_channels) / sizeof(tx_channels[0]),
};
ESP_ERROR_CHECK(rmt_new_sync_manager(&synchro_config, &synchro));

ESP_LOGI(TAG, "Install pulse sequence encoder");
rmt_encoder_handle_t pulse_encoder = NULL;
pulse_sequence_encoder_config_t encoder_config = { .resolution = RMT_PULSE_RESOLUTION_HZ };
ESP_ERROR_CHECK(rmt_new_pulse_sequence_encoder(&encoder_config, &pulse_encoder));

rmt_transmit_config_t tx_config = { .loop_count = 0, };

ESP_LOGI(TAG, "Sending the pulses...");
    while(1) {
        ESP_ERROR_CHECK(rmt_transmit(tx_channels[0], pulse_encoder, channelA_pulses, sizeof(channelA_pulses), &tx_config));
        ESP_ERROR_CHECK(rmt_transmit(tx_channels[1], pulse_encoder, channelB_pulses, sizeof(channelB_pulses), &tx_config));
        ESP_ERROR_CHECK(rmt_sync_reset(synchro));
        //vTaskDelay( 100 / portTICK_PERIOD_MS );
    } 
}

Code: Select all

#include "esp_check.h"
#include "pulse_sequence_encoder.h"

static const char *TAG = "score_encoder";
typedef struct {
    rmt_encoder_t base;
    rmt_encoder_t *copy_encoder;
    uint32_t resolution;
} rmt_pulse_sequence_encoder_t;

static size_t rmt_encode_pulse_sequence(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
    
    rmt_pulse_sequence_encoder_t *score_encoder = __containerof(encoder, rmt_pulse_sequence_encoder_t, base);
    rmt_encoder_handle_t copy_encoder = score_encoder->copy_encoder;
    rmt_encode_state_t session_state = 0;
    pulse_sequence_t *score = (pulse_sequence_t *)primary_data;
    size_t encoded_symbols = 0;
    
         for (uint32_t i=0; i<2; i++) {
        rmt_symbol_word_t pulse_sequence_rmt_symbol = {
            .level0 = 1, 
            .duration0 = score[i].leading_ticks,
            .level1 = 0, 
            .duration1 = score[i].trailing_ticks,
        };
        encoded_symbols += copy_encoder->encode(copy_encoder, channel, &pulse_sequence_rmt_symbol, sizeof(pulse_sequence_rmt_symbol), &session_state);
         }
         
         
return encoded_symbols;
}

static esp_err_t rmt_del_pulse_sequence_encoder(rmt_encoder_t *encoder)
{
    rmt_pulse_sequence_encoder_t *pulse_encoder = __containerof(encoder, rmt_pulse_sequence_encoder_t, base);
    rmt_del_encoder(pulse_encoder->copy_encoder);
    free(pulse_encoder);
    return ESP_OK;
}

static esp_err_t rmt_pulse_sequence_encoder_reset(rmt_encoder_t *encoder)
{
    rmt_pulse_sequence_encoder_t *pulse_encoder = __containerof(encoder, rmt_pulse_sequence_encoder_t, base);
    rmt_encoder_reset(pulse_encoder->copy_encoder);
    return ESP_OK;
}

esp_err_t rmt_new_pulse_sequence_encoder(const pulse_sequence_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
{
    esp_err_t ret = ESP_OK;
    rmt_pulse_sequence_encoder_t *pulse_encoder = NULL;
    ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
    pulse_encoder = calloc(1, sizeof(rmt_pulse_sequence_encoder_t));
    ESP_GOTO_ON_FALSE(pulse_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for musical score encoder");
    pulse_encoder->base.encode = rmt_encode_pulse_sequence;
    pulse_encoder->base.del = rmt_del_pulse_sequence_encoder;
    pulse_encoder->base.reset = rmt_pulse_sequence_encoder_reset;
    pulse_encoder->resolution = config->resolution;
    rmt_copy_encoder_config_t copy_encoder_config = {};
    ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(&copy_encoder_config, &pulse_encoder->copy_encoder), err, TAG, "create copy encoder failed");
    *ret_encoder = &pulse_encoder->base;
    return ESP_OK;
err:
    if (pulse_encoder) {
        if (pulse_encoder->copy_encoder) {
            rmt_del_encoder(pulse_encoder->copy_encoder);
        }
        free(pulse_encoder);
    }
    return ret;
}

Code: Select all

#pragma once

#include <stdint.h>
#include "driver/rmt_encoder.h"

#ifdef __cplusplus
extern "C" {
#endif
/**
 * @brief Type of pulse sequence
 */
typedef struct {
    uint32_t leading_ticks;     /* high by default */  
    uint32_t trailing_ticks;    /* low by default */
} pulse_sequence_t;


/**
 * @brief Type of musical score encoder configuration
 */
typedef struct {
    uint32_t resolution; /*!< Encoder resolution, in Hz */
} pulse_sequence_encoder_config_t;


/**
 * @brief Create RMT encoder for encoding musical score into RMT symbols
 *
 * @param[in] config Encoder configuration
 * @param[out] ret_encoder Returned encoder handle
 * @return
 *      - ESP_ERR_INVALID_ARG for any invalid arguments
 *      - ESP_ERR_NO_MEM out of memory when creating musical score encoder
 *      - ESP_OK if creating encoder successfully
 */
esp_err_t rmt_new_pulse_sequence_encoder(const pulse_sequence_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);

#ifdef __cplusplus
}
#endif

Last edited by Yossarian on Sat Apr 01, 2023 11:29 am, edited 3 times in total.

Who is online

Users browsing this forum: No registered users and 121 guests