Pages: [1]   Go Down
Author Topic: investigating delayMicroseconds()  (Read 1653 times)
0 Members and 1 Guest are viewing this topic.
Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 212
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

how to chase your own tail : smiley

Last year I investigated delayMicroseconds() for small values. That lead to a patch for the function
See - http://forum.arduino.cc/index.php/topic,132983.0.html -


Today I wrote a small program how delayMicroseconds() behaves in practice for values from 1-2000. Above 2000 micros, it is advised to use
Code:
delay(whole millis)
delay(remaindermicros);

The reason why I investigated this is because delayMicoseconds() is often used in handshakes with sensors (e.g. DHT22).
Errors in delayMicoseconds() might add up during a handshake which can cause hard to debug failures.

I started writing a simple test sketch in which I use micros() to test the timing of delayMicroseconds().
True, this is not a hardware timer accuracy but it gives a first indication.

As micros() returns always a multiple of four I decided to call delayMicroseconds four times which makes the math a bit easier.

Code: (test sketch)
//
//    FILE: test_delay_microseconds.ino
//  AUTHOR: Rob Tillaart
// VERSION: 2013-08-31
// PURPOSE: test accuracy of delayMicroseconds()
//     URL:
//
// Released to the public domain
//
void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.println();

  for (int i=1; i<2000; i+=10)
  {
    uint32_t start = micros();
    delayMicroseconds(i);
    delayMicroseconds(i);
    delayMicroseconds(i);
    delayMicroseconds(i);
    uint32_t stop = micros();
    int diff = (stop - start)/4;
    int delta = diff - i;
    Serial.print(i);
    Serial.print("\t");
    Serial.print(diff);
    Serial.print("\t");
    Serial.print(delta);
    Serial.print("\t");
    Serial.println(100.0*delta/i);
  }
}

void loop(){}

The output  (the comma is decimal separator)
Code:
1 2 1 100
11 12 1 9,09
21 23 2 9,52
31 33 2 6,45
41 44 3 7,32
51 55 4 7,84
61 67 6 9,84
71 77 6 8,45
81 86 5 6,17
91 97 6 6,59
101 109 8 7,92
111 119 8 7,21
121 129 8 6,61
131 140 9 6,87
141 152 11 7,8
151 160 9 5,96
161 173 12 7,45
171 182 11 6,43
181 195 14 7,73
191 204 13 6,81
201 216 15 7,46
211 226 15 7,11
221 238 17 7,69
231 247 16 6,93
241 258 17 7,05
251 269 18 7,17
261 280 19 7,28
271 290 19 7,01
281 302 21 7,47
291 312 21 7,22
301 324 23 7,64
311 334 23 7,4
321 343 22 6,85
331 357 26 7,85
341 368 27 7,92
351 378 27 7,69
361 387 26 7,2
371 397 26 7,01
381 408 27 7,09
391 413 22 5,63
401 418 17 4,24
411 428 17 4,14
421 438 17 4,04
431 449 18 4,18
441 460 19 4,31
451 469 18 3,99
461 480 19 4,12
471 490 19 4,03
481 500 19 3,95
491 510 19 3,87
501 519 18 3,59
511 529 18 3,52
521 540 19 3,65
531 550 19 3,58
541 559 18 3,33
551 569 18 3,27
561 579 18 3,21
571 591 20 3,5
581 600 19 3,27
591 610 19 3,21
601 620 19 3,16
611 632 21 3,44
621 641 20 3,22
631 651 20 3,17
641 661 20 3,12
651 671 20 3,07
661 682 21 3,18
671 691 20 2,98
681 702 21 3,08
691 711 20 2,89
701 721 20 2,85
711 732 21 2,95
721 741 20 2,77
731 751 20 2,74
741 761 20 2,7
751 771 20 2,66
761 781 20 2,63
771 791 20 2,59
781 802 21 2,69
791 811 20 2,53
801 822 21 2,62
811 831 20 2,47
821 841 20 2,44
831 853 22 2,65
841 861 20 2,38
851 871 20 2,35
861 882 21 2,44
871 892 21 2,41
881 903 22 2,5
891 913 22 2,47
901 923 22 2,44
911 933 22 2,41
921 942 21 2,28
931 953 22 2,36
941 962 21 2,23
951 973 22 2,31
961 981 20 2,08
971 993 22 2,27
981 1003 22 2,24
991 1014 23 2,32
1001 1024 23 2,3
1011 1035 24 2,37
1021 1045 24 2,35
1031 1055 24 2,33
1041 1065 24 2,31
1051 1076 25 2,38
1061 1086 25 2,36
1071 1097 26 2,43
1081 1105 24 2,22
1091 1115 24 2,2
1101 1126 25 2,27
1111 1137 26 2,34
1121 1147 26 2,32
1131 1156 25 2,21
1141 1167 26 2,28
1151 1177 26 2,26
1161 1187 26 2,24
1171 1197 26 2,22
1181 1207 26 2,2
1191 1217 26 2,18
1201 1227 26 2,16
1211 1237 26 2,15
1221 1247 26 2,13
1231 1257 26 2,11
1241 1265 24 1,93
1251 1277 26 2,08
1261 1287 26 2,06
1271 1297 26 2,05
1281 1307 26 2,03
1291 1317 26 2,01
1301 1327 26 2
1311 1337 26 1,98
1321 1349 28 2,12
1331 1356 25 1,88
1341 1367 26 1,94
1351 1376 25 1,85
1361 1388 27 1,98
1371 1399 28 2,04
1381 1408 27 1,96
1391 1419 28 2,01
1401 1428 27 1,93
1411 1439 28 1,98
1421 1448 27 1,9
1431 1459 28 1,96
1441 1469 28 1,94
1451 1479 28 1,93
1461 1488 27 1,85
1471 1498 27 1,84
1481 1509 28 1,89
1491 1519 28 1,88
1501 1528 27 1,8
1511 1539 28 1,85
1521 1548 27 1,78
1531 1558 27 1,76
1541 1569 28 1,82
1551 1579 28 1,81
1561 1590 29 1,86
1571 1598 27 1,72
1581 1609 28 1,77
1591 1620 29 1,82
1601 1630 29 1,81
1611 1638 27 1,68
1621 1649 28 1,73
1631 1659 28 1,72
1641 1669 28 1,71
1651 1680 29 1,76
1661 1690 29 1,75
1671 1700 29 1,74
1681 1710 29 1,73
1691 1718 27 1,6
1701 1728 27 1,59
1711 1740 29 1,69
1721 1750 29 1,69
1731 1760 29 1,68
1741 1768 27 1,55
1751 1780 29 1,66
1761 1790 29 1,65
1771 1800 29 1,64
1781 1810 29 1,63
1791 1820 29 1,62
1801 1830 29 1,61
1811 1840 29 1,6
1821 1850 29 1,59
1831 1860 29 1,58
1841 1870 29 1,58
1851 1881 30 1,62
1861 1890 29 1,56
1871 1900 29 1,55
1881 1910 29 1,54
1891 1920 29 1,53
1901 1932 31 1,63
1911 1942 31 1,62
1921 1952 31 1,61
1931 1962 31 1,61
1941 1971 30 1,55
1951 1980 29 1,49
1961 1992 31 1,58
1971 2001 30 1,52
1981 2011 30 1,51
1991 2022 31 1,56
What we see is there is a slowly increasing absolute error (3rd column) which is a decreasing relative error (4rd column).

When putting it in a graph one can estimate the error by 2 relative simple linear formulas.
0..250 => error = 0.0679x;
250..2000 => error = 13 + 0.0097x;

to be continued...

update: I've been chasing my own tail as the printing of the numbers in the test program is done in parallel with the "test", so this is affecting the measurement.
I could see this by removing the print statements and checked the cumulative error (abs & rel) for the range [0..2000].

Lesson learned:
Still the above does mean that if a handshake is implemented with delayMicroseconds() and there is a (busy) interrupt in the background, either due to printing or some other sensor increasing a counter every 10 usec, it might be worth to check the timing of the handshake.m
For larger micro delays it might be wiser to use a tight loop as its max abs error is 4 uS (UNO16MHz) and it is more interrupt proof than delayMicroseconds.
Code: (example)
uint32_t udelay = 300;
uint32_t start = micros();
while (micros() - start >= udelay);
[/i]
« Last Edit: August 31, 2013, 07:31:39 pm by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

U.K
Offline Offline
Jr. Member
**
Karma: 1
Posts: 72
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

the time delta from the start / stop checking of micros(); look okay,

here's the result of my modified delaymicros()

Code:

1 2 1 100.00
11 12 1 9.09
21 21 0 0.000000e+0
31 32 1 3.23
41 42 1 2.44
51 51 0 0.000000e+0
61 62 1 1.64
71 72 1 1.41
81 84 3 3.70
91 92 1 1.10
101 103 2 1.98
111 112 1 0.90
121 124 3 2.48
131 133 2 1.53
141 142 1 0.71
151 152 1 0.66
161 163 2 1.24
171 173 2 1.17
181 183 2 1.10
191 193 2 1.05
201 203 2 1.00
211 213 2 0.95
221 223 2 0.90
231 233 2 0.87
241 243 2 0.83
251 253 2 0.80
261 264 3 1.15
271 273 2 0.74
281 284 3 1.07
291 293 2 0.69
301 304 3 1.00
311 313 2 0.64
321 325 4 1.25
331 334 3 0.91
341 345 4 1.17
351 353 2 0.57
361 365 4 1.11
371 373 2 0.54
381 383 2 0.52
391 395 4 1.02
401 405 4 1.00
411 413 2 0.49
421 423 2 0.48
431 435 4 0.93
441 445 4 0.91
451 455 4 0.89
461 465 4 0.87
471 475 4 0.85
481 484 3 0.62
491 494 3 0.61
501 505 4 0.80
511 514 3 0.59
521 524 3 0.58
531 535 4 0.75
541 547 6 1.11
551 555 4 0.73
561 565 4 0.71
571 575 4 0.70
581 585 4 0.69
591 596 5 0.85
601 604 3 0.50
611 615 4 0.65
621 626 5 0.81
631 634 3 0.48
641 646 5 0.78
651 656 5 0.77
661 664 3 0.45
671 675 4 0.60
681 685 4 0.59
691 696 5 0.72
701 707 6 0.86
711 716 5 0.70
721 726 5 0.69
731 736 5 0.68
741 747 6 0.81
751 756 5 0.67
761 767 6 0.79
771 776 5 0.65
781 786 5 0.64
791 796 5 0.63
801 806 5 0.62
811 816 5 0.62
821 827 6 0.73
831 837 6 0.72
841 846 5 0.59
851 856 5 0.59
861 867 6 0.70
871 876 5 0.57
881 888 7 0.79
891 898 7 0.79
901 906 5 0.55
911 917 6 0.66
921 927 6 0.65
931 937 6 0.64
941 947 6 0.64
951 957 6 0.63
961 967 6 0.62
971 977 6 0.62
981 988 7 0.71
991 998 7 0.71
1001 1008 7 0.70
1011 1017 6 0.59
1021 1028 7 0.69
1031 1037 6 0.58
1041 1048 7 0.67
1051 1057 6 0.57
1061 1068 7 0.66
1071 1077 6 0.56
1081 1089 8 0.74
1091 1098 7 0.64
1101 1108 7 0.64
1111 1119 8 0.72
1121 1128 7 0.62
1131 1138 7 0.62
1141 1148 7 0.61
1151 1159 8 0.70
1161 1169 8 0.69
1171 1179 8 0.68
1181 1189 8 0.68
1191 1199 8 0.67
1201 1209 8 0.67
1211 1220 9 0.74
1221 1228 7 0.57
1231 1239 8 0.65
1241 1249 8 0.64
1251 1259 8 0.64
1261 1269 8 0.63
1271 1279 8 0.63
1281 1289 8 0.62
1291 1299 8 0.62
1301 1309 8 0.61
1311 1321 10 0.76
1321 1329 8 0.61
1331 1339 8 0.60
1341 1349 8 0.60
1351 1359 8 0.59
1361 1370 9 0.66
1371 1381 10 0.73
1381 1389 8 0.58
1391 1399 8 0.58
1401 1409 8 0.57
1411 1419 8 0.57
1421 1430 9 0.63
1431 1441 10 0.70
1441 1451 10 0.69
1451 1459 8 0.55
1461 1469 8 0.55
1471 1480 9 0.61
1481 1491 10 0.68
1491 1500 9 0.60
1501 1511 10 0.67
1511 1521 10 0.66
1521 1530 9 0.59
1531 1541 10 0.65
1541 1551 10 0.65
1551 1562 11 0.71
1561 1571 10 0.64
1571 1580 9 0.57
1581 1591 10 0.63
1591 1601 10 0.63
1601 1612 11 0.69
1611 1622 11 0.68
1621 1631 10 0.62
1631 1641 10 0.61
1641 1650 9 0.55
1651 1662 11 0.67
1661 1672 11 0.66
1671 1682 11 0.66
1681 1692 11 0.65
1691 1702 11 0.65
1701 1710 9 0.53
1711 1721 10 0.58
1721 1730 9 0.52
1731 1742 11 0.64
1741 1752 11 0.63
1751 1762 11 0.63
1761 1773 12 0.68
1771 1782 11 0.62
1781 1792 11 0.62
1791 1802 11 0.61
1801 1813 12 0.67
1811 1822 11 0.61
1821 1833 12 0.66
1831 1842 11 0.60
1841 1852 11 0.60
1851 1864 13 0.70
1861 1872 11 0.59
1871 1882 11 0.59
1881 1892 11 0.58
1891 1902 11 0.58
1901 1914 13 0.68
1911 1923 12 0.63
1921 1934 13 0.68
1931 1944 13 0.67
1941 1953 12 0.62
1951 1963 12 0.62
1961 1972 11 0.56
1971 1982 11 0.56
1981 1993 12 0.61
1991 2004 13 0.65
Logged

--
 Darryl

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 212
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Darryl,

do you have a link to your modified delaymicros()?

I still notice that although the relative error decreases the absolute error is slightly increasing - I'm very aware that it is very hard to minimize this .

Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

U.K
Offline Offline
Jr. Member
**
Karma: 1
Posts: 72
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

here is what I use, based on some mods as suggested by a certain you a while ago ! of course changed your code a bit, and lengthened the delay loop, as it seemed a good idear at the time, but I cant remember what it is now !

Code:
/* Delay for the given number of microseconds ( 1% accurate of clock rate >20 )
   max we can pass on 8MHz -> 65535, on 16MHz its 32768 and for 20MHz its
   only 13107 due to having to times 5, then divide by 2 :-(
*/
void delayMicroseconds(uint16_t us)
{
// playing around with altering _us_ means we top out early on the max value we can pass.
#if F_CPU >= 20000000L
    // for a zero  or one-microsecond delay, simply wait 2 cycle and return. The overhead
    // of the function call yields a delay of approx 0.8 microsecond.
    __asm__ volatile (
        "nop" "\n\t"
        "nop");
    if (us < 2) return;

    // the busy loop takes a 2/5 of a microsecond (8 cycles)
    // per iteration, so execute it 2.5 times for each microsecond
    us = (( us - 1 ) * 5 ) >>1;
#elif F_CPU >= 16000000L
    // for a zero or one-microsecond delay, simply return.  the overhead
    // of the function call yields a delay of approximately 1 us.
    if (us < 2) return;

    // the busy loop takes a half of a microsecond (8 cycles)
    // per iteration, so execute it twice for each microsecond of
    // delay requested. offset by time for above check
    us = ( us - 1 ) <<1;
#else
    // for a zero to two microsecond delay, simply return.  the overhead of
    // the function calls takes that. then each loop per microsecond :-)
    if (us < 3) return;
us = us - 2;
#endif

    // busy wait ( 8 cycles = 1/2 microsecond on 16MHz )
    __asm__ volatile (
        "1: sbiw %0,1" "\n\t" // 2 cycles
        "nop" "\n\t" // 1 cycle
        "nop" "\n\t" // 1 cycle
        "nop" "\n\t" // 1 cycle
        "nop" "\n\t" // 1 cycle
        "brne 1b" "\n\t" // 2 cycles
: "=w" (us)
: "0" (us)
    );
}

also noticed a error is printing 0.00 in my version of print.cpp. which I've now corrected !
Logged

--
 Darryl

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 212
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Recognition, I remember I lost those patches when switching to 1.0.4 have to check them tonight !
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Pages: [1]   Go Up
Jump to: