Skip to content

ESP32-S3 RMT-based PWM sweep causes glitches; not as seamless as Timer1 on Arduino Uno #11526

Closed
@Murugesh-Hobbyist

Description

@Murugesh-Hobbyist

Board

ESP32-S3

Device Description

ESP32-S3 module on custom board. Only one GPIO (GPIO10) is used for PWM output via RMT.

Hardware Configuration

Only GPIO10 is used as output to measure PWM via oscilloscope. No other components attached.

Version

latest stable Release (if not listed below)

IDE Name

Arduino IDE

Operating System

Windows 11

Flash frequency

80 MHz

PSRAM enabled

no

Upload speed

921600

Description

When sweeping PWM frequency and Ton dynamically using RMT on ESP32-S3, the output is not smooth. There's a noticeable glitch or "bang" at each frequency step. The same logic works perfectly and seamlessly on Arduino Uno using Timer1 hardware PWM with Phase Correct mode.

Expected: clean frequency and Ton sweep with no break in output
Observed: visible discontinuity or glitch at each step due to teardown/reinit of RMT channel

Sketch

Below code i used in Arduino UNO worked flawlessly.

const int pwmPin = 9;  // OC1A = Pin 9 on Arduino Uno

const float freqStart = 28000.0;  // Hz
const float freqEnd   = 24000.0;  // Hz
const float freqStep  = -10.0;    // Hz

const float tonStart = 8.0;       // µs
const float tonEnd   = 21.0;      // µs

const int delayMs    = 5;         // Delay per step
const int offDelayMs = 3000;      // 5 sec OFF after sweep

void setup() {
  pinMode(pwmPin, OUTPUT);
  delay(2000);

  // Timer1: Phase Correct PWM, TOP = ICR1
  TCCR1A = _BV(COM1A1);               // Non-inverting PWM on OC1A
  TCCR1B = _BV(WGM13) | _BV(CS10);    // Mode 8: Phase-correct, no prescaler

  Serial.begin(115200);
}

void loop() {
  float freq = freqStart;
  float ton = tonStart;
  float freqRange = freqStart - freqEnd;
  float tonRange = tonEnd - tonStart;
  int steps = freqRange / -freqStep;

  for (int i = 0; i <= steps; i++) {
    freq = freqStart + i * freqStep;
    ton = tonStart + (tonRange * i / steps);

    float period_us = 1000000.0 / freq;
    uint16_t top = (16000000UL / (2 * freq));  // ICR1 value
    uint16_t duty = (ton / period_us) * top;   // OCR1A value

    ICR1 = top;
    OCR1A = duty;

    Serial.print("Freq: ");
    Serial.print(freq, 1);
    Serial.print(" Hz | Ton: ");
    Serial.print(ton, 1);
    Serial.print(" us | TOP: ");
    Serial.print(top);
    Serial.print(" | OCR1A: ");
    Serial.println(duty);

    delay(delayMs);
  }

  // Turn off PWM
  OCR1A = 0;
  Serial.println("PWM OFF for 5 seconds");
  delay(offDelayMs);
}


Below code i used in ESP32-S3 but the wave is banging.

#include <Arduino.h>
#include <driver/rmt_tx.h>
#include <Ticker.h>

// --- Pin Setup ---
#define PWM_PIN 10  // Pick a reliable RMT-capable GPIO

// --- Sweep Parameters ---
const float freqStart = 28000.0;
const float freqEnd   = 24000.0;
const float freqStep  = -10.0;

const float tonStart = 8.0;
const float tonEnd   = 21.0;

const int delayMs    = 5;
const int offDelayMs = 5000;

rmt_channel_handle_t rmt_chan = nullptr;
rmt_encoder_handle_t rmt_encoder = nullptr;

void setupRMT(float freq, float ton_us) {
  if (rmt_chan) {
    rmt_disable(rmt_chan);
    rmt_del_channel(rmt_chan);
    rmt_chan = nullptr;
  }

  uint32_t period_us = 1000000.0 / freq;
  uint32_t toff_us = period_us - ton_us;

  rmt_tx_channel_config_t tx_config = {};
  tx_config.clk_src = RMT_CLK_SRC_DEFAULT;
  tx_config.gpio_num = (gpio_num_t)PWM_PIN;
  tx_config.mem_block_symbols = 64;
  tx_config.resolution_hz = 1000000; // 1 µs resolution
  tx_config.trans_queue_depth = 1;
  tx_config.flags.invert_out = false;
  tx_config.flags.with_dma = false;

  ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_config, &rmt_chan));

  rmt_copy_encoder_config_t enc_config = {};
  ESP_ERROR_CHECK(rmt_new_copy_encoder(&enc_config, &rmt_encoder));
  ESP_ERROR_CHECK(rmt_enable(rmt_chan));

  rmt_symbol_word_t item;
  item.level0 = 1;
  item.duration0 = (uint16_t)ton_us;
  item.level1 = 0;
  item.duration1 = (uint16_t)toff_us;

  rmt_transmit_config_t config = {};
  config.loop_count = 1000;
  ESP_ERROR_CHECK(rmt_transmit(rmt_chan, rmt_encoder, &item, sizeof(item), &config));
}


void stopPWM() {
  if (rmt_chan) {
    rmt_disable(rmt_chan);
    gpio_reset_pin((gpio_num_t)PWM_PIN);
    gpio_set_direction((gpio_num_t)PWM_PIN, GPIO_MODE_OUTPUT);
    gpio_set_level((gpio_num_t)PWM_PIN, 0);
    rmt_del_channel(rmt_chan);
    rmt_chan = nullptr;
  }
}

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("Starting PWM Sweep...");
}

void loop() {
  float freq = freqStart;
  float ton = tonStart;
  float freqRange = freqStart - freqEnd;
  float tonRange = tonEnd - tonStart;
  int steps = freqRange / -freqStep;

  for (int i = 0; i <= steps; i++) {
    freq = freqStart + i * freqStep;
    ton  = tonStart + (tonRange * i / steps);

    setupRMT(freq, ton);

    float period_us = 1000000.0 / freq;
    Serial.printf("Freq: %.1f Hz | Ton: %.1f us | Period: %.1f us\n", freq, ton, period_us);
    delay(delayMs);
  }

  stopPWM();
  Serial.println("PWM OFF for 5 seconds");
  delay(offDelayMs);
}

Debug Message

No crash or backtrace. The issue is in waveform stability and timing.

Other Steps to Reproduce

Output is connected to oscilloscope

Observe the waveform during frequency sweep

Clear gaps and interruptions are visible in ESP32-S3 output

Arduino Uno waveform is smooth and uninterrupted

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions