Arduino Timing - Page 1
Forum section: electronics / beginners

author: metrologist
author_rank: Super Contributor
timestamp: May 29, 2016, 12:40:06 am
content: I'm using Arduino to create pulse signals to test my new DS1054Z.I've modified the blink program to remove delay:Note that I've commented the for loop for now, but either way, I notice that some pulse widths are longer (by 50%) and sometimes the period between pulse (the bursts with the for loop) is longer.Another interesting thing is that with my scope, when I let it run with a normal trigger, I will see bursts or more than 5. I suspect this is just the end of sweep and a trigger picks up the end of a burst. Not thought enough about that yet.Anyway, I'm curious why the arduino timing varies so much? I'd not expect accurate absolute timing, but at least I would expect it to be consistent.Code:void loop() {if (micros() - time >= 1000){time = micros();//for (int i=0; i<5; i++){digitalWrite(2, HIGH);digitalWrite(3, HIGH);digitalWrite(4, HIGH);digitalWrite(5, HIGH);digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level)digitalWrite(2, LOW);digitalWrite(3, LOW);digitalWrite(4, LOW);digitalWrite(5, LOW);digitalWrite(13, LOW); // turn the LED off by making the voltage LO//}}}Thanks!

author: Ian.M
author_rank: Super Contributor
timestamp: May 29, 2016, 01:01:09 am
content: digitalWrite() sucks! (golfballs through a drinking straw suckitude)https://ucexperiment.wordpress.com/2013/03/25/arduino-digitalwritepin-value/

author: LabSpokane
author_rank: Super Contributor
timestamp: May 29, 2016, 01:39:32 am
content: Try using AnalogWrite to output PWM instead:https://www.arduino.cc/en/Reference/AnalogWriteI don't think an Arduino programmed through wiring is ever going to be a great function generator, but this should put some squarish waves on the screen of your o-scope.

author: Maxlor
author_rank: Frequent Contributor
timestamp: May 29, 2016, 02:37:25 am
content: Quote from: metrologist on May 29, 2016, 12:40:06 amYou're right in your observation, even though digitalWrite is inefficient, its execution time should be consistent. The reason it's not is that the Arduino uses interrupts for a couple of things, which mess up any attempt at accurate timing in the main loop.One way around this is to disable interrupts with noInterrupts(), although this will break things like delay(), and probably communication including the serial console. For a simple test program like this it'll improve things though.If you were to do this in seriousness, you'd do your pin changes in an interrupt triggered by a timer, alas I don't think the Arduino API lets you do that, you'll have to access the hardware directly. Or use the PWM functionality in one of the timers to create the signal you need, that's a very reliable and efficient way to do it. analogWrite(), which does make use of PWM, is very limited because it doesn't let you change the frequency. You can do it by modifying hardware registers directly, but again some things will probably break, since the Arduino lib assumes that various timers run at a certain speed.My point is, for accurate timing, program the hardware directly and don't use the Arduino lib, it'll only fight you at every step.

author: edavid
author_rank: Super Contributor
timestamp: May 29, 2016, 02:39:53 am
content: Why not use the tone function?https://www.arduino.cc/en/Reference/Tone

author: bitseeker
author_rank: Super Contributor
timestamp: May 29, 2016, 04:01:08 am
content: Here's some sample code for using the timer output for generating a signal:http://forum.arduino.cc/index.php?topic=122065.msg918270#msg918270

author: metrologist
author_rank: Super Contributor
timestamp: May 29, 2016, 04:38:47 am
content: Thanks folks! I got to learn some things about Arduino. I also have one of those DDS modules to play with. I noticed that when I turn off my scope with the probes connected to the Arduino pins, the Arduino reboots.

author: edavid
author_rank: Super Contributor
timestamp: May 29, 2016, 06:02:19 pm
content: Quote from: metrologist on May 29, 2016, 04:38:47 amYou should use 10X probes, not 1X.

author: Jeroen3
author_rank: Super Contributor
timestamp: May 29, 2016, 07:57:21 pm
content: Quote from: metrologist on May 29, 2016, 04:38:47 amThis is probably earth leakage. Is the rigol connected to a grounded socket?

author: metrologist
author_rank: Super Contributor
timestamp: May 29, 2016, 11:25:45 pm
content: I'm using the probes that came with the scope, switched to 10x. The scope is connected to a grounded mains, through an inexpensive 3 meter, 6 outlet surge protected supply. The PC in on another mains socket, however - still grounded through a PSU.

author: sleemanj
author_rank: Super Contributor
timestamp: May 29, 2016, 11:59:05 pm
content: Coincidentally, well sort of, I've spent a large proportion of the last week rewriting millis(), micros(), delay() and delayMicroseconds() code for my fork of ATTinyCore. Anyway, while my work is not directly related (or quite finished), I can offer two pieces of of advice.Instead of digitalWrite (which as others have pointed out, is slow and if you are trying to get us accuracy... good luck with that), toggle the pin directly....if (micros() - time >= 1000){time = micros();PIND |= (_BV(2) | _BV(3) | _BV(4) | _BV(5) ); // 2,3,4,5 are on PORTDPINB |= _BV(5); // 13 is PORTB.5}The other is that the millis timer interrupt handler (which also is used for timing micros() but not for timing delayMicroseconds()) takes a not-insignificant amount of time. In the ATTinyCore the interrupt handler is literally "ovrf++;" and that with it's interrupt handler overhead comes in at like 55-75 clocks, at 16 MHz that could be almost 5uS each time the interrupt handler runs. Now look at the millis interrupt handler in the mainline Arduino core....Code:{unsigned long m = timer0_millis;unsigned char f = timer0_fract;m += MILLIS_INC;f += FRACT_INC;if (f >= FRACT_MAX) {f -= FRACT_MAX;m += 1;}timer0_fract = f;timer0_millis = m;timer0_overflow_count++;}holy mother of god, I havn't disassembled it, but that looks freaking massive to me, I hate to think what it clocks in at.If you do not need to use millis(), or micros(), then you could freely disable the timer 0 overflow interrupt handler, and in the case of the standard arduino core instead of using delay (which requires millis()), you could craft a function to use delayMicroseconds().Incidentally, it's often overlooked that delayMicroseconds() does not disable the interrupt handler (it would be a bad idea to do so, even though it once did) and as a result it's horrifically inaccurate if you don't disable interrupts around it, especially that millis() (timer 0 overflow) interrupt.

author: metrologist
author_rank: Super Contributor
timestamp: May 30, 2016, 04:08:25 pm
content: Thanks. This does not work:Code:void setup(){DDRD = B11111110; // set PORTD (digital 7~0) to outputs}long pWidth = 1; //microseconds pulse widthlong pPeriod = 8; //microseconds pulse periodlong bPeriod = 100; //microseconds burst periodvoid loop() {PORTD = B11110000;delayMicroseconds(pWidth);PORTD = B00000000;delayMicroseconds(pPeriod);PORTD = B11110000;delayMicroseconds(pWidth);PORTD = B00000000;delayMicroseconds(pPeriod);PORTD = B11110000;delayMicroseconds(pWidth);PORTD = B00000000;delayMicroseconds(pPeriod);PORTD = B11110000;delayMicroseconds(pWidth);PORTD = B00000000;delayMicroseconds(pPeriod);PORTD = B11110000;delayMicroseconds(pWidth);PORTD = B00000000;delayMicroseconds(pPeriod);delayMicroseconds(bPeriod);}This does, but obviously no control:Code:void setup(){DDRD = B11111110; // set PORTD (digital 7~0) to outputs}void loop() {PORTD = B11110000;PORTD = B00000000;PORTD = B11110000;PORTD = B00000000;PORTD = B11110000;PORTD = B00000000;PORTD = B11110000;PORTD = B00000000;PORTD = B11110000;PORTD = B00000000;}

author: Aodhan145
author_rank: Frequent Contributor
timestamp: May 30, 2016, 04:39:14 pm
content: Quote from: metrologist on May 30, 2016, 04:08:25 pm

author: NivagSwerdna
author_rank: Super Contributor
timestamp: May 30, 2016, 04:51:44 pm
content: Firstly, with Arduino there is a lot of AVR goodness hidden from you... So for example the micros() function which is implemented here...https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring.cis hiding the fact that the TIM0_OVF_vect (i.e. Timer0 overflow interrupt) is being used for keeping track of time. This overflow occurs at regular intervals and is used to accumulate milliseconds however it is not continuous, it is quantised which will give you dither.There is also some other stuff happening in the background such as servicing the serial I/O which can lead you to further dither and finally there is a certain amount of latency introduced by going around the conditional loop, calling micros, testing and then executing depending on the condition.For more accurate timing you need to move nearer to the hardware...analogWrite(pinNumber, dutyCycle) with duty cycle between 0 (off) to 255 (on). The PWM square wave at various duty cycles can be used to simulate "analog" output (e.g., to control the brightness of LED or speed of motor). The frequency of PWM is 980Hz on pins 5 and 6; and 490Hz for other pins.analogWrite is a very easy way to generate 490Hz. (You should look a bit harder too to understand why you get 490Hz from some pins and 980Hz from others... the processor datasheet and the wiring source is your friend).You could also any hardware timer and an time related interrupt for accurate timing too.Incidentally, you will get a result dependent on the clock of the processor... for a crystal you might be within 20ppm but for some cheap chinese clones that use resonators you might find yourself up to 2000ppm out.Post your results.

author: lapm
author_rank: Frequent Contributor
timestamp: May 30, 2016, 06:56:05 pm
content: One thing that introduces timing error in digital write of arduino, is that it runs timing interupts on background even if you dont use any timing functions...There was recently interesting youtube video series about making arduino go in low power mode and how low one can make it go... In this series it was discoveret that inruptis runs on background even if you dont need it. Made for artist indeed...

author: macboy
author_rank: Super Contributor
timestamp: May 30, 2016, 07:00:03 pm
content: You should incrementtimeby 1000 rather than setting it to the present value of micros(). This will ensure that you will enter the loop once per 1000 micros (+/- some error). You also need to understand that micros() is approximate and on most platforms, it will not increment by 1 each 1 microsecond, rather it increments by 4 each 4 microseconds.digitalWrite() is sucky and slow as others pointed out, but at least it isconsistentlyslow, which means no timing jitter introduced by it.The jitter you see is introduced by the CPU needing to stop and do other things in the middle of your code. The "other things" more specifically, is servicing interrupts. Mainly, this means the Timer0 interrupt that is used for the millis/micros counters. There can be other interrupt sources too depending on what you are doing: serial communications, ADC, etc. You can disable interrupts completely before the code which creates the pulses, then enable them after. CallNoInterrupts()to disable andInterrupts()to enable. Try doing this inside the for loop. Try again outside the for loop but inside the if statement. If you keep interrupts disabled for too long you can end up with strange problems.

author: metrologist
author_rank: Super Contributor
timestamp: May 30, 2016, 10:03:00 pm
content: Quote from: Aodhan145 on May 30, 2016, 04:39:14 pmIt's actually very close. The delays add up to 145 us, and it should be slightly longer given time to execute each port switch.But the problem is that sometimes the bursts are further apart as shown, that's all that does not work. And I think I now do understand why, but not found an exact solution to what I was trying to do.

author: macboy
author_rank: Super Contributor
timestamp: May 31, 2016, 01:57:22 pm
content: Quote from: metrologist on May 30, 2016, 10:03:00 pmThis is not unexpected. As mentioned earlier, your CPU is doing some things other than executing your code, namely, servicing interrupts. So any time you call a delay function (delay() or delayMicroseconds() ), your code delays forapproximatelythe amount of time you ask for, but it could be more. You just can't do deterministic timing using in-line delays when you have interrupts running in the background. This isn't just an Arduino issue, although Arduino does an especially good job of hiding enough from you that you wouldn't know this is happening. On most microcontroller platforms, if you have interrupts running it because you were the one to turn them on, so it is less of a hidden issue.

author: metrologist
author_rank: Super Contributor
timestamp: May 31, 2016, 04:06:43 pm
content: Even after reading all that is posted above, I still was not expecting the loop to repeatbeforeall the delays. How can the main loop repeat in less than 145 µs, which is the addition of all the delays in my main loop?I started with the micros() function and not using delays at all, but even knowing that it has a resolution of 4 µs and trying multiples of 4 for my pulse periods and pulse burst times, there is still much randomness that I do not understand. Or, it may be more cyclical for some reason. It seems like every tick of the timer is not timed at all to the micro controller clock.This is from my OP:Code:if (micros() - time >= 1000){time = micros();... do something...I was expecting this condition to be true at regular intervals, not exactly every 1000 µs. I was actually expecting that one or two (16 MHz) clock cycles would be required to process the if statement, then a couple more to adjust the time variable. However, after someone mentioning that I should rather increment time by 1000 made sense, but did not make the execution consistent. So micros seems to return some value that is plus or minus 4 every time, or even plus or minus 2, on some kind of whim...I do not really care about the absolute accuracy of the events, just that they happen at regular intervals - that would allow me to calibrate or adjust timing as needed.I was trying to absorb manually setting up Timer2 with a prescaler of 1 (62.5 ns tick rate), and then try the same kind of approach I've been using with the micros function. I've exhausted my googling (google gave me a captcha) and ability to decode what's being said. I just keep finding the same 20 or so sites saying the same things.Anyway, it seems I should be able to test when Timer2 equals a value (if TCNT2 >= 1000) and then perform some action. It also seems that the timer should be able to be reset at some time, say like every 16000 counts to give an 1 ms range (CTC).https://arduino-info.wikispaces.com/Timers-Arduinoexplains some of this, but I'm not seeing how to actually set things differently than the examples.I also found a site with a 0.5 µs micros timer, but they wanted three bucks for the code. I'll keep looking and reading.Thanks.

author: sleemanj
author_rank: Super Contributor
timestamp: May 31, 2016, 08:13:32 pm
content: Look above at the code i posted which.is the arduino millis (and micros) timer interrupt. Note that as well as the fractional stuff it does increment a simple overflow count. You could simply roll your own timing off this count. The count will increase freq/prescale/256 per second. Prescale is set by Arduino core to be 8 or 64 from memory, depending on freq, I think it's 8 for 16MHz.