Multiple simultaneous Pulse generation using Arduino

Hi, Can you please show me the right direction.

I want to generate simultaneous six pulses with varying delays by using Arduino. I want to control all the pulses independently. I have an Arduino Uno R3 with me.

Each pulse has its own delay with respect to a common interrupt. When the interrupt is detected, the first pulse is generated with a delay of 5 ms, second with 10 ms and so on. The interrupt will be recurring within 20ms. So while next interrupt is detected the first pulse has to be generated again without disturbing fourth and fifth pulse from the previous cycle.

The interrupt is used for the synchronization of pulses with the state of the circuit.

Is this possible using Arduino? Or can it be done using any other logic?

Reads to me like this:

t=0  start
t=5  pulse1
t=10 pulse2
t=15 pulse3
t=18 new start (18 as an example of "within 20ms")
t=20 pulse4
t=23 new pulse1
t=25 pulse5
t=28 new pulse2
.
.

So the pulses would be timed like this? (all times in ms)

Or with 18ms interrupts as per LesserMole's example:

Or with interrupts at random intervals up to 20ms:

PaulRB:
So the pulses would be timed like this? (all times in ms)

That's my take, except I understood that the interrupts aren't necessarily "on the 20s" but "within 20", hence eg the 18 in my example.

LesserMole:
That's my take, except I understood that the interrupts aren't necessarily "on the 20s" but "within 20", hence eg the 18 in my example.

Agreed. I added more examples to my post above (and deleted my original response which you might have seen, as it was incorrect).

I would say that this is quite possible with Uno. But it depends on some questions:

  • What is the minimum time between interrupts?
  • How long will the pulses be? Will they always be shorter than the answer to Q1?
  • How accurate must the timing be?

The way I would approach this would be to write code to maintain a list or queue of times that output pulses are required. Each time an interrupt is detected, 6 required pulse times are calculated and added to the list/queue. New items can either be added to the end of the list, or inserted in requested time order. Another part of the code would monitor the list/queue, looking for pulse requests who's time has come. If the list is not maintained in time order, it may have to scan the list each time. But if the list is maintained in time order, then the next request will always be at the start of the list. When a pulse request's time arrives, a pulse is generated and the request is removed from the list.

How long is the high period of each pulse? How precise does the timing between the pulses need to be? Do you have an oscillocope to help you develop this code?

I am confused by the specifics or your requirement given your use of the words simultaneous and delay in the same sentence.

I think you will need two independent hardware timers to achieve this, and the interrupt can trigger the 6 pulse sequence alternatively out of one or the other.

Alternatively, you could have the interrupt trigger the first 4 pulses from one timer, and at the end of that sequence the first timer starts the second timer for the last two pulses.

I note that this is your first posting. What is your experience with the Arduino? What are you trying to achieve with your project?

PaulRB:
I would say that this is quite possible with Uno. But it depends on some questions:

  • What is the minimum time between interrupts?
  • How long will the pulses be? Will they always be shorter than the answer to Q1?
  • How accurate must the timing be?
  1. 20 milliseconds between interrupts. The time between interrupts is fixed. Its used to synchronize pulses with the three-phase power supply.
  2. 5 mS. Yes.
  3. 0.1 mS

cattledog:
How long is the high period of each pulse? How precise does the timing between the pulses need to be? Do you have an oscillocope to help you develop this code?

I am confused by the specifics or your requirement given your use of the words simultaneous and delay in the same sentence.

I think you will need two independent hardware timers to achieve this, and the interrupt can trigger the 6 pulse sequence alternatively out of one or the other.

Alternatively, you could have the interrupt trigger the first 4 pulses from one timer, and at the end of that sequence the first timer starts the second timer for the last two pulses.

I note that this is your first posting. What is your experience with the Arduino? What are you trying to achieve with your project?

  1. 5ms. It has to be precise about 0.1 ms. I have a digital Oscilloscope.
  2. Simultaneous because at any given moment two pulses will be overlapping. Delay is separate for each pulse.

taonresearch:
The interrupt will be recurring within 20ms.

taonresearch:

  1. 20 milliseconds between interrupts. The time between interrupts is fixed.

So not "within" (which is where my eg 18 came from, 18 being less than 20), but "every".

Sounds totally doable. Timing is no problem, 0.1 ms is easy to accomplish. The "interrupt" - I suppose that is a pulse from a zero crossing detector? At 50 Hz that'd give you 10 ms between pulses (as there are two zero crossings per cycle). So you have to find a way to detect whether it's the positive or negative half of the cycle.

You'll have to get smart with arrays to keep easy track of the various timings. Part of the challenge here is that the timing overlaps: the third pulse 4 happens about the same time as the third pulse 1.

With the required accuracy and overall time I'd go for micros() based timing (exactly the same principle as what we normally do with millis(), just a different scale).

taonresearch:
20 milliseconds between interrupts. The time between interrupts is fixed. Its used to synchronize pulses with the three-phase power supply.

Thats is only one phase, how are your pulses related to the other phases.
Can you draw a diagram, showing the pulses relationship to each phase.
20mS is for a 50Hz system, but there are 6 zero crossing points in 3 phase for each single phase cycle.

Tom.. :slight_smile:

PaulRB:
The way I would approach this would be to write code to maintain a list or queue of times that...

Ok I thought of a much easier way.

I agree with wvmarle about using micros(), given your 0.1ms accuracy requirement.

Will the 6 pulses be made on 6 output pins? I assume so, otherwise, with a single output, the overlapping pulses will corrupt each other.

So... An array of length 6 to hold the pin numbers. An array of length 6 to hold the next pulse times in us. The "interrupt" probably doesn't need actually need to be an interrupt, you can just poll the input pin. When it changes, the current time is captured, 5000 added, and the result updates the time in position 0 of the array. When that time is reached, the first pulse is started. 5000 is added to the time from array position zero and used to update the time in array position 1. When that time is reached, pulse 1 is ended, pulse 2 begins and the time for pulse 3 is calculated. And so on for the remaining pulses.

I guess OP wants to sample one phase, assumes the other two are perfectly in sync, and base pulses on that.

Now I do wonder what the purpose is of those six independent pulses, which are spread out over three cycles.

wvmarle:
I guess OP wants to sample one phase, assumes the other two are perfectly in sync, and base pulses on that.

Now I do wonder what the purpose is of those six independent pulses, which are spread out over three cycles.

yes, the first 4 pulses will, but as the OP states 5 and 6 pulses generated by the first interrupt have to still be allowed to occur even though they will occur after the next interrupt.
We await the OP and ask what the application is.
Tom... :o

Here is a approach using two alternately triggered software timers to generate the interleaved pulses. I am simulating the zero cross interrupt with a timer interrupt output on pin 3. Jumper pin 3 to pin 2 which is the external interrupt pin.

I've scaled everything down by a factor of 10 to see reasonable serial output of the interleaved timing. The serial output looks reasonable but this wasn't tested with a scope.

EDIT 11:00 revise location of counter reset to fixa bug where case 7 not being actually entered
change startTimer variables to unsigned long

//https://github.com/PaulStoffregen/TimerOne/blob/master/TimerOne.h
#include <TimerOne.h> //to generate external interrupt model zero cross

const unsigned long interval = 5000;//pulse length high microseconds and initial delay from trigger
const unsigned long scaleFactor = 10;//slowing factor, replace with 1 for full speed
const unsigned long period = interval*scaleFactor;

byte timerACount = 0;
byte timerBCount = 0;

volatile boolean runTimerA = false;
volatile unsigned long startTimerA;

volatile boolean runTimerB = false;
volatile unsigned long startTimerB;

volatile boolean intTrigger = false;

byte pinStart = 4;//start number output pins for 6 pulses

void setup()
{
  //set pins 4,5,6,7,8,9 as output for 5ms pulses
  for (byte i = 0; i <= 5; i++)
  {
    pinMode(pinStart + i, OUTPUT);
  }

  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), pulseTrainTrigger, FALLING);
 
  pinMode(3, OUTPUT);//timer trigger 
  Serial.begin(115200);
  
  Timer1.initialize(20000*scaleFactor);//every 200 ms scale factor 10
  Timer1.attachInterrupt(trigger);
  
  Serial.println(micros()/1000);
}

void loop()
{
  if (runTimerA ==true && (micros() - startTimerA >= period))
  {
    //enters 5ms after initial trigger
    //enters at 0 and increments counts to 1,2,3,4,5,6,7
    startTimerA += period;
    timerACount++;
    Serial.print('\t');
    Serial.print("A");
    Serial.print(timerACount);
    Serial.print(" ");
    Serial.println(micros()/1000);
   // if (timerACount == 7)
    //{
     // timerACount = 0;
     // runTimerA = false;
    //}
  }

  switch (timerACount)
  {
    case 1://5ms after trigger turn on first output
      digitalWrite(pinStart, HIGH);
      break;
    case 2://10ms after trigger
      digitalWrite(pinStart + 1, HIGH);
      digitalWrite(pinStart, LOW);
      break;
    case 3://15ms after trigger
      digitalWrite(pinStart + 2, HIGH);
      digitalWrite(pinStart + 1, LOW);
      break;
    case 4://20 ms after trigger
      digitalWrite(pinStart + 3, HIGH);
      digitalWrite(pinStart + 2, LOW);
      break;
    case 5://25ms after trigger
      digitalWrite(pinStart + 4, HIGH);
      digitalWrite(pinStart + 3, LOW);
      break;
    case 6://30ms after trigger
      digitalWrite(pinStart + 5, HIGH);
      digitalWrite(pinStart + 4, LOW);
      break;
    case 7://35 ms after trigger turn off last output
      digitalWrite(pinStart + 5 , LOW);
      timerACount = 0;
      runTimerA = false;
      break;
  }

  if (runTimerB == true && (micros() - startTimerB >= period))
  {
    //enters at 0/increments counts to 1,2,3,4,5,6,7
    startTimerB += period;
    timerBCount++;
    Serial.print('\t');
    Serial.print('\t');
    Serial.print("B");
    Serial.print(timerBCount);
    Serial.print(" ");
    Serial.println(micros()/1000);
   // if (timerBCount == 7)
   // {
    //  timerBCount = 0;
    //  runTimerB = false;
   // }
  }

  switch (timerBCount)
  {
    case 1://5ms after trigger
      digitalWrite(pinStart, HIGH);
      break;
    case 2://10ms after trigger
      digitalWrite(pinStart + 1, HIGH);
      digitalWrite(pinStart, LOW);
      break;
    case 3://15ms after trigger
      digitalWrite(pinStart + 2, HIGH);
      digitalWrite(pinStart + 1, LOW);
      break;
    case 4://20 ms after trigger
      digitalWrite(pinStart + 3, HIGH);
      digitalWrite(pinStart + 2, LOW);
      break;
    case 5://25ms after trigger
      digitalWrite(pinStart + 4, HIGH);
      digitalWrite(pinStart + 3, LOW);
      break;
    case 6://30ms after trigger
      digitalWrite(pinStart + 5, HIGH);
      digitalWrite(pinStart + 4, LOW);
      break;
    case 7://35 ms after trigger
      digitalWrite(pinStart + 5 , LOW);
      timerBCount = 0;
      runTimerB = false;
      break;
  }

  if (intTrigger)
  {
    intTrigger = false;
    Serial.println(millis());
  }
}

//alternate timers with external interrupt trigger
void pulseTrainTrigger()
{
  static byte triggerCount = 0;
  if (triggerCount == 0)
  {
    startTimerA = micros();
    runTimerA = true;
  }
  if (triggerCount == 1)
  {
    startTimerB = micros();
    runTimerB = true;
  }

  triggerCount++;

  if (triggerCount == 2)
  {
    triggerCount = 0;
  }
}

//test trigger
//generate trigger pulse on pin 3 with Timer1 jumper pin 3 to pin 2
void trigger()
{
  intTrigger = true;
  digitalWrite(3, HIGH);
  digitalWrite(3, LOW);
}

TomGeorge:
We await the OP and ask what the application is.
Tom... :o

The application is closed-loop control of three phase induction motor through three phase ac voltage regulator. I'm presently trying to do the open-loop control. The pulse is required for triggering the thyristors in the circuit.

Why six output pulses then? Three is enough. But you'll have to use TRIACs rather than thyristors.

Three zero crossing detectors on three inputs; three outputs timed based on the input. You should be able to readily find sketches and schematics for a single phase, probably also for three phases. If you're confident your power supply is stable (120° phase separation) you could indeed get away with a single zero crossing detector.

wvmarle:
Why six output pulses then? Three is enough. But you'll have to use TRIACs rather than thyristors.

It is mandatory for me to use six thyristors rather than three Triacs.