Esp32 PWM with different on off durations and duty cycle

Struggling to get this generating the correct PWM.
I need to drive an LED at 10MHz with 50% Duty Cycle
with a 2ms ON and 48ms OFF (total cycle for this 50ms)

Want it to run in the second processor so can constantly run in the background.

The 2nd LED is just to show the main loop is running.

Any help appreciated getting this working and also if possible explanation on how the end result is achieved.

//

//
// The LED flashing pin
#define LED0 17
#define LED1 16
 
int PWM_FREQUENCY = 10000; // this variable is used to define the time period
int PWM_CHANNEL = 0; // this variable is used to select the channel number
int PWM_RESOUTION = 8; // this will define the resolution of the signal which is 8 in this case
int dutyCycle = 127; // it will define the width of signal or also the one time
 
// We need to create a task (object)
TaskHandle_t Task0;
 
void setup()
{
  Serial.begin(115200);
  Serial.println("Setup started.");
 
  pinMode(LED1, OUTPUT);
  ledcSetup(PWM_CHANNEL, PWM_FREQUENCY, PWM_RESOUTION);
  ledcAttachPin(LED0, PWM_CHANNEL);
 
  xTaskCreatePinnedToCore(
      loop0,    /* Function to implement the task */
      "Task0",  /* Name of the task */
      1000,     /* Stack size in words */
      NULL,     /* Task input parameter */
      0,        /* Priority of the task */
      &Task0,   /* Task handle. */
      0);       /* Core where the task should run */
 
  Serial.println("Setup completed.");
}
 
// This is a new loop (any name will do) for our new task
void loop0(void * parameter) {
 
  // It must run forever, so this is the construct
  for (;;) {
 
    // Just put your code here as you would do for the std loop()
    Serial.print("Running on core: ");
    Serial.println(xPortGetCoreID());
    ledcWrite(PWM_CHANNEL, 50);
    delay(2);
    ledcWrite(PWM_CHANNEL, 0);
    delay(48);
  }
}
 
void loop()
{
  // If you don't put this in the original loop, NOTHING happens
    digitalWrite(LED1, HIGH);
    delay(1000);
    digitalWrite(LED1, LOW);
  delay(1000);
}

a 50% duty cycle of a 10 MHz signal would be 50 nanosec on and 50 nano sec off.

2 ms on and 48 msec off would be 4% duty cycle of a 20 Hz signal

but why do you care what the frequency is for controlling an LED? have you tried controlling the brightness of an LED on a PWM pin by just setting it using analogWrite ()? full on is 255. 4% is ~10

Im driving an LED driver and the LED is IR for a project I'm doing.
These are the requirements to drive the LED. I'm learning as i go with this but don't quite get how the frequency, and the on off durations work in conjunction with the frequency.
When i test this and adjust the code to do a 2ms on 2ms off with 100% duty the 2ms is actually about 2.9 when measured on oscilloscope.

Below is the correct calculation PWM and oscilator for ESP32.
// Calculation of adjustments for each frequency range
// Resolution = log2(Clock(80MHz)/f) + 1 ex: 50,000 HZ = 80,0000/50,000 = 1,600 log2(1600) = 10 + 1 = 11
// Duty 50% = (2 * * Resolution)/2 ex: 2**11 = 2048 2048/2 = 1024
Ex:
Clock..............Freq...........................calc Resolution
80,000,000....5000.........16000...........14,966
Duty 0 to 16000

it's not obvious what your needs are. what is the receiver of the IR pulse?

PWM of an LED is typically used to control the perceived brightness of a visible LED. But in your case, you may need a periodic IR pulse of some specific duration and it may be better to state your requirement that way rather than as a duty-cycle

Looks like your diagram calls for 10KHz -NOT- 10MHz

Is this the case?

If it IS the case that it's 10khz..

Then this should do it. You'll have to slow them all down to see 'em on the simulator. But I'm betting it'll work as it is on the ESP32.

Sorry yes your right 10khz
so the example you gave will provide the 10khz, 2ms on and 48ms off (total for the 2 of 50ms) ?
Will give it a go.

The receiver needs this pattern an frequency to detect the transmitted IR signal. sort of light barrier. Once the barrier is broken the other part of the circuit will drive a GPIO pin to either high or low depending on is state. the duty cycle is the LED is being overdriven to provide the range and not have the LED on fully. I originally had this circuit done externally and used a simple microcontroller. I want to make this more of an IOT project with wifi and web console.

why does the IR LED need to be pulsed? sounds like the logic is on the receiver side, do something when the light beam is broken

You can't independently set frequency, on time, and off time!!!
2ms ON + 48 ms OFF = 50ms Period ====> 20 Hz.

Perhaps it's similar to an IR Remote receiver that has a bandpass filter centered at some tens of KHz for filtering ambient light noise?

that make sense. but don't they operate at 38 kHz?

Seems there are Several Flavors. But none of them are 10KHz.

well that seems to work..the oscilloscope seems to pump out the right values.

Wil give this a go...thanks for your help. See if you can work out my other topic about SD card and RFID :slight_smile:

Finally!
You scope shots explained what you did a poor job of doing in text. You want a 10KHz carrier gated by 2ms ON + 48 ms OFF pulses. The ESP32 can do this in hardware check out its RMT Peripheral.

Thought I would do a quick update. The code suggested by jimLee worked fine and now tested with the actual circuit board. Thanks for the help guys, much appreciated./

Cool! Thanks for getting back to us.

As noted in Post #16, the ESP32's RMT peripheral can do all this in hardware without any software intervention once it's configured. In fact, the example code below even deletes the looptask:

#include "Arduino.h"
#include "driver/rmt.h"

void setup() {
	Serial.begin(115200);
	delay(2000);
	Serial.println("Starting");
	constexpr uint8_t clockDiv {200};
	constexpr uint32_t carrierFreq {10000};
	constexpr uint8_t dutyCycle {50};

	rmt_config_t config RMT_DEFAULT_CONFIG_TX(GPIO_NUM_33, RMT_CHANNEL_0);
	config.clk_div = clockDiv;
	config.tx_config.carrier_freq_hz = carrierFreq;
	config.tx_config.carrier_duty_percent = dutyCycle;
	config.tx_config.carrier_en = true;
	config.tx_config.loop_en = true;

	constexpr rmt_item32_t modulation[] {
			{ { {800, 1, 19200, 0}}},
			{ { {0, 1, 0, 0}}}
	};

	ESP_ERROR_CHECK(rmt_config(&config));
	ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
	ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, modulation, sizeof(modulation) / sizeof(modulation[0]), true));

}

void loop() {
	vTaskDelete(NULL);
}

Didn't have a scope handy at this location, so here's some Logic Analyzer shots, ON for 2ms OFF for 48ms:

Zoomed in on the 10KHz carrier:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.