1

Working on my first real sketch, so will try to keep this short. Feel free to ask more info if needed.

To power a flyback transformer I need to apply PWM -to- a PWM signal. The easiest way to describe this is that while a standard PWM signal is 'on' for some set time, then 'off' for the remainder of the period, I need to send a signal where the 'on' is actually a 14kHz square wave of 50% duty...then pulse this signal ~1 to 100% duty at ~500Hz.

No problem with the 14kHz PWM - I found the PWM.h library and set that up.

But what is the best way to chop that up with a second PWM at, say 500Hz?

With some amount of kludge, can I 'bit bang' it with DO - WHILE? Will WHILE read a variable outside the loop? Are there easier ways?

i=1;
do {                                //Pulse microsecond 'frames' of high freq signal?
                                    //ie 14kHz carrier PWM'd at 500Hz = 2000 microseconds per 'frame' cycle
                                    //at 50% duty would be 1000 micros high and 1000 micros low

    pwmWrite( outpin, 128 );        //Turn on 14kHz carrier at 50% duty
    delayMicroseconds(1000);        //Delay to generate the 'HIGH' portion of the frame
    pwmWrite( outpin, 0 );          //Turn 14kHz carrier off
    delayMicroseconds(1000);        //Delay to generate the 'LOW' portion of the frame
   } while (i == 1);                //...and keep doing loop while the state is '1' or 'ON'

delay(1000);                        //?Will this allow the above loop  to repeat while waiting here for 1 second?
i=0;                                //i != 1, so break out of the while loop above?
                                    //then continue doing other stuff below...

Any help greatly appreciated.

7
  • A 14kHz 50% pwm is not a pwm, it is a tone of 14kHz. How accurate should the 14kHz carrier and the 500Hz modulation frequency be? It is possible with 'tone' for the 14kHz and 'millis' for the 500Hz.
    – Jot
    Oct 3, 2018 at 19:50
  • Your do ... while here is an infinite loop. Oct 3, 2018 at 19:50
  • Thanks for the input. I considered the tone() command as well. In this instance they are virtually interchangeable. tone() might save one step because it is automatically 50% duty, where i have to specify 50% (128) with every call of pwmWrite. Accuracy is not critical. The 14kHz tone/pwm I currently have is more than accurate enough. The 500Hz could drift around quite a bit, too. Seems like I need to set up the i variable going into the loop and increment it from inside there?
    – Sam43
    Oct 3, 2018 at 20:02
  • Consider using the TimerOne library to set up a timer/interrupt-based callback for the 500Hz portion, which toggles a 14kHz tone(). Oct 3, 2018 at 20:08
  • Possibly this is a better way to attack from the WHILE side? (sorry, not getting code to work) ' i = 1 ; while (i<=100) { pwmWriteHR( pinout, 128 ); delayMicroseconds(500); pwmWriteHR( pinout, 0 ); delayMicroseconds(500); (i ++); }'
    – Sam43
    Oct 3, 2018 at 20:13

2 Answers 2

4

On an Arduino Mega (or an Uno with the ATmega328P replaced with an ATmega328PB, but then you'd have to adapt the code slightly), this can be done via the output compare modulator:

void setup() {
  // CTC, OC0A toggle on compare match, prescale 1/8
  TCCR0A = _BV(COM0A0)|_BV(WGM01);
  TCCR0B = _BV(CS01);

  // toggle every 35.5 μs at 16 MHz and 1/8 prescale
  OCR0A = 70;

  // phase and frequency correct PWM, TOP is OCR1A, OC1C clear going up and set going down, no prescale
  TCCR1A = _BV(COM1C1)|_BV(WGM10);
  TCCR1B = _BV(WGM13)|_BV(CS10);

  // period of 2 ms at 16 MHz and no prescale
  OCR1A = 16000;

  // on-time of 1 ms at 16 MHz and no prescale
  OCR1C = 8000;

  // no interrupts from the timers
  TIMSK0 = 0;
  TIMSK1 = 0;

  // only output a 1 when both timers are outputting a 1
  PORTB &= ~_BV(PORTB7);

  // halt the timers and reset the prescaler
  GTCCR |= _BV(TSM);
  GTCCR |= _BV(PSRSYNC);

  // reset both timers
  TCNT0 = 0;
  TCNT1 = 0;

  // enable output
  DDRB |= _BV(DDB7);

  // let the timers run
  GTCCR &= ~_BV(TSM);
}

void loop() {
  // Just adjust the frequencies and duty cycle as required here.
  // Everything is handled by the hardware and takes effect immediately upon writing to the register.
  // OCR1A: the desired period of the slower signal, in 8ths of microseconds. Can be up to 65535.
  // OCR1C: the desired on-time of the slower signal, in 8ths of microseconds. Can be up to 65535.
  // OCR0A: 1 less than the desired period of the faster signal, in microseconds. Can be up to 255.
  // The on-time of the faster signal is always half of the period.
  // If necessary, you can trade precision for more range for any of the above values by adjusting the prescaler.
}

With that sketch, pin 13 on the Mega will have the desired waveform. There's two big advantages to this method. First, it doesn't require any CPU resources at all (no loops or interrupts) once it's set up, so you can dedicate all of loop to reading the potentiometer and adjusting the duty cycle without worrying about glitches in the output. Second, being entirely hardware controlled means its output will be accurate and perfectly consistent (the 500 Hz component is perfect, and the 14 kHz component is approximately 14085 Hz). The one disadvantage, though, is that millis and everything else in the Arduino library that uses timers internally won't work quite right, since we're using the same hardware timers that they depend on.

1
3

Your 14kHz carrier can be generated by the tone() function that is part of the Arduino core. You can then use the TimerOne library to toggle the tone on and off.

Here is an untested sample sketch demonstrating the usage:

#include <TimerOne.h>

const uint8_t outpin=4;

void setup() {
  // put your setup code here, to run once:
  pinMode(outpin, OUTPUT);
  Timer1.initialize(1000);  // 1000 microseconds per half-cycle
  Timer1.attachInterrupt(toggleCarrier);
}

void loop() {
  // put your main code here, to run repeatedly:

}

void toggleCarrier() {
  static uint8_t isPlaying;
  if (isPlaying) {
    noTone(outpin);
    isPlaying=0;
  } else {
    tone(outpin, 14000);
    isPlaying=1;
  }
}
1
  • This code work great as written for the case of a 50% duty cycle at 500Hz. Thank you!! I have a lot more to study with timers and interrupts. The last piece of the puzzle is that I am reading a variable pot and converting that 0-1023 reading to 1-100% duty on the 500Hz signal. So given the 2000 microsecond full cycle I would read the pot, then cycle anywhere from 20uS on/ 1800uS off, to the full 2000uS on. I suspect I could add more code to the toggleCarrier() section, but is that the best method? set up another timer? TIA!
    – Sam43
    Oct 4, 2018 at 2:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.