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);
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.
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);
}
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()
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).
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.