Skip to content

Commit 2ca67fc

Browse files
WestfWladyada
authored andcommitted
Fix delayMicroseconds() on SAMD51 (arduino#77)
* Fix pulseIn() on SAMD51 by writing it in plain C, so that the CM0+-specific pulse_asm won't need to be linked. The SAMD51 is fast enough that we can time pulses with micros() Tested with a bunch of pulse lengths from <1us to >1s * Implement a new delayMicroseconds() function for SAMD51 This version enables the "Debug Watchpoint and Trace" module (DWT) in startup.c and then uses the 32bit cycle counter that is part of DWT to count cycles indepenent of instruction timing. Tested for good accuracy with various values between 1 and 2000us.
1 parent f4b1ecf commit 2ca67fc

File tree

3 files changed

+39
-17
lines changed

3 files changed

+39
-17
lines changed

cores/arduino/delay.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,33 @@ unsigned long micros( void )
6161
// a runtime multiplication and shift, saving a few cycles
6262
}
6363

64+
#ifdef __SAMD51__
65+
/*
66+
* On SAMD51, use the (32bit) cycle count maintained by the DWT unit,
67+
* and count exact number of cycles elapsed, rather than guessing how
68+
* many cycles a loop takes, which is dangerous in the presence of
69+
* cache. The overhead of the call and internal code is "about" 20
70+
* cycles. (at 120MHz, that's about 1/6 us)
71+
*/
72+
void delayMicroseconds(unsigned int us)
73+
{
74+
uint32_t start, elapsed;
75+
uint32_t count;
76+
77+
if (us == 0)
78+
return;
79+
80+
count = us * (VARIANT_MCK / 1000000) - 20; // convert us to cycles.
81+
start = DWT->CYCCNT; //CYCCNT is 32bits, takes 37s or so to wrap.
82+
while (1) {
83+
elapsed = DWT->CYCCNT - start;
84+
if (elapsed >= count)
85+
return;
86+
}
87+
}
88+
#endif
89+
90+
6491
void delay( unsigned long ms )
6592
{
6693
if ( ms == 0 )

cores/arduino/delay.h

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,28 +61,16 @@ extern void delay( unsigned long dwMs ) ;
6161
*
6262
* \param dwUs the number of microseconds to pause (uint32_t)
6363
*/
64+
#if defined(__SAMD51__)
65+
extern void delayMicroseconds( unsigned int );
66+
#else
6467
static __inline__ void delayMicroseconds( unsigned int ) __attribute__((always_inline, unused)) ;
6568
static __inline__ void delayMicroseconds( unsigned int usec )
6669
{
6770
if ( usec == 0 )
6871
{
6972
return ;
7073
}
71-
72-
#if defined(__SAMD51__)
73-
uint32_t n = usec * (VARIANT_MCK / 1000000) / 12;
74-
75-
__asm__ __volatile__(
76-
"1: \n"
77-
" sub %0, #1 \n" // substract 1 from %0 (n)
78-
" cmp %0, #0 \n" // compare to 0
79-
" bne 1b \n" // if result is not 0 jump to 1
80-
: "+r" (n) // '%0' is n variable with RW constraints
81-
: // no input
82-
: // no clobber
83-
);
84-
85-
#else
8674
/*
8775
* The following loop:
8876
*
@@ -109,10 +97,10 @@ static __inline__ void delayMicroseconds( unsigned int usec )
10997
: // no input
11098
: // no clobber
11199
);
112-
#endif
113100
// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
114101
// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Volatile
115102
}
103+
#endif
116104

117105
#ifdef __cplusplus
118106
}

cores/arduino/startup.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,14 @@ void SystemInit( void )
252252
CMCC->CTRL.reg = 1;
253253
__enable_irq();
254254
#endif
255-
255+
256+
/*---------------------------------------------------------------------
257+
* Start up the "Debug Watchpoint and Trace" unit, so that we can use
258+
* it's 32bit cycle counter for timing.
259+
*/
260+
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
261+
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
262+
256263
//*************** END SAMD51 *************************//
257264

258265
#else

0 commit comments

Comments
 (0)