From 0c9f0d50a52f72e5da5eb6b0a97aa7e743597248 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 2 Mar 2015 13:22:07 +0100 Subject: [PATCH 1/8] Revert "Temporary fix for pulseIn() regression." This reverts commit 8ddc5198f6ef8b2816a61b7b0fcce99f927d11a0. To be substituted by ASM generated code --- .../arduino/avr/cores/arduino/wiring_pulse.c | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/hardware/arduino/avr/cores/arduino/wiring_pulse.c b/hardware/arduino/avr/cores/arduino/wiring_pulse.c index 830c45408..0d968865d 100644 --- a/hardware/arduino/avr/cores/arduino/wiring_pulse.c +++ b/hardware/arduino/avr/cores/arduino/wiring_pulse.c @@ -61,25 +61,9 @@ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) width++; } - // convert the reading to microseconds. There will be some error introduced by + // convert the reading to microseconds. The loop has been determined + // to be 20 clock cycles long and have about 16 clocks between the edge + // and the start of the loop. There will be some error introduced by // the interrupt handlers. - - // Conversion constants are compiler-dependent, different compiler versions - // have different levels of optimization. -#if __GNUC__==4 && __GNUC_MINOR__==3 && __GNUC_PATCHLEVEL__==2 - // avr-gcc 4.3.2 - return clockCyclesToMicroseconds(width * 21 + 16); -#elif __GNUC__==4 && __GNUC_MINOR__==8 && __GNUC_PATCHLEVEL__==1 - // avr-gcc 4.8.1 - return clockCyclesToMicroseconds(width * 24 + 16); -#elif __GNUC__<=4 && __GNUC_MINOR__<=3 - // avr-gcc <=4.3.x - #warning "pulseIn() results may not be accurate" - return clockCyclesToMicroseconds(width * 21 + 16); -#else - // avr-gcc >4.3.x - #warning "pulseIn() results may not be accurate" - return clockCyclesToMicroseconds(width * 24 + 16); -#endif - + return clockCyclesToMicroseconds(width * 21 + 16); } From b37b0d1b6d246f5663313adfcd305a6369aa4027 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 2 Mar 2015 13:31:16 +0100 Subject: [PATCH 2/8] pulseIn: add ASM implementation this assembly code was generated by avr-gcc 4.8.3 --- .../arduino/avr/cores/arduino/wiring_pulse.S | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 hardware/arduino/avr/cores/arduino/wiring_pulse.S diff --git a/hardware/arduino/avr/cores/arduino/wiring_pulse.S b/hardware/arduino/avr/cores/arduino/wiring_pulse.S new file mode 100644 index 000000000..ffd70f575 --- /dev/null +++ b/hardware/arduino/avr/cores/arduino/wiring_pulse.S @@ -0,0 +1,176 @@ +/* + wiring_pulse.s - pulseInASM() function in different flavours + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2014 Martino Facchin + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +# The following routine was generated by avr-gcc 4.8.3 with the following parameters +# -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2 +# on the original C function +# +# unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops) +# { +# unsigned long width = 0; +# // wait for any previous pulse to end +# while ((*port & bit) == stateMask) +# if (--maxloops == 0) +# return 0; +# +# // wait for the pulse to start +# while ((*port & bit) != stateMask) +# if (--maxloops == 0) +# return 0; +# +# // wait for the pulse to stop +# while ((*port & bit) == stateMask) { +# if (++width == maxloops) +# return 0; +# } +# return width; +# } +# +# some compiler outputs were removed but the rest of the code is untouched + +#include + +.section .text + +.global countPulseASM + +countPulseASM: + +.LM0: +.LFBB1: + push r12 ; ; 130 pushqi1/1 [length = 1] + push r13 ; ; 131 pushqi1/1 [length = 1] + push r14 ; ; 132 pushqi1/1 [length = 1] + push r15 ; ; 133 pushqi1/1 [length = 1] + push r16 ; ; 134 pushqi1/1 [length = 1] + push r17 ; ; 135 pushqi1/1 [length = 1] +/* prologue: function */ +/* frame size = 0 */ +/* stack size = 6 */ +.L__stack_usage = 6 + mov r30,r24 ; port, port ; 2 *movhi/1 [length = 2] + mov r31,r25 ; port, port +/* unsigned long width = 0; +*** // wait for any previous pulse to end +*** while ((*port & bit) == stateMask) +*/ +.LM1: + rjmp .L2 ; ; 181 jump [length = 1] +.L4: +/* if (--maxloops == 0) */ +.LM2: + subi r16,1 ; maxloops, ; 17 addsi3/2 [length = 4] + sbc r17, r1 ; maxloops + sbc r18, r1 ; maxloops + sbc r19, r1 ; maxloops + breq .L13 ; , ; 19 branch [length = 1] +.L2: +/* if (--maxloops == 0) */ +.LM3: + ld r25,Z ; D.1554, *port_7(D) ; 22 movqi_insn/4 [length = 1] + and r25,r22 ; D.1554, bit ; 24 andqi3/1 [length = 1] + cp r25,r20 ; D.1554, stateMask ; 25 *cmpqi/2 [length = 1] + breq .L4 ; , ; 26 branch [length = 1] + rjmp .L6 ; ; 184 jump [length = 1] +.L7: +/* return 0; +*** +*** // wait for the pulse to start +*** while ((*port & bit) != stateMask) +*** if (--maxloops == 0) +*/ +.LM4: + subi r16,1 ; maxloops, ; 31 addsi3/2 [length = 4] + sbc r17, r1 ; maxloops + sbc r18, r1 ; maxloops + sbc r19, r1 ; maxloops + breq .L13 ; , ; 33 branch [length = 1] +.L6: +/* if (--maxloops == 0) */ +.LM5: + ld r25,Z ; D.1554, *port_7(D) ; 41 movqi_insn/4 [length = 1] + and r25,r22 ; D.1554, bit ; 43 andqi3/1 [length = 1] + cpse r25,r20 ; D.1554, stateMask ; 44 enable_interrupt-3 [length = 1] + rjmp .L7 ; + mov r12, r1 ; width ; 7 *movsi/2 [length = 4] + mov r13, r1 ; width + mov r14, r1 ; width + mov r15, r1 ; width + rjmp .L9 ; ; 186 jump [length = 1] +.L10: +/* return 0; +*** +*** // wait for the pulse to stop +*** while ((*port & bit) == stateMask) { +*** if (++width == maxloops) +*/ +.LM6: + ldi r24,-1 ; , ; 50 addsi3/3 [length = 5] + sub r12,r24 ; width, + sbc r13,r24 ; width, + sbc r14,r24 ; width, + sbc r15,r24 ; width, + cp r16,r12 ; maxloops, width ; 51 *cmpsi/2 [length = 4] + cpc r17,r13 ; maxloops, width + cpc r18,r14 ; maxloops, width + cpc r19,r15 ; maxloops, width + breq .L13 ; , ; 52 branch [length = 1] +.L9: +/* if (++width == maxloops) */ +.LM7: + ld r24,Z ; D.1554, *port_7(D) ; 60 movqi_insn/4 [length = 1] + and r24,r22 ; D.1554, bit ; 62 andqi3/1 [length = 1] + cp r24,r20 ; D.1554, stateMask ; 63 *cmpqi/2 [length = 1] + breq .L10 ; , ; 64 branch [length = 1] +/* return 0; +*** } +*** return width; +*/ +.LM8: + mov r22,r12 ; D.1553, width ; 108 movqi_insn/1 [length = 1] + mov r23,r13 ; D.1553, width ; 109 movqi_insn/1 [length = 1] + mov r24,r14 ; D.1553, width ; 110 movqi_insn/1 [length = 1] + mov r25,r15 ; D.1553, width ; 111 movqi_insn/1 [length = 1] +/* epilogue start */ +.LM9: + pop r17 ; ; 171 popqi [length = 1] + pop r16 ; ; 172 popqi [length = 1] + pop r15 ; ; 173 popqi [length = 1] + pop r14 ; ; 174 popqi [length = 1] + pop r13 ; ; 175 popqi [length = 1] + pop r12 ; ; 176 popqi [length = 1] + ret ; 177 return_from_epilogue [length = 1] +.L13: +.LM10: + ldi r22,0 ; D.1553 ; 120 movqi_insn/1 [length = 1] + ldi r23,0 ; D.1553 ; 121 movqi_insn/1 [length = 1] + ldi r24,0 ; D.1553 ; 122 movqi_insn/1 [length = 1] + ldi r25,0 ; D.1553 ; 123 movqi_insn/1 [length = 1] +/* epilogue start */ +.LM11: + pop r17 ; ; 138 popqi [length = 1] + pop r16 ; ; 139 popqi [length = 1] + pop r15 ; ; 140 popqi [length = 1] + pop r14 ; ; 141 popqi [length = 1] + pop r13 ; ; 142 popqi [length = 1] + pop r12 ; ; 143 popqi [length = 1] + ret ; 144 return_from_epilogue [length = 1] From 93f74f84ce122724ce0f2e7a76dafca452ad3d47 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 2 Mar 2015 13:33:50 +0100 Subject: [PATCH 3/8] pulseIn: modify function to use ASM implementation --- .../avr/cores/arduino/wiring_private.h | 2 + .../arduino/avr/cores/arduino/wiring_pulse.c | 41 ++++++------------- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/hardware/arduino/avr/cores/arduino/wiring_private.h b/hardware/arduino/avr/cores/arduino/wiring_private.h index 5dc7d4bed..3bd2900e0 100644 --- a/hardware/arduino/avr/cores/arduino/wiring_private.h +++ b/hardware/arduino/avr/cores/arduino/wiring_private.h @@ -43,6 +43,8 @@ extern "C"{ #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif +uint32_t countPulseASM(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops); + #define EXTERNAL_INT_0 0 #define EXTERNAL_INT_1 1 #define EXTERNAL_INT_2 2 diff --git a/hardware/arduino/avr/cores/arduino/wiring_pulse.c b/hardware/arduino/avr/cores/arduino/wiring_pulse.c index 0d968865d..2ac698881 100644 --- a/hardware/arduino/avr/cores/arduino/wiring_pulse.c +++ b/hardware/arduino/avr/cores/arduino/wiring_pulse.c @@ -28,7 +28,10 @@ /* Measures the length (in microseconds) of a pulse on the pin; state is HIGH * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds * to 3 minutes in length, but must be called at least a few dozen microseconds - * before the start of the pulse. */ + * before the start of the pulse. + * + * This function performs better with short pulses in noInterrupt() context + */ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) { // cache the port and bit of the pin in order to speed up the @@ -38,32 +41,12 @@ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) uint8_t port = digitalPinToPort(pin); uint8_t stateMask = (state ? bit : 0); unsigned long width = 0; // keep initialization out of time critical area - - // convert the timeout from microseconds to a number of times through - // the initial loop; it takes 16 clock cycles per iteration. - unsigned long numloops = 0; - unsigned long maxloops = microsecondsToClockCycles(timeout) / 16; - - // wait for any previous pulse to end - while ((*portInputRegister(port) & bit) == stateMask) - if (numloops++ == maxloops) - return 0; - - // wait for the pulse to start - while ((*portInputRegister(port) & bit) != stateMask) - if (numloops++ == maxloops) - return 0; - - // wait for the pulse to stop - while ((*portInputRegister(port) & bit) == stateMask) { - if (numloops++ == maxloops) - return 0; - width++; - } - // convert the reading to microseconds. The loop has been determined - // to be 20 clock cycles long and have about 16 clocks between the edge - // and the start of the loop. There will be some error introduced by - // the interrupt handlers. - return clockCyclesToMicroseconds(width * 21 + 16); -} + // convert the timeout from microseconds to a number of times through + // the initial loop; it takes approximately 16 clock cycles per iteration + unsigned long numloops = 0; + unsigned long maxloops = microsecondsToClockCycles(timeout)/16; + + width = countPulseASM(portInputRegister(port), bit, stateMask, maxloops); + return clockCyclesToMicroseconds(width * 16 + 16); +} \ No newline at end of file From d4a80be045f488b35093f329ed853ae8af0ff637 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 2 Mar 2015 13:35:30 +0100 Subject: [PATCH 4/8] pulseIn: add alternative implementation based on micros() pulseInLong is suitable for long pulses in interrupt context --- hardware/arduino/avr/cores/arduino/Arduino.h | 2 + .../arduino/avr/cores/arduino/wiring_pulse.c | 44 ++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/hardware/arduino/avr/cores/arduino/Arduino.h b/hardware/arduino/avr/cores/arduino/Arduino.h index 07bccd8fa..f1da68da7 100644 --- a/hardware/arduino/avr/cores/arduino/Arduino.h +++ b/hardware/arduino/avr/cores/arduino/Arduino.h @@ -134,6 +134,7 @@ unsigned long micros(void); void delay(unsigned long); void delayMicroseconds(unsigned int us); unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout); +unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout); void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); @@ -232,6 +233,7 @@ uint16_t makeWord(byte h, byte l); #define word(...) makeWord(__VA_ARGS__) unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); +unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); void noTone(uint8_t _pin); diff --git a/hardware/arduino/avr/cores/arduino/wiring_pulse.c b/hardware/arduino/avr/cores/arduino/wiring_pulse.c index 2ac698881..49fa38dc9 100644 --- a/hardware/arduino/avr/cores/arduino/wiring_pulse.c +++ b/hardware/arduino/avr/cores/arduino/wiring_pulse.c @@ -49,4 +49,46 @@ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) width = countPulseASM(portInputRegister(port), bit, stateMask, maxloops); return clockCyclesToMicroseconds(width * 16 + 16); -} \ No newline at end of file +} + +/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH + * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds + * to 3 minutes in length, but must be called at least a few dozen microseconds + * before the start of the pulse. + * + * ATTENTION: + * this function relies on micros() so cannot be used in noInterrupt() context + */ +unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout) +{ + // cache the port and bit of the pin in order to speed up the + // pulse width measuring loop and achieve finer resolution. calling + // digitalRead() instead yields much coarser resolution. + uint8_t bit = digitalPinToBitMask(pin); + uint8_t port = digitalPinToPort(pin); + uint8_t stateMask = (state ? bit : 0); + unsigned long width = 0; // keep initialization out of time critical area + + // convert the timeout from microseconds to a number of times through + // the initial loop; it takes 16 clock cycles per iteration. + unsigned long numloops = 0; + unsigned long maxloops = microsecondsToClockCycles(timeout); + + // wait for any previous pulse to end + while ((*portInputRegister(port) & bit) == stateMask) + if (numloops++ == maxloops) + return 0; + + // wait for the pulse to start + while ((*portInputRegister(port) & bit) != stateMask) + if (numloops++ == maxloops) + return 0; + + unsigned long start = micros(); + // wait for the pulse to stop + while ((*portInputRegister(port) & bit) == stateMask) { + if (numloops++ == maxloops) + return 0; + } + return micros() - start; +} From 689c654e8d12f5843e8c466a84ac003088443ee8 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 2 Mar 2015 13:46:22 +0100 Subject: [PATCH 5/8] pulseInASM: rework comment style --- .../arduino/avr/cores/arduino/wiring_pulse.S | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/hardware/arduino/avr/cores/arduino/wiring_pulse.S b/hardware/arduino/avr/cores/arduino/wiring_pulse.S index ffd70f575..1dd22e625 100644 --- a/hardware/arduino/avr/cores/arduino/wiring_pulse.S +++ b/hardware/arduino/avr/cores/arduino/wiring_pulse.S @@ -20,32 +20,34 @@ Boston, MA 02111-1307 USA */ -# The following routine was generated by avr-gcc 4.8.3 with the following parameters -# -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2 -# on the original C function -# -# unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops) -# { -# unsigned long width = 0; -# // wait for any previous pulse to end -# while ((*port & bit) == stateMask) -# if (--maxloops == 0) -# return 0; -# -# // wait for the pulse to start -# while ((*port & bit) != stateMask) -# if (--maxloops == 0) -# return 0; -# -# // wait for the pulse to stop -# while ((*port & bit) == stateMask) { -# if (++width == maxloops) -# return 0; -# } -# return width; -# } -# -# some compiler outputs were removed but the rest of the code is untouched +/* + * The following routine was generated by avr-gcc 4.8.3 with the following parameters + * -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2 + * on the original C function + * + * unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops) + * { + * unsigned long width = 0; + * // wait for any previous pulse to end + * while ((*port & bit) == stateMask) + * if (--maxloops == 0) + * return 0; + * + * // wait for the pulse to start + * while ((*port & bit) != stateMask) + * if (--maxloops == 0) + * return 0; + * + * // wait for the pulse to stop + * while ((*port & bit) == stateMask) { + * if (++width == maxloops) + * return 0; + * } + * return width; + * } + * + * some compiler outputs were removed but the rest of the code is untouched + */ #include From 7a22827359ab82ba352bdd93486527e59d7b6618 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 2 Mar 2015 13:47:16 +0100 Subject: [PATCH 6/8] pulseIn: be consistent with standard API return 0 if timeout has been reached --- hardware/arduino/avr/cores/arduino/wiring_pulse.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hardware/arduino/avr/cores/arduino/wiring_pulse.c b/hardware/arduino/avr/cores/arduino/wiring_pulse.c index 49fa38dc9..4da446c81 100644 --- a/hardware/arduino/avr/cores/arduino/wiring_pulse.c +++ b/hardware/arduino/avr/cores/arduino/wiring_pulse.c @@ -48,7 +48,12 @@ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) unsigned long maxloops = microsecondsToClockCycles(timeout)/16; width = countPulseASM(portInputRegister(port), bit, stateMask, maxloops); - return clockCyclesToMicroseconds(width * 16 + 16); + + //prevent clockCyclesToMicroseconds to return bogus values if countPulseASM timed out + if (width) + return clockCyclesToMicroseconds(width * 16 + 16); + else + return 0; } /* Measures the length (in microseconds) of a pulse on the pin; state is HIGH From bb3963c0e94ec9906e8b2483761c4b302857649c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 29 May 2015 14:19:54 +0200 Subject: [PATCH 7/8] pulseIn: add ASM implementation for Due --- .../sam/cores/arduino/wiring_pulse.cpp | 36 ++-- .../arduino/sam/cores/arduino/wiring_pulse.h | 1 + .../sam/cores/arduino/wiring_pulse_asm.S | 166 ++++++++++++++++++ hardware/arduino/sam/platform.txt | 7 +- 4 files changed, 185 insertions(+), 25 deletions(-) create mode 100644 hardware/arduino/sam/cores/arduino/wiring_pulse_asm.S diff --git a/hardware/arduino/sam/cores/arduino/wiring_pulse.cpp b/hardware/arduino/sam/cores/arduino/wiring_pulse.cpp index bf250ff69..df5ee7804 100644 --- a/hardware/arduino/sam/cores/arduino/wiring_pulse.cpp +++ b/hardware/arduino/sam/cores/arduino/wiring_pulse.cpp @@ -23,39 +23,27 @@ * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds * to 3 minutes in length, but must be called at least a few dozen microseconds * before the start of the pulse. */ -extern uint32_t pulseIn( uint32_t pin, uint32_t state, uint32_t timeout ) +uint32_t pulseIn( uint32_t pin, uint32_t state, uint32_t timeout ) { // cache the port and bit of the pin in order to speed up the // pulse width measuring loop and achieve finer resolution. calling // digitalRead() instead yields much coarser resolution. PinDescription p = g_APinDescription[pin]; - uint32_t width = 0; // keep initialization out of time critical area + uint32_t bit = p.ulPin; + uint32_t stateMask = state ? bit : 0; // convert the timeout from microseconds to a number of times through - // the initial loop; it takes 22 clock cycles per iteration. - uint32_t numloops = 0; - uint32_t maxloops = microsecondsToClockCycles(timeout) / 22; - - // wait for any previous pulse to end - while (PIO_Get(p.pPort, PIO_INPUT, p.ulPin) == state) - if (numloops++ == maxloops) - return 0; - - // wait for the pulse to start - while (PIO_Get(p.pPort, PIO_INPUT, p.ulPin) != state) - if (numloops++ == maxloops) - return 0; - - // wait for the pulse to stop - while (PIO_Get(p.pPort, PIO_INPUT, p.ulPin) == state) { - if (numloops++ == maxloops) - return 0; - width++; - } + // the initial loop; it takes (roughly) 18 clock cycles per iteration. + uint32_t maxloops = microsecondsToClockCycles(timeout) / 18; + + uint32_t width = countPulseASM(&(p.pPort->PIO_PDSR), bit, stateMask, maxloops); // convert the reading to microseconds. The loop has been determined - // to be 52 clock cycles long and have about 16 clocks between the edge + // to be 18 clock cycles long and have about 16 clocks between the edge // and the start of the loop. There will be some error introduced by // the interrupt handlers. - return clockCyclesToMicroseconds(width * 52 + 16); + if (width) + return clockCyclesToMicroseconds(width * 18 + 16); + else + return 0; } diff --git a/hardware/arduino/sam/cores/arduino/wiring_pulse.h b/hardware/arduino/sam/cores/arduino/wiring_pulse.h index f32896984..64e188ec3 100644 --- a/hardware/arduino/sam/cores/arduino/wiring_pulse.h +++ b/hardware/arduino/sam/cores/arduino/wiring_pulse.h @@ -23,6 +23,7 @@ extern "C" { #endif +unsigned long countPulseASM(const volatile uint32_t *port, uint32_t bit, uint32_t stateMask, unsigned long maxloops); /* * \brief Measures the length (in microseconds) of a pulse on the pin; state is HIGH * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds diff --git a/hardware/arduino/sam/cores/arduino/wiring_pulse_asm.S b/hardware/arduino/sam/cores/arduino/wiring_pulse_asm.S new file mode 100644 index 000000000..ad1835fa9 --- /dev/null +++ b/hardware/arduino/sam/cores/arduino/wiring_pulse_asm.S @@ -0,0 +1,166 @@ +/* + Copyright (c) 2015 Arduino LLC. All right reserved. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* +The following function has been compiled to ASM with gcc + unsigned long countPulseASM(const volatile uint32_t *port, uint32_t bit, uint32_t stateMask, unsigned long maxloops) + { + unsigned long width = 0; + // wait for any previous pulse to end + while ((*port & bit) == stateMask) + if (--maxloops == 0) + return 0; + // wait for the pulse to start + while ((*port & bit) != stateMask) + if (--maxloops == 0) + return 0; + // wait for the pulse to stop + while ((*port & bit) == stateMask) { + if (++width == maxloops) + return 0; + } + return width; + } + +using the command line: + + arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -O2 -W -ffunction-sections -fdata-sections -nostdlib \ + countPulseASM.c -Wa,-ahlmsd=output.lst -dp -fverbose-asm -S \ + -I.arduino15/packages/arduino/hardware/sam/1.6.3/cores/arduino \ + -I.arduino15/packages/arduino/hardware/sam/1.6.3/system/CMSIS/CMSIS/Include \ + -I.arduino15/packages/arduino/hardware/sam/1.6.3/system/CMSIS/Device/ATMEL \ + -I.arduino15/packages/arduino/hardware/sam/1.6.3/system/libsam/include \ + -I.arduino15/packages/arduino/hardware/sam/1.6.3/variants/arduino_due_x + +The result has been slightly edited to increase readability. +*/ + + .syntax unified + .cpu cortex-m3 + .fpu softvfp + .eabi_attribute 20, 1 @ Tag_ABI_FP_denormal + .eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions + .eabi_attribute 23, 3 @ Tag_ABI_FP_number_model + .eabi_attribute 24, 1 @ Tag_ABI_align8_needed + .eabi_attribute 25, 1 @ Tag_ABI_align8_preserved + .eabi_attribute 26, 1 @ Tag_ABI_enum_size + .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals + .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access + .eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t + .file "countPulseASM.c" +@ GNU C (GNU Tools for ARM Embedded Processors) version 4.9.3 20150303 (release) [ARM/embedded-4_9-branch revision 221220] (arm-none-eabi) +@ compiled by GNU C version 4.7.4, GMP version 4.3.2, MPFR version 2.4.2, MPC version 0.8.1 +@ GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 +@ options passed: +@ -I .arduino15/packages/arduino/hardware/sam/1.6.3/cores/arduino +@ -I .arduino15/packages/arduino/hardware/sam/1.6.3/system/CMSIS/CMSIS/Include +@ -I .arduino15/packages/arduino/hardware/sam/1.6.3/system/CMSIS/Device/ATMEL +@ -I .arduino15/packages/arduino/hardware/sam/1.6.3/system/libsam/include +@ -I .arduino15/packages/arduino/hardware/sam/1.6.3/variants/arduino_due_x +@ -imultilib armv7-m -iprefix /usr/bin/../lib/gcc/arm-none-eabi/4.9.3/ +@ -isysroot /usr/bin/../arm-none-eabi -D__USES_INITFINI__ countPulseASM.c +@ -mcpu=cortex-m3 -mthumb -O2 -Wextra -ffunction-sections -fdata-sections +@ -fverbose-asm +@ options enabled: -faggressive-loop-optimizations -fauto-inc-dec +@ -fbranch-count-reg -fcaller-saves -fcombine-stack-adjustments -fcommon +@ -fcompare-elim -fcprop-registers -fcrossjumping -fcse-follow-jumps +@ -fdata-sections -fdefer-pop -fdelete-null-pointer-checks -fdevirtualize +@ -fdevirtualize-speculatively -fdwarf2-cfi-asm -fearly-inlining +@ -feliminate-unused-debug-types -fexpensive-optimizations +@ -fforward-propagate -ffunction-cse -ffunction-sections -fgcse -fgcse-lm +@ -fgnu-runtime -fgnu-unique -fguess-branch-probability +@ -fhoist-adjacent-loads -fident -fif-conversion -fif-conversion2 +@ -findirect-inlining -finline -finline-atomics +@ -finline-functions-called-once -finline-small-functions -fipa-cp +@ -fipa-profile -fipa-pure-const -fipa-reference -fipa-sra +@ -fira-hoist-pressure -fira-share-save-slots -fira-share-spill-slots +@ -fisolate-erroneous-paths-dereference -fivopts -fkeep-static-consts +@ -fleading-underscore -flifetime-dse -fmath-errno -fmerge-constants +@ -fmerge-debug-strings -fmove-loop-invariants -fomit-frame-pointer +@ -foptimize-sibling-calls -foptimize-strlen -fpartial-inlining -fpeephole +@ -fpeephole2 -fprefetch-loop-arrays -freg-struct-return -freorder-blocks +@ -freorder-functions -frerun-cse-after-loop +@ -fsched-critical-path-heuristic -fsched-dep-count-heuristic +@ -fsched-group-heuristic -fsched-interblock -fsched-last-insn-heuristic +@ -fsched-pressure -fsched-rank-heuristic -fsched-spec +@ -fsched-spec-insn-heuristic -fsched-stalled-insns-dep -fschedule-insns +@ -fschedule-insns2 -fsection-anchors -fshow-column -fshrink-wrap +@ -fsigned-zeros -fsplit-ivs-in-unroller -fsplit-wide-types +@ -fstrict-aliasing -fstrict-overflow -fstrict-volatile-bitfields +@ -fsync-libcalls -fthread-jumps -ftoplevel-reorder -ftrapping-math +@ -ftree-bit-ccp -ftree-builtin-call-dce -ftree-ccp -ftree-ch +@ -ftree-coalesce-vars -ftree-copy-prop -ftree-copyrename -ftree-cselim +@ -ftree-dce -ftree-dominator-opts -ftree-dse -ftree-forwprop -ftree-fre +@ -ftree-loop-if-convert -ftree-loop-im -ftree-loop-ivcanon +@ -ftree-loop-optimize -ftree-parallelize-loops= -ftree-phiprop -ftree-pre +@ -ftree-pta -ftree-reassoc -ftree-scev-cprop -ftree-sink -ftree-slsr +@ -ftree-sra -ftree-switch-conversion -ftree-tail-merge -ftree-ter +@ -ftree-vrp -funit-at-a-time -fverbose-asm -fzero-initialized-in-bss +@ -mfix-cortex-m3-ldrd -mlittle-endian -mlra -mpic-data-is-text-relative +@ -msched-prolog -mthumb -munaligned-access -mvectorize-with-neon-quad + + .section .text.countPulseASM,"ax",%progbits + .align 2 + .global countPulseASM + .thumb + .thumb_func + .type countPulseASM, %function +countPulseASM: + @ args = 0, pretend = 0, frame = 0 + @ frame_needed = 0, uses_anonymous_args = 0 + @ link register save eliminated. + push {r4, r5} @ @ 132 *push_multi [length = 2] + b .L2 @ @ 178 *arm_jump [length = 2] +.L4: + subs r3, r3, #1 @ maxloops, maxloops, @ 18 thumb2_addsi3_compare0/1 [length = 2] + beq .L12 @, @ 19 arm_cond_branch [length = 2] +.L2: + ldr r4, [r0] @ D.4169, *port_7(D) @ 22 *thumb2_movsi_insn/6 [length = 4] + ands r4, r4, r1 @, D.4169, D.4169, bit @ 24 *thumb2_alusi3_short [length = 2] + cmp r4, r2 @ D.4169, stateMask @ 25 *arm_cmpsi_insn/2 [length = 2] + beq .L4 @, @ 26 arm_cond_branch [length = 2] + b .L6 @ @ 181 *arm_jump [length = 2] +.L7: + subs r3, r3, #1 @ maxloops, maxloops, @ 32 thumb2_addsi3_compare0/1 [length = 2] + beq .L12 @, @ 33 arm_cond_branch [length = 2] +.L6: + ldr r4, [r0] @ D.4169, *port_7(D) @ 41 *thumb2_movsi_insn/6 [length = 4] + ands r4, r4, r1 @, D.4169, D.4169, bit @ 43 *thumb2_alusi3_short [length = 2] + cmp r4, r2 @ D.4169, stateMask @ 44 *arm_cmpsi_insn/2 [length = 2] + bne .L7 @, @ 45 arm_cond_branch [length = 2] + movs r5, #0 @ width, @ 7 *thumb2_movsi_shortim [length = 2] + b .L9 @ @ 183 *arm_jump [length = 2] +.L10: + adds r5, r5, #1 @ width, width, @ 50 *thumb2_addsi_short/1 [length = 2] + cmp r3, r5 @ maxloops, width @ 51 *arm_cmpsi_insn/2 [length = 2] + beq .L22 @, @ 52 arm_cond_branch [length = 2] +.L9: + ldr r4, [r0] @ D.4169, *port_7(D) @ 60 *thumb2_movsi_insn/6 [length = 4] + ands r4, r4, r1 @, D.4169, D.4169, bit @ 62 *thumb2_alusi3_short [length = 2] + cmp r4, r2 @ D.4169, stateMask @ 63 *arm_cmpsi_insn/2 [length = 2] + beq .L10 @, @ 64 arm_cond_branch [length = 2] + mov r0, r5 @ D.4169, width @ 9 *thumb2_movsi_insn/1 [length = 2] + pop {r4, r5} @ @ 165 *load_multiple_with_writeback [length = 4] + bx lr @ @ 166 *thumb2_return [length = 4] +.L12: + mov r0, r3 @ D.4169, maxloops @ 8 *thumb2_movsi_insn/1 [length = 2] + pop {r4, r5} @ @ 137 *load_multiple_with_writeback [length = 4] + bx lr @ @ 138 *thumb2_return [length = 4] +.L22: + movs r0, #0 @ D.4169, @ 11 *thumb2_movsi_shortim [length = 2] + pop {r4, r5} @ @ 173 *load_multiple_with_writeback [length = 4] + bx lr @ @ 174 *thumb2_return [length = 4] + .size countPulseASM, .-countPulseASM + .ident "GCC: (GNU Tools for ARM Embedded Processors) 4.9.3 20150303 (release) [ARM/embedded-4_9-branch revision 221220]" diff --git a/hardware/arduino/sam/platform.txt b/hardware/arduino/sam/platform.txt index af846086a..d52fb98a4 100644 --- a/hardware/arduino/sam/platform.txt +++ b/hardware/arduino/sam/platform.txt @@ -22,7 +22,8 @@ compiler.c.cmd=arm-none-eabi-gcc compiler.c.flags=-c -g -Os {compiler.warning_flags} -ffunction-sections -fdata-sections -nostdlib --param max-inline-insns-single=500 -Dprintf=iprintf -MMD compiler.c.elf.cmd=arm-none-eabi-gcc compiler.c.elf.flags=-Os -Wl,--gc-sections -compiler.S.flags=-c -g -x assembler-with-cpp +compiler.S.cmd=arm-none-eabi-gcc +compiler.S.flags=-c -g -x assembler-with-cpp -mthumb compiler.cpp.cmd=arm-none-eabi-g++ compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -ffunction-sections -fdata-sections -nostdlib -fno-threadsafe-statics --param max-inline-insns-single=500 -fno-rtti -fno-exceptions -Dprintf=iprintf -MMD compiler.ar.cmd=arm-none-eabi-ar @@ -42,6 +43,7 @@ build.extra_flags= compiler.c.extra_flags= compiler.c.elf.extra_flags= compiler.cpp.extra_flags= +compiler.S.extra_flags= compiler.ar.extra_flags= compiler.elf2hex.extra_flags= @@ -66,6 +68,9 @@ recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -mcpu={b ## Compile c++ files recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -mcpu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {compiler.libsam.c.flags} {includes} "{source_file}" -o "{object_file}" +## Compile S files +recipe.S.o.pattern="{compiler.path}{compiler.S.cmd}" {compiler.S.flags} -mcpu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.S.extra_flags} {build.extra_flags} {compiler.libsam.c.flags} {includes} "{source_file}" -o "{object_file}" + ## Create archives recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{build.path}/{archive_file}" "{object_file}" From a7d81d0b1c14655483ab2a3029eeb8504986ca9c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 29 May 2015 14:50:42 +0200 Subject: [PATCH 8/8] pulseIn: add alternative implementation based on micros() --- .../sam/cores/arduino/wiring_pulse.cpp | 46 ++++++++++++++++++- .../arduino/sam/cores/arduino/wiring_pulse.h | 2 +- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/hardware/arduino/sam/cores/arduino/wiring_pulse.cpp b/hardware/arduino/sam/cores/arduino/wiring_pulse.cpp index df5ee7804..22c3d2bec 100644 --- a/hardware/arduino/sam/cores/arduino/wiring_pulse.cpp +++ b/hardware/arduino/sam/cores/arduino/wiring_pulse.cpp @@ -22,7 +22,11 @@ /* Measures the length (in microseconds) of a pulse on the pin; state is HIGH * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds * to 3 minutes in length, but must be called at least a few dozen microseconds - * before the start of the pulse. */ + * before the start of the pulse. + * + * ATTENTION: + * This function performs better with short pulses in noInterrupt() context + */ uint32_t pulseIn( uint32_t pin, uint32_t state, uint32_t timeout ) { // cache the port and bit of the pin in order to speed up the @@ -47,3 +51,43 @@ uint32_t pulseIn( uint32_t pin, uint32_t state, uint32_t timeout ) else return 0; } + +/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH + * or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds + * to 3 minutes in length, but must be called at least a few dozen microseconds + * before the start of the pulse. + * + * ATTENTION: + * this function relies on micros() so cannot be used in noInterrupt() context + */ +uint32_t pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout) +{ + // cache the port and bit of the pin in order to speed up the + // pulse width measuring loop and achieve finer resolution. calling + // digitalRead() instead yields much coarser resolution. + PinDescription p = g_APinDescription[pin]; + uint32_t bit = p.ulPin; + uint32_t stateMask = state ? bit : 0; + + // convert the timeout from microseconds to a number of times through + // the initial loop; it takes 18 clock cycles per iteration. + unsigned long maxloops = microsecondsToClockCycles(timeout) / 10; + + // wait for any previous pulse to end + while ((p.pPort->PIO_PDSR & bit) == stateMask) + if (--maxloops == 0) + return 0; + + // wait for the pulse to start + while ((p.pPort->PIO_PDSR & bit) != stateMask) + if (--maxloops == 0) + return 0; + + unsigned long start = micros(); + // wait for the pulse to stop + while ((p.pPort->PIO_PDSR & bit) == stateMask) { + if (--maxloops == 0) + return 0; + } + return micros() - start; +} \ No newline at end of file diff --git a/hardware/arduino/sam/cores/arduino/wiring_pulse.h b/hardware/arduino/sam/cores/arduino/wiring_pulse.h index 64e188ec3..3087cb93c 100644 --- a/hardware/arduino/sam/cores/arduino/wiring_pulse.h +++ b/hardware/arduino/sam/cores/arduino/wiring_pulse.h @@ -31,7 +31,7 @@ unsigned long countPulseASM(const volatile uint32_t *port, uint32_t bit, uint32_ * before the start of the pulse. */ extern uint32_t pulseIn( uint32_t ulPin, uint32_t ulState, uint32_t ulTimeout = 1000000L ) ; - +extern uint32_t pulseInLong( uint8_t pin, uint8_t state, unsigned long timeout = 1000000L ) ; #ifdef __cplusplus }