Longer than expected delay with delayMicroseconds (Biphasic Square Wave)

Hello, I am building a biphasic square wave using two arduino pins (5 and 6) connected to an H-Bridge (L293D). Since I need a frequency of maximum 1000Hz, I thought I can do it with manual PWM (toggle pins HIGH and LOW manually)

However, the second phase of the square wave lasts longer than expected with ~4uS. Can someone give an explanation to why this could happen?

void loop() {
    for (int i = 0; i < 5; i++) {
      noInterrupts();
      digitalWrite(5, HIGH);
      delayMicroseconds(100);
      PORTD = PORTD ^ B01100000;  // sets pins 5 LOW, 6 HIGH
      delayMicroseconds(100);
      digitalWrite(6, LOW);
      interrupts();
      delayMicroseconds(800);
      }
    delay(40);

If I set the second delay (2nd phase) to a shorter period like 96, the square wave is almost perfect. But it does not make sense.

void loop() {
    for (int i = 0; i < 5; i++) {
      noInterrupts();
      digitalWrite(5, HIGH);
      delayMicroseconds(100);
      PORTD = PORTD ^ B01100000;  // sets pins 5 LOW, 6 HIGH
      delayMicroseconds(96);
      digitalWrite(6, LOW);
      interrupts();
      delayMicroseconds(800);
      }
    delay(40);

For the schematic here it is (Ignore Motor B)

image

And also, I am not driving motors or inductive loads.

delayMicroseconds( ) has a range of 4 µs.

When it comes to your motors, does it matter ? :sunglasses:

1 Like

I am not driving motors with this square wave. I am using L293D (Which is common for driving motors) and for my application yes, it matters.

I made sure all my numbers are divisible by 4 for this reason.

You didn't say what Arduino you are using, but on an ATmega328@16MHz the digitalWrite() command takes approximately 4 to 6 microseconds. Better to use port manipulation everywhere. Even if you set delayMicroseseconds() divisible by 4, it won't give you better than 4 microseconds accuracy.

if you need high accuracy, you should consider using a timer

Your delays are added to the time it takes to execute the rest of the code. So, you should indeed use timers.

Uno Rev2. But why this delay only appears on the second phase and not the first? I will try with setting port manipulation in all logic since I do not use other pins from PORTD.

I thought about timers but I really can't think of a good way to implement it. I need to start the square wave precisely after 5ms of the initiation of another unrelated signal. I need to create two square waves that are phase shifted with exactly the same duration of the duty cycle so that it would produce the biphasic wave from the H-Bridge. I hope this makes sense.

Since I use 1000Hz frequency, using the builtin analogWrite was my first choice as it uses frequency of 980 which is ok for me but I found that when I infer analogWrite after 5ms the square wave does not start at 5ms but it waits for the next cycle hence the delay became unpredictable and somewhere between 5-6ms.

If you can point me out on how to generrate two square waves exactly after a predefined delay from another signal, phase shifted precisely with exactly the duty cycle that would fix that.

As @flashko pointed out, the problem is with the digitalWrites.
You can still use delays but you need to manipulate the PORT Bits directly like you did when you set pin5 LOW and 6 HIGH.

At the point where you want to start counting time:

long int start_time = micros();
//maybe you should have your first port manipulation here...

At the point where your timed port manipulation needs to be, add:

while (micros() - start_time < 10000ul) {
   ;  //do nothing (this will wait for the correct moment to move on (here 10 msec))
}
//here your port manipulation

This is still blocking code (so not the best solution), but as long as the code in between can be finished within 10 msec, your port manipulation will be in time.

After using direct port manipulation, I can confirm that the square wave output is now pretty accurate.

The second problem now, is keeping the delay between the pulses accurate. I can keep them accurate only if I disable interrupts in the whole loop (which takes 5ms total)

Is 5ms too long to disable interrupts? Things look ok on scope.

void loop() {
    noInterrupts();
    for (int i = 0; i < 5; i++) {
      PORTD = B00100000;
      delayMicroseconds(100);
      PORTD = B01000000;
      delayMicroseconds(100);
      PORTD = B00000000;
      delayMicroseconds(800);
      }
    interrupts();
    delay(40);
}

Does that mean a 3.3volt-logic Nano 33 BLE Sense Rev2, with nRF52840 microcontroller?
If so, then most of the info in this thread is incorrect.
Leo..

I meant Uno Rev2 sorry.

That will very much depend how your code depends on interrupts.
In this case you did not write any interrupts yourself.
So that leaves only interrupts in the background.
I know millis() depends on interrupts. I do not know if micros() also depends on interrupts.
If so, that may influence the proper functioning of delayMicroseconds...

On the scope it looks ok. I know that setting noInterrupts for this long could mess with millis() and maybe micros() too so maybe someone can confirm if it is ok to use it for this long if I am not using any other interrupts or even calling or using millis() or micros()

Can timers generate a signal like that?

1 KHz, 10% duty cycle, 100uS phase shift.

Maybe you should consider a gap between your pulses.
Mosfets need time to open up or close down. You do not want a shoot through...(google 'shoot through H-bridge').
Timers can be more accurate than the delays you used. You can also use the 800 us waiting to process your interrupts. Did you try to implement my code?
Timers will be as accurate as micros (4 us) (unless you change the pre-scaler of the timer, but then delay and delayMicroseconds and milis and micros will not work anymore). It will also screw up all libraries that depend on the timer (such as PID libraries, servo libraries, etc., maybe even Serial...). There is more than one timer in UNO, so better choose one that is not used in all those libraries (if possible/relevant).

The resolution of delayMicroseconds is 5uS. ~4uS falls within the resolution range of delayMicroseconds.

unsigned long firstreading = micros();
unsigned long secondreading = micros();
Serial.print( "firstreading ");
Serial.print( firstreading );
Serial.print( " secondreading " );
Serial.print( secondreading );
Serial.print ( " difference ");
Serial. print ( secondreading - firstreading );
Serial.println();

Yes I did, I used it instead of the 800us delay but it induces jitters and the period between each biphasic pulse is slightly increased (similar to the delayMicroseconds). I do not mind a blocking code anyhow.

I investigated using timer1 on pins 9 & 10 but as far as I understand, it is impossible to get the signal I want with both adjustable duty cycle and phase shift on a single timer.
Maybe it is possible with 2 timers? is there a library that I can utilize? It seems like a very tricky thing to achieve with my knowledge.

I do not use any servo/serial/pid.

Thanks bro

This was fixed using direct port manipulation instead of digitalWrite.

void setup() {

	DDRB |= _BV(PB1) | _BV(PB2);       /* set pins as outputs */	

	TCCR1A =B10110000;
	TCCR1B =B00010001;

	ICR1 = 12000;

  OCR1A = 5000 - 900;  //9пине
  OCR1B = 5000 + 900;  //10пине
/* Или	
	analogWrite(9,5000 - 900);
	analogWrite(10,5000 + 900);
*/
}
void loop(){
}

Thanks for the code, but I tested it on the scope it does not produce the needed signal.