Skip to content

Commit 3930bc2

Browse files
committed
Fix #8: make delayMicroseconds() accurate
1 parent 0bfdc0e commit 3930bc2

File tree

1 file changed

+53
-7
lines changed

1 file changed

+53
-7
lines changed

cores/arduino/wiring_time.h

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Copyright (c) 2011 Arduino. All right reserved.
33
Copyright (c) 2013 by Paul Stoffregen <paul@pjrc.com> (delayMicroseconds)
4+
Copyright (c) 2018 MCCI Corporation (correct delayMicroseconds)
45
56
This library is free software; you can redistribute it and/or
67
modify it under the terms of the GNU Lesser General Public
@@ -58,19 +59,64 @@ extern void delay( uint32_t dwMs ) ;
5859
* \brief Pauses the program for the amount of time (in microseconds) specified as parameter.
5960
*
6061
* \param dwUs the number of microseconds to pause (uint32_t)
62+
*
63+
* \note This implementation does not call GetMicroseconds(), thereby avoiding
64+
* time- and power-wasting multiplies at each step of the delay. It also avoids
65+
* unnecesary register reads. Instead it does one multiply/divide to convert
66+
* us to ticks, based on the current clock setting, and waits until that many
67+
* ticks are observed.
68+
* See https://github.com/mcci-catena/Arduino_Core_STM32/issues/8 for more.
6169
*/
6270
static inline void delayMicroseconds(uint32_t) __attribute__((always_inline, unused));
6371
static inline void delayMicroseconds(uint32_t usec)
6472
{
65-
uint32_t end;
73+
// if the delay is huge, just delay by milliseconds. Assume
74+
// that the compiler will optimize this out for small constant
75+
// delays, and that it will optimize away the other branch.
76+
// Chose the limit so that the multiply for `ticks` won't
77+
// overflow.
78+
if (usec >= 10*1000)
79+
{
80+
delay((usec + 1000 - 1) / 1000);
81+
return;
82+
}
83+
84+
// fetch initial time as a tick count.
85+
const uint32_t now = SysTick->VAL;
86+
87+
// fetch max count, which is tics/millisecond.
88+
const uint32_t tickmax = SysTick->LOAD;
89+
90+
// convert usec to an equivalent numer of ticks
91+
// there are tickmax/1000 ticks per microsecond.
92+
// so the following is usec * (tickmax/1000), rounded
93+
// and done in fixed point without loss of precision.
94+
const uint32_t ticks = (usec * tickmax + 500) / 1000;
95+
96+
// val and lastval track tick counts.
97+
uint32_t val, lastVal;
6698

67-
if (usec == 0)
68-
return;
99+
// dt tracks to the elapsed number of ticks.
100+
uint32_t dt;
69101

70-
end = GetCurrentMicro() + usec;
71-
if (end < usec)
72-
while (end < GetCurrentMicro());
73-
while (end > GetCurrentMicro());
102+
// loop until we've observed at least `ticks`
103+
// clicks of SysTick. NB: SysTick is a down-counter.
104+
for (dt = 0, lastVal = now;
105+
dt < ticks;
106+
lastVal = val)
107+
{
108+
val = SysTick->VAL;
109+
if (val >= lastVal)
110+
{
111+
// count wrapped from 0 to tickmax,
112+
// so add the wrap-around count.
113+
dt += tickmax + lastVal - val;
114+
}
115+
else
116+
{
117+
dt += lastVal - val;
118+
}
119+
}
74120
}
75121

76122
#ifdef __cplusplus

0 commit comments

Comments
 (0)