Square signal generation with VARIABLE phase and frequency, Timer1 and UNO

Hello there,
According to my research and this article,

It will be quite simple to generate square (50% duty) signal with a use of Timer1 interrupt.
I am interested in frequences from 4Hz to 1kHz. Signal will be controlling Strobo lamp timing.
To make frequency adjustable, I will write to OCR1A register in a loop().

HOWEVER, i have a problem with making phase of a signal adjustable also.
If I would take the delay() inside loop() approach, I would just add few millis delay to shift in time my waveform.
But when I am dealing with interrupts, delay() has no use.

Is it possible to freeze interrupt for given time?
Or maybe make it skip few ticks?
Or add some more ticks to compare match register for only 1 match? (so it would go back to previous value, but signal will be shifted for that extra ticks)

Thanks for any help in advance!

Grumpy_Mike idea to make adjustable frequency:

Grumpy_Mike:
Yes just bang a value in the appropriate register.
This code generates a signal on pin 3 using timer 2.

/* Code to pulse pin 3 with a modulated signal
  • Can be used to drive an IR LED to keep a TSOP IR reciever happy
  • This allows you to use a modulated reciever and a continious beam detector
  • By Mike Cook Nov 2011 - Released under the Open Source licence
    */
    volatile byte pulse = 0;

ISR(TIMER2_COMPB_vect){ // Interrupt service routine to pulse the modulated pin 3
pulse++;
if(pulse >= 8) { // change number for number of modulation cycles in a pulse
pulse =0;
TCCR2A ^= _BV(COM2B1); // toggle pin 3 enable, turning the pin on and off
}
}

You can suspend interrupts for a while and then activate them again.

Not sure what you are trying to achieve though

I am trying to make regulation of signal phase.
Look at attached picture.
For example, when strobo is blinking with given frequency at rotating shaft, its hitting some point, making it visible as static in the room. Shifting signal in time domain will make it show aoher point on a shaft, offsetem from previous.

If few words, I want to be able to shift signal in time.

You can easily generate 1khz (given your use I'm sure you don't care about a few microsecond errors) right in the loop with no interrupts nor timers... look at the blink without delay example and add a small time gap when needed

I think that the way to do this is to use Fast PWM to ICR1 (Mode 14). ICR1 and the prescaler will set the frequency. You will then have OCR1A and OCR1B available for compare match interrupts. You can turn the pin on at one compare match and turn it off at the other. You will move the pair of compare match values to shift the phase.

For example you have a frequency with an ICR1 value of 1000. For 50% duty cycle you want 500 ticks between OCR1A and OCR1B. If OCR1A = 1 and OCR1B = 501 the pulse is at one side of the period. If OCR1A = 499 and OCR1B = 999 it is at the other.

Thank you guys for an answer!
J-M-L solution would be my plan B, I want to try with interrupts at first (arent they made for such things? ).
cattledog, thats a nice idea. I havent thought about using 2 registers in that way.

I will try to code and post results :wink:

cattledog:
I think that the way to do this is to use Fast PWM to ICR1 (Mode 14). ICR1 and the prescaler will set the frequency. You will then have OCR1A and OCR1B available for compare match interrupts.

I digged into datasheet and found myself confused. Are you sure thats how PWM Interrupt is working?
It seems that OCR1A is for 1 channel signal and OCR1B is for another channel (ach timer has 2 chanels) .
So indeed I can set frequency with ICR1, but hten I have only PCR1A to set duty cycle.

It seems that OCR1A is for 1 channel signal and OCR1B is for another channel (each timer has 2 channels) .
So indeed I can set frequency with ICR1, but then I have only PCR1A to set duty cycle.

In the method I proposed you are not going to use the hardware outputs for the timer. You are going to use the compare match interrupts for OCR1A and OCR1B channels. In the interrupt service routines, you will turn another pin on and off. You can use either mode 12 (CTC to ICR1) or mode 14 (FastPWM to ICR1).

The duty cycle is set by the span between the two compare match interrupts.

What is PCR1A?

I made a typo (actually a lot of them 0.o ) in my previous post. I meant : OCR1A.
Ok, so I can set an interrupt to run with a given frequency (ICR1), that will trigger on rising counting an ISR (OCR1A ) and trigger again on falling counting (OCR1B) ?
Then in my ISR I will have a function to turn off/on a pin.

Ok, so I can set an interrupt to run with a given frequency (ICR1),

No you set a timer to cycle with a frequency determined by the prescaler and ICR1.

You set one interrupt to be executed when the timer count matches an OCR1A value. Turn an output pin on in the ISR.
Set another interrupt to be executed when the timer count matches an OCR1B value. Turn an output pin off in the ISR.

that will trigger on rising counting an ISR (OCR1A ) and trigger again on falling counting (OCR1B) ?

You are going to use a mode which only has a rising count (CTC or FastPWM) to the ICR1 value.

Ok, now I got it (havent I said that yesterday? :stuck_out_tongue: )
I understood your solution, but tried to implement it in a wrong way.

PSEUDOCODE:

ISR(TIMER1_COMPA_vect)
{
	//turn on pin
}

ISR(TIMER1_COMPB_vect)
{
	//turn off pin


setup()

TIMSK1 = (1<<OCIE1A)|(1<<OCIE1B);  //Output Compare A/B Match Interrupt Enable

I am going to give it a try tomorrow.
Thanks for all your effort!

I got the principle, now have to make whole program to adjust everything on the go.
Thank you once more for your help :slight_smile:

ISR(TIMER1_COMPA_vect)
{
   //set pin high
   digitalWrite(13,HIGH);
}
ISR(TIMER1_COMPB_vect)
{
   //set pin low
   digitalWrite(13,LOW);
}

void setup() {
    unsigned long TOP_VALUE = 62499;
    unsigned long DUTY_START = 0;
    unsigned long DUTY_STOP = TOP_VALUE /2;

    pinMode(13, OUTPUT);
  
    cli();          // disable global interrupts
    TCCR1A = 0;     // set entire TCCR1A register to 0
    TCCR1B = 0;     // same for TCCR1B

    ICR1 = TOP_VALUE ;
        
    OCR1A = DUTY_START;
    
    OCR1B = DUTY_STOP;

    TCCR1A = (1 << WGM11) | (1 << WGM10);
    TCCR1B = (1 << WGM12) | (1 << WGM13) ;
  
    TIMSK1 = (1 << OCIE1B) | (1 << OCIE1A);

    #define F_CPU 16000000
    TCCR1B = (1 << CS12) | (1 << CS10);  //enable interrupt with 1024 prescaller
    sei();

}

void loop() {


}

Of course I have tog et rid of digitalWrite() and make it direct port

TCCR1B = (1 << CS12) | (1 << CS10);  //enable interrupt with 1024 prescaller

This line is going to over write the previously set values in TCCR1B. Change the = sign to an |= (or equals).

TCCR1B |= (1 << CS12) | (1 << CS10);  //enable interrupt with 1024 prescaller

ah, of course. Thanks again, I have to be more focused.
Anyway I got power over pin 13 diode with a use of interrupts, so it's time to make the rest.
Thanks for your ideas and patience :wink: