Jitter on 'one shot' RMT output
Posted: Sun Sep 03, 2023 10:04 am
I implemented a programmable 'one-shot circuit' using RMT; a rising edge on an input GPIO pin starts a programmable delay (range from 5 us to at least 30 ms), after which an output GPIO is set high for a programmable duration (similar range).
The only problem is a 1 us – wide 'jitter' on the produced output pulse.
While applying a square wave signal from a signal generator to PIN_IN_EDGE_DETECT, measuring on a scope showed that RMT starts between 2.6 us and 3.6 us after the call to 'rmt_write_items(..)'. More precisely the start-time drifts from 2.6 us to 3.6 us in about 2 seconds, then it rolls over back to 2.6 us and repeats.
'rmt_write_items' in rmt.c has a lot of code to execute which probably explains it.
The constant latency of 2.6 us can be compensated for because it is a stable value, but how to get rid of the jitter?
Is there a way of initializing first (which may take variable time), and then sending a low - and fixed latency 'start'?
Code below runs on an AZ-Delivery ESP32-S2 @ 240 MHz using its 'dedicated GPIO bundles' for faster GPIO.
The only problem is a 1 us – wide 'jitter' on the produced output pulse.
While applying a square wave signal from a signal generator to PIN_IN_EDGE_DETECT, measuring on a scope showed that RMT starts between 2.6 us and 3.6 us after the call to 'rmt_write_items(..)'. More precisely the start-time drifts from 2.6 us to 3.6 us in about 2 seconds, then it rolls over back to 2.6 us and repeats.
'rmt_write_items' in rmt.c has a lot of code to execute which probably explains it.
The constant latency of 2.6 us can be compensated for because it is a stable value, but how to get rid of the jitter?
Is there a way of initializing first (which may take variable time), and then sending a low - and fixed latency 'start'?
Code below runs on an AZ-Delivery ESP32-S2 @ 240 MHz using its 'dedicated GPIO bundles' for faster GPIO.
Code: Select all
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/rmt_tx.h>
#include <inttypes.h>
#include "soc/gpio_struct.h"
#include <driver/rmt.h>
#include "sdkconfig.h"
// ESP32-S2 supports 'dedicated GPIO' for max speed
#include "driver/dedic_gpio.h"
#include "hal/dedic_gpio_cpu_ll.h"
//____ User Values ____________________________________________________________
#define PIN_IN_EDGE_DETECT 5
#define PIN_OUT_EDGE_DETECTED 15
#define PIN_OUT_RMT_ONE_SHOT 21
//____ Globals ________________________________________________________________
#define ESP_INTR_FLAG_DEFAULT 0
#define PIN_OUT_SEL (1ULL << PIN_OUT_EDGE_DETECTED)
#define PIN_IN_SEL (1ULL << PIN_IN_EDGE_DETECT)
#define BIT_OUT_EDGE_DETECTED 1
static const int _gpios_out_bundle[] = {PIN_OUT_EDGE_DETECTED};
static const int _gpios_in_bundle[] = {PIN_IN_EDGE_DETECT};
dedic_gpio_bundle_handle_t _bundle_in = NULL;
rmt_config_t _rmt_cfg;
rmt_item32_t _pulse_item;
//____ Code ___________________________________________________________________
void init_rmt_deprec_api() {
_rmt_cfg.rmt_mode = RMT_MODE_TX;
_rmt_cfg.channel = RMT_CHANNEL_0;
_rmt_cfg.gpio_num = PIN_OUT_RMT_ONE_SHOT;
_rmt_cfg.mem_block_num = 1;
_rmt_cfg.tx_config.loop_en = 0;
_rmt_cfg.tx_config.carrier_en = 0;
_rmt_cfg.tx_config.idle_output_en = 1;
_rmt_cfg.tx_config.idle_level = 0;
_rmt_cfg.tx_config.carrier_duty_percent = 50;
_rmt_cfg.tx_config.carrier_freq_hz = 1000;
_rmt_cfg.tx_config.carrier_level = 1;
_rmt_cfg.clk_div = 80; // -> 1 us resolution
ESP_ERROR_CHECK(rmt_config(&_rmt_cfg));
ESP_ERROR_CHECK(rmt_driver_install(_rmt_cfg.channel, 0, 0));
// values in us:
_pulse_item.duration0 = 100;
_pulse_item.level0 = 1;
_pulse_item.duration1 = 20;
_pulse_item.level1 = 0;
}
static void IRAM_ATTR _gpio_isr_handler(void * arg) {
uint32_t in_value = dedic_gpio_bundle_read_in(_bundle_in);
if (in_value != 0) { // rising edge detected
asm volatile("set_bit_gpio_out %0" : : "i"(BIT_OUT_EDGE_DETECTED) : ); // 1.6 us delay since rising edge
rmt_write_items(_rmt_cfg.channel, &_pulse_item, 1, 0);
// starts between 2.6 us and 3.6 us after PIN_OUT_EDGE_DETECTED goes high and drifts in this range :-(
}
else { // falling edge detected
asm volatile("clr_bit_gpio_out %0" : : "i"(BIT_OUT_EDGE_DETECTED) : ); // 1.6 us delay since falling edge
}
}
void init_gpio(void) {
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = PIN_OUT_SEL; // bit mask of output pins
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
ESP_ERROR_CHECK(gpio_config(&io_conf));
io_conf.intr_type = GPIO_INTR_POSEDGE;
io_conf.pin_bit_mask = PIN_IN_SEL; // bit mask of input pins
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
ESP_ERROR_CHECK(gpio_config(&io_conf));
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/dedic_gpio.html
dedic_gpio_bundle_handle_t bundle_out = NULL;
dedic_gpio_bundle_config_t bundle_out_config = {
.gpio_array = _gpios_out_bundle,
.array_size = 1,
.flags = { .out_en = 1, },
};
ESP_ERROR_CHECK(dedic_gpio_new_bundle(&bundle_out_config, &bundle_out));
dedic_gpio_bundle_config_t bundle_in_config = {
.gpio_array = _gpios_in_bundle,
.array_size = 1,
.flags = { .in_en = 1, },
};
ESP_ERROR_CHECK(dedic_gpio_new_bundle(&bundle_in_config, &_bundle_in));
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
gpio_isr_handler_add(PIN_IN_EDGE_DETECT, _gpio_isr_handler, (void *) PIN_IN_EDGE_DETECT);
}
void app_main(void) {
init_rmt_deprec_api();
init_gpio();
}