Generate 1 MHz clock and synchronize a pulse to it

I am trying to generate a 1 MHz clock and synchronize an output pulse to the generated 1 MHz clock.

The generation works fine, the synchronizing does not.

I thought I could sample the output clock using PINB until I saw it go from low to high in order to find the rising edge, then output my pulse.

The output pulse is not consistently placed relative to the rising edge. It's always placed during the same half-cycle of the clock, but it can be in any of 8 different places.

Any help is appreciated!

//use timer/counter 1 to generate a 1MHz square wave on Arduino pin 9
//output a pulse synchronized with the 1MHz clock

const int pulseOutPin = 7;
const int clkOutPin = 9;
int newTemp = 0;
int oldTemp = 0;

void setup() {
  TCCR1A = _BV(COM1A0);  //toggle OC1A on compare match
  OCR1A = 7;  //top value for counter, 7 = 1MHz
  TCCR1B = _BV(WGM12) | _BV(CS10);  //CTC mode, prescaler clock/1
  pinMode (pulseOutPin, OUTPUT);
  pinMode (clkOutPin, OUTPUT);
}

void loop() {
    oldTemp = PINB & B00000010;
    newTemp = PINB & B00000010;
    if (newTemp == 2 && oldTemp == 0){ 
      PORTD = PORTD & B01111111;  //take pulseOutPin low for 1 cycle to show sync
      PORTD = PORTD | B10000000;  //take pulseOutPin high again
    }
  }

Here is a scope image of my output. The yellow trace shows the 8 different locations of the output pulse. This was taken with infinite persistence. On any one cycle there is only one pulse, 125 ns wide, at one of the 8 locations.

I would like to get the output to be at the same location every time.

You need to use interrupts. It's the only way to do something fast after an edge.

Thanks for the suggestion! Here's some new code using interrupts, followed by a new scope plot.

Why is the output still varying in time? In the scope plot, the left pulse trails the rising edge of the 1 MHz clock by 5 cycles, the center pulse trails the rising edge by 6 cycles, and the right pulse trails the rising edge by 7 cycles. The placement appears random, but it is always one of these 3 positions. Why aren't they always in the same position?

They all appear to be trailing the rising edge by 5-7 cycles. I don't think I am doing anything to make them favor the rising edge rather than the trailing edge. Why do they never trail the falling edge?

Any help is appreciated!

//use timer/counter 1 to generate a 1MHz square wave on Arduino pin 9
//output a pulse synchronized with the 1MHz clock

const int pulseOutPin = 7;
const int clkOutPin =   9;

ISR(TIMER1_COMPA_vect)
{
  PORTD = PORTD & B01111111;  //take pulseOutPin low for 1 cycle to show sync
  PORTD = PORTD | B10000000;  //take pulseOutPin high again
}
void setup() 
{
  TCCR1A = _BV(COM1A0);             //toggle OC1A on compare match
  OCR1A = 7;                        //top value for counter, 7 = 1MHz
  TCCR1B = _BV(WGM12) | _BV(CS10);  //CTC mode, prescaler clock/1
  TIMSK1 = bit (OCIE1A);            //interrupt on Compare A Match
  pinMode (pulseOutPin, OUTPUT);
  pinMode (clkOutPin, OUTPUT);
}

void loop() {}

Scope plot:

Try replacing this:

PORTD = PORTD & B01111111; //take pulseOutPin low for 1 cycle to show sync PORTD = PORTD | B10000000; //take pulseOutPin high again

With this: PIND = 0b10000000; // toggle state of bit 7 by writing 1 to input register PIND = 0b10000000; // toggle state of bit 7 by writing 1 to input register.

The variation - 1 MHz is a change every 16 system clocks. Interrupt takes a couple clocks to save states on the stack/heap etc, jump to the ISR code, do that part, and return. You would think it'd be a bit more deterministic, I can't really explain the variation.

Thanks for the suggestion!

Unfortunately, that seems to make the pulse completely random across any of the 16 positions.

I will try to find the schematic in the data sheet to understand why that does what it does and where you were going with it.

You might find some insight here…

http://www.gammon.com.au/forum/?id=11488

There are other things going on as well as your code eg the counts for micros and mills which will delay taking action on the external interrupt. I think the arduino is just to slow for this job. A hardware only solution would work much better.

Mark

Crossroads, I see you were going for the 'toggle'. I'm not sure at all why that made it so much worse.

David, thank you for the suggestion. Much of my learning came from that site. My sketch is VERY similar to his 'Timer Interrupts' example, with the exception that I also use the 'toggle on compare match' function of the counter.

I got the 1 MHz output working, and then when I saw his example for the Timer Interrupts which only added 1 line to the timer setup and a simple ISR, I felt sure it would work.

Mark, you may be right. That was my initial feeling, but I'm pretty close. Is there a way to kill millis and micros so I can see if that is what causes the jitter? I considered switching to a UNO32 for the additional speed, but I'd have to relearn the timers and interrupts.

Huh, thought the PIN part would be faster. I think it's 14.2 in the datasheet; definitely in the IO section.

You can use sei(); and cli(); to turn ALL interrupts off & on. Unfortunately that also keeps you from reacting to the 1 MHz.

The only way I can think of to get rid of mills etc is to use AVR studio. But you maybe able to turn them off.

A hardware only solution would also be cheaper.

Mark

Unfortunately that also keeps you from reacting to the 1 MHz.

Not if,

Just thinking that you could (assuming that you will never do anything else in this program) then you could put a loop forever in your ISR. The asm would look something like this

ISR( label: set pin high set pin low nop * n // n = right number of nops to waste the rest of the micro second jump label )

Of course the Arduinos 16MHz freq has a lot of slop in it and will drift. Only the first edge of your 1MHz clk is ever, used just start the ISR.

Mark

The ISR has to wait for the currently executing instruction to finish. This could be one source of jitter. Another is that once the ISR completes, at least one instruction in the program is executed.

Try putting while (1); inside loop().