Generating pulse with Timer

Hello everyone. I'm reaserching for a solution to generate pulse with TIMER of a MEGA2560.
In order to explain better what i want you can see the image below.

I have the dotted line that is a square wave that I have in an input and each time I have the rising edge there is an interrupt in which I impose TCNT1 = 0 and the OCR1A. The pulses can move thanks a variable Beta that is the value of a potentiometer in an analog input.
Every pulse has a duration of 500us and there is a rising edge each 20ms.

In the image you can see the starting condition and the final one.

Now the question is: How can I manage the pulse number 4 in order to shift it expecially when the pulse is superimposed on the rising edge of the dotted line?

Ps: sorry for the bad quality of the photo

I hope that my question is clear. If not I will give you more informations.
Thanks

This should be pretty straight-forward for the Mega since it has all the timers you need.

You would need three timers if you need the two pulses independent.

Here's the basic logic I think

  • Your external interrupt starts Timer1
  • Timer1 counts from 0 to some value that corresponds to the 20 ms timeout (or the TOP value of Timer 1)
  • You enable the 2 compare interrupts (COMPB and COMPC) of Timer1
  • As Timer1 TCNT1 matches COMPB, you start Timer2
  • Timer2 is just a basic timer that counts from 0 to some value that corresponds to 500 us
  • You enable OVF interrupt for Timer2 so that you use that to STOP Timer2 itself
  • As Timer1 TCNT1 matches COMPC, you start Timer3
  • Timer3 is just a basic timer that counts from 0 to some value that corresponds to 500 us
  • You enable OVF interrupt for Timer3 so that you use that to STOP Timer3 itself
  • Rinse and repeat

The value of COMPB and COMPC determines when those 2 pulses happen within the 20ms time frame

Ok sorry for the missing important detail. I can use only one timer because i have a three phase system and the other two timers are using for the other two phases and i have to generate identical pulses also for the other two phases

The Atmega2560 has 6 Timers (2 8-bit and 4 16-bit timers)

The 8-bit timers can generate 500us pulse with the correct pre-scaler and TOP value

But the Timer0 is used by the function millis() or i'm wrong? And if I use this timer I will use the instruction "TCNT0 = 0". Furthermore I will implement a PID and the use of millis() is fundamental.

Avoid using TIMER0, it is used for millis(), and with a Mega, you should not need to use it for something else.

TIM0 is used by millis() I think. Just when I though the Mega has all the timers you need, and you still runs out.

Are the Pulses spaced enough that no pulses will trigger at the same time or maybe at least 100 usec in between?

I have for each phase 4 pulses and it is necessary that these 4 pulses are generated when there is the rising edge. In particular if I have the R,S and T phase i have to generate the 4 pulses when there is the rising edge of R. So i need one timer for each phase i think. I would like to understand if there is a way to manage the pulse when it is superimposed the rising edge

An idea that I though is to use the TIMER5. In particular, when i generate the falling edge of the pulse number 3 I set a flag (for example XR = 1) and start TIMER5 and imposing OCR5A= 5666 (5666 because between 3 and 4 there are 60 degrees, using a prescaler of 8).

In interrupt of TIMER5 I generate the pulse number 4. Now, since the fourth pulse of each phase occours 120 degrees from each other, I use the TIMER5 with the same mode

Back to the drawing you provided, I see four short pulses (1, 2, 3 and 4). I assume these are the 500 usec pulses you talk about. You also have the a timeline from 0 to 20 ms so my guess is all the four pulses fall within this time frame.

So my question is, are pulses 1,2,3,4 ever going to collide with each other or they are always some distance/time from each other (doesn't matter the sequence, you can fix that in software)? This will determine whether you need four independent timers or you can get away with a single timer...

You can see the pulses in couple. In particular one couple is formed by 1 and 2 and the second by 3 and 4.

The distance between 1 and 2 must be 3,33 ms.
The distance between 3 and 4 must be 3,33 ms.
The distance between 2 and 3 must be 6.66ms

So in short the pulse must have always this distance from each other

Everything is doable then with a single 16-bit timer. You can use CTC mode with TOP OCRA. You then use the COMPB and COMPC interrupts to generate the rising and falling edge of your 500 usec pulses.

COMPB interrupt for the rising edge, and COMPC for the falling edge. Also during COMPC ISR, you then set the next value of COMPB and COMPC, and so on.

But, when the pulse number 4 is superimposed the rising edge of the dotted line i have problem, because the timer will reset.

The pulses can move backwards and the pulse number 4 in the only one that pass from 0second to 20ms (taking in account the time line in the image)

Your drawing shows pulses within the 0 to 20ms line. Or the 20ms only covers the dotted area? In that case, you should change the drawing to show that clearly

I redraw the scheme. Hope that this is more clear

In this case you can find the critical situation that is the one that i want to manage

What's the bigger picture?

There's pulses coming from something that's 3-phase, your timings mentioned earlier seem to indicate a 60Hz system, however the 20ms duration indicates a 50Hz system.

What are you trying to accomplish ... determine phase sequence, phase shift, control 3-phase SSR, other???

Is synchronization of the pulses to a 3-phase source important?

One way to do it: not use the timer to set the output directly, but to trigger an interrupt when it reaches the compare value. In the interrupt routine you set the pin high for the required time and do the "bookkeeping".

Keep a status register:

  • 0: all pulses done,
  • 1: before pulse 1
  • 2: before pulse 2
  • 3: before pulse 3
  • 4: before pulse 4
  • 5: before late pulse 4, when the next trigger has already arrived.
    and a TMP register that stores the timer value for pulse 1 in case of a late pulse 4

The external interrupt routine:

  • if status 0: start the timer, set OCR2A = "beta" (the timer value that corresponds to time period beta), set status=1
  • if status 4: set TMP= "Beta" minus the remaining time on the timer ( OCR2A- TCNT2), that gives you the time between late pulse 4 and the next pulse 1), set status=5

The timer interrupt routine:
set the pin high for 500us (you can do it in assembler and add the necessary NOP instructions to get 500 usec), and

  • if status 1, set OCR2A for 3.33 ms, set status =2
  • if status 2, set OCR2A for 6.66 ms, set status = 3
    ..
  • if status = 4, set status = 0
  • if status = 5, set OCR2A = TMP, set status = 1.

My idea for this would be something like this

ISR(TIMER3_COMPA_vect)
{
	// end of 20ms cycle, set OCR3B and OCR3C with new values for the next pulse
	OCR3B = 6000;
	OCR3C = 6000 + 1000;
}

ISR(TIMER3_COMPB_vect)
{
	// set output HIGH
}

ISR(TIMER3_COMPC_vect)
{
	// set output LOW
	
	// configure OCR3B and OCR3C with new values for the next pulse
	OCR3B = 12000;
	OCR3C = 12000 + 1000;
	
	// ... and so on
}

void setup_master_timer(void)
{
	/***********************************
	set up TIM3
	CTC with TOP at OCR3A
	***********************************/
	
	/*************
	TIMER3
	*************/
	TCCR3A = 0<<WGM31 | 0<<WGM30;
	TCCR3B = 0<<WGM33 | 1<<WGM32;
	
	// 50Hz at 2MHz counter (16MHz DIV8)
	OCR3A = 40000 - 1;

	// enable all output compare interrupts
	TIMSK3 = 1<<OCIE3C | 1<<OCIE3B | 1<<OCIE3A;

	// assuming pulse1 starts 3ms from rising edge, generate 500usec pulse
	OCR3B = 6000;
	OCR3C = 6000 + 1000;
	TCNT3 = 0;

	// start TIM3 at 2MHz (DIV8)
	TCCR3B = 0<<WGM33 | 1<<WGM32 | 0<<CS32 | 1<<CS31 | 0<<CS30;
}

You use the CTC feature to change the values of OCR3B and OCR3C to point to the next pulse. Since you only require a 500 usec pulse, the distance between OCR3B and OCR3C interrupt is constant. In my example, running the TIM3 at 2MHz (16MHz pre-scaled to 8), OCR3C is always 1000 clocks after OCR3B. You modify these values in the OCR3C ISR. You basically re-triggering OCR3B and OCR3C multiple times within the 20ms period.

The system Is 3 phase and the pulses control a 3 phase rectifier SCR. The dotted line in figure is a square wave (basically the sine wave transformed in square)

And yes, the synch is very important

Thanks ... still confused by the 20ms period and the periods shown in post #10 (seems like these are for 60Hz system).

Your critical situation you want to manage seems to be when instant 2 cycles back to instant 0 ... or does it? If the system frequency is 60Hz, wouldn't this be at 16.667ms rather than 20ms?

Could sync be accomplished via opto-isolated zero-cross detection?

Is this any help?


/*
 * 16-bit counter
 *  @2MHz overflow occurs at 32.768mS intervals
 *      prescaler = /8
 * 
 * From time of input capture the output compares will
 * be enabled. Their COM pins will be set to trigger at
 * ICR5 + offset + OC specific output
 *  offset is potentiometer adjustment (drawing below approx.)
 *
 *     ____________________                         ______
 *    |                    |                       |
 *  __|                    |_______________________|
 *  
 *      base + zero adj
 *           _      _          _      _                      _
 *          | |    | |        | |    | |                    |
 *  ________| |____| |________| |____| |____________________|
 *           1      2          3      4                      1
 *      base + max adj
 *         _                   _      _          _      _
 *        | |                 | |    | |        | |    | |
 *  ______| |_________________| |____| |________| |____| |_____
 *         4                   1      2          3      4
 */

const volatile uint16_t g_cBaseOffset = 100u;    //50uS base offset from input edge to pulse 1

const uint8_t pinInput = 48;    //ICP5
const uint8_t pinAdjust = A0;   //adjustment potentiometer
const uint8_t pinOC1 = 11;      //OC1A  pulse 1
const uint8_t pinOC2 = 12;      //OC1B  pulse 2
const uint8_t pinOC3 = 13;      //OC1C  pulse 3
const uint8_t pinOC4 = 5;       //OC3A  pulse 4

const uint8_t pinTestSig = 3;   //signal output for bench testing; connect to pinInput 48 for testing

//additional offset from 0 to 10mS set by potentiometer
volatile uint16_t  g_vPotOffset = 0u;
            
void setup( void )
{
    //inputs
    pinMode( pinAdjust, INPUT );
    pinMode( pinInput, INPUT_PULLUP );
    
    //outputs
    digitalWrite( pinOC1, LOW );
    digitalWrite( pinOC2, LOW );
    digitalWrite( pinOC3, LOW );
    digitalWrite( pinOC4, LOW );
    pinMode( pinOC1, OUTPUT );
    pinMode( pinOC2, OUTPUT );
    pinMode( pinOC3, OUTPUT );
    pinMode( pinOC4, OUTPUT );

    //test signal
    digitalWrite( pinTestSig, LOW );
    pinMode( pinTestSig, OUTPUT );

    //init timers 1 and 3
    TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(COM1C1);   //init WGM0; compare clears pins
    TCCR1B = _BV(CS11);                                 //prescaler /8
    TCCR3A = _BV(COM3A1);
    TCCR3B = _BV(CS31);

    //timer 5 is used for input capture
    TCCR5A = 0;                                         //WGM0
    TCCR5B = _BV(ICES5) | _BV(CS51);                    //IC on rising edge; /8 prescaler (same as T1 & T3)
    TIMSK5 |= _BV(ICIE5);                               //enable input capture interrupt

}//setup

void loop( void )
{
    static uint32_t
        tAdj = 0ul;
    uint32_t
        tNow = millis();

    //every 50mS read the pot
    if( (tNow - tAdj) >= 50ul )
    {        
        uint16_t adj = analogRead( pinAdjust );
        //map the pot range from 0 to 10mS
        adj = map( adj, 0, 1023, 0, 20000ul );
        noInterrupts();
        g_vPotOffset = adj; //update the volatile safely
        interrupts();
        
        tAdj = tNow;    
        
    }//if

    //comment out if using external source
    DoTestPulse();
    
    
}//loop

//////////////////////////////////////////////////////////////////////
//generate a 50Hz square wave for bench testing
void DoTestPulse( void )
{
    static bool
        state = false;
    static uint32_t
        tPulse = 0ul;
    uint32_t
        tNow = micros();
        
    if( (tNow - tPulse) >= 10000ul )
    {
        state ^= true;
        digitalWrite( pinTestSig, state ? HIGH:LOW );
        tPulse = tNow;
        
    }//if
    
}//DoTestPulse

//////////////////////////////////////////////////////////////////////
//captures the rising edge of the input signal and
//initiates the pulse sequence starting with pulse 1
ISR( TIMER5_CAPT_vect )
{
    uint16_t
        ic5Capt;
    
    //grab the time and compute the necessary offset
    ic5Capt = ICR5 + g_cBaseOffset + g_vPotOffset;

    //set up pulse 1
    OCR1A = ic5Capt;        //set the time
    TCCR1A |= _BV(COM1A0);  //set pin to go high on compare
    TIMSK1 |= _BV(OCIE1A);  //enable interrupt
        
}//ISR T5CAPT

ISR( TIMER1_COMPA_vect )
{
    //if pin is commanded high on this interrupt
    if( (TCCR1A & (_BV(COM1A1) | _BV(COM1A0))) == (_BV(COM1A1) | _BV(COM1A0) ) )
    {
        //set up pulse 2
        OCR1B = OCR1A + 6667u;
        TCCR1A |= _BV(COM1B0);
        TIMSK1 |= _BV(OCIE1B);
        
        //and set pulse 1 for a 500uS width
        OCR1A = OCR1A + 1000;
        TCCR1A &= ~_BV(COM1A0);                
                
    }//if
    else
    {        
        //interrupted on falling edge (end of 500uS wide pulse)
        //done pulse 1; shut off the interrupt
        TIMSK1 &= ~_BV(OCIE1A);
                    
    }//else
    
}//ISR T1CA

ISR( TIMER1_COMPB_vect )
{
    //logic for pulses 2 and 3 is the same as pulse 1
    if( (TCCR1A & (_BV(COM1B1) | _BV(COM1B0))) == (_BV(COM1B1) | _BV(COM1B0) ) )
    {
        //set up the next pulse
        OCR1C = OCR1B + 13333u;
        TCCR1A |= _BV(COM1C0);
        TIMSK1 |= _BV(OCIE1C);
        
        //500uS wide pulse
        OCR1B = OCR1B + 1000;
        TCCR1A &= ~_BV(COM1B0);                
        
    }//if
    else
    {                
        //done pulse 2
        TIMSK1 &= ~_BV(OCIE1B);
                    
    }//else
    
}//ISR T1CB

ISR( TIMER1_COMPC_vect )
{    
    if( (TCCR1A & (_BV(COM1C1) | _BV(COM1C0))) == (_BV(COM1C1) | _BV(COM1C0) ) )
    {
        //set up the next pulse
        OCR3A = OCR1C + 6667u;
        TCCR3A |= _BV(COM3A0);
        TIMSK3 |= _BV(OCIE3A);
        
        //500uS wide pulse
        OCR1C = OCR1C + 1000;
        TCCR1A &= ~_BV(COM1C0);                
        
    }//if
    else
    {        
        //done pulse 3
        TIMSK1 &= ~_BV(OCIE1C);
                    
    }//else
    
}//ISR T1CC

ISR( TIMER3_COMPA_vect )
{
    //similar to pulse 1
    if( (TCCR3A & (_BV(COM3A1) | _BV(COM3A0))) == (_BV(COM3A1) | _BV(COM3A0) ) )
    {
        //we don't set up the next pulse here; instead, the input capture does it
        //for pulse 1
        //so set up pulse 4 for a 500uS width
        OCR3A = OCR3A + 1000;
        TCCR3A &= ~_BV(COM3A0);                
        
    }//if
    else
    {        
        //done pulse 4
        TIMSK3 &= ~_BV(OCIE3A);
                    
    }//else
    
}//ISR T3CA