Using interrupts to sync PWM cycles

Hello everyone,

I’m using an Arduino Mega 2560 to create a trigger sequence with Timer4.
The interrupt generated by Timer4 will be used to start a PWM waveform, count the number of pulses, stop the PWM when a certain number of pulses have been generated, restart PWM on the next interrupt cycle.

The PWM pin is connected to an optocoupler that will go to an external device; the opto I’m using is a HCPL-0201 (Datasheet link) but I don’t think it the cause of my problem.

Basically the problem is the PWM not restarting properly with the Timer4 interrupt and I’m getting a reduced pulse width with the first pulse generated.

The last pulse of the PWM sequence is also shortened, which is not what I want either.
Inverting the PWM and reducing the duty cycle to around 128 kinda works to get what I want, but feels like a bad workaround.

What I want to know is how do I reset the PWM state to get the ideal output shown in the 3rd, badly photoshopped, image.

Thanks in advance for your help!

Images attached are screen captures of my scope readout.
Link to images ( https://imgur.com/a/DElZj )

And my code is here:

#include <PinDefs.h>  //contains list of pins to use, only kSyncPin = 12 is of interest here
#include <avr/io.h>
#include <TimerOne.h>
#include <TimerFour.h>
#include <DigitalPin.h>


byte resEnable = 1;
byte resStop = 0;
volatile boolean resFlag = 0;
unsigned long V1Time, V2Time;
const unsigned long syncPulsePeriod = 33333UL;
volatile unsigned long syncPulseCount = 0;
unsigned long syncPulseCountLimit = 18;


void inspirationIsr()
{
  //Inspiration cycle done, switch to exp

  //Serial.println("Inspiration ISR");
  if ( resFlag == 0 )
  {
    fastDigitalWrite(kMuxLine[ 1 ], LOW);
    fastDigitalWrite(kMuxLine[ 0 ], HIGH);
    Timer4.setPeriod( V1Time);
    Timer1.pwm(kSyncPin, 512);
    Timer1.restart();
    resFlag = 1;
  }
  else if ( resFlag == 1 )
  {
    fastDigitalWrite(kMuxLine[ 1 ], HIGH);
    fastDigitalWrite(kMuxLine[ 0 ], LOW);
    Timer4.setPeriod( V2Time);
    resFlag = 0;
  }
}

void syncIsr ()
{
  syncPulseCount++;
  if ( syncPulseCount >= syncPulseCountLimit )
  {
    //enough pulses have been produced, so stop pulse generation
    syncPulseCount = 0;
    Timer1.disablePwm(kSyncPin);
    Timer1.stop();
   
  }
  

}


void setup() {

  //pinmode setup for spi devices on a bus
  for ( byte slave = 0; slave < kNumSlaveSelects; ++slave )
  {
    // set slave select pins as output
    pinMode(      kSlaveSelect[ slave ], OUTPUT);
    // make sure other devices are unselected (pin is HIGH) and setup spi
    fastDigitalWrite( kSlaveSelect[ slave ], HIGH)  ;
  }

  for ( byte mux = 0; mux < kNumMuxLines; ++mux )
  {
    //set mux selects as outputs
    pinMode(kMuxLine[ mux ], OUTPUT);
    //make sure mux selects are held LOW
    fastDigitalWrite( kMuxLine[ mux ], LOW);
  }
  // end pinmode setup for spi devices on a bus

  
  pinMode(kSyncPin, OUTPUT);  //pin 12

  V1Time = 300000UL;
  V2Time = 300000UL;

  noInterrupts();
  Timer4.initialize( V1Time);
  Timer4.attachInterrupt( inspirationIsr );
  Timer4.start();

  Timer1.initialize ( syncPulsePeriod );
  Timer1.stop();
  Timer1.attachInterrupt( syncIsr );
  Timer1.pwm(kSyncPin, 512);
  //TCCR1A |= _BV(COM1B0);  //to set up inverted PWM
  interrupts();
  Serial.begin(19200);
  Serial.println("Setup complete");
}

void loop()
{

}

There is a great deal of your code that is not included in the program in your Original Post.

...R

You restart Timer1 in shwwn code. Maybe source of the bug? EDIT: I am curious - what are you doing? Some ventilator?

I would suggest that you should address the registers directly rather than going through a library that might not be doing what you think. Same goes for the fast digital write, use direct port addressing.

DigitalWrite takes about 6uS.

Direct port writes take 2 clock cycles or 125nS

Both on a 16MHz AT328.

I've measured this.

Allan

Actually I was wrong about the direct port writes.

Fo some reason the high write takes 125nS, whereas the write low takes 187.5nS. ie 2 and 3 cycles.

Don't ask me why.

Here's the code, running on a nano....

void loop() {

  while (1) {

    PORTD |= 0x20;
    PORTD &= 0xcf;
    PORTD |= 0x20;
    PORTD &= 0xcf;
    PORTD |= 0x20;
    PORTD &= 0xcf;
 

    digitalWrite( pulsepin , LOW);
    digitalWrite( pulsepin , HIGH);
    digitalWrite( pulsepin , LOW);
    digitalWrite( pulsepin , HIGH);
    digitalWrite( pulsepin , LOW);
  }
}

And here a pic of the resulting waveform...

Allan

edit: you can see the while(1) comparison take about 300nS as well..

allanhurst: Fo some reason the high write takes 125nS, whereas the write low takes 187.5nS. ie 2 and 3 cycles.

You are setting 1 bit but clearing two (0xC==12==B1100). SBI is used for setting the bit but read - modify - write for clearing.

Aaaargh!

You're right, of course. Forgot my hex, and at my age as well.... it should be........

void loop() {

  while (1) {

    
    PORTD &= 0xdf;
    PORTD |= 0x20;
    PORTD &= 0xdf;
    PORTD |= 0x20;
    PORTD &= 0xdf;
    PORTD |= 0x20;

 

    digitalWrite( pulsepin , LOW);
    digitalWrite( pulsepin , HIGH);
    digitalWrite( pulsepin , LOW);
    digitalWrite( pulsepin , HIGH);
    digitalWrite( pulsepin , LOW);
    digitalWrite( pulsepin , HIGH);
    }
}

Upon which I get the expected 2-cycles per write.....

Duh.

Allan