diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index fa362d288e75b2ffb2005694c71fe2f1676609e9..844789674e391581ffdabdf899e7911f857ee3f1 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -71,7 +71,7 @@ void loop( void ) ; #include "Tone.h" #include "WMath.h" #include "HardwareSerial.h" -// #include "wiring_pulse.h" + #include "pulse.h" #include "delay.h" #include "Uart.h" #endif // __cplusplus diff --git a/cores/arduino/pulse.c b/cores/arduino/pulse.c new file mode 100644 index 0000000000000000000000000000000000000000..2dd03bf476f8c6dd2596859b8b1eb14fa96b5f0b --- /dev/null +++ b/cores/arduino/pulse.c @@ -0,0 +1,52 @@ +/* + 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 +*/ + +#include <Arduino.h> + +// See pulse_asm.S +extern unsigned long countPulseASM(const volatile uint32_t *port, uint32_t bit, uint32_t stateMask, unsigned long maxloops); + +/* 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. */ +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 bit = 1 << p.ulPin; + uint32_t stateMask = state ? bit : 0; + + // convert the timeout from microseconds to a number of times through + // the initial loop; it takes (roughly) 13 clock cycles per iteration. + uint32_t maxloops = microsecondsToClockCycles(timeout) / 13; + + uint32_t width = countPulseASM(&(PORT->Group[p.ulPort].IN.reg), bit, stateMask, maxloops); + + // convert the reading to microseconds. The loop has been determined + // to be 13 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. + if (width) + return clockCyclesToMicroseconds(width * 13 + 16); + else + return 0; +} + diff --git a/cores/arduino/pulse.h b/cores/arduino/pulse.h new file mode 100644 index 0000000000000000000000000000000000000000..9c620f12adb0dfe1acaa059d4d9b63fa936f6872 --- /dev/null +++ b/cores/arduino/pulse.h @@ -0,0 +1,39 @@ +/* + 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 +*/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * \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 + * to 3 minutes in length, but must be called at least a few dozen microseconds + * before the start of the pulse. + */ +uint32_t pulseIn(uint32_t pin, uint32_t state, uint32_t timeout); + +#ifdef __cplusplus +// Provides a version of pulseIn with a default argument (C++ only) +uint32_t pulseIn(uint32_t pin, uint32_t state, uint32_t timeout = 1000000L); + +} // extern "C" +#endif + diff --git a/cores/arduino/pulse_asm.S b/cores/arduino/pulse_asm.S new file mode 100644 index 0000000000000000000000000000000000000000..7b94f158bfce52b76d3901d1bfd4eefa2b2016d8 --- /dev/null +++ b/cores/arduino/pulse_asm.S @@ -0,0 +1,175 @@ +/* + 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-m0plus -mthumb -c -Os -W -ffunction-sections -fdata-sections \ + -nostdlib --param max-inline-insns-single=500 -fno-exceptions -MMD \ + -DF_CPU=48000000L -DARDUINO=10602 -DARDUINO_SAMD_ZERO -DARDUINO_ARCH_SAMD \ + -D__SAMD21G18A__ -DUSB_VID=0x2341 -DUSB_PID=0x004d -DUSBCON \ + -DUSB_MANUFACTURER="Arduino LLC" -DUSB_PRODUCT="Arduino Zero" \ + -I/Code/arduino/build/linux/work/hardware/tools/CMSIS/CMSIS/Include/ \ + -I/Code/arduino/build/linux/work/hardware/tools/CMSIS/Device/ATMEL/ \ + -I/Code/arduino/build/linux/work/hardware/arduino/samd/cores/arduino \ + -I/Code/arduino/build/linux/work/hardware/arduino/samd/variants/arduino_zero \ + count.c -Wa,-ahlmsd=output.lst -dp -fverbose-asm -S + +The result has been slightly edited to increase readability. + +*/ + + .cpu cortex-m0plus + .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, 4 @ Tag_ABI_optimization_goals + .eabi_attribute 34, 0 @ Tag_CPU_unaligned_access + .eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t + .file "count.c" +@ GNU C (GNU Tools for ARM Embedded Processors (Arduino build)) version 4.8.3 20140228 (release) [ARM/embedded-4_8-branch revision 208322] (arm-none-eabi) +@ compiled by GNU C version 4.3.2, 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 /Code/arduino/build/linux/work/hardware/tools/CMSIS/CMSIS/Include/ +@ -I /Code/arduino/build/linux/work/hardware/tools/CMSIS/Device/ATMEL/ +@ -I /Code/arduino/build/linux/work/hardware/arduino/samd/cores/arduino +@ -I /Code/arduino/build/linux/work/hardware/arduino/samd/variants/arduino_zero +@ -imultilib armv6-m +@ -iprefix /Code/arduino/build/linux/work/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin/../lib/gcc/arm-none-eabi/4.8.3/ +@ -isysroot /Code/arduino/build/linux/work/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin/../arm-none-eabi +@ -MMD count.d -D__USES_INITFINI__ -D F_CPU=48000000L -D ARDUINO=10602 +@ -D ARDUINO_SAMD_ZERO -D ARDUINO_ARCH_SAMD -D __SAMD21G18A__ +@ -D USB_VID=0x2341 -D USB_PID=0x004d -D USBCON +@ -D USB_MANUFACTURER=Arduino LLC -D USB_PRODUCT=Arduino Zero count.c +@ -mcpu=cortex-m0plus -mthumb -Os -Wextra -ffunction-sections +@ -fdata-sections -fno-exceptions -fverbose-asm +@ --param max-inline-insns-single=500 +@ 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 +@ -fdwarf2-cfi-asm -fearly-inlining -feliminate-unused-debug-types +@ -fexpensive-optimizations -fforward-propagate -ffunction-cse +@ -ffunction-sections -fgcse -fgcse-lm -fgnu-runtime +@ -fguess-branch-probability -fhoist-adjacent-loads -fident -fif-conversion +@ -fif-conversion2 -findirect-inlining -finline -finline-atomics +@ -finline-functions -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 -fivopts -fkeep-static-consts +@ -fleading-underscore -fmath-errno -fmerge-constants -fmerge-debug-strings +@ -fomit-frame-pointer -foptimize-register-move -foptimize-sibling-calls +@ -fpartial-inlining -fpeephole -fpeephole2 -fprefetch-loop-arrays +@ -freg-struct-return -fregmove -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-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-slp-vectorize -ftree-slsr -ftree-sra -ftree-switch-conversion +@ -ftree-tail-merge -ftree-ter -ftree-vect-loop-version -ftree-vrp +@ -funit-at-a-time -fverbose-asm -fzero-initialized-in-bss -mlittle-endian +@ -mpic-data-is-text-relative -msched-prolog -mthumb +@ -mvectorize-with-neon-quad + + .section .text.countPulseASM,"ax",%progbits + .align 1 + .global countPulseASM + .code 16 + .thumb_func + .type countPulseASM, %function +countPulseASM: + push {r4, r5, lr} @ @ 112 *push_multi [length = 2] +.L2: + ldr r4, [r0] @ D.11539, *port_7(D) @ 22 *thumb1_movsi_insn/7 [length = 2] + and r4, r1 @ D.11539, bit @ 24 *thumb1_andsi3_insn [length = 2] + cmp r4, r2 @ D.11539, stateMask @ 25 cbranchsi4_insn/1 [length = 4] + bne .L5 @, + sub r3, r3, #1 @ maxloops, @ 17 *thumb1_addsi3/2 [length = 2] + cmp r3, #0 @ maxloops, @ 18 cbranchsi4_insn/1 [length = 4] + bne .L2 @, + b .L10 @ @ 127 *thumb_jump [length = 2] +.L6: + sub r3, r3, #1 @ maxloops, @ 30 *thumb1_addsi3/2 [length = 2] + cmp r3, #0 @ maxloops, @ 31 cbranchsi4_insn/1 [length = 4] + beq .L10 @, +.L5: + ldr r4, [r0] @ D.11539, *port_7(D) @ 35 *thumb1_movsi_insn/7 [length = 2] + and r4, r1 @ D.11539, bit @ 37 *thumb1_andsi3_insn [length = 2] + cmp r4, r2 @ D.11539, stateMask @ 38 cbranchsi4_insn/1 [length = 4] + bne .L6 @, + mov r4, #0 @ width, @ 7 *thumb1_movsi_insn/2 [length = 2] +.L7: + ldr r5, [r0] @ D.11539, *port_7(D) @ 48 *thumb1_movsi_insn/7 [length = 2] + and r5, r1 @ D.11539, bit @ 50 *thumb1_andsi3_insn [length = 2] + cmp r5, r2 @ D.11539, stateMask @ 51 cbranchsi4_insn/1 [length = 4] + bne .L13 @, + add r4, r4, #1 @ width, @ 43 *thumb1_addsi3/1 [length = 2] + cmp r4, r3 @ width, maxloops @ 44 cbranchsi4_insn/1 [length = 4] + bne .L7 @, + mov r0, #0 @ D.11539, @ 11 *thumb1_movsi_insn/2 [length = 2] + b .L3 @ @ 130 *thumb_jump [length = 2] +.L13: + mov r0, r4 @ D.11539, width @ 9 *thumb1_movsi_insn/1 [length = 2] + b .L3 @ @ 132 *thumb_jump [length = 2] +.L10: + mov r0, r3 @ D.11539, maxloops @ 8 *thumb1_movsi_insn/1 [length = 2] +.L3: + @ sp needed @ @ 115 force_register_use [length = 0] + pop {r4, r5, pc} + .size countPulseASM, .-countPulseASM + .ident "GCC: (GNU Tools for ARM Embedded Processors (Arduino build)) 4.8.3 20140228 (release) [ARM/embedded-4_8-branch revision 208322]"