Skip to content

Commit 3859fe6

Browse files
committed
Merge pull request #2495 from facchinm/test-pulseIn-pullreq2
rework pulseIn function to solve issue #2409
2 parents 5933a7e + a7d81d0 commit 3859fe6

File tree

8 files changed

+459
-59
lines changed

8 files changed

+459
-59
lines changed

hardware/arduino/avr/cores/arduino/Arduino.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ unsigned long micros(void);
134134
void delay(unsigned long);
135135
void delayMicroseconds(unsigned int us);
136136
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout);
137+
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout);
137138

138139
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);
139140
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder);
@@ -232,6 +233,7 @@ uint16_t makeWord(byte h, byte l);
232233
#define word(...) makeWord(__VA_ARGS__)
233234

234235
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
236+
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
235237

236238
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0);
237239
void noTone(uint8_t _pin);

hardware/arduino/avr/cores/arduino/wiring_private.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ extern "C"{
4343
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
4444
#endif
4545

46+
uint32_t countPulseASM(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops);
47+
4648
#define EXTERNAL_INT_0 0
4749
#define EXTERNAL_INT_1 1
4850
#define EXTERNAL_INT_2 2
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
wiring_pulse.s - pulseInASM() function in different flavours
3+
Part of Arduino - http://www.arduino.cc/
4+
5+
Copyright (c) 2014 Martino Facchin
6+
7+
This library is free software; you can redistribute it and/or
8+
modify it under the terms of the GNU Lesser General Public
9+
License as published by the Free Software Foundation; either
10+
version 2.1 of the License, or (at your option) any later version.
11+
12+
This library is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
Lesser General Public License for more details.
16+
17+
You should have received a copy of the GNU Lesser General
18+
Public License along with this library; if not, write to the
19+
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20+
Boston, MA 02111-1307 USA
21+
*/
22+
23+
/*
24+
* The following routine was generated by avr-gcc 4.8.3 with the following parameters
25+
* -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2
26+
* on the original C function
27+
*
28+
* unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops)
29+
* {
30+
* unsigned long width = 0;
31+
* // wait for any previous pulse to end
32+
* while ((*port & bit) == stateMask)
33+
* if (--maxloops == 0)
34+
* return 0;
35+
*
36+
* // wait for the pulse to start
37+
* while ((*port & bit) != stateMask)
38+
* if (--maxloops == 0)
39+
* return 0;
40+
*
41+
* // wait for the pulse to stop
42+
* while ((*port & bit) == stateMask) {
43+
* if (++width == maxloops)
44+
* return 0;
45+
* }
46+
* return width;
47+
* }
48+
*
49+
* some compiler outputs were removed but the rest of the code is untouched
50+
*/
51+
52+
#include <avr/io.h>
53+
54+
.section .text
55+
56+
.global countPulseASM
57+
58+
countPulseASM:
59+
60+
.LM0:
61+
.LFBB1:
62+
push r12 ; ; 130 pushqi1/1 [length = 1]
63+
push r13 ; ; 131 pushqi1/1 [length = 1]
64+
push r14 ; ; 132 pushqi1/1 [length = 1]
65+
push r15 ; ; 133 pushqi1/1 [length = 1]
66+
push r16 ; ; 134 pushqi1/1 [length = 1]
67+
push r17 ; ; 135 pushqi1/1 [length = 1]
68+
/* prologue: function */
69+
/* frame size = 0 */
70+
/* stack size = 6 */
71+
.L__stack_usage = 6
72+
mov r30,r24 ; port, port ; 2 *movhi/1 [length = 2]
73+
mov r31,r25 ; port, port
74+
/* unsigned long width = 0;
75+
*** // wait for any previous pulse to end
76+
*** while ((*port & bit) == stateMask)
77+
*/
78+
.LM1:
79+
rjmp .L2 ; ; 181 jump [length = 1]
80+
.L4:
81+
/* if (--maxloops == 0) */
82+
.LM2:
83+
subi r16,1 ; maxloops, ; 17 addsi3/2 [length = 4]
84+
sbc r17, r1 ; maxloops
85+
sbc r18, r1 ; maxloops
86+
sbc r19, r1 ; maxloops
87+
breq .L13 ; , ; 19 branch [length = 1]
88+
.L2:
89+
/* if (--maxloops == 0) */
90+
.LM3:
91+
ld r25,Z ; D.1554, *port_7(D) ; 22 movqi_insn/4 [length = 1]
92+
and r25,r22 ; D.1554, bit ; 24 andqi3/1 [length = 1]
93+
cp r25,r20 ; D.1554, stateMask ; 25 *cmpqi/2 [length = 1]
94+
breq .L4 ; , ; 26 branch [length = 1]
95+
rjmp .L6 ; ; 184 jump [length = 1]
96+
.L7:
97+
/* return 0;
98+
***
99+
*** // wait for the pulse to start
100+
*** while ((*port & bit) != stateMask)
101+
*** if (--maxloops == 0)
102+
*/
103+
.LM4:
104+
subi r16,1 ; maxloops, ; 31 addsi3/2 [length = 4]
105+
sbc r17, r1 ; maxloops
106+
sbc r18, r1 ; maxloops
107+
sbc r19, r1 ; maxloops
108+
breq .L13 ; , ; 33 branch [length = 1]
109+
.L6:
110+
/* if (--maxloops == 0) */
111+
.LM5:
112+
ld r25,Z ; D.1554, *port_7(D) ; 41 movqi_insn/4 [length = 1]
113+
and r25,r22 ; D.1554, bit ; 43 andqi3/1 [length = 1]
114+
cpse r25,r20 ; D.1554, stateMask ; 44 enable_interrupt-3 [length = 1]
115+
rjmp .L7 ;
116+
mov r12, r1 ; width ; 7 *movsi/2 [length = 4]
117+
mov r13, r1 ; width
118+
mov r14, r1 ; width
119+
mov r15, r1 ; width
120+
rjmp .L9 ; ; 186 jump [length = 1]
121+
.L10:
122+
/* return 0;
123+
***
124+
*** // wait for the pulse to stop
125+
*** while ((*port & bit) == stateMask) {
126+
*** if (++width == maxloops)
127+
*/
128+
.LM6:
129+
ldi r24,-1 ; , ; 50 addsi3/3 [length = 5]
130+
sub r12,r24 ; width,
131+
sbc r13,r24 ; width,
132+
sbc r14,r24 ; width,
133+
sbc r15,r24 ; width,
134+
cp r16,r12 ; maxloops, width ; 51 *cmpsi/2 [length = 4]
135+
cpc r17,r13 ; maxloops, width
136+
cpc r18,r14 ; maxloops, width
137+
cpc r19,r15 ; maxloops, width
138+
breq .L13 ; , ; 52 branch [length = 1]
139+
.L9:
140+
/* if (++width == maxloops) */
141+
.LM7:
142+
ld r24,Z ; D.1554, *port_7(D) ; 60 movqi_insn/4 [length = 1]
143+
and r24,r22 ; D.1554, bit ; 62 andqi3/1 [length = 1]
144+
cp r24,r20 ; D.1554, stateMask ; 63 *cmpqi/2 [length = 1]
145+
breq .L10 ; , ; 64 branch [length = 1]
146+
/* return 0;
147+
*** }
148+
*** return width;
149+
*/
150+
.LM8:
151+
mov r22,r12 ; D.1553, width ; 108 movqi_insn/1 [length = 1]
152+
mov r23,r13 ; D.1553, width ; 109 movqi_insn/1 [length = 1]
153+
mov r24,r14 ; D.1553, width ; 110 movqi_insn/1 [length = 1]
154+
mov r25,r15 ; D.1553, width ; 111 movqi_insn/1 [length = 1]
155+
/* epilogue start */
156+
.LM9:
157+
pop r17 ; ; 171 popqi [length = 1]
158+
pop r16 ; ; 172 popqi [length = 1]
159+
pop r15 ; ; 173 popqi [length = 1]
160+
pop r14 ; ; 174 popqi [length = 1]
161+
pop r13 ; ; 175 popqi [length = 1]
162+
pop r12 ; ; 176 popqi [length = 1]
163+
ret ; 177 return_from_epilogue [length = 1]
164+
.L13:
165+
.LM10:
166+
ldi r22,0 ; D.1553 ; 120 movqi_insn/1 [length = 1]
167+
ldi r23,0 ; D.1553 ; 121 movqi_insn/1 [length = 1]
168+
ldi r24,0 ; D.1553 ; 122 movqi_insn/1 [length = 1]
169+
ldi r25,0 ; D.1553 ; 123 movqi_insn/1 [length = 1]
170+
/* epilogue start */
171+
.LM11:
172+
pop r17 ; ; 138 popqi [length = 1]
173+
pop r16 ; ; 139 popqi [length = 1]
174+
pop r15 ; ; 140 popqi [length = 1]
175+
pop r14 ; ; 141 popqi [length = 1]
176+
pop r13 ; ; 142 popqi [length = 1]
177+
pop r12 ; ; 143 popqi [length = 1]
178+
ret ; 144 return_from_epilogue [length = 1]

hardware/arduino/avr/cores/arduino/wiring_pulse.c

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH
2929
* or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds
3030
* to 3 minutes in length, but must be called at least a few dozen microseconds
31-
* before the start of the pulse. */
31+
* before the start of the pulse.
32+
*
33+
* This function performs better with short pulses in noInterrupt() context
34+
*/
3235
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
3336
{
3437
// cache the port and bit of the pin in order to speed up the
@@ -38,48 +41,59 @@ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
3841
uint8_t port = digitalPinToPort(pin);
3942
uint8_t stateMask = (state ? bit : 0);
4043
unsigned long width = 0; // keep initialization out of time critical area
41-
44+
45+
// convert the timeout from microseconds to a number of times through
46+
// the initial loop; it takes approximately 16 clock cycles per iteration
47+
unsigned long numloops = 0;
48+
unsigned long maxloops = microsecondsToClockCycles(timeout)/16;
49+
50+
width = countPulseASM(portInputRegister(port), bit, stateMask, maxloops);
51+
52+
//prevent clockCyclesToMicroseconds to return bogus values if countPulseASM timed out
53+
if (width)
54+
return clockCyclesToMicroseconds(width * 16 + 16);
55+
else
56+
return 0;
57+
}
58+
59+
/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH
60+
* or LOW, the type of pulse to measure. Works on pulses from 2-3 microseconds
61+
* to 3 minutes in length, but must be called at least a few dozen microseconds
62+
* before the start of the pulse.
63+
*
64+
* ATTENTION:
65+
* this function relies on micros() so cannot be used in noInterrupt() context
66+
*/
67+
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)
68+
{
69+
// cache the port and bit of the pin in order to speed up the
70+
// pulse width measuring loop and achieve finer resolution. calling
71+
// digitalRead() instead yields much coarser resolution.
72+
uint8_t bit = digitalPinToBitMask(pin);
73+
uint8_t port = digitalPinToPort(pin);
74+
uint8_t stateMask = (state ? bit : 0);
75+
unsigned long width = 0; // keep initialization out of time critical area
76+
4277
// convert the timeout from microseconds to a number of times through
4378
// the initial loop; it takes 16 clock cycles per iteration.
4479
unsigned long numloops = 0;
45-
unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;
46-
80+
unsigned long maxloops = microsecondsToClockCycles(timeout);
81+
4782
// wait for any previous pulse to end
4883
while ((*portInputRegister(port) & bit) == stateMask)
4984
if (numloops++ == maxloops)
5085
return 0;
51-
86+
5287
// wait for the pulse to start
5388
while ((*portInputRegister(port) & bit) != stateMask)
5489
if (numloops++ == maxloops)
5590
return 0;
56-
57-
// wait for the pulse to stop
58-
while ((*portInputRegister(port) & bit) == stateMask) {
59-
if (numloops++ == maxloops)
60-
return 0;
61-
width++;
62-
}
63-
64-
// convert the reading to microseconds. There will be some error introduced by
65-
// the interrupt handlers.
66-
67-
// Conversion constants are compiler-dependent, different compiler versions
68-
// have different levels of optimization.
69-
#if __GNUC__==4 && __GNUC_MINOR__==3 && __GNUC_PATCHLEVEL__==2
70-
// avr-gcc 4.3.2
71-
return clockCyclesToMicroseconds(width * 21 + 16);
72-
#elif __GNUC__==4 && __GNUC_MINOR__==8 && __GNUC_PATCHLEVEL__==1
73-
// avr-gcc 4.8.1
74-
return clockCyclesToMicroseconds(width * 24 + 16);
75-
#elif __GNUC__<=4 && __GNUC_MINOR__<=3
76-
// avr-gcc <=4.3.x
77-
#warning "pulseIn() results may not be accurate"
78-
return clockCyclesToMicroseconds(width * 21 + 16);
79-
#else
80-
// avr-gcc >4.3.x
81-
#warning "pulseIn() results may not be accurate"
82-
return clockCyclesToMicroseconds(width * 24 + 16);
83-
#endif
8491

92+
unsigned long start = micros();
93+
// wait for the pulse to stop
94+
while ((*portInputRegister(port) & bit) == stateMask) {
95+
if (numloops++ == maxloops)
96+
return 0;
97+
}
98+
return micros() - start;
8599
}

0 commit comments

Comments
 (0)