unstable 40Khz Phase shift - see video for behaviour

I tried to implement a 40Khz phase shift for testing ultrasonic transducers(arduino uno r3). I am having an issue with getting the second waveform to be stable.

phase shift error

The second square wave should position up and down between 0 and 180 degrees however the oscilloscope shows that the signal jumps/skips to unwanted positions.

This as close to I have come to getting a phase shift at all on the UNO and I am wondering if anyone has any insight to how to prevent the errors or even it is possible.

I am guessing its a problem with either resolution/clock timing but honestly I am lost as its not at all transparent. Does anyone understand some more reliable ways to phase shift at this frequency 100% stable at 40Hkz? Even better if I can do a full 360/pi.

// This code demonstrates how to generate two output signals phase counting up and down to 18-
// with variable phase shift between them using an AVR Timer 

// The output shows up on Arduino pin 9, 10


void setup() {
  pinMode( 9 , OUTPUT );    // Arduino Pin  9 = OCR1A
  pinMode( 10 , OUTPUT );   // Arduino Pin 10 = OCR1B

  // Both outputs in toggle mode  
  TCCR1A = _BV( COM1A0 ) |_BV( COM1B0 );


  // CTC Waveform Generation Mode
  // TOP=ICR1  
  // Note clock is left off for now

  TCCR1B = _BV( WGM13) | _BV( WGM12);

  OCR1A = 0;    // First output is the base, it always toggles at 0


}

// prescaler of 1 will get us 8MHz - 488Hz
// User a higher prescaler for lower freqncies

#define PRESCALER 1
#define PRESCALER_BITS 0x01

#define CLK 16000000UL    // Default clock speed is 16MHz on Arduino Uno

// Output phase shifted wave forms on Arduino Pins 9 & 10
// freq = freqnecy in Hertz (  122 < freq <8000000 )
// shift = phase shift in degrees ( 0 <= shift < 180 )

// Do do shifts 180-360 degrees, you could invert the OCR1B by doing an extra toggle using FOC

/// Note phase shifts will be rounded down to the next neared possible value so the higher the frequency, the less phase shift resolution you get. At 8Mhz, you can only have 0 or 180 degrees because there are only 2 clock ticks per cycle.  

int setWaveforms( unsigned long freq , int shift ) {

  // This assumes prescaler = 1. For lower freqnecies, use a larger prescaler.

  unsigned long clocks_per_toggle = (CLK / freq) / 2;    // /2 becuase it takes 2 toggles to make a full wave

  ICR1 = clocks_per_toggle;

  unsigned long offset_clocks = (clocks_per_toggle * shift) / 180UL; // Do mult first to save precision

  OCR1B= offset_clocks;

  // Turn on timer now if is was not already on
  // Clock source = clkio/1 (no prescaling)
  // Note: you could use a prescaller here for lower freqnencies


}

// Demo by cycling through some phase shifts at 50Khz  

void loop() {
TCCR1B |= _BV( CS10 ); 
       int x = 1;
   for (int i = 0; i > -1; i = i + x){
    setWaveforms( 40200 , i ); //40200 on scope is 39.9992Khz
      if (i == 180) x = -1;             // switch direction at peak
      delay(20);
  }


}

Hi carbon_adam. I think the the root cause of your problem is that you are not synchronizing the changing of the OCR1B with when the actual compare occurs. Basically this will mean that sometimes you may completely skip a toggle, and other times you may get a double toggle. Both of these occurrences will give you "random" 180 degree phase shifts.

When you are incrementing phase delay a double toggle may occur if the OCR1B is changed to close to the toggle point. Similarly when you are decrementing the phase delay a missed toggle may occure under the same circumstances.

As to a solution, well I haven't really though too much about this problem before, so this just the first thing that came to mind. Anyway this is what I'd try:

  • If you're incrementing the phase delay then write the OCR1B just after the desired new phase count is reached.

  • If decrementing the phase delay then write the new OCR1B just after the existing phase count is reached.

stuart0 Do your perhaps have an idea how to do that, I am afraid im not sure how to implement it

What do you see if you just cycle through the OCR1B without resetting ICR1 each time?

To synchronize the change of OCR1B to the cycle, you can use an overflow interrupt so that each time the timer reaches ICR1, the value of OCR1B can be changed. You will not be able to put the delay(20) between values in the interrupt.

carbon_adam:
stuart0 Do your perhaps have an idea how to do that, I am afraid im not sure how to implement it

Well one way would be to constantly poll the counter value to determine when to make the phase jump, but you’d have to be doing almost nothing else at all in the main loop to make that feasible.

For a more robust method you’d need to use interrupts. The decrementing phase case would be fairly easy, because it would always be safe to change it on the OCRB1 interrupt.

The incrementing case is a bit trickier, because you want to do it just after the desired new count is reached rather than when the current count (and hence the OCR1B) occurs. Assuming however that the increment values are always going to be reasonably small (it’s only 1 in your example), then using the same interrupt and just “waiting it out” (as in waiting for the desired new count to be reached) could work. My only hesitation with this method is that having to wait (blocking code) is not ideal in an interrupt routine. If the phase jumps and hence the waits are small enough then it might still be ok however.

Certainly with the current example (40kHz rate and one degree of phase increment) you are only waiting approx one count value each time, so the new value is 100% safe to write as soon as you get into the interrupt due simply to interrupt latency. If however you start operating at lower frequency, or with larger than one degree phase increments then you might have check that the desired new count has been reached before writing the the new OCRB1 value.

cattledog: To synchronize the change of OCR1B to the cycle, you can use an overflow interrupt so that each time the timer reaches ICR1, the value of OCR1B can be changed.

Unfortunately that's not always going to prevent the anomalies though. Phase shifts that are too close to 0 degrees could still cause the double (or missed) toggles.

Thanks for the advice :slight_smile: Christmas coming up so hopefully I will get a chance to try this out a bit more but it seems a deeper study of the timers is in order. I did also find another thread that seems to propose some working solution however it needs to be ported to use timer1 …which I tried and failed yet todo but I will keep working on it

40khz pwm reverse

carbon_adam: Thanks for the advice :) Christmas coming up so hopefully I will get a chance to try this out a bit more but it seems a deeper study of the timers is in order.

I think the method outlined above is probably the only way to get truly glitch free operation, but yeah it's not necessarily easy at the frequency you need to operate.

There is however another way of approaching it, if you don't mind a one off glitch each time you change the phase. The main problem with the code the way it is, is that a single glitch can throw it out by 180 degrees and this error persists until a second glitch occurs. (This is just the nature of getting one too many or one too few toggles.)

The other alternative is to effectively restart the whole show every time you set a new phase. By stopping the count and initializing both outputs to whatever starting state you require then you can guaranty no persisting 180 degree errors. The downside is that you're pretty much sure to get a single cycle glitch every time you change phase.

BTW. The above method also addresses one of your other earlier question, namely how to get 180 to 360 degree operation. Essentially your "circuit" operates in two separate modes, a 0 to 180 degree mode if both outputs are started from the same initial state, and 180 to 360 degree mode if the counters are started from alternate states.

I did also find another thread that seems to propose some working solution however it needs to be ported to use timer1 ..which I tried and failed yet todo but I will keep working on it

40khz pwm reverse

I got a little further today and have some working code that is glitch free enough to use for my purpose at the moment. This is only going up to 180 and Ill have to extend that somehow but after the Christmas party drinking games last night my brain is a bit fried today lol.

// 2 channel PWM with pahse shift on the second. pins 9 and 10 Uno R3
void setup() {
  PORTB=0x00;
  DDRB=0x06;
 
  ICR1=199;
  OCR1A=100;
  OCR1B=100;
  TCNT1=0;
  TCCR1A=0x50;
  TCCR1B=0x19;
}

int j=0;
int a;

void loop() {
  delay(10);
  
  a = analogRead(0);
 j = (a/3);
  if(j>199) j=199;

  //update ocr registerj with the value
  TCCR1B=0x18;
  if((PINB&0x02)!=00) TCCR1C=0x80;
  if((PINB&0x04)!=00) TCCR1C=0x40;
  TCNT1=0;
  OCR1B = j;
  if(j==0) TCCR1C=0x40;
  TCCR1B=0x19;
}