Creating a modulated signal using timers with a variable duty cycle

Hey all,

For a project I am experminating with IR transmitters and IR receivers using arduino uno rev3.
I am trying to send a carrier signal of 38khz modulated at 300/150hz as a form of special encoding. I have figured out how to do that at 50% duty cycle using an interrupt as shown below. However,I want to lower the duty cycle so I can add more current to the IR transmitter currently working at Ifp of 140ma.

My problem is understanding timers.
I will explain the code below:

Here I am creating a 38khz using timer2:

void setIrModOutput() {
  const int DUTY_CYCLE = 50;  // 50%
  const bool isFrequency150hz = false;

  noInterrupts();
/* Set up timer2 at pin 11(OC2A) and pin 3(OC2B)(Disabled, normal port) 
  f(OCR2A) = fclk/(2*N*(OCR2A+1)), fclk = 16Mhz, N-prescaler = 1. */
  pinMode(LED, OUTPUT);
  TCCR2A = 0; TCCR2B = 0; // ensure 0
  TCCR2A = _BV(WGM21);  // set mode 2 CTC, OC2A amd OC2B normal port operation.
  TCCR2B = _BV(CS20);   // no prescaler.
  // Timer2 38Khz - 16 Mhz clock divided by the prescaler(1) and the desired frequency 38000hz divided again by 2 and minus 1, CTC Mode.
  OCR2A = calcTimerFrequency(38000, 1, 0);  // OCR2A = 209 => 3

And Here I am creating 300/150hz using timer1(Note I did that so OCR1A values will be higher and I could get a much much lower duty cycle. However now I found out Vishay recommended 30% duty cycle).

/* Set up timer1 at pin 9(OC1A)(Disabled, normal port) and pin 10(OC1B)(Disabled, normal port)
  I dont Use OCR1A, Hence it is disabled. f(OCR1A) = fclk/(2*N*(OCR1A+1)), fclk = 16Mhz, N-prescaler = 256.
  I only use the interupt of OCR1B, Hence it is disabled also. f(OCR1B) = fclk/(N*(OCR1B+1)), fclk = 16Mhz, N-prescaler = 256.
  
  OCR1A = 207, OCR1B = 20 => 300.480hz with duty cycle of 10.09% (T = 3328(usec), Pulse width t1 = 336(usec)).
  OCR1A = 415, OCR1B = 20 => 150.24hz width duty cycle of 4.819% (T = 6656(usec), Pulse width t1 = 320(usec)). 
  */
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  TCCR1A = 0; TCCR1B = 0; // ensure 0
  TCCR1A = _BV(COM1A0) | _BV(COM1B1) | _BV(WGM11) | _BV(WGM10);  // set mode 15 fast pwm, OC1A and OC1B normal port operation.
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);                  //prescaler of 256.

  if (isFrequency150hz) {
    // Timer1 150/300hz - 16 Mhz clock divided by the prescaler(256) and the desired frequency 150/300hz minus 1, Fast PWM Mode.
    OCR1A = calcTimerFrequency(150, 256, 1);
    OCR1B = calcTimerDutyCycle(50, OCR1A);
  } else {
    OCR1A = calcTimerFrequency(300, 256, 1);
    OCR1B = calcTimerDutyCycle(30, OCR1A);
  }
  TIMSK1 = _BV(OCIE1B);  // enable Timer1 Interrupt for OC1B.
  interrupts();
}

The Main problem is using the interrupt of timer1 of vector OC1B
I am not sure how to switch between toggle compare mode and off mode of COM2A0 using the output of timer1 OCR1B(pin 10).
shown below:

/** Interrupt for timer1 COMPB Vector.
The Interrupt occurs when the timer reach its OCR1A value
Inside we check when the pin is on to enable/disable output of OCR2A(pin 11)
**/
ISR(TIMER1_COMPB_vect) {
  TCCR2A ^= _BV (COM2A0) ;  // Toggle OC2A on Compare Match
  if ((TCCR2A & _BV (COM2A0)) == 0)
    digitalWrite (LED, LOW);  // ensure off
  }
}

what microcontroller are you using?
I find the simplest way to generate modulated signal in 10's of KHz rage is to use a DDS module ( Direct Digital Synthesizer) such as the MD_AD9833
e.g. 20KHz modulated at 50Hz
image

20KHz carrier
image

Oh, thats a unique way to generate a modulated signal.
However it is a sin wave not a pulse and it seems the amplitude is raising and decreasing interesting. I Will surely look into that module. thanks.
The microcontroller I use is Arduino uno rev 3 based on ATmega328P (Datasheet)

However, I am generating only pulses of 38khz using the arduino shown below:
image

and sending it through the IR transmitter.
The receiver receives only frequency of about 38khz and what I get is pulses of what is shown below:
image

I am just having a hard time understanding the arduino timer functionality to combine two timer to get a carrier signal using one and a modulated signal using the other.

your modulation approach sounds similar to Digital Command Control (DCC) used in model RRs to sends data over the track by altering the polarity of the track voltage

DCCSIG

each bit cause 2 polarity reversals, either 100 or 58 usec apart. the symmetry results in a positive pulse regardless of the connections to the track.

in your case, you could have a fixed period between positive pulses where the pulse with represents different bits

Does seem an interesting concept but my approach is much simpler let me explain: the voltage goes from 0 to 5v for simplicity and I change the duty cycle of only the modulated signal. The change between two differnt modulated signal is only for making the program robust to handle more frequencies.

Regarding to your solution a fixed period to check whether timer 1 ocr1b is on to enable timer 2 com1A is a solution i tried.

not sure what you are attempting to do
possibly this - 38KHz carrier
image

300Hz modulation??
image

Yes, as you have shown i have created that signal at duty cycle of 50% and sending it to the ir transmitter at If(forward current)= 150mA. Using a constant driving circuit, it works great for max 5 meters.

although, I cant figure out how to update the timer 2 compare match toggle on com2A0 and to disable it in the interrupt to match the duty cycle I can control using Fast PWM mode in timer 1 OCR1B output (pin 10)

If you switch to an ESP32 processor, it has a dedicated hardware peripheral just for this application:
https://docs.espressif.com/projects/esp-idf/en/v4.4.6/esp32/api-reference/peripherals/rmt.html

I am using a ESP32 using the ESP32TimerInterrupt library
this is outputting alternate modulation 150Hz (period 6.6mSec) and 300Hz (3.3mSec)

image

1 Like

That is some really nice modulation I wonder how you did that.
Having a look at the library esp32timerinterupt which could be useful and I ordered a few esp32 modules for more fun embedded projects it should be useful thanks.

yes I could have more variety in frequency and more timers for other tasks. I will take your advice and test it after I will figure the problem I have in arduino uno.

However, the solution is much simpler than that. I only use one modulation frequency so for example only 300hz just lower duty cycle of 300hz instead of 50% I need 30%. not alternating both frequencies which does seem cool for a unique encoded command.

For example 38khz carrier wave and 300hz modulated at duty cycle of 50% will be T=1/f = 3.3(mSec), tOn =1.66(mSec) and I want the same signal at lower duty cycle of 30% which will give tOn = 1(mSec).

I am sorry for not explaining more clearly.

38KHz square wave modulated 300Hz alternating 50% and 30% duty cycle
image

ESP32 code

// esp32 interrupt generate 38KHz square wave modulated 300Hz variable duty cycle on GPIO 19

// original code from https://github.com/khoih-prog/ESP32TimerInterrupt

#if !defined(ESP32)
#error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting.
#endif

// These define's must be placed at the beginning before #include "ESP32_New_TimerInterrupt.h"
// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
#define _TIMERINTERRUPT_LOGLEVEL_ 4

// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "ESP32TimerInterrupt.h"

// Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868
// Don't use PIN_D2 with ESP32_C3 (crash)
#define PIN_D19 19  // Pin GPIO19 signal output

#define TIMER0_INTERVAL_US 13                                       // timer interrupts in uSec to give carrier 38Kz
const int MODULATION = (1000000 / 300) / (TIMER0_INTERVAL_US * 2);  // counter to modulate 300Hz
int duty_cycle = 30;                                                // % duty cycle of modulation

// With core v2.0.0+, you can't use Serial.print/println in ISR or crash.
// and you can't use float calculation inside ISR  Only OK in core v1.0.6-
volatile int counter = 0;  // counting timer interrupts
volatile int modulation = 0;
bool IRAM_ATTR TimerHandler0(void* timerNo) {
  static bool toggle0 = false;
  counter++;
  // HIGH signal of duty cycle
  if (++modulation <= MODULATION * 2 * duty_cycle / 100) {
    digitalWrite(PIN_D19, toggle0);
    toggle0 = !toggle0;  //timer interrupt toggles pin PIN_D19
  } else {
    // LOW signal of duty cycle
    digitalWrite(PIN_D19, toggle0 = false);
    if (modulation >= MODULATION * 2) {
      modulation = 0;
      // alternate 50% and 30% duty cycles
      if(duty_cycle==50)duty_cycle=30;
      else              duty_cycle=50;
    }
  }
  return true;
}



// Init ESP32 timer 0 and 1
ESP32Timer ITimer0(0);

void setup() {
  pinMode(PIN_D19, OUTPUT);
  Serial.begin(115200);
  while (!Serial && millis() < 5000)
    ;
  delay(500);
  Serial.print(F("\nStarting TimerInterruptTest on "));
  Serial.println(ARDUINO_BOARD);
  Serial.println(ESP32_TIMER_INTERRUPT_VERSION);
  Serial.print(F("CPU Frequency = "));
  Serial.print(F_CPU / 1000000);
  Serial.println(F(" MHz"));

  // Using ESP32  => 80 / 160 / 240MHz CPU clock ,
  // For 64-bit timer counter
  // For 16-bit timer prescaler up to 1024

  // Interval in microsecs
  if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_US, TimerHandler0)) {
    Serial.print(F("Starting  ITimer0 OK"));
  } else
    Serial.println(F("Can't set ITimer0. Select another freq. or timer"));
  Serial.flush();
}

void loop() {
  // display interrupt counter every second
  static long timer = millis();
  if (millis() - timer >= 10000) {
    timer = millis();
    Serial.println(counter);
    counter = 0;
  }
}
1 Like

Thanks, the ISR solution is great. I have been researching on it for two weeks, you are a lifesaver. :smiley:

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