From b89a808059654b2198659cd2e2af68f1b1bd502b Mon Sep 17 00:00:00 2001 From: Rongzhong Li Date: Tue, 7 Nov 2023 22:08:01 +0800 Subject: [PATCH] add testSensorPack --- .../testSensorPack/Adafruit_NeoPixel.cpp | 2601 +++++++++++++++++ .../testSensorPack/Adafruit_NeoPixel.h | 366 +++ .../testSensorPack/Arduino_APDS9960.cpp | 443 +++ ModuleTests/testSensorPack/Arduino_APDS9960.h | 153 + ModuleTests/testSensorPack/infrared.h | 162 + ModuleTests/testSensorPack/pitches.h | 93 + ModuleTests/testSensorPack/sound.h | 60 + ModuleTests/testSensorPack/src/IRremote.cpp | 206 ++ ModuleTests/testSensorPack/src/IRremote.h | 490 ++++ ModuleTests/testSensorPack/src/esp32.cpp | 47 + ModuleTests/testSensorPack/src/irRecv.cpp | 287 ++ ModuleTests/testSensorPack/src/irSend.cpp | 168 ++ ModuleTests/testSensorPack/src/ir_NEC.cpp | 116 + .../src/private/IRremoteBoardDefs.h | 912 ++++++ .../testSensorPack/src/private/IRremoteInt.h | 126 + ModuleTests/testSensorPack/testSensorPack.ino | 303 ++ 16 files changed, 6533 insertions(+) create mode 100644 ModuleTests/testSensorPack/Adafruit_NeoPixel.cpp create mode 100644 ModuleTests/testSensorPack/Adafruit_NeoPixel.h create mode 100644 ModuleTests/testSensorPack/Arduino_APDS9960.cpp create mode 100644 ModuleTests/testSensorPack/Arduino_APDS9960.h create mode 100644 ModuleTests/testSensorPack/infrared.h create mode 100644 ModuleTests/testSensorPack/pitches.h create mode 100644 ModuleTests/testSensorPack/sound.h create mode 100644 ModuleTests/testSensorPack/src/IRremote.cpp create mode 100644 ModuleTests/testSensorPack/src/IRremote.h create mode 100644 ModuleTests/testSensorPack/src/esp32.cpp create mode 100644 ModuleTests/testSensorPack/src/irRecv.cpp create mode 100644 ModuleTests/testSensorPack/src/irSend.cpp create mode 100644 ModuleTests/testSensorPack/src/ir_NEC.cpp create mode 100644 ModuleTests/testSensorPack/src/private/IRremoteBoardDefs.h create mode 100644 ModuleTests/testSensorPack/src/private/IRremoteInt.h create mode 100644 ModuleTests/testSensorPack/testSensorPack.ino diff --git a/ModuleTests/testSensorPack/Adafruit_NeoPixel.cpp b/ModuleTests/testSensorPack/Adafruit_NeoPixel.cpp new file mode 100644 index 00000000..cb49b4ce --- /dev/null +++ b/ModuleTests/testSensorPack/Adafruit_NeoPixel.cpp @@ -0,0 +1,2601 @@ +/*! + * @file Adafruit_NeoPixel.cpp + * + * @mainpage Arduino Library for driving Adafruit NeoPixel addressable LEDs, + * FLORA RGB Smart Pixels and compatible devicess -- WS2811, WS2812, WS2812B, + * SK6812, etc. + * + * @section intro_sec Introduction + * + * This is the documentation for Adafruit's NeoPixel library for the + * Arduino platform, allowing a broad range of microcontroller boards + * (most AVR boards, many ARM devices, ESP8266 and ESP32, among others) + * to control Adafruit NeoPixels, FLORA RGB Smart Pixels and compatible + * devices -- WS2811, WS2812, WS2812B, SK6812, etc. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing products + * from Adafruit! + * + * @section author Author + * + * Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries, + * with contributions by PJRC, Michael Miller and other members of the + * open source community. + * + * @section license License + * + * This file is part of the Adafruit_NeoPixel library. + * + * Adafruit_NeoPixel 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 3 of the + * License, or (at your option) any later version. + * + * Adafruit_NeoPixel 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 NeoPixel. If not, see + * . + * + */ + +#include "Adafruit_NeoPixel.h" + +#if defined(TARGET_LPC1768) + #include +#endif + +#if defined(NRF52) || defined(NRF52_SERIES) +#include "nrf.h" + +// Interrupt is only disabled if there is no PWM device available +// Note: Adafruit Bluefruit nrf52 does not use this option +//#define NRF52_DISABLE_INT +#endif + +/*! + @brief NeoPixel constructor when length, pin and pixel type are known + at compile-time. + @param n Number of NeoPixels in strand. + @param p Arduino pin number which will drive the NeoPixel data in. + @param t Pixel type -- add together NEO_* constants defined in + Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for + NeoPixels expecting an 800 KHz (vs 400 KHz) data stream + with color bytes expressed in green, red, blue order per + pixel. + @return Adafruit_NeoPixel object. Call the begin() function before use. +*/ +Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) : + begun(false), brightness(0), pixels(NULL), endTime(0) { + updateType(t); + updateLength(n); + setPin(p); +} + +/*! + @brief "Empty" NeoPixel constructor when length, pin and/or pixel type + are not known at compile-time, and must be initialized later with + updateType(), updateLength() and setPin(). + @return Adafruit_NeoPixel object. Call the begin() function before use. + @note This function is deprecated, here only for old projects that + may still be calling it. New projects should instead use the + 'new' keyword with the first constructor syntax (length, pin, + type). +*/ +Adafruit_NeoPixel::Adafruit_NeoPixel() : +#if defined(NEO_KHZ400) + is800KHz(true), +#endif + begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), pixels(NULL), + rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) { +} + +/*! + @brief Deallocate Adafruit_NeoPixel object, set data pin back to INPUT. +*/ +Adafruit_NeoPixel::~Adafruit_NeoPixel() { + free(pixels); + if(pin >= 0) pinMode(pin, INPUT); +} + +/*! + @brief Configure NeoPixel pin for output. +*/ +void Adafruit_NeoPixel::begin(void) { + if(pin >= 0) { + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + } + begun = true; +} + +/*! + @brief Change the length of a previously-declared Adafruit_NeoPixel + strip object. Old data is deallocated and new data is cleared. + Pin number and pixel format are unchanged. + @param n New length of strip, in pixels. + @note This function is deprecated, here only for old projects that + may still be calling it. New projects should instead use the + 'new' keyword with the first constructor syntax (length, pin, + type). +*/ +void Adafruit_NeoPixel::updateLength(uint16_t n) { + free(pixels); // Free existing data (if any) + + // Allocate new data -- note: ALL PIXELS ARE CLEARED + numBytes = n * ((wOffset == rOffset) ? 3 : 4); + if((pixels = (uint8_t *)malloc(numBytes))) { + memset(pixels, 0, numBytes); + numLEDs = n; + } else { + numLEDs = numBytes = 0; + } +} + +/*! + @brief Change the pixel format of a previously-declared + Adafruit_NeoPixel strip object. If format changes from one of + the RGB variants to an RGBW variant (or RGBW to RGB), the old + data will be deallocated and new data is cleared. Otherwise, + the old data will remain in RAM and is not reordered to the + new format, so it's advisable to follow up with clear(). + @param t Pixel type -- add together NEO_* constants defined in + Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for + NeoPixels expecting an 800 KHz (vs 400 KHz) data stream + with color bytes expressed in green, red, blue order per + pixel. + @note This function is deprecated, here only for old projects that + may still be calling it. New projects should instead use the + 'new' keyword with the first constructor syntax + (length, pin, type). +*/ +void Adafruit_NeoPixel::updateType(neoPixelType t) { + bool oldThreeBytesPerPixel = (wOffset == rOffset); // false if RGBW + + wOffset = (t >> 6) & 0b11; // See notes in header file + rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets + gOffset = (t >> 2) & 0b11; + bOffset = t & 0b11; +#if defined(NEO_KHZ400) + is800KHz = (t < 256); // 400 KHz flag is 1<<8 +#endif + + // If bytes-per-pixel has changed (and pixel data was previously + // allocated), re-allocate to new size. Will clear any data. + if(pixels) { + bool newThreeBytesPerPixel = (wOffset == rOffset); + if(newThreeBytesPerPixel != oldThreeBytesPerPixel) updateLength(numLEDs); + } +} + +#if defined(ESP8266) +// ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution +extern "C" void ICACHE_RAM_ATTR espShow( + uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); +#elif defined(ESP32) +extern "C" void espShow( + uint16_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t type); +#endif // ESP8266 + +#if defined(K210) +#define KENDRYTE_K210 1 +#endif + +#if defined(KENDRYTE_K210) +extern "C" void k210Show( + uint8_t pin, uint8_t *pixels, uint32_t numBytes, boolean is800KHz); +#endif //KENDRYTE_K210 +/*! + @brief Transmit pixel data in RAM to NeoPixels. + @note On most architectures, interrupts are temporarily disabled in + order to achieve the correct NeoPixel signal timing. This means + that the Arduino millis() and micros() functions, which require + interrupts, will lose small intervals of time whenever this + function is called (about 30 microseconds per RGB pixel, 40 for + RGBW pixels). There's no easy fix for this, but a few + specialized alternative or companion libraries exist that use + very device-specific peripherals to work around it. +*/ +void Adafruit_NeoPixel::show(void) { + + if(!pixels) return; + + // Data latch = 300+ microsecond pause in the output stream. Rather than + // put a delay at the end of the function, the ending time is noted and + // the function will simply hold off (if needed) on issuing the + // subsequent round of data until the latch time has elapsed. This + // allows the mainline code to start generating the next frame of data + // rather than stalling for the latch. + while(!canShow()); + // endTime is a private member (rather than global var) so that multiple + // instances on different pins can be quickly issued in succession (each + // instance doesn't delay the next). + + // In order to make this code runtime-configurable to work with any pin, + // SBI/CBI instructions are eschewed in favor of full PORT writes via the + // OUT or ST instructions. It relies on two facts: that peripheral + // functions (such as PWM) take precedence on output pins, so our PORT- + // wide writes won't interfere, and that interrupts are globally disabled + // while data is being issued to the LEDs, so no other code will be + // accessing the PORT. The code takes an initial 'snapshot' of the PORT + // state, computes 'pin high' and 'pin low' values, and writes these back + // to the PORT register as needed. + + // NRF52 may use PWM + DMA (if available), may not need to disable interrupt +#if !( defined(NRF52) || defined(NRF52_SERIES) ) + noInterrupts(); // Need 100% focus on instruction timing +#endif + +#if defined(__AVR__) +// AVR MCUs -- ATmega & ATtiny (no XMEGA) --------------------------------- + + volatile uint16_t + i = numBytes; // Loop counter + volatile uint8_t + *ptr = pixels, // Pointer to next byte + b = *ptr++, // Current byte value + hi, // PORT w/output bit set high + lo; // PORT w/output bit set low + + // Hand-tuned assembly code issues data to the LED drivers at a specific + // rate. There's separate code for different CPU speeds (8, 12, 16 MHz) + // for both the WS2811 (400 KHz) and WS2812 (800 KHz) drivers. The + // datastream timing for the LED drivers allows a little wiggle room each + // way (listed in the datasheets), so the conditions for compiling each + // case are set up for a range of frequencies rather than just the exact + // 8, 12 or 16 MHz values, permitting use with some close-but-not-spot-on + // devices (e.g. 16.5 MHz DigiSpark). The ranges were arrived at based + // on the datasheet figures and have not been extensively tested outside + // the canonical 8/12/16 MHz speeds; there's no guarantee these will work + // close to the extremes (or possibly they could be pushed further). + // Keep in mind only one CPU speed case actually gets compiled; the + // resulting program isn't as massive as it might look from source here. + +// 8 MHz(ish) AVR --------------------------------------------------------- +#if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + + volatile uint8_t n1, n2 = 0; // First, next bits out + + // Squeezing an 800 KHz stream out of an 8 MHz chip requires code + // specific to each PORT register. + + // 10 instruction clocks per bit: HHxxxxxLLL + // OUT instructions: ^ ^ ^ (T=0,2,7) + + // PORTD OUTPUT ---------------------------------------------------- + +#if defined(PORTD) + #if defined(PORTB) || defined(PORTC) || defined(PORTF) + if(port == &PORTD) { + #endif // defined(PORTB/C/F) + + hi = PORTD | pinMask; + lo = PORTD & ~pinMask; + n1 = lo; + if(b & 0x80) n1 = hi; + + // Dirty trick: RJMPs proceeding to the next instruction are used + // to delay two clock cycles in one instruction word (rather than + // using two NOPs). This was necessary in order to squeeze the + // loop down to exactly 64 words -- the maximum possible for a + // relative branch. + + asm volatile( + "headD:" "\n\t" // Clk Pseudocode + // Bit 7: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 6" "\n\t" // 1-2 if(b & 0x40) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 6: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 5" "\n\t" // 1-2 if(b & 0x20) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 5: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 4" "\n\t" // 1-2 if(b & 0x10) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 4: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 3" "\n\t" // 1-2 if(b & 0x08) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 3: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 2" "\n\t" // 1-2 if(b & 0x04) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 2: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 1" "\n\t" // 1-2 if(b & 0x02) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "rjmp .+0" "\n\t" // 2 nop nop + // Bit 1: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n2] , %[lo]" "\n\t" // 1 n2 = lo + "out %[port] , %[n1]" "\n\t" // 1 PORT = n1 + "rjmp .+0" "\n\t" // 2 nop nop + "sbrc %[byte] , 0" "\n\t" // 1-2 if(b & 0x01) + "mov %[n2] , %[hi]" "\n\t" // 0-1 n2 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "sbiw %[count], 1" "\n\t" // 2 i-- (don't act on Z flag yet) + // Bit 0: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi + "mov %[n1] , %[lo]" "\n\t" // 1 n1 = lo + "out %[port] , %[n2]" "\n\t" // 1 PORT = n2 + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) + "mov %[n1] , %[hi]" "\n\t" // 0-1 n1 = hi + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo + "brne headD" "\n" // 2 while(i) (Z flag set above) + : [byte] "+r" (b), + [n1] "+r" (n1), + [n2] "+r" (n2), + [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTD)), + [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTB) || defined(PORTC) || defined(PORTF) + } else // other PORT(s) + #endif // defined(PORTB/C/F) +#endif // defined(PORTD) + + // PORTB OUTPUT ---------------------------------------------------- + +#if defined(PORTB) + #if defined(PORTD) || defined(PORTC) || defined(PORTF) + if(port == &PORTB) { + #endif // defined(PORTD/C/F) + + // Same as above, just switched to PORTB and stripped of comments. + hi = PORTB | pinMask; + lo = PORTB & ~pinMask; + n1 = lo; + if(b & 0x80) n1 = hi; + + asm volatile( + "headB:" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 6" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 5" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 4" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 3" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 2" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 1" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 0" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "brne headB" "\n" + : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTC) || defined(PORTF) + } + #endif + #if defined(PORTC) || defined(PORTF) + else + #endif // defined(PORTC/F) +#endif // defined(PORTB) + + // PORTC OUTPUT ---------------------------------------------------- + +#if defined(PORTC) + #if defined(PORTD) || defined(PORTB) || defined(PORTF) + if(port == &PORTC) { + #endif // defined(PORTD/B/F) + + // Same as above, just switched to PORTC and stripped of comments. + hi = PORTC | pinMask; + lo = PORTC & ~pinMask; + n1 = lo; + if(b & 0x80) n1 = hi; + + asm volatile( + "headC:" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 6" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 5" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 4" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 3" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 2" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 1" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 0" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "brne headC" "\n" + : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTB) || defined(PORTF) + } + #endif // defined(PORTD/B/F) + #if defined(PORTF) + else + #endif +#endif // defined(PORTC) + + // PORTF OUTPUT ---------------------------------------------------- + +#if defined(PORTF) + #if defined(PORTD) || defined(PORTB) || defined(PORTC) + if(port == &PORTF) { + #endif // defined(PORTD/B/C) + + hi = PORTF | pinMask; + lo = PORTF & ~pinMask; + n1 = lo; + if(b & 0x80) n1 = hi; + + asm volatile( + "headF:" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 6" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 5" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 4" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 3" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 2" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 1" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "rjmp .+0" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n2] , %[lo]" "\n\t" + "out %[port] , %[n1]" "\n\t" + "rjmp .+0" "\n\t" + "sbrc %[byte] , 0" "\n\t" + "mov %[n2] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "out %[port] , %[hi]" "\n\t" + "mov %[n1] , %[lo]" "\n\t" + "out %[port] , %[n2]" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[n1] , %[hi]" "\n\t" + "out %[port] , %[lo]" "\n\t" + "brne headF" "\n" + : [byte] "+r" (b), [n1] "+r" (n1), [n2] "+r" (n2), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTB) || defined(PORTC) + } + #endif // defined(PORTD/B/C) +#endif // defined(PORTF) + +#if defined(NEO_KHZ400) + } else { // end 800 KHz, do 400 KHz + + // Timing is more relaxed; unrolling the inner loop for each bit is + // not necessary. Still using the peculiar RJMPs as 2X NOPs, not out + // of need but just to trim the code size down a little. + // This 400-KHz-datastream-on-8-MHz-CPU code is not quite identical + // to the 800-on-16 code later -- the hi/lo timing between WS2811 and + // WS2812 is not simply a 2:1 scale! + + // 20 inst. clocks per bit: HHHHxxxxxxLLLLLLLLLL + // ST instructions: ^ ^ ^ (T=0,4,10) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head20:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 6) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) + "dec %[bit]" "\n\t" // 1 bit-- (T = 8) + "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) + "rjmp .+0" "\n\t" // 2 nop nop (T = 14) + "rjmp .+0" "\n\t" // 2 nop nop (T = 16) + "rjmp .+0" "\n\t" // 2 nop nop (T = 18) + "rjmp head20" "\n\t" // 2 -> head20 (next bit out) + "nextbyte20:" "\n\t" // (T = 10) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 12) + "nop" "\n\t" // 1 nop (T = 13) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 14) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 16) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) + "brne head20" "\n" // 2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [hi] "r" (hi), + [lo] "r" (lo), + [ptr] "e" (ptr)); + } +#endif // NEO_KHZ400 + +// 12 MHz(ish) AVR -------------------------------------------------------- +#elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + + // In the 12 MHz case, an optimized 800 KHz datastream (no dead time + // between bytes) requires a PORT-specific loop similar to the 8 MHz + // code (but a little more relaxed in this case). + + // 15 instruction clocks per bit: HHHHxxxxxxLLLLL + // OUT instructions: ^ ^ ^ (T=0,4,10) + + volatile uint8_t next; + + // PORTD OUTPUT ---------------------------------------------------- + +#if defined(PORTD) + #if defined(PORTB) || defined(PORTC) || defined(PORTF) + if(port == &PORTD) { + #endif // defined(PORTB/C/F) + + hi = PORTD | pinMask; + lo = PORTD & ~pinMask; + next = lo; + if(b & 0x80) next = hi; + + // Don't "optimize" the OUT calls into the bitTime subroutine; + // we're exploiting the RCALL and RET as 3- and 4-cycle NOPs! + asm volatile( + "headD:" "\n\t" // (T = 0) + "out %[port], %[hi]" "\n\t" // (T = 1) + "rcall bitTimeD" "\n\t" // Bit 7 (T = 15) + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 6 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 5 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 4 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 3 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 2 + "out %[port], %[hi]" "\n\t" + "rcall bitTimeD" "\n\t" // Bit 1 + // Bit 0: + "out %[port] , %[hi]" "\n\t" // 1 PORT = hi (T = 1) + "rjmp .+0" "\n\t" // 2 nop nop (T = 3) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 5) + "out %[port] , %[next]" "\n\t" // 1 PORT = next (T = 6) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 7) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) + "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 9) + "nop" "\n\t" // 1 (T = 10) + "out %[port] , %[lo]" "\n\t" // 1 PORT = lo (T = 11) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 13) + "brne headD" "\n\t" // 2 if(i != 0) -> (next byte) + "rjmp doneD" "\n\t" + "bitTimeD:" "\n\t" // nop nop nop (T = 4) + "out %[port], %[next]" "\n\t" // 1 PORT = next (T = 5) + "mov %[next], %[lo]" "\n\t" // 1 next = lo (T = 6) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 7) + "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 0x80) (T = 8) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 9) + "nop" "\n\t" // 1 (T = 10) + "out %[port], %[lo]" "\n\t" // 1 PORT = lo (T = 11) + "ret" "\n\t" // 4 nop nop nop nop (T = 15) + "doneD:" "\n" + : [byte] "+r" (b), + [next] "+r" (next), + [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTD)), + [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTB) || defined(PORTC) || defined(PORTF) + } else // other PORT(s) + #endif // defined(PORTB/C/F) +#endif // defined(PORTD) + + // PORTB OUTPUT ---------------------------------------------------- + +#if defined(PORTB) + #if defined(PORTD) || defined(PORTC) || defined(PORTF) + if(port == &PORTB) { + #endif // defined(PORTD/C/F) + + hi = PORTB | pinMask; + lo = PORTB & ~pinMask; + next = lo; + if(b & 0x80) next = hi; + + // Same as above, just set for PORTB & stripped of comments + asm volatile( + "headB:" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeB" "\n\t" + "out %[port] , %[hi]" "\n\t" + "rjmp .+0" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "out %[port] , %[next]" "\n\t" + "mov %[next] , %[lo]" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[next] , %[hi]" "\n\t" + "nop" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "brne headB" "\n\t" + "rjmp doneB" "\n\t" + "bitTimeB:" "\n\t" + "out %[port], %[next]" "\n\t" + "mov %[next], %[lo]" "\n\t" + "rol %[byte]" "\n\t" + "sbrc %[byte], 7" "\n\t" + "mov %[next], %[hi]" "\n\t" + "nop" "\n\t" + "out %[port], %[lo]" "\n\t" + "ret" "\n\t" + "doneB:" "\n" + : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTB)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTC) || defined(PORTF) + } + #endif + #if defined(PORTC) || defined(PORTF) + else + #endif // defined(PORTC/F) +#endif // defined(PORTB) + + // PORTC OUTPUT ---------------------------------------------------- + +#if defined(PORTC) + #if defined(PORTD) || defined(PORTB) || defined(PORTF) + if(port == &PORTC) { + #endif // defined(PORTD/B/F) + + hi = PORTC | pinMask; + lo = PORTC & ~pinMask; + next = lo; + if(b & 0x80) next = hi; + + // Same as above, just set for PORTC & stripped of comments + asm volatile( + "headC:" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port] , %[hi]" "\n\t" + "rjmp .+0" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "out %[port] , %[next]" "\n\t" + "mov %[next] , %[lo]" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[next] , %[hi]" "\n\t" + "nop" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "brne headC" "\n\t" + "rjmp doneC" "\n\t" + "bitTimeC:" "\n\t" + "out %[port], %[next]" "\n\t" + "mov %[next], %[lo]" "\n\t" + "rol %[byte]" "\n\t" + "sbrc %[byte], 7" "\n\t" + "mov %[next], %[hi]" "\n\t" + "nop" "\n\t" + "out %[port], %[lo]" "\n\t" + "ret" "\n\t" + "doneC:" "\n" + : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTC)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTB) || defined(PORTF) + } + #endif // defined(PORTD/B/F) + #if defined(PORTF) + else + #endif +#endif // defined(PORTC) + + // PORTF OUTPUT ---------------------------------------------------- + +#if defined(PORTF) + #if defined(PORTD) || defined(PORTB) || defined(PORTC) + if(port == &PORTF) { + #endif // defined(PORTD/B/C) + + hi = PORTF | pinMask; + lo = PORTF & ~pinMask; + next = lo; + if(b & 0x80) next = hi; + + // Same as above, just set for PORTF & stripped of comments + asm volatile( + "headF:" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port], %[hi]" "\n\t" + "rcall bitTimeC" "\n\t" + "out %[port] , %[hi]" "\n\t" + "rjmp .+0" "\n\t" + "ld %[byte] , %a[ptr]+" "\n\t" + "out %[port] , %[next]" "\n\t" + "mov %[next] , %[lo]" "\n\t" + "sbrc %[byte] , 7" "\n\t" + "mov %[next] , %[hi]" "\n\t" + "nop" "\n\t" + "out %[port] , %[lo]" "\n\t" + "sbiw %[count], 1" "\n\t" + "brne headF" "\n\t" + "rjmp doneC" "\n\t" + "bitTimeC:" "\n\t" + "out %[port], %[next]" "\n\t" + "mov %[next], %[lo]" "\n\t" + "rol %[byte]" "\n\t" + "sbrc %[byte], 7" "\n\t" + "mov %[next], %[hi]" "\n\t" + "nop" "\n\t" + "out %[port], %[lo]" "\n\t" + "ret" "\n\t" + "doneC:" "\n" + : [byte] "+r" (b), [next] "+r" (next), [count] "+w" (i) + : [port] "I" (_SFR_IO_ADDR(PORTF)), [ptr] "e" (ptr), [hi] "r" (hi), + [lo] "r" (lo)); + + #if defined(PORTD) || defined(PORTB) || defined(PORTC) + } + #endif // defined(PORTD/B/C) +#endif // defined(PORTF) + +#if defined(NEO_KHZ400) + } else { // 400 KHz + + // 30 instruction clocks per bit: HHHHHHxxxxxxxxxLLLLLLLLLLLLLLL + // ST instructions: ^ ^ ^ (T=0,6,15) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head30:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "rjmp .+0" "\n\t" // 2 nop nop (T = 6) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 8) + "rjmp .+0" "\n\t" // 2 nop nop (T = 10) + "rjmp .+0" "\n\t" // 2 nop nop (T = 12) + "rjmp .+0" "\n\t" // 2 nop nop (T = 14) + "nop" "\n\t" // 1 nop (T = 15) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 17) + "rjmp .+0" "\n\t" // 2 nop nop (T = 19) + "dec %[bit]" "\n\t" // 1 bit-- (T = 20) + "breq nextbyte30" "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 22) + "rjmp .+0" "\n\t" // 2 nop nop (T = 24) + "rjmp .+0" "\n\t" // 2 nop nop (T = 26) + "rjmp .+0" "\n\t" // 2 nop nop (T = 28) + "rjmp head30" "\n\t" // 2 -> head30 (next bit out) + "nextbyte30:" "\n\t" // (T = 22) + "nop" "\n\t" // 1 nop (T = 23) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 24) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 26) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 28) + "brne head30" "\n" // 1-2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [hi] "r" (hi), + [lo] "r" (lo), + [ptr] "e" (ptr)); + } +#endif // NEO_KHZ400 + +// 16 MHz(ish) AVR -------------------------------------------------------- +#elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000L) + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + + // WS2811 and WS2812 have different hi/lo duty cycles; this is + // similar but NOT an exact copy of the prior 400-on-8 code. + + // 20 inst. clocks per bit: HHHHHxxxxxxxxLLLLLLL + // ST instructions: ^ ^ ^ (T=0,5,13) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head20:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte], 7" "\n\t" // 1-2 if(b & 128) + "mov %[next], %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "dec %[bit]" "\n\t" // 1 bit-- (T = 5) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 7) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 8) + "breq nextbyte20" "\n\t" // 1-2 if(bit == 0) (from dec above) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 10) + "rjmp .+0" "\n\t" // 2 nop nop (T = 12) + "nop" "\n\t" // 1 nop (T = 13) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) + "nop" "\n\t" // 1 nop (T = 16) + "rjmp .+0" "\n\t" // 2 nop nop (T = 18) + "rjmp head20" "\n\t" // 2 -> head20 (next bit out) + "nextbyte20:" "\n\t" // (T = 10) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 11) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 13) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 15) + "nop" "\n\t" // 1 nop (T = 16) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 18) + "brne head20" "\n" // 2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); + +#if defined(NEO_KHZ400) + } else { // 400 KHz + + // The 400 KHz clock on 16 MHz MCU is the most 'relaxed' version. + + // 40 inst. clocks per bit: HHHHHHHHxxxxxxxxxxxxLLLLLLLLLLLLLLLLLLLL + // ST instructions: ^ ^ ^ (T=0,8,20) + + volatile uint8_t next, bit; + + hi = *port | pinMask; + lo = *port & ~pinMask; + next = lo; + bit = 8; + + asm volatile( + "head40:" "\n\t" // Clk Pseudocode (T = 0) + "st %a[port], %[hi]" "\n\t" // 2 PORT = hi (T = 2) + "sbrc %[byte] , 7" "\n\t" // 1-2 if(b & 128) + "mov %[next] , %[hi]" "\n\t" // 0-1 next = hi (T = 4) + "rjmp .+0" "\n\t" // 2 nop nop (T = 6) + "rjmp .+0" "\n\t" // 2 nop nop (T = 8) + "st %a[port], %[next]" "\n\t" // 2 PORT = next (T = 10) + "rjmp .+0" "\n\t" // 2 nop nop (T = 12) + "rjmp .+0" "\n\t" // 2 nop nop (T = 14) + "rjmp .+0" "\n\t" // 2 nop nop (T = 16) + "rjmp .+0" "\n\t" // 2 nop nop (T = 18) + "rjmp .+0" "\n\t" // 2 nop nop (T = 20) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 22) + "nop" "\n\t" // 1 nop (T = 23) + "mov %[next] , %[lo]" "\n\t" // 1 next = lo (T = 24) + "dec %[bit]" "\n\t" // 1 bit-- (T = 25) + "breq nextbyte40" "\n\t" // 1-2 if(bit == 0) + "rol %[byte]" "\n\t" // 1 b <<= 1 (T = 27) + "nop" "\n\t" // 1 nop (T = 28) + "rjmp .+0" "\n\t" // 2 nop nop (T = 30) + "rjmp .+0" "\n\t" // 2 nop nop (T = 32) + "rjmp .+0" "\n\t" // 2 nop nop (T = 34) + "rjmp .+0" "\n\t" // 2 nop nop (T = 36) + "rjmp .+0" "\n\t" // 2 nop nop (T = 38) + "rjmp head40" "\n\t" // 2 -> head40 (next bit out) + "nextbyte40:" "\n\t" // (T = 27) + "ldi %[bit] , 8" "\n\t" // 1 bit = 8 (T = 28) + "ld %[byte] , %a[ptr]+" "\n\t" // 2 b = *ptr++ (T = 30) + "rjmp .+0" "\n\t" // 2 nop nop (T = 32) + "st %a[port], %[lo]" "\n\t" // 2 PORT = lo (T = 34) + "rjmp .+0" "\n\t" // 2 nop nop (T = 36) + "sbiw %[count], 1" "\n\t" // 2 i-- (T = 38) + "brne head40" "\n" // 1-2 if(i != 0) -> (next byte) + : [port] "+e" (port), + [byte] "+r" (b), + [bit] "+r" (bit), + [next] "+r" (next), + [count] "+w" (i) + : [ptr] "e" (ptr), + [hi] "r" (hi), + [lo] "r" (lo)); + } +#endif // NEO_KHZ400 + +#else + #error "CPU SPEED NOT SUPPORTED" +#endif // end F_CPU ifdefs on __AVR__ + +// END AVR ---------------------------------------------------------------- + + +#elif defined(__arm__) + +// ARM MCUs -- Teensy 3.0, 3.1, LC, Arduino Due --------------------------- + +#if defined(TEENSYDUINO) && defined(KINETISK) // Teensy 3.0, 3.1, 3.2, 3.5, 3.6 +#define CYCLES_800_T0H (F_CPU / 4000000) +#define CYCLES_800_T1H (F_CPU / 1250000) +#define CYCLES_800 (F_CPU / 800000) +#define CYCLES_400_T0H (F_CPU / 2000000) +#define CYCLES_400_T1H (F_CPU / 833333) +#define CYCLES_400 (F_CPU / 400000) + + uint8_t *p = pixels, + *end = p + numBytes, pix, mask; + volatile uint8_t *set = portSetRegister(pin), + *clr = portClearRegister(pin); + uint32_t cyc; + + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + cyc = ARM_DWT_CYCCNT + CYCLES_800; + while(p < end) { + pix = *p++; + for(mask = 0x80; mask; mask >>= 1) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_800); + cyc = ARM_DWT_CYCCNT; + *set = 1; + if(pix & mask) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H); + } else { + while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H); + } + *clr = 1; + } + } + while(ARM_DWT_CYCCNT - cyc < CYCLES_800); +#if defined(NEO_KHZ400) + } else { // 400 kHz bitstream + cyc = ARM_DWT_CYCCNT + CYCLES_400; + while(p < end) { + pix = *p++; + for(mask = 0x80; mask; mask >>= 1) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_400); + cyc = ARM_DWT_CYCCNT; + *set = 1; + if(pix & mask) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H); + } else { + while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H); + } + *clr = 1; + } + } + while(ARM_DWT_CYCCNT - cyc < CYCLES_400); + } +#endif // NEO_KHZ400 + +#elif defined(TEENSYDUINO) && (defined(__IMXRT1052__) || defined(__IMXRT1062__)) +#define CYCLES_800_T0H (F_CPU_ACTUAL / 4000000) +#define CYCLES_800_T1H (F_CPU_ACTUAL / 1250000) +#define CYCLES_800 (F_CPU_ACTUAL / 800000) +#define CYCLES_400_T0H (F_CPU_ACTUAL / 2000000) +#define CYCLES_400_T1H (F_CPU_ACTUAL / 833333) +#define CYCLES_400 (F_CPU_ACTUAL / 400000) + + uint8_t *p = pixels, + *end = p + numBytes, pix, mask; + volatile uint32_t *set = portSetRegister(pin), + *clr = portClearRegister(pin); + uint32_t cyc, + msk = digitalPinToBitMask(pin); + + ARM_DEMCR |= ARM_DEMCR_TRCENA; + ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + cyc = ARM_DWT_CYCCNT + CYCLES_800; + while(p < end) { + pix = *p++; + for(mask = 0x80; mask; mask >>= 1) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_800); + cyc = ARM_DWT_CYCCNT; + *set = msk; + if(pix & mask) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H); + } else { + while(ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H); + } + *clr = msk; + } + } + while(ARM_DWT_CYCCNT - cyc < CYCLES_800); +#if defined(NEO_KHZ400) + } else { // 400 kHz bitstream + cyc = ARM_DWT_CYCCNT + CYCLES_400; + while(p < end) { + pix = *p++; + for(mask = 0x80; mask; mask >>= 1) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_400); + cyc = ARM_DWT_CYCCNT; + *set = msk; + if(pix & mask) { + while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T1H); + } else { + while(ARM_DWT_CYCCNT - cyc < CYCLES_400_T0H); + } + *clr = msk; + } + } + while(ARM_DWT_CYCCNT - cyc < CYCLES_400); + } +#endif // NEO_KHZ400 + +#elif defined(TEENSYDUINO) && defined(__MKL26Z64__) // Teensy-LC + +#if F_CPU == 48000000 + uint8_t *p = pixels, + pix, count, dly, + bitmask = digitalPinToBitMask(pin); + volatile uint8_t *reg = portSetRegister(pin); + uint32_t num = numBytes; + asm volatile( + "L%=_begin:" "\n\t" + "ldrb %[pix], [%[p], #0]" "\n\t" + "lsl %[pix], #24" "\n\t" + "movs %[count], #7" "\n\t" + "L%=_loop:" "\n\t" + "lsl %[pix], #1" "\n\t" + "bcs L%=_loop_one" "\n\t" + "L%=_loop_zero:" "\n\t" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #4" "\n\t" + "L%=_loop_delay_T0H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T0H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #13" "\n\t" + "L%=_loop_delay_T0L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T0L" "\n\t" + "b L%=_next" "\n\t" + "L%=_loop_one:" "\n\t" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #13" "\n\t" + "L%=_loop_delay_T1H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T1H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #4" "\n\t" + "L%=_loop_delay_T1L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_loop_delay_T1L" "\n\t" + "nop" "\n\t" + "L%=_next:" "\n\t" + "sub %[count], #1" "\n\t" + "bne L%=_loop" "\n\t" + "lsl %[pix], #1" "\n\t" + "bcs L%=_last_one" "\n\t" + "L%=_last_zero:" "\n\t" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #4" "\n\t" + "L%=_last_delay_T0H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T0H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #10" "\n\t" + "L%=_last_delay_T0L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T0L" "\n\t" + "b L%=_repeat" "\n\t" + "L%=_last_one:" "\n\t" + "strb %[bitmask], [%[reg], #0]" "\n\t" + "movs %[dly], #13" "\n\t" + "L%=_last_delay_T1H:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T1H" "\n\t" + "strb %[bitmask], [%[reg], #4]" "\n\t" + "movs %[dly], #1" "\n\t" + "L%=_last_delay_T1L:" "\n\t" + "sub %[dly], #1" "\n\t" + "bne L%=_last_delay_T1L" "\n\t" + "nop" "\n\t" + "L%=_repeat:" "\n\t" + "add %[p], #1" "\n\t" + "sub %[num], #1" "\n\t" + "bne L%=_begin" "\n\t" + "L%=_done:" "\n\t" + : [p] "+r" (p), + [pix] "=&r" (pix), + [count] "=&r" (count), + [dly] "=&r" (dly), + [num] "+r" (num) + : [bitmask] "r" (bitmask), + [reg] "r" (reg) + ); +#else +#error "Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz" +#endif // F_CPU == 48000000 + +// Begin of support for nRF52 based boards ------------------------- + +#elif defined(NRF52) || defined(NRF52_SERIES) +// [[[Begin of the Neopixel NRF52 EasyDMA implementation +// by the Hackerspace San Salvador]]] +// This technique uses the PWM peripheral on the NRF52. The PWM uses the +// EasyDMA feature included on the chip. This technique loads the duty +// cycle configuration for each cycle when the PWM is enabled. For this +// to work we need to store a 16 bit configuration for each bit of the +// RGB(W) values in the pixel buffer. +// Comparator values for the PWM were hand picked and are guaranteed to +// be 100% organic to preserve freshness and high accuracy. Current +// parameters are: +// * PWM Clock: 16Mhz +// * Minimum step time: 62.5ns +// * Time for zero in high (T0H): 0.31ms +// * Time for one in high (T1H): 0.75ms +// * Cycle time: 1.25us +// * Frequency: 800Khz +// For 400Khz we just double the calculated times. +// ---------- BEGIN Constants for the EasyDMA implementation ----------- +// The PWM starts the duty cycle in LOW. To start with HIGH we +// need to set the 15th bit on each register. + +// WS2812 (rev A) timing is 0.35 and 0.7us +//#define MAGIC_T0H 5UL | (0x8000) // 0.3125us +//#define MAGIC_T1H 12UL | (0x8000) // 0.75us + +// WS2812B (rev B) timing is 0.4 and 0.8 us +#define MAGIC_T0H 6UL | (0x8000) // 0.375us +#define MAGIC_T1H 13UL | (0x8000) // 0.8125us + +// WS2811 (400 khz) timing is 0.5 and 1.2 +#define MAGIC_T0H_400KHz 8UL | (0x8000) // 0.5us +#define MAGIC_T1H_400KHz 19UL | (0x8000) // 1.1875us + +// For 400Khz, we double value of CTOPVAL +#define CTOPVAL 20UL // 1.25us +#define CTOPVAL_400KHz 40UL // 2.5us + +// ---------- END Constants for the EasyDMA implementation ------------- +// +// If there is no device available an alternative cycle-counter +// implementation is tried. +// The nRF52 runs with a fixed clock of 64Mhz. The alternative +// implementation is the same as the one used for the Teensy 3.0/1/2 but +// with the Nordic SDK HAL & registers syntax. +// The number of cycles was hand picked and is guaranteed to be 100% +// organic to preserve freshness and high accuracy. +// ---------- BEGIN Constants for cycle counter implementation --------- +#define CYCLES_800_T0H 18 // ~0.36 uS +#define CYCLES_800_T1H 41 // ~0.76 uS +#define CYCLES_800 71 // ~1.25 uS + +#define CYCLES_400_T0H 26 // ~0.50 uS +#define CYCLES_400_T1H 70 // ~1.26 uS +#define CYCLES_400 156 // ~2.50 uS +// ---------- END of Constants for cycle counter implementation -------- + + // To support both the SoftDevice + Neopixels we use the EasyDMA + // feature from the NRF25. However this technique implies to + // generate a pattern and store it on the memory. The actual + // memory used in bytes corresponds to the following formula: + // totalMem = numBytes*8*2+(2*2) + // The two additional bytes at the end are needed to reset the + // sequence. + // + // If there is not enough memory, we will fall back to cycle counter + // using DWT + uint32_t pattern_size = numBytes*8*sizeof(uint16_t)+2*sizeof(uint16_t); + uint16_t* pixels_pattern = NULL; + + NRF_PWM_Type* pwm = NULL; + + // Try to find a free PWM device, which is not enabled + // and has no connected pins + NRF_PWM_Type* PWM[] = { + NRF_PWM0, NRF_PWM1, NRF_PWM2 +#if defined(NRF_PWM3) + ,NRF_PWM3 +#endif + }; + + for(unsigned int device = 0; device < (sizeof(PWM)/sizeof(PWM[0])); device++) { + if( (PWM[device]->ENABLE == 0) && + (PWM[device]->PSEL.OUT[0] & PWM_PSEL_OUT_CONNECT_Msk) && + (PWM[device]->PSEL.OUT[1] & PWM_PSEL_OUT_CONNECT_Msk) && + (PWM[device]->PSEL.OUT[2] & PWM_PSEL_OUT_CONNECT_Msk) && + (PWM[device]->PSEL.OUT[3] & PWM_PSEL_OUT_CONNECT_Msk) + ) { + pwm = PWM[device]; + break; + } + } + + // only malloc if there is PWM device available + if ( pwm != NULL ) { + #if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe malloc + pixels_pattern = (uint16_t *) rtos_malloc(pattern_size); + #else + pixels_pattern = (uint16_t *) malloc(pattern_size); + #endif + } + + // Use the identified device to choose the implementation + // If a PWM device is available use DMA + if( (pixels_pattern != NULL) && (pwm != NULL) ) { + uint16_t pos = 0; // bit position + + for(uint16_t n=0; n0; mask >>= 1) { + #if defined(NEO_KHZ400) + if( !is800KHz ) { + pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H_400KHz : MAGIC_T0H_400KHz; + }else + #endif + { + pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H : MAGIC_T0H; + } + + pos++; + } + } + + // Zero padding to indicate the end of que sequence + pixels_pattern[pos++] = 0 | (0x8000); // Seq end + pixels_pattern[pos++] = 0 | (0x8000); // Seq end + + // Set the wave mode to count UP + pwm->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos); + + // Set the PWM to use the 16MHz clock + pwm->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos); + + // Setting of the maximum count + // but keeping it on 16Mhz allows for more granularity just + // in case someone wants to do more fine-tuning of the timing. +#if defined(NEO_KHZ400) + if( !is800KHz ) { + pwm->COUNTERTOP = (CTOPVAL_400KHz << PWM_COUNTERTOP_COUNTERTOP_Pos); + }else +#endif + { + pwm->COUNTERTOP = (CTOPVAL << PWM_COUNTERTOP_COUNTERTOP_Pos); + } + + // Disable loops, we want the sequence to repeat only once + pwm->LOOP = (PWM_LOOP_CNT_Disabled << PWM_LOOP_CNT_Pos); + + // On the "Common" setting the PWM uses the same pattern for the + // for supported sequences. The pattern is stored on half-word + // of 16bits + pwm->DECODER = (PWM_DECODER_LOAD_Common << PWM_DECODER_LOAD_Pos) | + (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos); + + // Pointer to the memory storing the patter + pwm->SEQ[0].PTR = (uint32_t)(pixels_pattern) << PWM_SEQ_PTR_PTR_Pos; + + // Calculation of the number of steps loaded from memory. + pwm->SEQ[0].CNT = (pattern_size/sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos; + + // The following settings are ignored with the current config. + pwm->SEQ[0].REFRESH = 0; + pwm->SEQ[0].ENDDELAY = 0; + + // The Neopixel implementation is a blocking algorithm. DMA + // allows for non-blocking operation. To "simulate" a blocking + // operation we enable the interruption for the end of sequence + // and block the execution thread until the event flag is set by + // the peripheral. +// pwm->INTEN |= (PWM_INTEN_SEQEND0_Enabled<PSEL.OUT[0] = g_APinDescription[pin].name; + #else + pwm->PSEL.OUT[0] = g_ADigitalPinMap[pin]; + #endif + + // Enable the PWM + pwm->ENABLE = 1; + + // After all of this and many hours of reading the documentation + // we are ready to start the sequence... + pwm->EVENTS_SEQEND[0] = 0; + pwm->TASKS_SEQSTART[0] = 1; + + // But we have to wait for the flag to be set. + while(!pwm->EVENTS_SEQEND[0]) + { + #if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_NRF52840) + yield(); + #endif + } + + // Before leave we clear the flag for the event. + pwm->EVENTS_SEQEND[0] = 0; + + // We need to disable the device and disconnect + // all the outputs before leave or the device will not + // be selected on the next call. + // TODO: Check if disabling the device causes performance issues. + pwm->ENABLE = 0; + + pwm->PSEL.OUT[0] = 0xFFFFFFFFUL; + + #if defined(ARDUINO_NRF52_ADAFRUIT) // use thread-safe free + rtos_free(pixels_pattern); + #else + free(pixels_pattern); + #endif + }// End of DMA implementation + // --------------------------------------------------------------------- + else{ +#ifndef ARDUINO_ARCH_NRF52840 + // Fall back to DWT + #if defined(ARDUINO_NRF52_ADAFRUIT) + // Bluefruit Feather 52 uses freeRTOS + // Critical Section is used since it does not block SoftDevice execution + taskENTER_CRITICAL(); + #elif defined(NRF52_DISABLE_INT) + // If you are using the Bluetooth SoftDevice we advise you to not disable + // the interrupts. Disabling the interrupts even for short periods of time + // causes the SoftDevice to stop working. + // Disable the interrupts only in cases where you need high performance for + // the LEDs and if you are not using the EasyDMA feature. + __disable_irq(); + #endif + + NRF_GPIO_Type* nrf_port = (NRF_GPIO_Type*) digitalPinToPort(pin); + uint32_t pinMask = digitalPinToBitMask(pin); + + uint32_t CYCLES_X00 = CYCLES_800; + uint32_t CYCLES_X00_T1H = CYCLES_800_T1H; + uint32_t CYCLES_X00_T0H = CYCLES_800_T0H; + +#if defined(NEO_KHZ400) + if( !is800KHz ) + { + CYCLES_X00 = CYCLES_400; + CYCLES_X00_T1H = CYCLES_400_T1H; + CYCLES_X00_T0H = CYCLES_400_T0H; + } +#endif + + // Enable DWT in debug core + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + + // Tries to re-send the frame if is interrupted by the SoftDevice. + while(1) { + uint8_t *p = pixels; + + uint32_t cycStart = DWT->CYCCNT; + uint32_t cyc = 0; + + for(uint16_t n=0; n>= 1) { + while(DWT->CYCCNT - cyc < CYCLES_X00); + cyc = DWT->CYCCNT; + + nrf_port->OUTSET |= pinMask; + + if(pix & mask) { + while(DWT->CYCCNT - cyc < CYCLES_X00_T1H); + } else { + while(DWT->CYCCNT - cyc < CYCLES_X00_T0H); + } + + nrf_port->OUTCLR |= pinMask; + } + } + while(DWT->CYCCNT - cyc < CYCLES_X00); + + + // If total time longer than 25%, resend the whole data. + // Since we are likely to be interrupted by SoftDevice + if ( (DWT->CYCCNT - cycStart) < ( 8*numBytes*((CYCLES_X00*5)/4) ) ) { + break; + } + + // re-send need 300us delay + delayMicroseconds(300); + } + + // Enable interrupts again + #if defined(ARDUINO_NRF52_ADAFRUIT) + taskEXIT_CRITICAL(); + #elif defined(NRF52_DISABLE_INT) + __enable_irq(); + #endif +#endif + } +// END of NRF52 implementation + +#elif defined (__SAMD21E17A__) || defined(__SAMD21G18A__) || defined(__SAMD21E18A__) || defined(__SAMD21J18A__) // Arduino Zero, Gemma/Trinket M0, SODAQ Autonomo and others + // Tried this with a timer/counter, couldn't quite get adequate + // resolution. So yay, you get a load of goofball NOPs... + + uint8_t *ptr, *end, p, bitMask, portNum; + uint32_t pinMask; + + portNum = g_APinDescription[pin].ulPort; + pinMask = 1ul << g_APinDescription[pin].ulPin; + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; + + volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg), + *clr = &(PORT->Group[portNum].OUTCLR.reg); + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + for(;;) { + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;"); + if(p & bitMask) { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + *clr = pinMask; + } else { + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + } + if(bitMask >>= 1) { + asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } +#if defined(NEO_KHZ400) + } else { // 400 KHz bitstream + for(;;) { + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;"); + if(p & bitMask) { + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop;"); + *clr = pinMask; + } else { + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop;"); + } + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;"); + if(bitMask >>= 1) { + asm("nop; nop; nop; nop; nop; nop; nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } + } +#endif + +#elif defined (__SAMD51__) // M4 + + uint8_t *ptr, *end, p, bitMask, portNum, bit; + uint32_t pinMask; + + portNum = g_APinDescription[pin].ulPort; + pinMask = 1ul << g_APinDescription[pin].ulPin; + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; + + volatile uint32_t *set = &(PORT->Group[portNum].OUTSET.reg), + *clr = &(PORT->Group[portNum].OUTCLR.reg); + + // SAMD51 overclock-compatible timing is only a mild abomination. + // It uses SysTick for a consistent clock reference regardless of + // optimization / cache settings. That's the good news. The bad news, + // since SysTick->VAL is a volatile type it's slow to access...and then, + // with the SysTick interval that Arduino sets up (1 ms), this would + // require a subtract and MOD operation for gauging elapsed time, and + // all taken in combination that lacks adequate temporal resolution + // for NeoPixel timing. So a kind of horrible thing is done here... + // since interrupts are turned off anyway and it's generally accepted + // by now that we're gonna lose track of time in the NeoPixel lib, + // the SysTick timer is reconfigured for a period matching the NeoPixel + // bit timing (either 800 or 400 KHz) and we watch SysTick->VAL very + // closely (just a threshold, no subtract or MOD or anything) and that + // seems to work just well enough. When finished, the SysTick + // peripheral is set back to its original state. + + uint32_t t0, t1, top, ticks, + saveLoad = SysTick->LOAD, saveVal = SysTick->VAL; + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + top = (uint32_t)(F_CPU * 0.00000125); // Bit hi + lo = 1.25 uS + t0 = top - (uint32_t)(F_CPU * 0.00000040); // 0 = 0.4 uS hi + t1 = top - (uint32_t)(F_CPU * 0.00000080); // 1 = 0.8 uS hi +#if defined(NEO_KHZ400) + } else { // 400 KHz bitstream + top = (uint32_t)(F_CPU * 0.00000250); // Bit hi + lo = 2.5 uS + t0 = top - (uint32_t)(F_CPU * 0.00000050); // 0 = 0.5 uS hi + t1 = top - (uint32_t)(F_CPU * 0.00000120); // 1 = 1.2 uS hi + } +#endif + + SysTick->LOAD = top; // Config SysTick for NeoPixel bit freq + SysTick->VAL = top; // Set to start value (counts down) + (void)SysTick->VAL; // Dummy read helps sync up 1st bit + + for(;;) { + *set = pinMask; // Set output high + ticks = (p & bitMask) ? t1 : t0; // SysTick threshold, + while(SysTick->VAL > ticks); // wait for it + *clr = pinMask; // Set output low + if(!(bitMask >>= 1)) { // Next bit for this byte...done? + if(ptr >= end) break; // If last byte sent, exit loop + p = *ptr++; // Fetch next byte + bitMask = 0x80; // Reset bitmask + } + while(SysTick->VAL <= ticks); // Wait for rollover to 'top' + } + + SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms + SysTick->VAL = saveVal; // Restore SysTick value + +#elif defined (ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz) + + // Tried this with a timer/counter, couldn't quite get adequate + // resolution. So yay, you get a load of goofball NOPs... + + uint8_t *ptr, *end, p, bitMask; + uint32_t pinMask; + + pinMask = BIT(PIN_MAP[pin].gpio_bit); + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; + + volatile uint16_t *set = &(PIN_MAP[pin].gpio_device->regs->BSRRL); + volatile uint16_t *clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH); + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + for(;;) { + if(p & bitMask) { // ONE + // High 800ns + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop;"); + // Low 450ns + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop;"); + } else { // ZERO + // High 400ns + *set = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop;"); + // Low 850ns + *clr = pinMask; + asm("nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;"); + } + if(bitMask >>= 1) { + // Move on to the next pixel + asm("nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } +#if defined(NEO_KHZ400) + } else { // 400 KHz bitstream + // ToDo! + } +#endif + +#elif defined(TARGET_LPC1768) + uint8_t *ptr, *end, p, bitMask; + ptr = pixels; + end = ptr + numBytes; + p = *ptr++; + bitMask = 0x80; + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + for(;;) { + if(p & bitMask) { + // data ONE high + // min: 550 typ: 700 max: 5,500 + gpio_set(pin); + time::delay_ns(550); + // min: 450 typ: 600 max: 5,000 + gpio_clear(pin); + time::delay_ns(450); + } else { + // data ZERO high + // min: 200 typ: 350 max: 500 + gpio_set(pin); + time::delay_ns(200); + // data low + // min: 450 typ: 600 max: 5,000 + gpio_clear(pin); + time::delay_ns(450); + } + if(bitMask >>= 1) { + // Move on to the next pixel + asm("nop;"); + } else { + if(ptr >= end) break; + p = *ptr++; + bitMask = 0x80; + } + } +#if defined(NEO_KHZ400) + } else { // 400 KHz bitstream + // ToDo! + } +#endif +#elif defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) + uint8_t *p = pixels, *end = p + numBytes, + pix = *p++, mask = 0x80; + uint32_t cyc; + uint32_t saveLoad = SysTick->LOAD, saveVal = SysTick->VAL; +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + uint32_t top = (F_CPU / 800000); // 1.25µs + uint32_t t0 = top - (F_CPU / 2500000); // 0.4µs + uint32_t t1 = top - (F_CPU / 1250000); // 0.8µs + SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq + SysTick->VAL = 0; // Set to start value + for(;;) { + LL_GPIO_SetOutputPin(gpioPort, gpioPin); + cyc = (pix & mask) ? t1 : t0; + while(SysTick->VAL > cyc); + LL_GPIO_ResetOutputPin(gpioPort, gpioPin); + if(!(mask >>= 1)) { + if(p >= end) break; + pix = *p++; + mask = 0x80; + } + while(SysTick->VAL <= cyc); + } +#if defined(NEO_KHZ400) + } else { // 400 kHz bitstream + uint32_t top = (F_CPU / 400000); // 2.5µs + uint32_t t0 = top - (F_CPU / 2000000); // 0.5µs + uint32_t t1 = top - (F_CPU / 833333); // 1.2µs + SysTick->LOAD = top - 1; // Config SysTick for NeoPixel bit freq + SysTick->VAL = 0; // Set to start value + for(;;) { + LL_GPIO_SetOutputPin(gpioPort, gpioPin); + cyc = (pix & mask) ? t1 : t0; + while(SysTick->VAL > cyc); + LL_GPIO_ResetOutputPin(gpioPort, gpioPin); + if(!(mask >>= 1)) { + if(p >= end) break; + pix = *p++; + mask = 0x80; + } + while(SysTick->VAL <= cyc); + } + } +#endif // NEO_KHZ400 + SysTick->LOAD = saveLoad; // Restore SysTick rollover to 1 ms + SysTick->VAL = saveVal; // Restore SysTick value +#elif defined (NRF51) + uint8_t *p = pixels, + pix, count, mask; + int32_t num = numBytes; + unsigned int bitmask = ( 1 << g_ADigitalPinMap[pin] ); +// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/variants/BBCmicrobit/variant.cpp + + volatile unsigned int *reg = (unsigned int *) (0x50000000UL + 0x508); + +// https://github.com/sandeepmistry/arduino-nRF5/blob/dc53980c8bac27898fca90d8ecb268e11111edc1/cores/nRF5/SDK/components/device/nrf51.h +// http://www.iot-programmer.com/index.php/books/27-micro-bit-iot-in-c/chapters-micro-bit-iot-in-c/47-micro-bit-iot-in-c-fast-memory-mapped-gpio?showall=1 +// https://github.com/Microsoft/pxt-neopixel/blob/master/sendbuffer.asm + + asm volatile( + // "cpsid i" ; disable irq + + // b .start + "b L%=_start" "\n\t" + // .nextbit: ; C0 + "L%=_nextbit:" "\n\t" //; C0 + // str r1, [r3, #0] ; pin := hi C2 + "strb %[bitmask], [%[reg], #0]" "\n\t" //; pin := hi C2 + // tst r6, r0 ; C3 + "tst %[mask], %[pix]" "\n\t"// ; C3 + // bne .islate ; C4 + "bne L%=_islate" "\n\t" //; C4 + // str r1, [r2, #0] ; pin := lo C6 + "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C6 + // .islate: + "L%=_islate:" "\n\t" + // lsrs r6, r6, #1 ; r6 >>= 1 C7 + "lsr %[mask], %[mask], #1" "\n\t" //; r6 >>= 1 C7 + // bne .justbit ; C8 + "bne L%=_justbit" "\n\t" //; C8 + + // ; not just a bit - need new byte + // adds r4, #1 ; r4++ C9 + "add %[p], #1" "\n\t" //; r4++ C9 + // subs r5, #1 ; r5-- C10 + "sub %[num], #1" "\n\t" //; r5-- C10 + // bcc .stop ; if (r5<0) goto .stop C11 + "bcc L%=_stop" "\n\t" //; if (r5<0) goto .stop C11 + // .start: + "L%=_start:" + // movs r6, #0x80 ; reset mask C12 + "movs %[mask], #0x80" "\n\t" //; reset mask C12 + // nop ; C13 + "nop" "\n\t" //; C13 + + // .common: ; C13 + "L%=_common:" "\n\t" //; C13 + // str r1, [r2, #0] ; pin := lo C15 + "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo C15 + // ; always re-load byte - it just fits with the cycles better this way + // ldrb r0, [r4, #0] ; r0 := *r4 C17 + "ldrb %[pix], [%[p], #0]" "\n\t" //; r0 := *r4 C17 + // b .nextbit ; C20 + "b L%=_nextbit" "\n\t" //; C20 + + // .justbit: ; C10 + "L%=_justbit:" "\n\t" //; C10 + // ; no nops, branch taken is already 3 cycles + // b .common ; C13 + "b L%=_common" "\n\t" //; C13 + + // .stop: + "L%=_stop:" "\n\t" + // str r1, [r2, #0] ; pin := lo + "strb %[bitmask], [%[reg], #4]" "\n\t" //; pin := lo + // cpsie i ; enable irq + + : [p] "+r" (p), + [pix] "=&r" (pix), + [count] "=&r" (count), + [mask] "=&r" (mask), + [num] "+r" (num) + : [bitmask] "r" (bitmask), + [reg] "r" (reg) + ); + +#elif defined(__SAM3X8E__) // Arduino Due + + #define SCALE VARIANT_MCK / 2UL / 1000000UL + #define INST (2UL * F_CPU / VARIANT_MCK) + #define TIME_800_0 ((int)(0.40 * SCALE + 0.5) - (5 * INST)) + #define TIME_800_1 ((int)(0.80 * SCALE + 0.5) - (5 * INST)) + #define PERIOD_800 ((int)(1.25 * SCALE + 0.5) - (5 * INST)) + #define TIME_400_0 ((int)(0.50 * SCALE + 0.5) - (5 * INST)) + #define TIME_400_1 ((int)(1.20 * SCALE + 0.5) - (5 * INST)) + #define PERIOD_400 ((int)(2.50 * SCALE + 0.5) - (5 * INST)) + + int pinMask, time0, time1, period, t; + Pio *port; + volatile WoReg *portSet, *portClear, *timeValue, *timeReset; + uint8_t *p, *end, pix, mask; + + pmc_set_writeprotect(false); + pmc_enable_periph_clk((uint32_t)TC3_IRQn); + TC_Configure(TC1, 0, + TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1); + TC_Start(TC1, 0); + + pinMask = g_APinDescription[pin].ulPin; // Don't 'optimize' these into + port = g_APinDescription[pin].pPort; // declarations above. Want to + portSet = &(port->PIO_SODR); // burn a few cycles after + portClear = &(port->PIO_CODR); // starting timer to minimize + timeValue = &(TC1->TC_CHANNEL[0].TC_CV); // the initial 'while'. + timeReset = &(TC1->TC_CHANNEL[0].TC_CCR); + p = pixels; + end = p + numBytes; + pix = *p++; + mask = 0x80; + +#if defined(NEO_KHZ400) // 800 KHz check needed only if 400 KHz support enabled + if(is800KHz) { +#endif + time0 = TIME_800_0; + time1 = TIME_800_1; + period = PERIOD_800; +#if defined(NEO_KHZ400) + } else { // 400 KHz bitstream + time0 = TIME_400_0; + time1 = TIME_400_1; + period = PERIOD_400; + } +#endif + + for(t = time0;; t = time0) { + if(pix & mask) t = time1; + while(*timeValue < (unsigned)period); + *portSet = pinMask; + *timeReset = TC_CCR_CLKEN | TC_CCR_SWTRG; + while(*timeValue < (unsigned)t); + *portClear = pinMask; + if(!(mask >>= 1)) { // This 'inside-out' loop logic utilizes + if(p >= end) break; // idle time to minimize inter-byte delays. + pix = *p++; + mask = 0x80; + } + } + while(*timeValue < (unsigned)period); // Wait for last bit + TC_Stop(TC1, 0); + +#endif // end Due + +// END ARM ---------------------------------------------------------------- + + +#elif defined(ESP8266) || defined(ESP32) + +// ESP8266 ---------------------------------------------------------------- + + // ESP8266 show() is external to enforce ICACHE_RAM_ATTR execution + espShow(pin, pixels, numBytes, is800KHz); + +#elif defined(KENDRYTE_K210) + + k210Show(pin, pixels, numBytes, is800KHz); + +#elif defined(__ARDUINO_ARC__) + +// Arduino 101 ----------------------------------------------------------- + +#define NOPx7 { __builtin_arc_nop(); \ + __builtin_arc_nop(); __builtin_arc_nop(); \ + __builtin_arc_nop(); __builtin_arc_nop(); \ + __builtin_arc_nop(); __builtin_arc_nop(); } + + PinDescription *pindesc = &g_APinDescription[pin]; + register uint32_t loop = 8 * numBytes; // one loop to handle all bytes and all bits + register uint8_t *p = pixels; + register uint32_t currByte = (uint32_t) (*p); + register uint32_t currBit = 0x80 & currByte; + register uint32_t bitCounter = 0; + register uint32_t first = 1; + + // The loop is unusual. Very first iteration puts all the way LOW to the wire - + // constant LOW does not affect NEOPIXEL, so there is no visible effect displayed. + // During that very first iteration CPU caches instructions in the loop. + // Because of the caching process, "CPU slows down". NEOPIXEL pulse is very time sensitive + // that's why we let the CPU cache first and we start regular pulse from 2nd iteration + if (pindesc->ulGPIOType == SS_GPIO) { + register uint32_t reg = pindesc->ulGPIOBase + SS_GPIO_SWPORTA_DR; + uint32_t reg_val = __builtin_arc_lr((volatile uint32_t)reg); + register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId); + register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); + + loop += 1; // include first, special iteration + while(loop--) { + if(!first) { + currByte <<= 1; + bitCounter++; + } + + // 1 is >550ns high and >450ns low; 0 is 200..500ns high and >450ns low + __builtin_arc_sr(first ? reg_bit_low : reg_bit_high, (volatile uint32_t)reg); + if(currBit) { // ~400ns HIGH (740ns overall) + NOPx7 + NOPx7 + } + // ~340ns HIGH + NOPx7 + __builtin_arc_nop(); + + // 820ns LOW; per spec, max allowed low here is 5000ns */ + __builtin_arc_sr(reg_bit_low, (volatile uint32_t)reg); + NOPx7 + NOPx7 + + if(bitCounter >= 8) { + bitCounter = 0; + currByte = (uint32_t) (*++p); + } + + currBit = 0x80 & currByte; + first = 0; + } + } else if(pindesc->ulGPIOType == SOC_GPIO) { + register uint32_t reg = pindesc->ulGPIOBase + SOC_GPIO_SWPORTA_DR; + uint32_t reg_val = MMIO_REG_VAL(reg); + register uint32_t reg_bit_high = reg_val | (1 << pindesc->ulGPIOId); + register uint32_t reg_bit_low = reg_val & ~(1 << pindesc->ulGPIOId); + + loop += 1; // include first, special iteration + while(loop--) { + if(!first) { + currByte <<= 1; + bitCounter++; + } + MMIO_REG_VAL(reg) = first ? reg_bit_low : reg_bit_high; + if(currBit) { // ~430ns HIGH (740ns overall) + NOPx7 + NOPx7 + __builtin_arc_nop(); + } + // ~310ns HIGH + NOPx7 + + // 850ns LOW; per spec, max allowed low here is 5000ns */ + MMIO_REG_VAL(reg) = reg_bit_low; + NOPx7 + NOPx7 + + if(bitCounter >= 8) { + bitCounter = 0; + currByte = (uint32_t) (*++p); + } + + currBit = 0x80 & currByte; + first = 0; + } + } + +#else +#error Architecture not supported +#endif + + +// END ARCHITECTURE SELECT ------------------------------------------------ + +#if !( defined(NRF52) || defined(NRF52_SERIES) ) + interrupts(); +#endif + + endTime = micros(); // Save EOD time for latch on next call +} + +/*! + @brief Set/change the NeoPixel output pin number. Previous pin, + if any, is set to INPUT and the new pin is set to OUTPUT. + @param p Arduino pin number (-1 = no pin). +*/ +void Adafruit_NeoPixel::setPin(uint16_t p) { + if(begun && (pin >= 0)) pinMode(pin, INPUT); + pin = p; + if(begun) { + pinMode(p, OUTPUT); + digitalWrite(p, LOW); + } +#if defined(__AVR__) + port = portOutputRegister(digitalPinToPort(p)); + pinMask = digitalPinToBitMask(p); +#endif +#if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) + gpioPort = digitalPinToPort(p); + gpioPin = STM_LL_GPIO_PIN(digitalPinToPinName(p)); +#endif +} + +/*! + @brief Set a pixel's color using separate red, green and blue + components. If using RGBW pixels, white will be set to 0. + @param n Pixel index, starting from 0. + @param r Red brightness, 0 = minimum (off), 255 = maximum. + @param g Green brightness, 0 = minimum (off), 255 = maximum. + @param b Blue brightness, 0 = minimum (off), 255 = maximum. +*/ +void Adafruit_NeoPixel::setPixelColor( + uint16_t n, uint8_t r, uint8_t g, uint8_t b) { + + if(n < numLEDs) { + if(brightness) { // See notes in setBrightness() + r = (r * brightness) >> 8; + g = (g * brightness) >> 8; + b = (b * brightness) >> 8; + } + uint8_t *p; + if(wOffset == rOffset) { // Is an RGB-type strip + p = &pixels[n * 3]; // 3 bytes per pixel + } else { // Is a WRGB-type strip + p = &pixels[n * 4]; // 4 bytes per pixel + p[wOffset] = 0; // But only R,G,B passed -- set W to 0 + } + p[rOffset] = r; // R,G,B always stored + p[gOffset] = g; + p[bOffset] = b; + } +} + +/*! + @brief Set a pixel's color using separate red, green, blue and white + components (for RGBW NeoPixels only). + @param n Pixel index, starting from 0. + @param r Red brightness, 0 = minimum (off), 255 = maximum. + @param g Green brightness, 0 = minimum (off), 255 = maximum. + @param b Blue brightness, 0 = minimum (off), 255 = maximum. + @param w White brightness, 0 = minimum (off), 255 = maximum, ignored + if using RGB pixels. +*/ +void Adafruit_NeoPixel::setPixelColor( + uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + + if(n < numLEDs) { + if(brightness) { // See notes in setBrightness() + r = (r * brightness) >> 8; + g = (g * brightness) >> 8; + b = (b * brightness) >> 8; + w = (w * brightness) >> 8; + } + uint8_t *p; + if(wOffset == rOffset) { // Is an RGB-type strip + p = &pixels[n * 3]; // 3 bytes per pixel (ignore W) + } else { // Is a WRGB-type strip + p = &pixels[n * 4]; // 4 bytes per pixel + p[wOffset] = w; // Store W + } + p[rOffset] = r; // Store R,G,B + p[gOffset] = g; + p[bOffset] = b; + } +} + +/*! + @brief Set a pixel's color using a 32-bit 'packed' RGB or RGBW value. + @param n Pixel index, starting from 0. + @param c 32-bit color value. Most significant byte is white (for RGBW + pixels) or ignored (for RGB pixels), next is red, then green, + and least significant byte is blue. +*/ +void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) { + if(n < numLEDs) { + uint8_t *p, + r = (uint8_t)(c >> 16), + g = (uint8_t)(c >> 8), + b = (uint8_t)c; + if(brightness) { // See notes in setBrightness() + r = (r * brightness) >> 8; + g = (g * brightness) >> 8; + b = (b * brightness) >> 8; + } + if(wOffset == rOffset) { + p = &pixels[n * 3]; + } else { + p = &pixels[n * 4]; + uint8_t w = (uint8_t)(c >> 24); + p[wOffset] = brightness ? ((w * brightness) >> 8) : w; + } + p[rOffset] = r; + p[gOffset] = g; + p[bOffset] = b; + } +} + +/*! + @brief Fill all or part of the NeoPixel strip with a color. + @param c 32-bit color value. Most significant byte is white (for + RGBW pixels) or ignored (for RGB pixels), next is red, + then green, and least significant byte is blue. If all + arguments are unspecified, this will be 0 (off). + @param first Index of first pixel to fill, starting from 0. Must be + in-bounds, no clipping is performed. 0 if unspecified. + @param count Number of pixels to fill, as a positive value. Passing + 0 or leaving unspecified will fill to end of strip. +*/ +void Adafruit_NeoPixel::fill(uint32_t c, uint16_t first, uint16_t count) { + uint16_t i, end; + + if(first >= numLEDs) { + return; // If first LED is past end of strip, nothing to do + } + + // Calculate the index ONE AFTER the last pixel to fill + if(count == 0) { + // Fill to end of strip + end = numLEDs; + } else { + // Ensure that the loop won't go past the last pixel + end = first + count; + if(end > numLEDs) end = numLEDs; + } + + for(i = first; i < end; i++) { + this->setPixelColor(i, c); + } +} + +/*! + @brief Convert hue, saturation and value into a packed 32-bit RGB color + that can be passed to setPixelColor() or other RGB-compatible + functions. + @param hue An unsigned 16-bit value, 0 to 65535, representing one full + loop of the color wheel, which allows 16-bit hues to "roll + over" while still doing the expected thing (and allowing + more precision than the wheel() function that was common to + prior NeoPixel examples). + @param sat Saturation, 8-bit value, 0 (min or pure grayscale) to 255 + (max or pure hue). Default of 255 if unspecified. + @param val Value (brightness), 8-bit value, 0 (min / black / off) to + 255 (max or full brightness). Default of 255 if unspecified. + @return Packed 32-bit RGB with the most significant byte set to 0 -- the + white element of WRGB pixels is NOT utilized. Result is linearly + but not perceptually correct, so you may want to pass the result + through the gamma32() function (or your own gamma-correction + operation) else colors may appear washed out. This is not done + automatically by this function because coders may desire a more + refined gamma-correction function than the simplified + one-size-fits-all operation of gamma32(). Diffusing the LEDs also + really seems to help when using low-saturation colors. +*/ +uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) { + + uint8_t r, g, b; + + // Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover; + // 0 is not the start of pure red, but the midpoint...a few values above + // zero and a few below 65536 all yield pure red (similarly, 32768 is the + // midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values + // each for red, green, blue) really only allows for 1530 distinct hues + // (not 1536, more on that below), but the full unsigned 16-bit type was + // chosen for hue so that one's code can easily handle a contiguous color + // wheel by allowing hue to roll over in either direction. + hue = (hue * 1530L + 32768) / 65536; + // Because red is centered on the rollover point (the +32768 above, + // essentially a fixed-point +0.5), the above actually yields 0 to 1530, + // where 0 and 1530 would yield the same thing. Rather than apply a + // costly modulo operator, 1530 is handled as a special case below. + + // So you'd think that the color "hexcone" (the thing that ramps from + // pure red, to pure yellow, to pure green and so forth back to red, + // yielding six slices), and with each color component having 256 + // possible values (0-255), might have 1536 possible items (6*256), + // but in reality there's 1530. This is because the last element in + // each 256-element slice is equal to the first element of the next + // slice, and keeping those in there this would create small + // discontinuities in the color wheel. So the last element of each + // slice is dropped...we regard only elements 0-254, with item 255 + // being picked up as element 0 of the next slice. Like this: + // Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0 + // Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0 + // Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254 + // and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why + // the constants below are not the multiples of 256 you might expect. + + // Convert hue to R,G,B (nested ifs faster than divide+mod+switch): + if(hue < 510) { // Red to Green-1 + b = 0; + if(hue < 255) { // Red to Yellow-1 + r = 255; + g = hue; // g = 0 to 254 + } else { // Yellow to Green-1 + r = 510 - hue; // r = 255 to 1 + g = 255; + } + } else if(hue < 1020) { // Green to Blue-1 + r = 0; + if(hue < 765) { // Green to Cyan-1 + g = 255; + b = hue - 510; // b = 0 to 254 + } else { // Cyan to Blue-1 + g = 1020 - hue; // g = 255 to 1 + b = 255; + } + } else if(hue < 1530) { // Blue to Red-1 + g = 0; + if(hue < 1275) { // Blue to Magenta-1 + r = hue - 1020; // r = 0 to 254 + b = 255; + } else { // Magenta to Red-1 + r = 255; + b = 1530 - hue; // b = 255 to 1 + } + } else { // Last 0.5 Red (quicker than % operator) + r = 255; + g = b = 0; + } + + // Apply saturation and value to R,G,B, pack into 32-bit result: + uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255 + uint16_t s1 = 1 + sat; // 1 to 256; same reason + uint8_t s2 = 255 - sat; // 255 to 0 + return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) | + (((((g * s1) >> 8) + s2) * v1) & 0xff00) | + ( ((((b * s1) >> 8) + s2) * v1) >> 8); +} + +/*! + @brief Query the color of a previously-set pixel. + @param n Index of pixel to read (0 = first). + @return 'Packed' 32-bit RGB or WRGB value. Most significant byte is white + (for RGBW pixels) or 0 (for RGB pixels), next is red, then green, + and least significant byte is blue. + @note If the strip brightness has been changed from the default value + of 255, the color read from a pixel may not exactly match what + was previously written with one of the setPixelColor() functions. + This gets more pronounced at lower brightness levels. +*/ +uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const { + if(n >= numLEDs) return 0; // Out of bounds, return no color. + + uint8_t *p; + + if(wOffset == rOffset) { // Is RGB-type device + p = &pixels[n * 3]; + if(brightness) { + // Stored color was decimated by setBrightness(). Returned value + // attempts to scale back to an approximation of the original 24-bit + // value used when setting the pixel color, but there will always be + // some error -- those bits are simply gone. Issue is most + // pronounced at low brightness levels. + return (((uint32_t)(p[rOffset] << 8) / brightness) << 16) | + (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | + ( (uint32_t)(p[bOffset] << 8) / brightness ); + } else { + // No brightness adjustment has been made -- return 'raw' color + return ((uint32_t)p[rOffset] << 16) | + ((uint32_t)p[gOffset] << 8) | + (uint32_t)p[bOffset]; + } + } else { // Is RGBW-type device + p = &pixels[n * 4]; + if(brightness) { // Return scaled color + return (((uint32_t)(p[wOffset] << 8) / brightness) << 24) | + (((uint32_t)(p[rOffset] << 8) / brightness) << 16) | + (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | + ( (uint32_t)(p[bOffset] << 8) / brightness ); + } else { // Return raw color + return ((uint32_t)p[wOffset] << 24) | + ((uint32_t)p[rOffset] << 16) | + ((uint32_t)p[gOffset] << 8) | + (uint32_t)p[bOffset]; + } + } +} + + +/*! + @brief Adjust output brightness. Does not immediately affect what's + currently displayed on the LEDs. The next call to show() will + refresh the LEDs at this level. + @param b Brightness setting, 0=minimum (off), 255=brightest. + @note This was intended for one-time use in one's setup() function, + not as an animation effect in itself. Because of the way this + library "pre-multiplies" LED colors in RAM, changing the + brightness is often a "lossy" operation -- what you write to + pixels isn't necessary the same as what you'll read back. + Repeated brightness changes using this function exacerbate the + problem. Smart programs therefore treat the strip as a + write-only resource, maintaining their own state to render each + frame of an animation, not relying on read-modify-write. +*/ +void Adafruit_NeoPixel::setBrightness(uint8_t b) { + // Stored brightness value is different than what's passed. + // This simplifies the actual scaling math later, allowing a fast + // 8x8-bit multiply and taking the MSB. 'brightness' is a uint8_t, + // adding 1 here may (intentionally) roll over...so 0 = max brightness + // (color values are interpreted literally; no scaling), 1 = min + // brightness (off), 255 = just below max brightness. + uint8_t newBrightness = b + 1; + if(newBrightness != brightness) { // Compare against prior value + // Brightness has changed -- re-scale existing data in RAM, + // This process is potentially "lossy," especially when increasing + // brightness. The tight timing in the WS2811/WS2812 code means there + // aren't enough free cycles to perform this scaling on the fly as data + // is issued. So we make a pass through the existing color data in RAM + // and scale it (subsequent graphics commands also work at this + // brightness level). If there's a significant step up in brightness, + // the limited number of steps (quantization) in the old data will be + // quite visible in the re-scaled version. For a non-destructive + // change, you'll need to re-render the full strip data. C'est la vie. + uint8_t c, + *ptr = pixels, + oldBrightness = brightness - 1; // De-wrap old brightness value + uint16_t scale; + if(oldBrightness == 0) scale = 0; // Avoid /0 + else if(b == 255) scale = 65535 / oldBrightness; + else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness; + for(uint16_t i=0; i> 8; + } + brightness = newBrightness; + } +} + +/*! + @brief Retrieve the last-set brightness value for the strip. + @return Brightness value: 0 = minimum (off), 255 = maximum. +*/ +uint8_t Adafruit_NeoPixel::getBrightness(void) const { + return brightness - 1; +} + +/*! + @brief Fill the whole NeoPixel strip with 0 / black / off. +*/ +void Adafruit_NeoPixel::clear(void) { + memset(pixels, 0, numBytes); +} + +// A 32-bit variant of gamma8() that applies the same function +// to all components of a packed RGB or WRGB value. +uint32_t Adafruit_NeoPixel::gamma32(uint32_t x) { + uint8_t *y = (uint8_t *)&x; + // All four bytes of a 32-bit value are filtered even if RGB (not WRGB), + // to avoid a bunch of shifting and masking that would be necessary for + // properly handling different endianisms (and each byte is a fairly + // trivial operation, so it might not even be wasting cycles vs a check + // and branch for the RGB case). In theory this might cause trouble *if* + // someone's storing information in the unused most significant byte + // of an RGB value, but this seems exceedingly rare and if it's + // encountered in reality they can mask values going in or coming out. + for(uint8_t i=0; i<4; i++) y[i] = gamma8(y[i]); + return x; // Packed 32-bit return +} diff --git a/ModuleTests/testSensorPack/Adafruit_NeoPixel.h b/ModuleTests/testSensorPack/Adafruit_NeoPixel.h new file mode 100644 index 00000000..955be39a --- /dev/null +++ b/ModuleTests/testSensorPack/Adafruit_NeoPixel.h @@ -0,0 +1,366 @@ +/*! + * @file Adafruit_NeoPixel.h + * + * This is part of Adafruit's NeoPixel library for the Arduino platform, + * allowing a broad range of microcontroller boards (most AVR boards, + * many ARM devices, ESP8266 and ESP32, among others) to control Adafruit + * NeoPixels, FLORA RGB Smart Pixels and compatible devices -- WS2811, + * WS2812, WS2812B, SK6812, etc. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing products + * from Adafruit! + * + * Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries, + * with contributions by PJRC, Michael Miller and other members of the + * open source community. + * + * This file is part of the Adafruit_NeoPixel library. + * + * Adafruit_NeoPixel 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 3 of the + * License, or (at your option) any later version. + * + * Adafruit_NeoPixel 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 NeoPixel. If not, see + * . + * + */ + +#ifndef ADAFRUIT_NEOPIXEL_H +#define ADAFRUIT_NEOPIXEL_H + +#ifdef ARDUINO + #if (ARDUINO >= 100) + #include + #else + #include + #include + #endif +#endif + +#ifdef TARGET_LPC1768 + #include +#endif + +// The order of primary colors in the NeoPixel data stream can vary among +// device types, manufacturers and even different revisions of the same +// item. The third parameter to the Adafruit_NeoPixel constructor encodes +// the per-pixel byte offsets of the red, green and blue primaries (plus +// white, if present) in the data stream -- the following #defines provide +// an easier-to-use named version for each permutation. e.g. NEO_GRB +// indicates a NeoPixel-compatible device expecting three bytes per pixel, +// with the first byte transmitted containing the green value, second +// containing red and third containing blue. The in-memory representation +// of a chain of NeoPixels is the same as the data-stream order; no +// re-ordering of bytes is required when issuing data to the chain. +// Most of these values won't exist in real-world devices, but it's done +// this way so we're ready for it (also, if using the WS2811 driver IC, +// one might have their pixels set up in any weird permutation). + +// Bits 5,4 of this value are the offset (0-3) from the first byte of a +// pixel to the location of the red color byte. Bits 3,2 are the green +// offset and 1,0 are the blue offset. If it is an RGBW-type device +// (supporting a white primary in addition to R,G,B), bits 7,6 are the +// offset to the white byte...otherwise, bits 7,6 are set to the same value +// as 5,4 (red) to indicate an RGB (not RGBW) device. +// i.e. binary representation: +// 0bWWRRGGBB for RGBW devices +// 0bRRRRGGBB for RGB + +// RGB NeoPixel permutations; white and red offsets are always same +// Offset: W R G B +#define NEO_RGB ((0<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B +#define NEO_RBG ((0<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G +#define NEO_GRB ((1<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B +#define NEO_GBR ((2<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R +#define NEO_BRG ((1<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G +#define NEO_BGR ((2<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R + +// RGBW NeoPixel permutations; all 4 offsets are distinct +// Offset: W R G B +#define NEO_WRGB ((0<<6) | (1<<4) | (2<<2) | (3)) ///< Transmit as W,R,G,B +#define NEO_WRBG ((0<<6) | (1<<4) | (3<<2) | (2)) ///< Transmit as W,R,B,G +#define NEO_WGRB ((0<<6) | (2<<4) | (1<<2) | (3)) ///< Transmit as W,G,R,B +#define NEO_WGBR ((0<<6) | (3<<4) | (1<<2) | (2)) ///< Transmit as W,G,B,R +#define NEO_WBRG ((0<<6) | (2<<4) | (3<<2) | (1)) ///< Transmit as W,B,R,G +#define NEO_WBGR ((0<<6) | (3<<4) | (2<<2) | (1)) ///< Transmit as W,B,G,R + +#define NEO_RWGB ((1<<6) | (0<<4) | (2<<2) | (3)) ///< Transmit as R,W,G,B +#define NEO_RWBG ((1<<6) | (0<<4) | (3<<2) | (2)) ///< Transmit as R,W,B,G +#define NEO_RGWB ((2<<6) | (0<<4) | (1<<2) | (3)) ///< Transmit as R,G,W,B +#define NEO_RGBW ((3<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B,W +#define NEO_RBWG ((2<<6) | (0<<4) | (3<<2) | (1)) ///< Transmit as R,B,W,G +#define NEO_RBGW ((3<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G,W + +#define NEO_GWRB ((1<<6) | (2<<4) | (0<<2) | (3)) ///< Transmit as G,W,R,B +#define NEO_GWBR ((1<<6) | (3<<4) | (0<<2) | (2)) ///< Transmit as G,W,B,R +#define NEO_GRWB ((2<<6) | (1<<4) | (0<<2) | (3)) ///< Transmit as G,R,W,B +#define NEO_GRBW ((3<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B,W +#define NEO_GBWR ((2<<6) | (3<<4) | (0<<2) | (1)) ///< Transmit as G,B,W,R +#define NEO_GBRW ((3<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R,W + +#define NEO_BWRG ((1<<6) | (2<<4) | (3<<2) | (0)) ///< Transmit as B,W,R,G +#define NEO_BWGR ((1<<6) | (3<<4) | (2<<2) | (0)) ///< Transmit as B,W,G,R +#define NEO_BRWG ((2<<6) | (1<<4) | (3<<2) | (0)) ///< Transmit as B,R,W,G +#define NEO_BRGW ((3<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G,W +#define NEO_BGWR ((2<<6) | (3<<4) | (1<<2) | (0)) ///< Transmit as B,G,W,R +#define NEO_BGRW ((3<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R,W + +// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device. +// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is +// the default if unspecified. Because flash space is very limited on ATtiny +// devices (e.g. Trinket, Gemma), v1 NeoPixels aren't handled by default on +// those chips, though it can be enabled by removing the ifndef/endif below, +// but code will be bigger. Conversely, can disable the NEO_KHZ400 line on +// other MCUs to remove v1 support and save a little space. + +#define NEO_KHZ800 0x0000 ///< 800 KHz data transmission +#ifndef __AVR_ATtiny85__ +#define NEO_KHZ400 0x0100 ///< 400 KHz data transmission +#endif + +// If 400 KHz support is enabled, the third parameter to the constructor +// requires a 16-bit value (in order to select 400 vs 800 KHz speed). +// If only 800 KHz is enabled (as is default on ATtiny), an 8-bit value +// is sufficient to encode pixel color order, saving some space. + +#ifdef NEO_KHZ400 +typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor +#else +typedef uint8_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor +#endif + +// These two tables are declared outside the Adafruit_NeoPixel class +// because some boards may require oldschool compilers that don't +// handle the C++11 constexpr keyword. + +/* A PROGMEM (flash mem) table containing 8-bit unsigned sine wave (0-255). + Copy & paste this snippet into a Python REPL to regenerate: +import math +for x in range(256): + print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))), + if x&15 == 15: print +*/ +static const uint8_t PROGMEM _NeoPixelSineTable[256] = { + 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173, + 176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215, + 218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244, + 245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255, + 255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246, + 245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220, + 218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179, + 176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131, + 128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82, + 79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, + 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, + 10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, + 10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, + 37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, + 79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124}; + +/* Similar to above, but for an 8-bit gamma-correction table. + Copy & paste this snippet into a Python REPL to regenerate: +import math +gamma=2.6 +for x in range(256): + print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))), + if x&15 == 15: print +*/ +static const uint8_t PROGMEM _NeoPixelGammaTable[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, + 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, + 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, + 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29, + 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, + 76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, + 97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120, + 122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148, + 150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180, + 182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215, + 218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255}; + +/*! + @brief Class that stores state and functions for interacting with + Adafruit NeoPixels and compatible devices. +*/ +class Adafruit_NeoPixel { + + public: + + // Constructor: number of LEDs, pin number, LED type + Adafruit_NeoPixel(uint16_t n, uint16_t pin=6, + neoPixelType type=NEO_GRB + NEO_KHZ800); + Adafruit_NeoPixel(void); + ~Adafruit_NeoPixel(); + + void begin(void); + void show(void); + void setPin(uint16_t p); + void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); + void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, + uint8_t w); + void setPixelColor(uint16_t n, uint32_t c); + void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0); + void setBrightness(uint8_t); + void clear(void); + void updateLength(uint16_t n); + void updateType(neoPixelType t); + /*! + @brief Check whether a call to show() will start sending data + immediately or will 'block' for a required interval. NeoPixels + require a short quiet time (about 300 microseconds) after the + last bit is received before the data 'latches' and new data can + start being received. Usually one's sketch is implicitly using + this time to generate a new frame of animation...but if it + finishes very quickly, this function could be used to see if + there's some idle time available for some low-priority + concurrent task. + @return 1 or true if show() will start sending immediately, 0 or false + if show() would block (meaning some idle time is available). + */ + bool canShow(void) { + if (endTime > micros()) { + endTime = micros(); + } + return (micros() - endTime) >= 300L; + } + /*! + @brief Get a pointer directly to the NeoPixel data buffer in RAM. + Pixel data is stored in a device-native format (a la the NEO_* + constants) and is not translated here. Applications that access + this buffer will need to be aware of the specific data format + and handle colors appropriately. + @return Pointer to NeoPixel buffer (uint8_t* array). + @note This is for high-performance applications where calling + setPixelColor() on every single pixel would be too slow (e.g. + POV or light-painting projects). There is no bounds checking + on the array, creating tremendous potential for mayhem if one + writes past the ends of the buffer. Great power, great + responsibility and all that. + */ + uint8_t *getPixels(void) const { return pixels; }; + uint8_t getBrightness(void) const; + /*! + @brief Retrieve the pin number used for NeoPixel data output. + @return Arduino pin number (-1 if not set). + */ + int16_t getPin(void) const { return pin; }; + /*! + @brief Return the number of pixels in an Adafruit_NeoPixel strip object. + @return Pixel count (0 if not set). + */ + uint16_t numPixels(void) const { return numLEDs; } + uint32_t getPixelColor(uint16_t n) const; + /*! + @brief An 8-bit integer sine wave function, not directly compatible + with standard trigonometric units like radians or degrees. + @param x Input angle, 0-255; 256 would loop back to zero, completing + the circle (equivalent to 360 degrees or 2 pi radians). + One can therefore use an unsigned 8-bit variable and simply + add or subtract, allowing it to overflow/underflow and it + still does the expected contiguous thing. + @return Sine result, 0 to 255, or -128 to +127 if type-converted to + a signed int8_t, but you'll most likely want unsigned as this + output is often used for pixel brightness in animation effects. + */ + static uint8_t sine8(uint8_t x) { + return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out + } + /*! + @brief An 8-bit gamma-correction function for basic pixel brightness + adjustment. Makes color transitions appear more perceptially + correct. + @param x Input brightness, 0 (minimum or off/black) to 255 (maximum). + @return Gamma-adjusted brightness, can then be passed to one of the + setPixelColor() functions. This uses a fixed gamma correction + exponent of 2.6, which seems reasonably okay for average + NeoPixels in average tasks. If you need finer control you'll + need to provide your own gamma-correction function instead. + */ + static uint8_t gamma8(uint8_t x) { + return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out + } + /*! + @brief Convert separate red, green and blue values into a single + "packed" 32-bit RGB color. + @param r Red brightness, 0 to 255. + @param g Green brightness, 0 to 255. + @param b Blue brightness, 0 to 255. + @return 32-bit packed RGB value, which can then be assigned to a + variable for later use or passed to the setPixelColor() + function. Packed RGB format is predictable, regardless of + LED strand color order. + */ + static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + } + /*! + @brief Convert separate red, green, blue and white values into a + single "packed" 32-bit WRGB color. + @param r Red brightness, 0 to 255. + @param g Green brightness, 0 to 255. + @param b Blue brightness, 0 to 255. + @param w White brightness, 0 to 255. + @return 32-bit packed WRGB value, which can then be assigned to a + variable for later use or passed to the setPixelColor() + function. Packed WRGB format is predictable, regardless of + LED strand color order. + */ + static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + } + static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255); + /*! + @brief A gamma-correction function for 32-bit packed RGB or WRGB + colors. Makes color transitions appear more perceptially + correct. + @param x 32-bit packed RGB or WRGB color. + @return Gamma-adjusted packed color, can then be passed in one of the + setPixelColor() functions. Like gamma8(), this uses a fixed + gamma correction exponent of 2.6, which seems reasonably okay + for average NeoPixels in average tasks. If you need finer + control you'll need to provide your own gamma-correction + function instead. + */ + static uint32_t gamma32(uint32_t x); + + protected: + +#ifdef NEO_KHZ400 // If 400 KHz NeoPixel support enabled... + bool is800KHz; ///< true if 800 KHz pixels +#endif + bool begun; ///< true if begin() previously called + uint16_t numLEDs; ///< Number of RGB LEDs in strip + uint16_t numBytes; ///< Size of 'pixels' buffer below + int16_t pin; ///< Output pin number (-1 if not yet set) + uint8_t brightness; ///< Strip brightness 0-255 (stored as +1) + uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each) + uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel + uint8_t gOffset; ///< Index of green byte + uint8_t bOffset; ///< Index of blue byte + uint8_t wOffset; ///< Index of white (==rOffset if no white) + uint32_t endTime; ///< Latch timing reference +#ifdef __AVR__ + volatile uint8_t *port; ///< Output PORT register + uint8_t pinMask; ///< Output PORT bitmask +#endif +#if defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_ARDUINO_CORE_STM32) + GPIO_TypeDef *gpioPort; ///< Output GPIO PORT + uint32_t gpioPin; ///< Output GPIO PIN +#endif +}; + +#endif // ADAFRUIT_NEOPIXEL_H diff --git a/ModuleTests/testSensorPack/Arduino_APDS9960.cpp b/ModuleTests/testSensorPack/Arduino_APDS9960.cpp new file mode 100644 index 00000000..6583f316 --- /dev/null +++ b/ModuleTests/testSensorPack/Arduino_APDS9960.cpp @@ -0,0 +1,443 @@ +/* + This file is part of the Arduino_APDS9960 library. + Copyright (c) 2019 Arduino SA. All rights 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 Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "Arduino_APDS9960.h" + +APDS9960::APDS9960(TwoWire& wire, int intPin) : + _wire(wire), + _intPin(intPin), + _gestureEnabled(false), + _proximityEnabled(false), + _colorEnabled(false), + _gestureIn(false), + _gestureDirectionX(0), + _gestureDirectionY(0), + _gestureDirInX(0), + _gestureDirInY(0), + _gestureSensitivity(20), + _detectedGesture(GESTURE_NONE) +{ +} + +APDS9960::~APDS9960() +{ +} + +bool APDS9960::begin() { + _wire.begin(); + + // Check ID register + uint8_t id; + if (!getID(&id)) return false; + Serial.println(id,HEX); + if (id!=0xAB) return false; + + // Disable everything + if (!setENABLE(0x00)) return false; + if (!setWTIME(0xFF)) return false; + if (!setGPULSE(0x8F)) return false; // 16us, 16 pulses // default is: 0x40 = 8us, 1 pulse + if (!setPPULSE(0x8F)) return false; // 16us, 16 pulses // default is: 0x40 = 8us, 1 pulse + if (!setGestureIntEnable(true)) return false; + if (!setGestureMode(true)) return false; + if (!enablePower()) return false; + if (!enableWait()) return false; + // set ADC integration time to 10 ms + if (!setATIME(256 - (10 / 2.78))) return false; + // set ADC gain 4x (0x00 => 1x, 0x01 => 4x, 0x02 => 16x, 0x03 => 64x) + if (!setCONTROL(0x02)) return false; + delay(10); + // enable power + if (!enablePower()) return false; + + if (_intPin > -1) { + pinMode(_intPin, INPUT); + } + + return true; +} + +void APDS9960::end() { + // Disable everything + setENABLE(0x00); + + _gestureEnabled = false; + + _wire.end(); +} + +// Sets the LED current boost value: +// 0=100%, 1=150%, 2=200%, 3=300% +bool APDS9960::setLEDBoost(uint8_t boost) { + uint8_t r; + if (!getCONFIG2(&r)) return false; + r &= 0b11001111; + r |= (boost << 4) & 0b00110000; + return setCONFIG2(r); +} + +void APDS9960::setGestureSensitivity(uint8_t sensitivity) { + if (sensitivity > 100) sensitivity = 100; + _gestureSensitivity = 100 - sensitivity; +} + +void APDS9960::setInterruptPin(int pin) { + _intPin = pin; +} + +bool APDS9960::setGestureIntEnable(bool en) { + uint8_t r; + if (!getGCONF4(&r)) return false; + if (en) { + r |= 0b00000010; + } else { + r &= 0b11111101; + } + return setGCONF4(r); +} + +bool APDS9960::setGestureMode(bool en) +{ + uint8_t r; + if (!getGCONF4(&r)) return false; + if (en) { + r |= 0b00000001; + } else { + r &= 0b11111110; + } + return setGCONF4(r); +} + +bool APDS9960::enablePower() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b00000001) != 0) return true; + r |= 0b00000001; + return setENABLE(r); +} + +bool APDS9960::disablePower() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b00000001) == 0) return true; + r &= 0b11111110; + return setENABLE(r); +} + +bool APDS9960::enableColor() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b00000010) != 0) { + _colorEnabled = true; + return true; + } + r |= 0b00000010; + bool res = setENABLE(r); + _colorEnabled = res; + return res; +} + +bool APDS9960::disableColor() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b00000010) == 0) { + _colorEnabled = false; + return true; + } + r &= 0b11111101; + bool res = setENABLE(r); + _colorEnabled = !res; // (res == true) if successfully disabled + return res; +} + +bool APDS9960::enableProximity() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b00000100) != 0) { + _proximityEnabled = true; + return true; + } + r |= 0b00000100; + bool res = setENABLE(r); + _proximityEnabled = res; + return res; +} + +bool APDS9960::disableProximity() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b00000100) == 0) { + _proximityEnabled = false; + return true; + } + r &= 0b11111011; + bool res = setENABLE(r); + _proximityEnabled = !res; // (res == true) if successfully disabled + return res; +} + +bool APDS9960::enableWait() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b00001000) != 0) return true; + r |= 0b00001000; + return setENABLE(r); +} + +bool APDS9960::disableWait() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b00001000) == 0) return true; + r &= 0b11110111; + return setENABLE(r); +} + +bool APDS9960::enableGesture() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b01000000) != 0) { + _gestureEnabled = true; + return true; + } + r |= 0b01000000; + bool res = setENABLE(r); + _gestureEnabled = res; + return res; +} + +bool APDS9960::disableGesture() { + uint8_t r; + if (!getENABLE(&r)) return false; + if ((r & 0b01000000) == 0) { + _gestureEnabled = false; + return true; + } + r &= 0b10111111; + bool res = setENABLE(r); + _gestureEnabled = !res; // (res == true) if successfully disabled + return res; +} + +#define APDS9960_ADDR 0x39 + +bool APDS9960::write(uint8_t val) { + _wire.beginTransmission(APDS9960_ADDR); + _wire.write(val); + return _wire.endTransmission() == 0; +} + +bool APDS9960::write(uint8_t reg, uint8_t val) { + _wire.beginTransmission(APDS9960_ADDR); + _wire.write(reg); + _wire.write(val); + return _wire.endTransmission() == 0; +} + +bool APDS9960::read(uint8_t reg, uint8_t *val) { + if (!write(reg)) return false; + _wire.requestFrom(APDS9960_ADDR, 1); + if (!_wire.available()) return false; + *val = _wire.read(); + return true; +} + +size_t APDS9960::readBlock(uint8_t reg, uint8_t *val, unsigned int len) { + size_t i = 0; + if (!write(reg)) return 0; + _wire.requestFrom((uint8_t)APDS9960_ADDR, len); + while (_wire.available()) { + if (i == len) return 0; + val[i++] = _wire.read(); + } + return i; +} + +int APDS9960::gestureFIFOAvailable() { + uint8_t r; + if (!getGSTATUS(&r)) return -1; + if ((r & 0x01) == 0x00) return -2; + if (!getGFLVL(&r)) return -3; + return r; +} + +int APDS9960::handleGesture() { + const int gestureThreshold = 30; + while (true) { + int available = gestureFIFOAvailable(); + if (available <= 0) return 0; + + uint8_t fifo_data[128]; + uint8_t bytes_read = readGFIFO_U(fifo_data, available * 4); + if (bytes_read == 0) return 0; + + for (int i = 0; i+3 < bytes_read; i+=4) { + uint8_t u,d,l,r; + u = fifo_data[i]; + d = fifo_data[i+1]; + l = fifo_data[i+2]; + r = fifo_data[i+3]; + // Serial.print(u); + // Serial.print(","); + // Serial.print(d); + // Serial.print(","); + // Serial.print(l); + // Serial.print(","); + // Serial.println(r); + + if (u _gestureSensitivity) { _detectedGesture = GESTURE_RIGHT; } + if (totalY < -_gestureSensitivity) { _detectedGesture = GESTURE_DOWN; } + if (totalY > _gestureSensitivity) { _detectedGesture = GESTURE_UP; } + _gestureDirectionX = 0; + _gestureDirectionY = 0; + _gestureDirInX = 0; + _gestureDirInY = 0; + } + continue; + } + + _gestureDirectionX = r - l; + _gestureDirectionY = u - d; + if (_gestureIn) { + _gestureIn = false; + _gestureDirInX = _gestureDirectionX; + _gestureDirInY = _gestureDirectionY; + // Serial.print("IN "); + // Serial.print(_gestureDirInX); + // Serial.print(","); + // Serial.print(_gestureDirInY); + // Serial.print(" "); + } + } + } +} + +int APDS9960::gestureAvailable() { + if (!_gestureEnabled) enableGesture(); + + if (_intPin > -1) { + if (digitalRead(_intPin) != LOW) { + return 0; + } + } else if (gestureFIFOAvailable() <= 0) { + return 0; + } + + handleGesture(); + if (_proximityEnabled) { + setGestureMode(false); + } + return (_detectedGesture == GESTURE_NONE) ? 0 : 1; +} + +int APDS9960::readGesture() { + int gesture = _detectedGesture; + + _detectedGesture = GESTURE_NONE; + + return gesture; +} + +int APDS9960::colorAvailable() { + uint8_t r; + + enableColor(); + + if (!getSTATUS(&r)) { + return 0; + } + + if (r & 0b00000001) { + return 1; + } + + return 0; +} + +bool APDS9960::readColor(int& r, int& g, int& b) { + int c; + + return readColor(r, g, b, c); +} + +bool APDS9960::readColor(int& r, int& g, int& b, int& c) { + uint16_t colors[4]; + + if (!readCDATAL((uint8_t *)colors, sizeof(colors))) { + r = -1; + g = -1; + b = -1; + c = -1; + + return false; + } + + c = colors[0]; + r = colors[1]; + g = colors[2]; + b = colors[3]; + + disableColor(); + + return true; +} + +int APDS9960::proximityAvailable() { + uint8_t r; + + enableProximity(); + + if (!getSTATUS(&r)) { + return 0; + } + + if (r & 0b00000010) { + return 1; + } + + return 0; +} + +int APDS9960::readProximity() { + uint8_t r; + + if (!getPDATA(&r)) { + return -1; + } + + disableProximity(); + + return (255 - r); +} + +#if defined(APDS9960_INT_PIN) +APDS9960 APDS(APDS9960_WIRE_INSTANCE, APDS9960_INT_PIN); +#elif defined(ARDUINO_ARDUINO_NANO33BLE) +APDS9960 APDS(Wire1, PIN_INT_APDS); +#else +APDS9960 APDS(Wire, -1); +#endif diff --git a/ModuleTests/testSensorPack/Arduino_APDS9960.h b/ModuleTests/testSensorPack/Arduino_APDS9960.h new file mode 100644 index 00000000..f81f05ad --- /dev/null +++ b/ModuleTests/testSensorPack/Arduino_APDS9960.h @@ -0,0 +1,153 @@ +/* + This file is part of the Arduino_APDS9960 library. + Copyright (c) 2019 Arduino SA. All rights 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 Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ARDUINO_APDS9960 +#define ARDUINO_APDS9960 + +#include +#include + +enum { + GESTURE_NONE = -1, + GESTURE_UP = 0, + GESTURE_DOWN = 1, + GESTURE_LEFT = 2, + GESTURE_RIGHT = 3 +}; + +class APDS9960 { +public: + APDS9960(TwoWire &wire, int intPin); + virtual ~APDS9960(); + + bool begin(); + void end(); + + int gestureAvailable(); + int readGesture(); + + int colorAvailable(); + bool readColor(int& r, int& g, int& b); + bool readColor(int& r, int& g, int& b, int& c); + + int proximityAvailable(); + int readProximity(); + + void setGestureSensitivity(uint8_t sensitivity); + + void setInterruptPin(int pin); + + bool setLEDBoost(uint8_t boost); + +private: + bool setGestureIntEnable(bool en); + bool setGestureMode(bool en); + int gestureFIFOAvailable(); + int handleGesture(); + + bool enablePower(); + bool disablePower(); + bool enableColor(); + bool disableColor(); + bool enableProximity(); + bool disableProximity(); + bool enableWait(); + bool disableWait(); + bool enableGesture(); + bool disableGesture(); + +private: + TwoWire& _wire; + int _intPin; + + bool _gestureEnabled; + bool _proximityEnabled; + bool _colorEnabled; + bool _gestureIn; + int _gestureDirectionX; + int _gestureDirectionY; + int _gestureDirInX; + int _gestureDirInY; + int _gestureSensitivity; + int _detectedGesture; + + bool write(uint8_t val); + bool write(uint8_t reg, uint8_t val); + bool read(uint8_t reg, uint8_t *val); + size_t readBlock(uint8_t reg, uint8_t *val, unsigned int len); + +private: +#define REG(name, addr) \ + bool get##name(uint8_t *val) { return read(addr, val); } \ + bool set##name(uint8_t val) { return write(addr, val); } \ + size_t read##name(uint8_t *val, uint8_t len) { return readBlock(addr, val, len); } + REG(ENABLE, 0x80) + REG(ATIME, 0x81) + REG(WTIME, 0x83) + REG(AILTL, 0x84) + REG(AILTH, 0x85) + REG(AIHTL, 0x86) + REG(AIHTH, 0x87) + REG(PILT, 0x89) + REG(PIHT, 0x8B) + REG(PERS, 0x8C) + REG(CONFIG1, 0x8D) + REG(PPULSE, 0x8E) + REG(CONTROL, 0x8F) + REG(CONFIG2, 0x90) + REG(ID, 0x92) + REG(STATUS, 0x93) + REG(CDATAL, 0x94) + REG(CDATAH, 0x95) + REG(RDATAL, 0x96) + REG(RDATAH, 0x97) + REG(GDATAL, 0x98) + REG(GDATAH, 0x99) + REG(BDATAL, 0x9A) + REG(BDATAH, 0x9B) + REG(PDATA, 0x9C) + REG(POFFSET_UR, 0x9D) + REG(POFFSET_DL, 0x9E) + REG(CONFIG3, 0x9F) + REG(GPENTH, 0xA0) + REG(GEXTH, 0xA1) + REG(GCONF1, 0xA2) + REG(GCONF2, 0xA3) + REG(GOFFSET_U, 0xA4) + REG(GOFFSET_D, 0xA5) + REG(GPULSE, 0xA6) + REG(GOFFSET_L, 0xA7) + REG(GOFFSET_R, 0xA9) + REG(GCONF3, 0xAA) + REG(GCONF4, 0xAB) + REG(GFLVL, 0xAE) + REG(GSTATUS, 0xAF) + REG(IFORCE, 0xE4) + REG(PICLEAR, 0xE5) + REG(CICLEAR, 0xE6) + REG(AICLEAR, 0xE7) + REG(GFIFO_U, 0xFC) + REG(GFIFO_D, 0xFD) + REG(GFIFO_L, 0xFE) + REG(GFIFO_R, 0xFF) +}; + +extern APDS9960 APDS; + +#endif // ARDUINO_APDS9960 diff --git a/ModuleTests/testSensorPack/infrared.h b/ModuleTests/testSensorPack/infrared.h new file mode 100644 index 00000000..f9e03632 --- /dev/null +++ b/ModuleTests/testSensorPack/infrared.h @@ -0,0 +1,162 @@ +#define IR_PIN 4 +#include "src/IRremote.h" + +//#include +//The included library is identical to the IRremote library by shirriff, version 2.6.1 +//Source: https://github.com/Arduino-IRremote/Arduino-IRremote +//Here, we include the decoding functions in our folder only to make it more convenient for newbie users +//All rights belong to the original author, and we follow the MIT license. +//You no longer need to modify ~/Documents/Arduino/libraries/src/IRremote/IRremote.h as mentioned in our old manual. +bool makeSound = false; +bool printValue = false; +IRrecv irrecv(IR_PIN); +decode_results results; +//abbreviation //gait/posture/function names +#define K00 "0" //rest and shutdown all servos +#define K01 "1" //forward +#define K02 "2" //turn off gyro feedback to boost speed + +#define K10 "3" //left +#define K11 "4" //neutral stand up posture +#define K12 "5" //right + +#define K20 "p" //pause motion and shut off all servos +#define K21 "B" //backward +#define K22 "c" //calibration mode with IMU turned off + +#define K30 "vt" //stepping +#define K31 "cr" //crawl +#define K32 "wk" //walk + +#define K40 "tr" //trot +#define K41 "sit" //sit +#define K42 "str" //stretch + +#define K50 "Old" //greeting +#define K51 "pu" //push up +#define K52 "pee" //standng with three legs + +#define K60 "New" //"lu" //look up +#define K61 "p" //butt up +#define K62 "s" //call your customized Newbility saved to PROGMEM +#define KREPEAT "!" + +#define SHORT_ENCODING // activating this line will use a shorter encoding of the HEX values +String translateIR() // takes action based on IR code received +// describing Remote IR codes. +{ +#ifndef SHORT_ENCODING + switch (results.value) { + //IR signal key on IR remote //key mapping + case 0xFFA25D: /*PTLF(" CH-"); */ return (F(K00)); + case 0xFF629D: /*PTLF(" CH"); */ return (F(K01)); + case 0xFFE21D: /*PTLF(" CH+"); */ return (F(K02)); + + case 0xFF22DD: /*PTLF(" |<<"); */ return (F(K10)); + case 0xFF02FD: /*PTLF(" >>|"); */ return (F(K11)); + case 0xFFC23D: /*PTLF(" >||"); */ return (F(K12)); + + case 0xFFE01F: /*PTLF(" -"); */ return (F(K20)); + case 0xFFA857: /*PTLF(" +"); */ return (F(K21)); + case 0xFF906F: /*PTLF(" EQ"); */ return (F(K22)); + + case 0xFF6897: /*PTLF(" 0"); */ return (F(K30)); + case 0xFF9867: /*PTLF(" 100+"); */ return (F(K31)); + case 0xFFB04F: /*PTLF(" 200+"); */ return (F(K32)); + + case 0xFF30CF: /*PTLF(" 1"); */ return (F(K40)); + case 0xFF18E7: /*PTLF(" 2"); */ return (F(K41)); + case 0xFF7A85: /*PTLF(" 3"); */ return (F(K42)); + + case 0xFF10EF: /*PTLF(" 4"); */ return (F(K50)); + case 0xFF38C7: /*PTLF(" 5"); */ return (F(K51)); + case 0xFF5AA5: /*PTLF(" 6"); */ return (F(K52)); + + case 0xFF42BD: /*PTLF(" 7"); */ return (F(K60)); + case 0xFF4AB5: /*PTLF(" 8"); */ return (F(K61)); + case 0xFF52AD: /*PTLF(" 9"); */ return (F(K62)); + + case 0xFFFFFFFF: return (F(KREPEAT)); //Serial.println(" REPEAT"); +#else + uint8_t trimmed = (results.value >> 8); + switch (trimmed) { + //IR signal key on IR remote //key mapping + case 0xA2: /*PTLF(" CH-"); */ return (F(K00)); + case 0x62: /*PTLF(" CH"); */ return (F(K01)); + case 0xE2: /*PTLF(" CH+"); */ return (F(K02)); + + case 0x22: /*PTLF(" |<<"); */ return (F(K10)); + case 0x02: /*PTLF(" >>|"); */ return (F(K11)); + case 0xC2: /*PTLF(" >||"); */ return (F(K12)); + + case 0xE0: /*PTLF(" -"); */ return (F(K20)); + case 0xA8: /*PTLF(" +"); */ return (F(K21)); + case 0x90: /*PTLF(" EQ"); */ return (F(K22)); + + case 0x68: /*PTLF(" 0"); */ return (F(K30)); + case 0x98: /*PTLF(" 100+"); */ return (F(K31)); + case 0xB0: /*PTLF(" 200+"); */ return (F(K32)); + + case 0x30: /*PTLF(" 1"); */ return (F(K40)); + case 0x18: /*PTLF(" 2"); */ return (F(K41)); + case 0x7A: /*PTLF(" 3"); */ return (F(K42)); + + case 0x10: /*PTLF(" 4"); */ return (F(K50)); + case 0x38: /*PTLF(" 5"); */ return (F(K51)); + case 0x5A: /*PTLF(" 6"); */ return (F(K52)); + + case 0x42: /*PTLF(" 7"); */ return (F(K60)); + case 0x4A: /*PTLF(" 8"); */ return (F(K61)); + case 0x52: /*PTLF(" 9"); */ return (F(K62)); + + case 0x12: return ("ts"); //for factory use + + case 0xFF: return (F(KREPEAT)); //Serial.println(" REPEAT"); +#endif + default: + { + //Serial.println(results.value, HEX); + } + return ""; //Serial.println("null"); + } // End Case + //delay(100); // Do not get immediate repeat //no need because the main loop is slow + + // The control could be organized in another way, such as: + // forward/backward to change the gaits corresponding to different speeds. + // left/right key for turning left and right + // number keys for different postures or behaviors +} + +String testCase[] = { "testTouch", "testI2Cdevice", "testLight", "testIrDistance", "testPIR", "testNoise" }; + +void read_infrared() { + if (irrecv.decode(&results)) { + Serial.println(instruction); + String value = translateIR(); + if (value == "s") { + makeSound = !makeSound; + if (makeSound) + playMelody(soundOn, sizeof(soundOn) / 2); + else + playMelody(mute, sizeof(mute) / 2); + } else if (value == "p") { + printValue = !printValue; + if (printValue) + Serial.println("Print enabled"); + else + Serial.println("Print disabled"); + } else if (value == "New") { + compatible_3V = true; + Serial.println("New 3.3V"); + } else if (value == "Old") { + compatible_3V = false; + Serial.println("Old 5V"); + } else + testID = value[0] - '0' >= 0 && value[0] - '0' < 6 ? value[0] - '0' : testID; + + Serial.print("Change mode "); + Serial.println(testCase[testID]); + irrecv.resume(); // receive the next value + } + delay(50); +} diff --git a/ModuleTests/testSensorPack/pitches.h b/ModuleTests/testSensorPack/pitches.h new file mode 100644 index 00000000..5643c89a --- /dev/null +++ b/ModuleTests/testSensorPack/pitches.h @@ -0,0 +1,93 @@ +/************************************************* + Public Constants + *************************************************/ + +#define NOTE_B0 31 +#define NOTE_C1 33 +#define NOTE_CS1 35 +#define NOTE_D1 37 +#define NOTE_DS1 39 +#define NOTE_E1 41 +#define NOTE_F1 44 +#define NOTE_FS1 46 +#define NOTE_G1 49 +#define NOTE_GS1 52 +#define NOTE_A1 55 +#define NOTE_AS1 58 +#define NOTE_B1 62 +#define NOTE_C2 65 +#define NOTE_CS2 69 +#define NOTE_D2 73 +#define NOTE_DS2 78 +#define NOTE_E2 82 +#define NOTE_F2 87 +#define NOTE_FS2 93 +#define NOTE_G2 98 +#define NOTE_GS2 104 +#define NOTE_A2 110 +#define NOTE_AS2 117 +#define NOTE_B2 123 +#define NOTE_C3 131 +#define NOTE_CS3 139 +#define NOTE_D3 147 +#define NOTE_DS3 156 +#define NOTE_E3 165 +#define NOTE_F3 175 +#define NOTE_FS3 185 +#define NOTE_G3 196 +#define NOTE_GS3 208 +#define NOTE_A3 220 +#define NOTE_AS3 233 +#define NOTE_B3 247 +#define NOTE_C4 262 +#define NOTE_CS4 277 +#define NOTE_D4 294 +#define NOTE_DS4 311 +#define NOTE_E4 330 +#define NOTE_F4 349 +#define NOTE_FS4 370 +#define NOTE_G4 392 +#define NOTE_GS4 415 +#define NOTE_A4 440 +#define NOTE_AS4 466 +#define NOTE_B4 494 +#define NOTE_C5 523 +#define NOTE_CS5 554 +#define NOTE_D5 587 +#define NOTE_DS5 622 +#define NOTE_E5 659 +#define NOTE_F5 698 +#define NOTE_FS5 740 +#define NOTE_G5 784 +#define NOTE_GS5 831 +#define NOTE_A5 880 +#define NOTE_AS5 932 +#define NOTE_B5 988 +#define NOTE_C6 1047 +#define NOTE_CS6 1109 +#define NOTE_D6 1175 +#define NOTE_DS6 1245 +#define NOTE_E6 1319 +#define NOTE_F6 1397 +#define NOTE_FS6 1480 +#define NOTE_G6 1568 +#define NOTE_GS6 1661 +#define NOTE_A6 1760 +#define NOTE_AS6 1865 +#define NOTE_B6 1976 +#define NOTE_C7 2093 +#define NOTE_CS7 2217 +#define NOTE_D7 2349 +#define NOTE_DS7 2489 +#define NOTE_E7 2637 +#define NOTE_F7 2794 +#define NOTE_FS7 2960 +#define NOTE_G7 3136 +#define NOTE_GS7 3322 +#define NOTE_A7 3520 +#define NOTE_AS7 3729 +#define NOTE_B7 3951 +#define NOTE_C8 4186 +#define NOTE_CS8 4435 +#define NOTE_D8 4699 +#define NOTE_DS8 4978 diff --git a/ModuleTests/testSensorPack/sound.h b/ModuleTests/testSensorPack/sound.h new file mode 100644 index 00000000..e649add4 --- /dev/null +++ b/ModuleTests/testSensorPack/sound.h @@ -0,0 +1,60 @@ +#define LENGTH_FACTOR 2 +#define BASE_PITCH 1046.50 +#ifndef BUZZER +#define BUZZER 5 +#endif +// tone: pause,1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4 +// code: 0, 8, 10, 12, 13, 15, 17, 19, 20, 22,24,25 + +byte melodyNormalBoot[] = { + 8, 13, 10, 13, 8, 5, 8, 3, 5, 8, //tone + 4, 4, 8, 8, 4, 8, 8, 8, 8, 4, //relative duration, 8 means 1/8 note length +}; +byte melodyInit[] = { + 15, 12, 15, 12, 15, 12, 8, 10, 13, 12, 10, 15, //tone + 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 2, //relative duration, 8 means 1/8 note length +}; +byte melodyLowBattery[] = { + 15, 11, 13, 10, 11, //tone + 4, 4, 4, 4, 4, //relative duration, 8 means 1/8 note length +}; +byte melody1[] = { 15, 8, 10, 12, 13, 15, 8, 8, 17, 13, 15, 17, 19, 20, 8, 8, + 2, 4, 4, 4, 4, 2, 2, 2, 2, 4, 4, 4, 4, 2, 2, 2 }; + +void beep(float note, float duration = 50, int pause = 0, byte repeat = 1) { + for (byte r = 0; r < repeat; r++) { + if (note) + tone(BUZZER, BASE_PITCH * pow(1.05946, note)); //tone(pin, frequency, duration) the duration doesn't work + else + delay(duration); + delay(duration); + noTone(BUZZER); + delay(pause); + } +} + +byte pass[] = { 10, 4, 20, 2, 0,2 }; +byte fail[] = { 20, 4, 10, 2, 0,2}; +byte mute[] = {20,4,16,4,12,4}; +byte soundOn[] = {12,4,16,4,20,4}; +void playMelody(byte *mel, int len) { + for (int i = 0; i < len; i++) + beep(mel[i*2], 1000 / mel[i*2 + 1]); +} + +void playTone(uint16_t tone1, uint16_t duration) { + if (tone1 < 50 || tone1 > 15000) return; // these do not play on a piezo + for (long i = 0; i < duration * 1000L; i += tone1 * 2) { + digitalWrite(BUZZER, HIGH); + delayMicroseconds(tone1); + digitalWrite(BUZZER, LOW); + delayMicroseconds(tone1); + } +} + +void meow(byte repeat = 2, byte duration = 10, byte startF = 210 + random() % 10, byte endF = 220 + random() % 10) { // Bird chirp + for (byte r = 0; r < repeat; r++) + for (byte i = startF; i < endF; i++) { + playTone(i, duration); + } +} diff --git a/ModuleTests/testSensorPack/src/IRremote.cpp b/ModuleTests/testSensorPack/src/IRremote.cpp new file mode 100644 index 00000000..1db783c0 --- /dev/null +++ b/ModuleTests/testSensorPack/src/IRremote.cpp @@ -0,0 +1,206 @@ +//****************************************************************************** +// IRremote +// Version 2.0.1 June, 2015 +// Copyright 2009 Ken Shirriff +// For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html +// +// Modified by Paul Stoffregen to support other boards and timers +// Modified by Mitra Ardron +// Added Sanyo and Mitsubishi controllers +// Modified Sony to spot the repeat codes that some Sony's send +// +// Interrupt code based on NECIRrcv by Joe Knapp +// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 +// Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ +// +// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) +// LG added by Darryl Smith (based on the JVC protocol) +// Whynter A/C ARC-110WD added by Francesco Meschia +//****************************************************************************** + +// Defining IR_GLOBAL here allows us to declare the instantiation of global variables +#define IR_GLOBAL +#include "IRremote.h" +#undef IR_GLOBAL + +//+============================================================================= +// The match functions were (apparently) originally MACROs to improve code speed +// (although this would have bloated the code) hence the names being CAPS +// A later release implemented debug output and so they needed to be converted +// to functions. +// I tried to implement a dual-compile mode (DEBUG/non-DEBUG) but for some +// reason, no matter what I did I could not get them to function as macros again. +// I have found a *lot* of bugs in the Arduino compiler over the last few weeks, +// and I am currently assuming that one of these bugs is my problem. +// I may revisit this code at a later date and look at the assembler produced +// in a hope of finding out what is going on, but for now they will remain as +// functions even in non-DEBUG mode +// +int MATCH(int measured, int desired) { +#if DEBUG + Serial.print(F("Testing: ")); + Serial.print(TICKS_LOW(desired), DEC); + Serial.print(F(" <= ")); + Serial.print(measured, DEC); + Serial.print(F(" <= ")); + Serial.print(TICKS_HIGH(desired), DEC); +#endif + bool passed = ((measured >= TICKS_LOW(desired)) && (measured <= TICKS_HIGH(desired))); +#if DEBUG + if (passed) { + Serial.println(F("?; passed")); + } else { + Serial.println(F("?; FAILED")); + } +#endif + return passed; +} + +//+======================================================== +// Due to sensor lag, when received, Marks tend to be 100us too long +// +int MATCH_MARK(int measured_ticks, int desired_us) { +#if DEBUG + Serial.print(F("Testing mark (actual vs desired): ")); + Serial.print(measured_ticks * MICROS_PER_TICK, DEC); + Serial.print(F("us vs ")); + Serial.print(desired_us, DEC); + Serial.print(F("us: ")); + Serial.print(TICKS_LOW(desired_us + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); + Serial.print(F(" <= ")); + Serial.print(measured_ticks * MICROS_PER_TICK, DEC); + Serial.print(F(" <= ")); + Serial.print(TICKS_HIGH(desired_us + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); +#endif + // compensate for marks exceeded by demodulator hardware + bool passed = ((measured_ticks >= TICKS_LOW(desired_us + MARK_EXCESS_MICROS)) + && (measured_ticks <= TICKS_HIGH(desired_us + MARK_EXCESS_MICROS))); +#if DEBUG + if (passed) { + Serial.println(F("?; passed")); + } else { + Serial.println(F("?; FAILED")); + } +#endif + return passed; +} + +//+======================================================== +// Due to sensor lag, when received, Spaces tend to be 100us too short +// +int MATCH_SPACE(int measured_ticks, int desired_us) { +#if DEBUG + Serial.print(F("Testing space (actual vs desired): ")); + Serial.print(measured_ticks * MICROS_PER_TICK, DEC); + Serial.print(F("us vs ")); + Serial.print(desired_us, DEC); + Serial.print(F("us: ")); + Serial.print(TICKS_LOW(desired_us - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); + Serial.print(F(" <= ")); + Serial.print(measured_ticks * MICROS_PER_TICK, DEC); + Serial.print(F(" <= ")); + Serial.print(TICKS_HIGH(desired_us - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); +#endif + // compensate for marks exceeded and spaces shortened by demodulator hardware + bool passed = ((measured_ticks >= TICKS_LOW(desired_us - MARK_EXCESS_MICROS)) + && (measured_ticks <= TICKS_HIGH(desired_us - MARK_EXCESS_MICROS))); +#if DEBUG + if (passed) { + Serial.println(F("?; passed")); + } else { + Serial.println(F("?; FAILED")); + } +#endif + return passed; +} + +//+============================================================================= +// Interrupt Service Routine - Fires every 50uS +// TIMER2 interrupt code to collect raw data. +// Widths of alternating SPACE, MARK are recorded in rawbuf. +// Recorded in ticks of 50uS [microseconds, 0.000050 seconds] +// 'rawlen' counts the number of entries recorded so far. +// First entry is the SPACE between transmissions. +// As soon as a the first [SPACE] entry gets long: +// Ready is set; State switches to IDLE; Timing of SPACE continues. +// As soon as first MARK arrives: +// Gap width is recorded; Ready is cleared; New logging starts +// +ISR (TIMER_INTR_NAME) { + TIMER_RESET; // reset timer interrupt flag if required (currently only for Teensy and ATmega4809) + + // Read if IR Receiver -> SPACE [xmt LED off] or a MARK [xmt LED on] + // digitalRead() is very slow. Optimisation is possible, but makes the code unportable + uint8_t irdata = (uint8_t) digitalRead(irparams.recvpin); + + irparams.timer++; // One more 50uS tick + if (irparams.rawlen >= RAW_BUFFER_LENGTH) { + irparams.rcvstate = IR_REC_STATE_OVERFLOW; // Buffer overflow + } + + /* + * Due to a ESP32 compiler bug https://github.com/espressif/esp-idf/issues/1552 no switch statements are possible for ESP32 + * So we change the code to if / else if + */ +// switch (irparams.rcvstate) { + //...................................................................... + if (irparams.rcvstate == IR_REC_STATE_IDLE) { // In the middle of a gap + if (irdata == MARK) { + if (irparams.timer < GAP_TICKS) { // Not big enough to be a gap. + irparams.timer = 0; + } else { + // Gap just ended; Record duration; Start recording transmission + irparams.overflow = false; + irparams.rawlen = 0; + irparams.rawbuf[irparams.rawlen++] = irparams.timer; + irparams.timer = 0; + irparams.rcvstate = IR_REC_STATE_MARK; + } + } + } else if (irparams.rcvstate == IR_REC_STATE_MARK) { // Timing Mark + if (irdata == SPACE) { // Mark ended; Record time + irparams.rawbuf[irparams.rawlen++] = irparams.timer; + irparams.timer = 0; + irparams.rcvstate = IR_REC_STATE_SPACE; + } + } else if (irparams.rcvstate == IR_REC_STATE_SPACE) { // Timing Space + if (irdata == MARK) { // Space just ended; Record time + irparams.rawbuf[irparams.rawlen++] = irparams.timer; + irparams.timer = 0; + irparams.rcvstate = IR_REC_STATE_MARK; + + } else if (irparams.timer > GAP_TICKS) { // Space + // A long Space, indicates gap between codes + // Flag the current code as ready for processing + // Switch to STOP + // Don't reset timer; keep counting Space width + irparams.rcvstate = IR_REC_STATE_STOP; + } + } else if (irparams.rcvstate == IR_REC_STATE_STOP) { // Waiting; Measuring Gap + if (irdata == MARK) { + irparams.timer = 0; // Reset gap timer + } + } else if (irparams.rcvstate == IR_REC_STATE_OVERFLOW) { // Flag up a read overflow; Stop the State Machine + irparams.overflow = true; + irparams.rcvstate = IR_REC_STATE_STOP; + } + +#ifdef BLINKLED + // If requested, flash LED while receiving IR data + if (irparams.blinkflag) { + if (irdata == MARK) { + if (irparams.blinkpin) { + digitalWrite(irparams.blinkpin, HIGH); // Turn user defined pin LED on + } else { + BLINKLED_ON(); // if no user defined LED pin, turn default LED pin for the hardware on + } + } else { + if (irparams.blinkpin) { + digitalWrite(irparams.blinkpin, LOW); // Turn user defined pin LED on + } else { + BLINKLED_OFF(); // if no user defined LED pin, turn default LED pin for the hardware on + } + } + } +#endif // BLINKLED +} diff --git a/ModuleTests/testSensorPack/src/IRremote.h b/ModuleTests/testSensorPack/src/IRremote.h new file mode 100644 index 00000000..c11d2906 --- /dev/null +++ b/ModuleTests/testSensorPack/src/IRremote.h @@ -0,0 +1,490 @@ +/** + * @file IRremote.h + * @brief Public API to the library. + */ + +//****************************************************************************** +// IRremote +// Version 2.0.1 June, 2015 +// Copyright 2009 Ken Shirriff +// For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html +// Edited by Mitra to add new controller SANYO +// +// Interrupt code based on NECIRrcv by Joe Knapp +// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 +// Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ +// +// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) +// LG added by Darryl Smith (based on the JVC protocol) +// Whynter A/C ARC-110WD added by Francesco Meschia +// MagiQuest added by E. Stuart Hicks (based on code by mpflaga - https://github.com/mpflaga/Arduino-IRremote/) +//****************************************************************************** +#ifndef IRremote_h +#define IRremote_h + +//------------------------------------------------------------------------------ +// The ISR header contains several useful macros the user may wish to use +// +#include "private/IRremoteInt.h" + +#ifdef ARDUINO_ARCH_AVR +#include +#define HAS_FLASH_READ 1 +#define STRCPY_PF_CAST(x) (x) +#else +#define HAS_FLASH_READ 0 +#endif + +//------------------------------------------------------------------------------ +// Supported IR protocols +// Each protocol you include costs memory and, during decode, costs time +// Disable (set to 0) all the protocols you do not need/want! +// +#define DECODE_RC5 0 +#define SEND_RC5 0 + +#define DECODE_RC6 0 +#define SEND_RC6 0 + +#define DECODE_NEC 1 +#define SEND_NEC 0 + +#define DECODE_SONY 0 +#define SEND_SONY 0 + +#define DECODE_PANASONIC 0 +#define SEND_PANASONIC 0 + +#define DECODE_JVC 0 +#define SEND_JVC 0 + +#define DECODE_SAMSUNG 0 +#define SEND_SAMSUNG 0 + +#define DECODE_WHYNTER 0 +#define SEND_WHYNTER 0 + +#define DECODE_AIWA_RC_T501 0 +#define SEND_AIWA_RC_T501 0 + +#define DECODE_LG 0 +#define SEND_LG 0 + +#define DECODE_SANYO 0 +#define SEND_SANYO 0 // NOT WRITTEN + +#define DECODE_MITSUBISHI 0 +#define SEND_MITSUBISHI 0 // NOT WRITTEN + +#define DECODE_DISH 0 // NOT WRITTEN +#define SEND_DISH 0 + +#define DECODE_SHARP 0 +#define SEND_SHARP 0 + +#define DECODE_SHARP_ALT 0 +#define SEND_SHARP_ALT 0 + +#define DECODE_DENON 0 +#define SEND_DENON 0 + +#define DECODE_LEGO_PF 0 // NOT WRITTEN +#define SEND_LEGO_PF 0 + +#define DECODE_BOSEWAVE 0 +#define SEND_BOSEWAVE 0 + +#define DECODE_MAGIQUEST 0 +#define SEND_MAGIQUEST 0 + +#define DECODE_HASH 0 // special decoder for all protocols + +/** + * An enum consisting of all supported formats. + * You do NOT need to remove entries from this list when disabling protocols! + */ +typedef enum { + UNKNOWN = -1, + UNUSED = 0, + RC5, + RC6, + NEC, + SONY, + PANASONIC, + JVC, + SAMSUNG, + WHYNTER, + AIWA_RC_T501, + LG, + SANYO, + MITSUBISHI, + DISH, + SHARP, + SHARP_ALT, + DENON, + LEGO_PF, + BOSEWAVE, + MAGIQUEST, +} decode_type_t; + +/** + * Set DEBUG to 1 for lots of lovely debug output. + */ +#define DEBUG 0 + +//------------------------------------------------------------------------------ +// Debug directives +// +#if DEBUG +# define DBG_PRINT(...) Serial.print(__VA_ARGS__) +# define DBG_PRINTLN(...) Serial.println(__VA_ARGS__) +#else +/** + * If DEBUG, print the arguments, otherwise do nothing. + */ +# define DBG_PRINT(...) void() +/** + * If DEBUG, print the arguments as a line, otherwise do nothing. + */ +# define DBG_PRINTLN(...) void() +#endif + +//------------------------------------------------------------------------------ +// Helper macro for getting a macro definition as string +// +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +//------------------------------------------------------------------------------ +// Mark & Space matching functions +// +int MATCH(int measured, int desired); +int MATCH_MARK(int measured_ticks, int desired_us); +int MATCH_SPACE(int measured_ticks, int desired_us); + +/** + * Results returned from the decoder + */ +class decode_results { +public: + decode_type_t decode_type; ///< UNKNOWN, NEC, SONY, RC5, ... + unsigned int address; ///< Used by Panasonic & Sharp [16-bits] + unsigned long value; ///< Decoded value [max 32-bits] + unsigned int magnitude; ///< Used by MagiQuest [16-bits] + int bits; ///< Number of bits in decoded value + volatile unsigned int *rawbuf; ///< Raw intervals in 50uS ticks + unsigned int rawlen; ///< Number of records in rawbuf + int overflow; ///< true if IR raw code too long +}; + +/** + * Decoded value for NEC when a repeat code is received + */ +#define REPEAT 0xFFFFFFFF + +/** + * Main class for receiving IR + */ +class IRrecv { +public: + /** + * Instantiate the IRrecv class. Multiple instantiation is not supported. + * @param recvpin Arduino pin to use. No sanity check is made. + */ + IRrecv(int recvpin); + /** + * Instantiate the IRrecv class. Multiple instantiation is not supported. + * @param recvpin Arduino pin to use, where a demodulating IR receiver is connected. + * @param blinkpin pin to blink when receiving IR. Not supported by all hardware. No sanity check is made. + */ + IRrecv(int recvpin, int blinkpin); + + /** + * TODO: Why is this public??? + * @param blinkflag + */ + void blink13(int blinkflag); + + /** + * Attempt to decode the recently receive IR signal + * @param results decode_results instance returning the decode, if any. + * @return success of operation. TODO: convert to bool + */ + int decode(decode_results *results); + + /** + * Enable IR reception. + */ + void enableIRIn(); + + /** + * Disable IR reception. + */ + void disableIRIn(); + + /** + * Returns status of reception + * @return true if no reception is on-going. + */ + bool isIdle(); + + /** + * Called to re-enable IR reception. + */ + void resume(); + + /** + * Print the result (second argument) as Pronto Hex on the Stream supplied as argument. + * @param stream The Stream on which to write, often Serial + * @param results the decode_results as delivered from irrecv.decode. + * @param frequency Modulation frequency in Hz. Often 38000Hz. + */ + void dumpPronto(Stream& stream, decode_results *results, unsigned int frequency = 38000U); + +private: +#if DECODE_HASH + long decodeHash(decode_results *results); + int compare(unsigned int oldval, unsigned int newval); +#endif + + //...................................................................... +#if (DECODE_RC5 || DECODE_RC6) + /** + * This helper function is shared by RC5 and RC6 + */ + int getRClevel(decode_results *results, unsigned int *offset, int *used, int t1); +#endif +#if DECODE_RC5 + /** + * Try to decode the recently received IR signal as an RC5 signal- + * @param results decode_results instance returning the decode, if any. + * @return Success of the operation. + */ + bool decodeRC5(decode_results *results); +#endif +#if DECODE_RC6 + bool decodeRC6(decode_results *results); +#endif + //...................................................................... +#if DECODE_NEC + bool decodeNEC(decode_results *results); +#endif + //...................................................................... +#if DECODE_SONY + bool decodeSony(decode_results *results); +#endif + //...................................................................... +#if DECODE_PANASONIC + bool decodePanasonic(decode_results *results); +#endif + //...................................................................... +#if DECODE_JVC + bool decodeJVC(decode_results *results); +#endif + //...................................................................... +#if DECODE_SAMSUNG + bool decodeSAMSUNG(decode_results *results); +#endif + //...................................................................... +#if DECODE_WHYNTER + bool decodeWhynter(decode_results *results); +#endif + //...................................................................... +#if DECODE_AIWA_RC_T501 + bool decodeAiwaRCT501(decode_results *results); +#endif + //...................................................................... +#if DECODE_LG + bool decodeLG(decode_results *results); +#endif + //...................................................................... +#if DECODE_SANYO + bool decodeSanyo(decode_results *results); +#endif + //...................................................................... +#if DECODE_MITSUBISHI + bool decodeMitsubishi(decode_results *results); +#endif + //...................................................................... +#if DECODE_DISH + bool decodeDish (decode_results *results) ; // NOT WRITTEN +#endif + //...................................................................... +#if DECODE_SHARP + bool decodeSharp(decode_results *results); // NOT WRITTEN +#endif +#if DECODE_SHARP_ALT + bool decodeSharpAlt(decode_results *results); +#endif + //...................................................................... +#if DECODE_DENON + bool decodeDenon(decode_results *results); +#endif + //...................................................................... +#if DECODE_LEGO_PF + bool decodeLegoPowerFunctions (decode_results *results) ; +#endif + //...................................................................... +#if DECODE_BOSEWAVE + bool decodeBoseWave(decode_results *results); +#endif + //...................................................................... +#if DECODE_MAGIQUEST + bool decodeMagiQuest(decode_results *results); +#endif +}; + +/** + * Main class for sending IR + */ +class IRsend { +public: +#if defined(USE_SOFT_CARRIER) || defined(USE_NO_CARRIER) + IRsend(int pin = IR_SEND_PIN) { + sendPin = pin; + } +#else + IRsend() { + } +#endif + + void custom_delay_usec(unsigned long uSecs); + void enableIROut(int khz); + void mark(unsigned int usec); + void space(unsigned int usec); + void sendRaw(const unsigned int buf[], unsigned int len, unsigned int hz); + void sendRaw_P(const unsigned int buf[], unsigned int len, unsigned int hz); + + //...................................................................... +#if SEND_RC5 + void sendRC5(unsigned long data, int nbits); + void sendRC5ext(unsigned long addr, unsigned long cmd, boolean toggle); +#endif +#if SEND_RC6 + void sendRC6(unsigned long data, int nbits); +#endif + //...................................................................... +#if SEND_NEC + void sendNEC(unsigned long data, int nbits, bool repeat = false); +#endif + //...................................................................... +#if SEND_SONY + void sendSony(unsigned long data, int nbits); +#endif + //...................................................................... +#if SEND_PANASONIC + void sendPanasonic(unsigned int address, unsigned long data); +#endif + //...................................................................... +#if SEND_JVC + // JVC does NOT repeat by sending a separate code (like NEC does). + // The JVC protocol repeats by skipping the header. + // To send a JVC repeat signal, send the original code value + // and set 'repeat' to true + void sendJVC(unsigned long data, int nbits, bool repeat = false); +#endif + //...................................................................... +#if SEND_SAMSUNG + void sendSAMSUNG(unsigned long data, int nbits); +#endif + //...................................................................... +#if SEND_WHYNTER + void sendWhynter(unsigned long data, int nbits); +#endif + //...................................................................... +#if SEND_AIWA_RC_T501 + void sendAiwaRCT501(int code); +#endif + //...................................................................... +#if SEND_LG + void sendLG(unsigned long data, int nbits); +#endif + //...................................................................... +#if SEND_SANYO + void sendSanyo ( ) ; // NOT WRITTEN +#endif + //...................................................................... +#if SEND_MISUBISHI + void sendMitsubishi ( ) ; // NOT WRITTEN +#endif + //...................................................................... +#if SEND_DISH + void sendDISH(unsigned long data, int nbits); +#endif + //...................................................................... +#if SEND_SHARP + void sendSharpRaw(unsigned long data, int nbits); + void sendSharp(unsigned int address, unsigned int command); +#endif +#if SEND_SHARP_ALT + void sendSharpAltRaw(unsigned long data, int nbits); + void sendSharpAlt(unsigned int address, unsigned long command); +#endif + //...................................................................... +#if SEND_DENON + void sendDenon(unsigned long data, int nbits); +#endif + //...................................................................... +#if SEND_LEGO_PF + void sendLegoPowerFunctions(uint16_t data, bool repeat = true); +#endif + //...................................................................... +#if SEND_BOSEWAVE + void sendBoseWave(unsigned char code); +#endif + //...................................................................... +#if SEND_MAGIQUEST + void sendMagiQuest(unsigned long wand_id, unsigned int magnitude); +#endif + + /** + * Parse the string given as Pronto Hex, and send it a number of times given + * as the second argument. Thereby the division of the Pronto Hex into + * an intro-sequence and a repeat sequence is taken into account: + * First the intro sequence is sent, then the repeat sequence is sent times-1 times. + * However, if the intro sequence is empty, the repeat sequence is sent times times. + * Reference. + * + * Note: Using this function is very wasteful for the memory consumption on + * a small board. + * Normally it is a much better ide to use a tool like e.g. IrScrutinizer + * to transform Pronto type signals offline + * to a more memory efficient format. + * + * @param prontoHexString C type string (null terminated) containing a Pronto Hex representation. + * @param times Number of times to send the signal. + */ + void sendPronto(const char* prontoHexString, unsigned int times = 1U); + + void sendPronto(const uint16_t* data, unsigned int length, unsigned int times = 1U); + +#if HAS_FLASH_READ || defined(DOXYGEN) + void sendPronto_PF(uint_farptr_t str, unsigned int times = 1U); + + /** + * Version of sendPronto that reads from PROGMEM, saving RAM memory. + * @param pronto C type string (null terminated) containing a Pronto Hex representation. + * @param times Number of times to send the signal. + */ + void sendPronto_PF(const char *str, unsigned int times = 1U); + void sendPronto(const __FlashStringHelper *str, unsigned int times = 1U); +#endif + +#if defined(USE_SOFT_CARRIER) || defined(USE_NO_CARRIER) + private: + int sendPin; + +# if defined(USE_SOFT_CARRIER) + unsigned int periodTime; + unsigned int periodOnTime; + + void sleepMicros(unsigned long us); + void sleepUntilMicros(unsigned long targetTime); +# endif + +#else + const int sendPin = IR_SEND_PIN; +#endif +}; + +#endif diff --git a/ModuleTests/testSensorPack/src/esp32.cpp b/ModuleTests/testSensorPack/src/esp32.cpp new file mode 100644 index 00000000..e3bc5ec7 --- /dev/null +++ b/ModuleTests/testSensorPack/src/esp32.cpp @@ -0,0 +1,47 @@ +#ifdef ESP32 + +// This file contains functions specific to the ESP32. + +#include "IRremote.h" + +// "Idiot check" +#ifdef USE_DEFAULT_ENABLE_IR_IN +#error Must undef USE_DEFAULT_ENABLE_IR_IN +#endif + +hw_timer_t *timer; +IRAM_ATTR void IRTimer(); // defined in IRremote.cpp, masqueraded as ISR(TIMER_INTR_NAME) + +//+============================================================================= +// initialization +// +void IRrecv::enableIRIn() { +// Interrupt Service Routine - Fires every 50uS + // ESP32 has a proper API to setup timers, no weird chip macros needed + // simply call the readable API versions :) + // 3 timers, choose #1, 80 divider nanosecond precision, 1 to count up + timer = timerBegin(1, 80, 1); + timerAttachInterrupt(timer, &IRTimer, 1); + // every 50ns, autoreload = true + timerAlarmWrite(timer, 50, true); + timerAlarmEnable(timer); + + // Initialize state machine variables + irparams.rcvstate = IR_REC_STATE_IDLE; + irparams.rawlen = 0; + + // Set pin modes + pinMode(irparams.recvpin, INPUT); +} + +void IRrecv::disableIRIn() { + timerEnd(timer); + timerDetachInterrupt(timer); +} + +void IRsend::enableIROut(int khz) { + ledcSetup(LEDCHANNEL, khz * 1000, 8); // 8 bit PWM resolution + ledcAttachPin(IR_SEND_PIN, LEDCHANNEL); // bind pin to channel +} + +#endif // ESP32 diff --git a/ModuleTests/testSensorPack/src/irRecv.cpp b/ModuleTests/testSensorPack/src/irRecv.cpp new file mode 100644 index 00000000..f2280979 --- /dev/null +++ b/ModuleTests/testSensorPack/src/irRecv.cpp @@ -0,0 +1,287 @@ +#include "IRremote.h" + +//+============================================================================= +// Decodes the received IR message +// Returns 0 if no data ready, 1 if data ready. +// Results of decoding are stored in results +// +int IRrecv::decode(decode_results *results) { + results->rawbuf = irparams.rawbuf; + results->rawlen = irparams.rawlen; + + results->overflow = irparams.overflow; + + if (irparams.rcvstate != IR_REC_STATE_STOP) { + return false; + } + +#if DECODE_NEC + DBG_PRINTLN("Attempting NEC decode"); + if (decodeNEC(results)) { + return true; + } +#endif + +#if DECODE_SHARP + DBG_PRINTLN("Attempting Sharp decode"); + if (decodeSharp(results)) { + return true; + } +#endif + +#if DECODE_SHARP_ALT + DBG_PRINTLN("Attempting SharpAlt decode"); + if (decodeSharpAlt(results)) { + return true; + } +#endif + +#if DECODE_SONY + DBG_PRINTLN("Attempting Sony decode"); + if (decodeSony(results)) { + return true; + } +#endif + +#if DECODE_SANYO + DBG_PRINTLN("Attempting Sanyo decode"); + if (decodeSanyo(results)) { + return true; + } +#endif + +#if DECODE_MITSUBISHI + DBG_PRINTLN("Attempting Mitsubishi decode"); + if (decodeMitsubishi(results)) { + return true; + } +#endif + +#if DECODE_RC5 + DBG_PRINTLN("Attempting RC5 decode"); + if (decodeRC5(results)) { + return true; + } +#endif + +#if DECODE_RC6 + DBG_PRINTLN("Attempting RC6 decode"); + if (decodeRC6(results)) { + return true; + } +#endif + +#if DECODE_PANASONIC + DBG_PRINTLN("Attempting Panasonic decode"); + if (decodePanasonic(results)) { + return true; + } +#endif + +#if DECODE_LG + DBG_PRINTLN("Attempting LG decode"); + if (decodeLG(results)) { + return true; + } +#endif + +#if DECODE_JVC + DBG_PRINTLN("Attempting JVC decode"); + if (decodeJVC(results)) { + return true; + } +#endif + +#if DECODE_SAMSUNG + DBG_PRINTLN("Attempting SAMSUNG decode"); + if (decodeSAMSUNG(results)) { + return true; + } +#endif + +#if DECODE_WHYNTER + DBG_PRINTLN("Attempting Whynter decode"); + if (decodeWhynter(results)) { + return true; + } +#endif + +#if DECODE_AIWA_RC_T501 + DBG_PRINTLN("Attempting Aiwa RC-T501 decode"); + if (decodeAiwaRCT501(results)) { + return true; + } +#endif + +#if DECODE_DENON + DBG_PRINTLN("Attempting Denon decode"); + if (decodeDenon(results)) { + return true; + } +#endif + +#if DECODE_LEGO_PF + DBG_PRINTLN("Attempting Lego Power Functions"); + if (decodeLegoPowerFunctions(results)) { + return true; + } +#endif + +#if DECODE_MAGIQUEST + DBG_PRINTLN("Attempting MagiQuest decode"); + if (decodeMagiQuest(results)) { + return true; + } +#endif + +#if DECODE_HASH + DBG_PRINTLN("Hash decode"); + // decodeHash returns a hash on any input. + // Thus, it needs to be last in the list. + // If you add any decodes, add them before this. + if (decodeHash(results)) { + return true; + } +#endif + + // Throw away and start over + resume(); + return false; +} + +//+============================================================================= +IRrecv::IRrecv(int recvpin) { + irparams.recvpin = recvpin; + irparams.blinkflag = 0; +} + +IRrecv::IRrecv(int recvpin, int blinkpin) { + irparams.recvpin = recvpin; + irparams.blinkpin = blinkpin; + pinMode(blinkpin, OUTPUT); + irparams.blinkflag = 0; +} + +//+============================================================================= +// initialization +// +#ifdef USE_DEFAULT_ENABLE_IR_IN +void IRrecv::enableIRIn() { +// Interrupt Service Routine - Fires every 50uS + cli(); + // Setup pulse clock timer interrupt + // Prescale /8 (16M/8 = 0.5 microseconds per tick) + // Therefore, the timer interval can range from 0.5 to 128 microseconds + // Depending on the reset value (255 to 0) + timerConfigNormal(); + + // Timer2 Overflow Interrupt Enable + TIMER_ENABLE_INTR; + + TIMER_RESET; + + sei(); + // enable interrupts + + // Initialize state machine variables + irparams.rcvstate = IR_REC_STATE_IDLE; + irparams.rawlen = 0; + + // Set pin modes + pinMode(irparams.recvpin, INPUT); +} + +void IRrecv::disableIRIn() { + cli(); + TIMER_DISABLE_INTR; + sei(); + // enable interrupts +} + +#endif // USE_DEFAULT_ENABLE_IR_IN + +//+============================================================================= +// Enable/disable blinking of pin 13 on IR processing +// +void IRrecv::blink13(int blinkflag) { +#ifdef BLINKLED + irparams.blinkflag = blinkflag; + if (blinkflag) { + pinMode(BLINKLED, OUTPUT); + } +#endif +} + +//+============================================================================= +// Return if receiving new IR signals +// +bool IRrecv::isIdle() { + return (irparams.rcvstate == IR_REC_STATE_IDLE || irparams.rcvstate == IR_REC_STATE_STOP) ? true : false; +} +//+============================================================================= +// Restart the ISR state machine +// +void IRrecv::resume() { + irparams.rcvstate = IR_REC_STATE_IDLE; + irparams.rawlen = 0; +} + +# if DECODE_HASH +//+============================================================================= +// hashdecode - decode an arbitrary IR code. +// Instead of decoding using a standard encoding scheme +// (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. +// +// The algorithm: look at the sequence of MARK signals, and see if each one +// is shorter (0), the same length (1), or longer (2) than the previous. +// Do the same with the SPACE signals. Hash the resulting sequence of 0's, +// 1's, and 2's to a 32-bit value. This will give a unique value for each +// different code (probably), for most code systems. +// +// http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html +// +// Compare two tick values, returning 0 if newval is shorter, +// 1 if newval is equal, and 2 if newval is longer +// Use a tolerance of 20% +// +int IRrecv::compare(unsigned int oldval, unsigned int newval) { +// @formatter:off + if (newval * 10 < oldval * 8) { + return 0; + } + if (oldval * 10 < newval * 8) { + return 2; + } + return 1; + +// @formatter:on +} //+============================================================================= +// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param +// Converts the raw code values into a 32-bit hash code. +// Hopefully this code is unique for each button. +// This isn't a "real" decoding, just an arbitrary value. +// +#define FNV_PRIME_32 16777619 +#define FNV_BASIS_32 2166136261 + +long IRrecv::decodeHash(decode_results *results) { + long hash = FNV_BASIS_32; + + // Require at least 6 samples to prevent triggering on noise + if (results->rawlen < 6) { + return false; + } + + for (unsigned int i = 1; (i + 2) < results->rawlen; i++) { + int value = compare(results->rawbuf[i], results->rawbuf[i + 2]); + // Add value into the hash + hash = (hash * FNV_PRIME_32) ^ value; + } + + results->value = hash; + results->bits = 32; + results->decode_type = UNKNOWN; + + return true; +} +#endif // defined(DECODE_HASH) diff --git a/ModuleTests/testSensorPack/src/irSend.cpp b/ModuleTests/testSensorPack/src/irSend.cpp new file mode 100644 index 00000000..1abd16fc --- /dev/null +++ b/ModuleTests/testSensorPack/src/irSend.cpp @@ -0,0 +1,168 @@ +#include "IRremote.h" + +#ifdef SENDING_SUPPORTED +//+============================================================================= +void IRsend::sendRaw(const unsigned int buf[], unsigned int len, unsigned int hz) { + // Set IR carrier frequency + enableIROut(hz); + + for (unsigned int i = 0; i < len; i++) { + if (i & 1) { + space(buf[i]); + } else { + mark(buf[i]); + } + } + + space(0); // Always end with the LED off +} + +void IRsend::sendRaw_P(const unsigned int buf[], unsigned int len, unsigned int hz) { +#if !defined(__AVR__) + sendRaw(buf,len,hz); // Let the function work for non AVR platforms +#else + // Set IR carrier frequency + enableIROut(hz); + + for (unsigned int i = 0; i < len; i++) { + uint16_t duration = pgm_read_word_near(buf + sizeof(uint16_t) * i); + if (i & 1) { + space(duration); + } else { + mark(duration); + } + } + space(0); // Always end with the LED off +#endif + +} + +#ifdef USE_SOFT_CARRIER +void inline IRsend::sleepMicros(unsigned long us) { +#ifdef USE_SPIN_WAIT + sleepUntilMicros(micros() + us); +#else + if (us > 0U) { // Is this necessary? (Official docu https://www.arduino.cc/en/Reference/DelayMicroseconds does not tell.) + delayMicroseconds((unsigned int) us); + } +#endif +} + +void inline IRsend::sleepUntilMicros(unsigned long targetTime) { +#ifdef USE_SPIN_WAIT + while (micros() < targetTime) + ; +#else + unsigned long now = micros(); + if (now < targetTime) { + sleepMicros(targetTime - now); + } +#endif +} +#endif // USE_SOFT_CARRIER + +//+============================================================================= +// Sends an IR mark for the specified number of microseconds. +// The mark output is modulated at the PWM frequency. +// + +void IRsend::mark(unsigned int time) { +#ifdef USE_SOFT_CARRIER + unsigned long start = micros(); + unsigned long stop = start + time; + if (stop + periodTime < start) { + // Counter wrap-around, happens very seldomly, but CAN happen. + // Just give up instead of possibly damaging the hardware. + return; + } + unsigned long nextPeriodEnding = start; + unsigned long now = micros(); + while (now < stop) { + SENDPIN_ON(sendPin); + sleepMicros (periodOnTime); + SENDPIN_OFF(sendPin); + nextPeriodEnding += periodTime; + sleepUntilMicros(nextPeriodEnding); + now = micros(); + } +#elif defined(USE_NO_CARRIER) + digitalWrite(sendPin, LOW); // Set output to active low. +#else + TIMER_ENABLE_PWM; // Enable pin 3 PWM output +#endif + if (time > 0) { + custom_delay_usec(time); + } +} + +//+============================================================================= +// Leave pin off for time (given in microseconds) +// Sends an IR space for the specified number of microseconds. +// A space is no output, so the PWM output is disabled. +// +void IRsend::space(unsigned int time) { +#if defined(USE_NO_CARRIER) + digitalWrite(sendPin, HIGH); // Set output to inactive high. +#else + TIMER_DISABLE_PWM; // Disable pin 3 PWM output +#endif + if (time > 0) { + IRsend::custom_delay_usec(time); + } +} + +#ifdef USE_DEFAULT_ENABLE_IR_OUT +//+============================================================================= +// Enables IR output. The khz value controls the modulation frequency in kilohertz. +// The IR output will be on pin 3 (OC2B). +// This routine is designed for 36-40KHz; if you use it for other values, it's up to you +// to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.) +// TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B +// controlling the duty cycle. +// There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A) +// To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin. +// A few hours staring at the ATmega documentation and this will all make sense. +// See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details. +// +void IRsend::enableIROut(int khz) { +#ifdef USE_SOFT_CARRIER + periodTime = (1000U + khz / 2) / khz; // = 1000/khz + 1/2 = round(1000.0/khz) + periodOnTime = periodTime * DUTY_CYCLE / 100U - PULSE_CORRECTION; +#endif + +#if defined(USE_NO_CARRIER) + pinMode(sendPin, OUTPUT); + digitalWrite(sendPin, HIGH); // Set output to inactive high. +#else + // Disable the Timer2 Interrupt (which is used for receiving IR) + TIMER_DISABLE_INTR; //Timer2 Overflow Interrupt + + pinMode(sendPin, OUTPUT); + + SENDPIN_OFF(sendPin); // When not sending, we want it low + + timerConfigkHz(khz); +#endif +} +#endif + +//+============================================================================= +// Custom delay function that circumvents Arduino's delayMicroseconds limit + +void IRsend::custom_delay_usec(unsigned long uSecs) { + if (uSecs > 4) { + unsigned long start = micros(); + unsigned long endMicros = start + uSecs - 4; + if (endMicros < start) { // Check if overflow + while (micros() > start) { + } // wait until overflow + } + while (micros() < endMicros) { + } // normal wait + } + //else { + // __asm__("nop\n\t"); // must have or compiler optimizes out + //} +} + +#endif // SENDING_SUPPORTED diff --git a/ModuleTests/testSensorPack/src/ir_NEC.cpp b/ModuleTests/testSensorPack/src/ir_NEC.cpp new file mode 100644 index 00000000..81641e4e --- /dev/null +++ b/ModuleTests/testSensorPack/src/ir_NEC.cpp @@ -0,0 +1,116 @@ +#include "IRremote.h" + +//============================================================================== +// N N EEEEE CCCC +// NN N E C +// N N N EEE C +// N NN E C +// N N EEEEE CCCC +//============================================================================== + +#define NEC_BITS 32 +#define NEC_HDR_MARK 9000 +#define NEC_HDR_SPACE 4500 +#define NEC_BIT_MARK 560 +#define NEC_ONE_SPACE 1690 +#define NEC_ZERO_SPACE 560 +#define NEC_RPT_SPACE 2250 + +//+============================================================================= +#if SEND_NEC +/* + * Repeat commands should be sent in a 110 ms raster. + * https://www.sbprojects.net/knowledge/ir/nec.php + */ +void IRsend::sendNEC(unsigned long data, int nbits, bool repeat) { + // Set IR carrier frequency + enableIROut(38); + + // Header + mark(NEC_HDR_MARK); + + if (data == REPEAT || repeat) { + // repeat "space and data" + space(NEC_RPT_SPACE); + mark(NEC_BIT_MARK); + } else { + + space(NEC_HDR_SPACE); + + // Data + for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) { + if (data & mask) { + mark(NEC_BIT_MARK); + space(NEC_ONE_SPACE); + } else { + mark(NEC_BIT_MARK); + space(NEC_ZERO_SPACE); + } + } + + // Footer + mark(NEC_BIT_MARK); + } + space(0); // Always end with the LED off +} +#endif + +//+============================================================================= +// NECs have a repeat only 4 items long +// +#if DECODE_NEC +bool IRrecv::decodeNEC(decode_results *results) { + long data = 0; // We decode in to here; Start with nothing + int offset = 1; // Index in to results; Skip first entry!? + + // Check header "mark" + if (!MATCH_MARK(results->rawbuf[offset], NEC_HDR_MARK)) { + return false; + } + offset++; + + // Check for repeat + if ((irparams.rawlen == 4) && MATCH_SPACE(results->rawbuf[offset], NEC_RPT_SPACE) + && MATCH_MARK(results->rawbuf[offset + 1], NEC_BIT_MARK)) { + results->bits = 0; + results->value = REPEAT; + results->decode_type = NEC; + return true; + } + + // Check we have enough data + if (irparams.rawlen < (2 * NEC_BITS) + 4) { + return false; + } + // Check header "space" + if (!MATCH_SPACE(results->rawbuf[offset], NEC_HDR_SPACE)) { + return false; + } + offset++; + + // Build the data + for (int i = 0; i < NEC_BITS; i++) { + // Check data "mark" + if (!MATCH_MARK(results->rawbuf[offset], NEC_BIT_MARK)) { + return false; + } + offset++; + + if (MATCH_SPACE(results->rawbuf[offset], NEC_ONE_SPACE)) { + data = (data << 1) | 1; + } else if (MATCH_SPACE(results->rawbuf[offset], NEC_ZERO_SPACE)) { + data = (data << 1) | 0; + } else { + return false; + } + offset++; + } + + // Success + results->bits = NEC_BITS; + results->value = data; + results->decode_type = NEC; + + return true; +} +#endif diff --git a/ModuleTests/testSensorPack/src/private/IRremoteBoardDefs.h b/ModuleTests/testSensorPack/src/private/IRremoteBoardDefs.h new file mode 100644 index 00000000..5a0ba000 --- /dev/null +++ b/ModuleTests/testSensorPack/src/private/IRremoteBoardDefs.h @@ -0,0 +1,912 @@ +/** + * @file IRremoteBoardDefs.h + * + * @brief All board specific information should be contained in this file. + * It defines a number of macros, depending on the board, as determined by + * pre-proccesor symbols. + * It was previously contained within IRremoteInt.h. + */ +// IRremote +// Version 2.0.1 June, 2015 +// Copyright 2009 Ken Shirriff +// For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html +// Modified by Paul Stoffregen to support other boards and timers +// +// Interrupt code based on NECIRrcv by Joe Knapp +// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 +// Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ +// +// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) +// Whynter A/C ARC-110WD added by Francesco Meschia +// Sparkfun Pro Micro support by Alastair McCormack +//****************************************************************************** +#ifndef IRremoteBoardDefs_h +#define IRremoteBoardDefs_h + +// Define some defaults, that some boards may like to override +// (This is to avoid negative logic, ! DONT_... is just awkward.) + +/** + * Defined if the standard enableIRIn function should be used. + * Undefine for boards supplying their own. + */ +#define USE_DEFAULT_ENABLE_IR_IN + +/** + * Define if the current board supports sending. + * Currently not used. + */ +#define SENDING_SUPPORTED + +/** + * Defined if the standard enableIROut function should be used. + * Undefine for boards supplying their own. + */ +#define USE_DEFAULT_ENABLE_IR_OUT + +/** + * Duty cycle in percent for sent signals. + */ +#if ! defined(DUTY_CYCLE) +#define DUTY_CYCLE 30 // 30 saves power and is compatible to the old existing code +#endif + +/** + * If USE_SOFT_CARRIER or USE_NO_CARRIER, this amount (in micro seconds) is subtracted from the + * on-time of the pulses. + */ +#define PULSE_CORRECTION 3 + +//------------------------------------------------------------------------------ +// This first #ifdef statement contains defines for blinking the LED, +// as well as all other board specific information, with the exception of +// timers and the sending pin (IR_SEND_PIN). + +#ifdef DOXYGEN +/** + * If defined, denotes pin number of LED that should be blinked during IR reception. + * Leave undefined to disable blinking. + */ +#define BLINKLED LED_BUILTIN + +/** + * Board dependent macro to turn BLINKLED on. + */ +#define BLINKLED_ON() digitalWrite(BLINKLED, HIGH) + +/** + * Board dependent macro to turn BLINKLED off. + */ +#define BLINKLED_OFF() digitalWrite(BLINKLED, LOW) + +/** + * Define to use no carrier PWM, just simulate a receiver signal. + */ +#define USE_NO_CARRIER + +/** + * Define to use carrier generation in software, instead of hardware PWM. + */ +#define USE_SOFT_CARRIER + +/** + * Define to use spin wait instead of delayMicros() for USE_SOFT_CARRIER. + */ +#define USE_SPIN_WAIT + +#elif ! defined(ARDUINO) +// Assume that we compile a test version, to be executed on the host, not on a board. + +// Do not define anything. + +#elif defined(CORE_LED0_PIN) +#define BLINKLED CORE_LED0_PIN +#define BLINKLED_ON() (digitalWrite(CORE_LED0_PIN, HIGH)) +#define BLINKLED_OFF() (digitalWrite(CORE_LED0_PIN, LOW)) + +// Arduino Uno, Nano etc (previously default clause) +#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega32U4__) +#define BLINKLED LED_BUILTIN +#define BLINKLED_ON() (PORTB |= B00100000) +#define BLINKLED_OFF() (PORTB &= B11011111) + +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define BLINKLED 13 +#define BLINKLED_ON() (PORTB |= B10000000) +#define BLINKLED_OFF() (PORTB &= B01111111) + +#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) +#define BLINKLED 0 +#define BLINKLED_ON() (PORTD |= B00000001) +#define BLINKLED_OFF() (PORTD &= B11111110) + +// Nano Every, Uno WiFi Rev2 +#elif defined(__AVR_ATmega4809__) +#define BLINKLED LED_BUILTIN +#define BLINKLED_ON() (digitalWrite(BLINKLED, HIGH)) +#define BLINKLED_OFF() (digitalWrite(BLINKLED, LOW)) + +#elif defined(ARDUINO_ARCH_SAMD) +#define BLINKLED LED_BUILTIN +#define BLINKLED_ON() (digitalWrite(LED_BUILTIN, HIGH)) +#define BLINKLED_OFF() (digitalWrite(LED_BUILTIN, LOW)) + +#define USE_SOFT_CARRIER +// Define to use spin wait instead of delayMicros() +//#define USE_SPIN_WAIT +// Supply own enableIRIn() +#undef USE_DEFAULT_ENABLE_IR_IN + +#elif defined(ESP32) +// No system LED on ESP32, disable blinking by NOT defining BLINKLED + +// Supply own enableIRIn() and enableIROut() +#undef USE_DEFAULT_ENABLE_IR_IN +#undef USE_DEFAULT_ENABLE_IR_OUT + +#else +#warning No blinking definition found. Check IRremoteBoardDefs.h. +#ifdef LED_BUILTIN +#define BLINKLED LED_BUILTIN +#define BLINKLED_ON() digitalWrite(BLINKLED, HIGH) +#define BLINKLED_OFF() digitalWrite(BLINKLED, LOW) +#endif +#endif + +//------------------------------------------------------------------------------ +// microseconds per clock interrupt tick +#define MICROS_PER_TICK 50 + +//------------------------------------------------------------------------------ +// Define which timer to use +// +// Uncomment the timer you wish to use on your board. +// If you are using another library which uses timer2, you have options to +// switch IRremote to use a different timer. +// + +#ifndef ARDUINO +// Assume that we compile a test version, to be executed on the host, +// not on a board. + +// Do not define any timer. + +/********************* + * ARDUINO Boards + *********************/ +// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, Nano, etc +// ATmega48, ATmega88, ATmega168, ATmega328 +#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) // old default clause +# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) +#define IR_USE_TIMER1 // tx = pin 9 +//#define IR_USE_TIMER2 // tx = pin 3 +# endif + +// Arduino Mega +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) && !defined(IR_USE_TIMER3) && !defined(IR_USE_TIMER4) && !defined(IR_USE_TIMER5) +//#define IR_USE_TIMER1 // tx = pin 11 +#define IR_USE_TIMER2 // tx = pin 9 +//#define IR_USE_TIMER3 // tx = pin 5 +//#define IR_USE_TIMER4 // tx = pin 6 +//#define IR_USE_TIMER5 // tx = pin 46 +# endif + +// Leonardo +#elif defined(__AVR_ATmega32U4__) && ! defined(TEENSYDUINO) && ! defined(ARDUINO_AVR_PROMICRO) +# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER3) && !defined(IR_USE_TIMER4_HS) +//#define IR_USE_TIMER1 // tx = pin 9 +#define IR_USE_TIMER3 // tx = pin 5 +//#define IR_USE_TIMER4_HS // tx = pin 13 +# endif + +// Nano Every, Uno WiFi Rev2 +#elif defined(__AVR_ATmega4809__) +# if !defined(IR_USE_TIMER_4809_1) && !defined(IR_USE_TIMER_4809_2) +#define IR_USE_TIMER_4809_1 // tx = pin 24 +//#define IR_USE_TIMER_4809_2 // TODO tx = pin 21 +# endif + +/********************* + * Plain AVR CPU's + *********************/ +// ATmega8u2, ATmega16U2, ATmega32U2 +#elif defined(__AVR_ATmega8U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) +# if !defined(IR_USE_TIMER1) + #define IR_USE_TIMER1 // tx = pin C6 +#endif + +// Atmega8 +#elif defined(__AVR_ATmega8__) +# if !defined(IR_USE_TIMER1) +#define IR_USE_TIMER1 // tx = pin 9 +# endif + + +// ATtiny84 +#elif defined(__AVR_ATtiny84__) +# if !defined(IR_USE_TIMER1) +#define IR_USE_TIMER1 // tx = pin 6 +# endif + +//ATtiny85 +#elif defined(__AVR_ATtiny85__) +# if !defined(IR_USE_TIMER_TINY0) +#define IR_USE_TIMER_TINY0 // tx = pin 1 +# endif + +/********************* + * SPARKFUN Boards + *********************/ +// Sparkfun Pro Micro +#elif defined(ARDUINO_AVR_PROMICRO) +# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER3) && !defined(IR_USE_TIMER4_HS) +//#define IR_USE_TIMER1 // tx = pin 9 +#define IR_USE_TIMER3 // tx = pin 5 +//#define IR_USE_TIMER4_HS // tx = pin 13 +# endif + +/********************* + * TEENSY Boards + *********************/ +// Teensy 1.0 +#elif defined(__AVR_AT90USB162__) +# if !defined(IR_USE_TIMER1) +#define IR_USE_TIMER1 // tx = pin 17 +# endif + +// Teensy 2.0 +#elif defined(__AVR_ATmega32U4__) && defined(TEENSYDUINO) +# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER3) && !defined(IR_USE_TIMER4_HS) +//#define IR_USE_TIMER1 // tx = pin 14 (Teensy 2.0 - physical pin: B5) +#define IR_USE_TIMER3 // tx = pin 9 (Teensy 2.0 - physical pin: C6) +//#define IR_USE_TIMER4_HS // tx = pin 10 (Teensy 2.0 - physical pin: C7) +# endif + +// Teensy 3.0 / Teensy 3.1 +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) +# if !defined(IR_USE_TIMER_CMT) +#define IR_USE_TIMER_CMT // tx = pin 5 +# endif + +// Teensy-LC +#elif defined(__MKL26Z64__) +# if !defined(IR_USE_TIMER_TPM1) +#define IR_USE_TIMER_TPM1 // tx = pin 16 +# endif + +// Teensy++ 1.0 & 2.0 +#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) +# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) && !defined(IR_USE_TIMER3) +//#define IR_USE_TIMER1 // tx = pin 25 +#define IR_USE_TIMER2 // tx = pin 1 +//#define IR_USE_TIMER3 // tx = pin 16 +# endif + +/********************* + * CPU's with MegaCore + *********************/ +// MegaCore - ATmega64, ATmega128 +#elif defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) +# if !defined(IR_USE_TIMER1) + #define IR_USE_TIMER1 // tx = pin 13 +# endif + +/********************* + * CPU's with MajorCore + *********************/ +#elif defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER3) + #define IR_USE_TIMER1 // tx = pin 13 + //#define IR_USE_TIMER3 // tx = pin 12 - ATmega162 only +#endif + +/********************* + * CPU's with MightyCore + *********************/ +// MightyCore - ATmega1284 +#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) +# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) && !defined(IR_USE_TIMER3) +//#define IR_USE_TIMER1 // tx = pin 13 +#define IR_USE_TIMER2 // tx = pin 14 +//#define IR_USE_TIMER3 // tx = pin 6 +# endif + +// MightyCore - ATmega164, ATmega324, ATmega644 +#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) +# if !defined(IR_USE_TIMER1) && !defined(IR_USE_TIMER2) +//#define IR_USE_TIMER1 // tx = pin 13 +#define IR_USE_TIMER2 // tx = pin 14 +# endif + +// MightyCore - ATmega8535, ATmega16, ATmega32 +#elif defined(__AVR_ATmega8535__) || defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) +# if !defined(IR_USE_TIMER1) +#define IR_USE_TIMER1 // tx = pin 13 +# endif + +/********************* + * OTHER CPU's + *********************/ +#elif defined(ESP32) +# if !defined(IR_TIMER_USE_ESP32) +#define IR_TIMER_USE_ESP32 +# endif + +#elif defined(ARDUINO_ARCH_SAMD) +#define TIMER_PRESCALER_DIV 64 + +#else +// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, Nano, etc +// ATmega48, ATmega88, ATmega168, ATmega328 +//#define IR_USE_TIMER1 // tx = pin 9 +#error Board could not be identified from pre-processor symbols. Please extend IRremoteBoardDefs.h. +#endif + +// Provide default definitions, portable but possibly slower than necessary. +// digitalWrite is supposed to be slow. If this is an issue, define faster, +// board-dependent versions of these macros SENDPIN_ON(pin) and SENDPIN_OFF(pin). +// Portable, possibly slow, default definitions are given at the end of this file. +// If defining new versions, feel free to ignore the pin argument if it +// is not configurable on the current board. + +#ifndef SENDPIN_ON +/** Board dependent macro to turn on the pin given as argument. */ +#define SENDPIN_ON(pin) digitalWrite(pin, HIGH) +#endif + +#ifndef SENDPIN_OFF +/** + * Board dependent macro to turn off the pin given as argument. + */ +#define SENDPIN_OFF(pin) digitalWrite(pin, LOW) +#endif + +//------------------------------------------------------------------------------ +// CPU Frequency +// +#if !defined(SYSCLOCK) && defined(ARDUINO) // allow for processor specific code to define SYSCLOCK +#ifndef F_CPU +#error SYSCLOCK cannot be determined. Define it for your board in IRremoteBoardDefs.h. +#endif // ! F_CPU +/** + * Clock frequency to be used for timing. + */ +#define SYSCLOCK F_CPU // main Arduino clock +#endif // ! SYSCLOCK + +//------------------------------------------------------------------------------ +// Defines for Timer + +//--------------------------------------------------------- +#ifdef DOXYGEN +/** + * If applicable, pin number for sending IR. Note that in most cases, this is not + * used and ignored if set. Instead, the sending pin is determined by the timer + * deployed. + */ +#define IR_SEND_PIN + +/** + * Interrupt service routine. Called as interrupt routine to collect read IR data. + */ +#define ISR + +#elif ! defined(ARDUINO) +// Assume that we compile a test version, to be executed on the host, +// not on a board. +// Do nothing. +#ifdef ISR +#undef ISR +#endif +#define ISR(f) void do_not_use__(void) +#define TIMER_RESET + +//--------------------------------------------------------- +// Timer2 (8 bits) +// +#elif defined(IR_USE_TIMER2) + +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR2A |= _BV(COM2B1)) +#define TIMER_DISABLE_PWM (TCCR2A &= ~(_BV(COM2B1))) +#define TIMER_ENABLE_INTR (TIMSK2 = _BV(OCIE2A)) +#define TIMER_DISABLE_INTR (TIMSK2 = 0) +#define TIMER_INTR_NAME TIMER2_COMPA_vect +// COM2A = 00: disconnect OC2A +// COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted +// WGM2 = 101: phase-correct PWM with OCRA as top +// CS2 = 000: no prescaling +// The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A. +#pragma GCC diagnostic ignored "-Wunused-function" +static void timerConfigkHz(uint16_t frequency) { + const uint16_t pwmval = (SYSCLOCK / 2000) / (frequency); + TCCR2A = _BV(WGM20); + TCCR2B = _BV(WGM22) | _BV(CS20); + OCR2A = pwmval; + OCR2B = pwmval * DUTY_CYCLE / 100; +} + +#define TIMER_COUNT_TOP (SYSCLOCK * MICROS_PER_TICK / 1000000) +static void timerConfigNormal() { +#if (TIMER_COUNT_TOP < 256) + TCCR2A = _BV(WGM21); + TCCR2B = _BV(CS20); + OCR2A = TIMER_COUNT_TOP; + TCNT2 = 0; +#else + TCCR2A = _BV(WGM21); + TCCR2B = _BV(CS21); + OCR2A = TIMER_COUNT_TOP / 8; + TCNT2 = 0; +#endif +} + +//----------------- +#if defined(CORE_OC2B_PIN) +#define IR_SEND_PIN CORE_OC2B_PIN // Teensy + +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define IR_SEND_PIN 9 // Arduino Mega + +#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) +#define IR_SEND_PIN 14 // MightyCore, MegaCore + +#else +#define IR_SEND_PIN 3 // Arduino Duemilanove, Diecimila, LilyPad, etc +#endif // defined(CORE_OC2B_PIN) + +//--------------------------------------------------------- +// Timer1 (16 bits) +// +#elif defined(IR_USE_TIMER1) + +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR1A |= _BV(COM1A1)) +#define TIMER_DISABLE_PWM (TCCR1A &= ~(_BV(COM1A1))) + +//----------------- +#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8515__) \ +|| defined(__AVR_ATmega8535__) || defined(__AVR_ATmega16__) \ +|| defined(__AVR_ATmega32__) || defined(__AVR_ATmega64__) \ +|| defined(__AVR_ATmega128__) || defined(__AVR_ATmega162__) +#define TIMER_ENABLE_INTR (TIMSK |= _BV(OCIE1A)) +#define TIMER_DISABLE_INTR (TIMSK &= ~_BV(OCIE1A)) +#else +#define TIMER_ENABLE_INTR (TIMSK1 = _BV(OCIE1A)) +#define TIMER_DISABLE_INTR (TIMSK1 = 0) +#endif + +//----------------- +#define TIMER_INTR_NAME TIMER1_COMPA_vect + +static void timerConfigkHz(uint16_t frequency) { + const uint32_t pwmval = SYSCLOCK / 2000 / (frequency); + TCCR1A = _BV(WGM11); + TCCR1B = _BV(WGM13) | _BV(CS10); + ICR1 = pwmval; + OCR1A = pwmval * DUTY_CYCLE / 100; +} + +static void timerConfigNormal() { + TCCR1A = 0; + TCCR1B = _BV(WGM12) | _BV(CS10); + OCR1A = SYSCLOCK * MICROS_PER_TICK / 1000000; + TCNT1 = 0; +} + +//----------------- +#if defined(CORE_OC1A_PIN) +#define IR_SEND_PIN CORE_OC1A_PIN // Teensy + +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define IR_SEND_PIN 11 // Arduino Mega + +#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) \ +|| defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) \ +|| defined(__AVR_ATmega324P__) || defined(__AVR_ATmega324A__) \ +|| defined(__AVR_ATmega324PA__) || defined(__AVR_ATmega164A__) \ +|| defined(__AVR_ATmega164P__) || defined(__AVR_ATmega32__) \ +|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega8535__) \ +|| defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__) \ +|| defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) \ +|| defined(__AVR_ATmega8515__) || defined(__AVR_ATmega162__) +#define IR_SEND_PIN 13 // MightyCore, MegaCore, MajorCore + +#elif defined(__AVR_ATtiny84__) +# define IR_SEND_PIN 6 + +#else +#define IR_SEND_PIN 9 // Arduino Duemilanove, Diecimila, LilyPad, Sparkfun Pro Micro, Leonardo etc. +#endif // defined(CORE_OC1A_PIN) + +//--------------------------------------------------------- +// Timer3 (16 bits) +// +#elif defined(IR_USE_TIMER3) + +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR3A |= _BV(COM3A1)) +#define TIMER_DISABLE_PWM (TCCR3A &= ~(_BV(COM3A1))) +#define TIMER_ENABLE_INTR (TIMSK3 = _BV(OCIE3A)) +#define TIMER_DISABLE_INTR (TIMSK3 = 0) +#define TIMER_INTR_NAME TIMER3_COMPA_vect + +static void timerConfigkHz(uint16_t frequency) { + const uint32_t pwmval = SYSCLOCK / 2000 / (frequency); + TCCR3A = _BV(WGM31); + TCCR3B = _BV(WGM33) | _BV(CS30); + ICR3 = pwmval; + OCR3A = pwmval * DUTY_CYCLE / 100; +} + +static void timerConfigNormal() { + TCCR3A = 0; + TCCR3B = _BV(WGM32) | _BV(CS30); + OCR3A = SYSCLOCK * MICROS_PER_TICK / 1000000; + TCNT3 = 0; +} + +//----------------- +#if defined(CORE_OC3A_PIN) +#define IR_SEND_PIN CORE_OC3A_PIN // Teensy + +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) \ +|| defined(__AVR_ATmega32U4__) || defined(ARDUINO_AVR_PROMICRO) +#define IR_SEND_PIN 5 // Arduino Mega, Arduino Leonardo, Sparkfun Pro Micro + +#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) +#define IR_SEND_PIN 6 // MightyCore, MegaCore + +#else +#error "Please add OC3A pin number here\n" +#endif + +//--------------------------------------------------------- +// Timer4 (10 bits, high speed option) +// +#elif defined(IR_USE_TIMER4_HS) + +#define TIMER_RESET +#if defined(ARDUINO_AVR_PROMICRO) // Sparkfun Pro Micro +#define TIMER_ENABLE_PWM (TCCR4A |= _BV(COM4A0)) // Use complimentary O̅C̅4̅A̅ output on pin 5 +#define TIMER_DISABLE_PWM (TCCR4A &= ~(_BV(COM4A0))) // (Pro Micro does not map PC7 (32/ICP3/CLK0/OC4A) + // of ATmega32U4 ) +#else +#define TIMER_ENABLE_PWM (TCCR4A |= _BV(COM4A1)) +#define TIMER_DISABLE_PWM (TCCR4A &= ~(_BV(COM4A1))) +#endif +#define TIMER_ENABLE_INTR (TIMSK4 = _BV(TOIE4)) +#define TIMER_DISABLE_INTR (TIMSK4 = 0) +#define TIMER_INTR_NAME TIMER4_OVF_vect + +static void timerConfigkHz(uint16_t frequency) { + const uint32_t pwmval = SYSCLOCK / 2000 / (frequency); + TCCR4A = (1 << PWM4A); + TCCR4B = _BV(CS40); + TCCR4C = 0; + TCCR4D = (1 << WGM40); + TCCR4E = 0; + TC4H = pwmval >> 8; + OCR4C = pwmval; + TC4H = (pwmval * DUTY_CYCLE / 100) >> 8; + OCR4A = (pwmval * DUTY_CYCLE / 100) & 255; +} + +static void timerConfigNormal() { + TCCR4A = 0; + TCCR4B = _BV(CS40); + TCCR4C = 0; + TCCR4D = 0; + TCCR4E = 0; + TC4H = (SYSCLOCK * MICROS_PER_TICK / 1000000) >> 8; + OCR4C = (SYSCLOCK * MICROS_PER_TICK / 1000000) & 255; + TC4H = 0; + TCNT4 = 0; +} + +//----------------- +#if defined(CORE_OC4A_PIN) +#define IR_SEND_PIN CORE_OC4A_PIN // Teensy +#elif defined(ARDUINO_AVR_PROMICRO) +# define IR_SEND_PIN 5 // Sparkfun Pro Micro +#elif defined(__AVR_ATmega32U4__) +#define IR_SEND_PIN 13 // Leonardo +#else +#error "Please add OC4A pin number here\n" +#endif + +//--------------------------------------------------------- +// Timer4 (16 bits) +// +#elif defined(IR_USE_TIMER4) + +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR4A |= _BV(COM4A1)) +#define TIMER_DISABLE_PWM (TCCR4A &= ~(_BV(COM4A1))) +#define TIMER_ENABLE_INTR (TIMSK4 = _BV(OCIE4A)) +#define TIMER_DISABLE_INTR (TIMSK4 = 0) +#define TIMER_INTR_NAME TIMER4_COMPA_vect + +static void timerConfigkHz(uint16_t frequency) { + const uint32_t pwmval = SYSCLOCK / 2000 / (frequency); + TCCR4A = _BV(WGM41); + TCCR4B = _BV(WGM43) | _BV(CS40); + ICR4 = pwmval; + OCR4A = pwmval * DUTY_CYCLE / 100; +} + +static void timerConfigNormal() { + TCCR4A = 0; + TCCR4B = _BV(WGM42) | _BV(CS40); + OCR4A = SYSCLOCK * MICROS_PER_TICK / 1000000; + TCNT4 = 0; +} + +//----------------- +#if defined(CORE_OC4A_PIN) +#define IR_SEND_PIN CORE_OC4A_PIN +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define IR_SEND_PIN 6 // Arduino Mega +#else +#error "Please add OC4A pin number here\n" +#endif + +//--------------------------------------------------------- +// Timer5 (16 bits) +// +#elif defined(IR_USE_TIMER5) + +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR5A |= _BV(COM5A1)) +#define TIMER_DISABLE_PWM (TCCR5A &= ~(_BV(COM5A1))) +#define TIMER_ENABLE_INTR (TIMSK5 = _BV(OCIE5A)) +#define TIMER_DISABLE_INTR (TIMSK5 = 0) +#define TIMER_INTR_NAME TIMER5_COMPA_vect + +static void timerConfigkHz(uint16_t frequency) { + const uint32_t pwmval = SYSCLOCK / 2000 / (frequency); + TCCR5A = _BV(WGM51); + TCCR5B = _BV(WGM53) | _BV(CS50); + ICR5 = pwmval; + OCR5A = pwmval * DUTY_CYCLE / 100; +} + +static void timerConfigNormal() { + TCCR5A = 0; + TCCR5B = _BV(WGM52) | _BV(CS50); + OCR5A = SYSCLOCK * MICROS_PER_TICK / 1000000; + TCNT5 = 0; +} + +//----------------- +#if defined(CORE_OC5A_PIN) +#define IR_SEND_PIN CORE_OC5A_PIN +#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +#define IR_SEND_PIN 46 // Arduino Mega +#else +#error "Please add OC5A pin number here\n" +#endif + +//--------------------------------------------------------- +// Special carrier modulator timer for Teensy 3.0 / Teensy 3.1 +// +#elif defined(IR_USE_TIMER_CMT) + +#define TIMER_RESET ({ \ +uint8_t tmp __attribute__((unused)) = CMT_MSC; \ +CMT_CMD2 = 30; \ +}) + +#define TIMER_ENABLE_PWM do { \ +CORE_PIN5_CONFIG = PORT_PCR_MUX(2) | PORT_PCR_DSE | PORT_PCR_SRE; \ +} while(0) + +#define TIMER_DISABLE_PWM do { \ +CORE_PIN5_CONFIG = PORT_PCR_MUX(1) | PORT_PCR_DSE | PORT_PCR_SRE; \ +} while(0) + +#define TIMER_ENABLE_INTR NVIC_ENABLE_IRQ(IRQ_CMT) +#define TIMER_DISABLE_INTR NVIC_DISABLE_IRQ(IRQ_CMT) +#define TIMER_INTR_NAME cmt_isr + +//----------------- +#ifdef ISR +#undef ISR +#endif +#define ISR(f) void f(void) + +//----------------- +#define CMT_PPS_DIV ((F_BUS + 7999999) / 8000000) +#if F_BUS < 8000000 +#error IRremote requires at least 8 MHz on Teensy 3.x +#endif + +static void timerConfigkHz(uint16_t frequency) { + SIM_SCGC4 |= SIM_SCGC4_CMT; + SIM_SOPT2 |= SIM_SOPT2_PTD7PAD; + CMT_PPS = CMT_PPS_DIV - 1; + CMT_CGH1 = ((F_BUS / CMT_PPS_DIV / 3000) + ((frequency) / 2)) / (frequency); + CMT_CGL1 = ((F_BUS / CMT_PPS_DIV / 1500) + ((frequency) / 2)) / (frequency); + CMT_CMD1 = 0; + CMT_CMD2 = 30; + CMT_CMD3 = 0; + CMT_CMD4 = 0; + CMT_OC = 0x60; + CMT_MSC = 0x01; +} + +static void timerConfigNormal() { + SIM_SCGC4 |= SIM_SCGC4_CMT; + CMT_PPS = CMT_PPS_DIV - 1; + CMT_CGH1 = 1; + CMT_CGL1 = 1; + CMT_CMD1 = 0; + CMT_CMD2 = 30; + CMT_CMD3 = 0; + CMT_CMD4 = (F_BUS / 160000 + CMT_PPS_DIV / 2) / CMT_PPS_DIV - 31; + CMT_OC = 0; + CMT_MSC = 0x03; +} + +#define IR_SEND_PIN 5 + +// defines for TPM1 timer on Teensy-LC +#elif defined(IR_USE_TIMER_TPM1) +#define TIMER_RESET FTM1_SC |= FTM_SC_TOF; +#define TIMER_ENABLE_PWM CORE_PIN16_CONFIG = PORT_PCR_MUX(3)|PORT_PCR_DSE|PORT_PCR_SRE +#define TIMER_DISABLE_PWM CORE_PIN16_CONFIG = PORT_PCR_MUX(1)|PORT_PCR_SRE +#define TIMER_ENABLE_INTR NVIC_ENABLE_IRQ(IRQ_FTM1) +#define TIMER_DISABLE_INTR NVIC_DISABLE_IRQ(IRQ_FTM1) +#define TIMER_INTR_NAME ftm1_isr +#ifdef ISR +#undef ISR +#endif +#define ISR(f) void f(void) + +static void timerConfigkHz(uint16_t frequency) { + SIM_SCGC6 |= SIM_SCGC6_TPM1; + FTM1_SC = 0; + FTM1_CNT = 0; + FTM1_MOD = (F_PLL / 2000) / frequency - 1; + FTM1_C0V = (F_PLL / 6000) / frequency - 1; + FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); +} + +static void timerConfigNormal() { + SIM_SCGC6 |= SIM_SCGC6_TPM1; + FTM1_SC = 0; + FTM1_CNT = 0; + FTM1_MOD = (F_PLL / 40000) - 1; + FTM1_C0V = 0; + FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0) | FTM_SC_TOF | FTM_SC_TOIE; +} +#define IR_SEND_PIN 16 + +// defines for timer_tiny0 (8 bits) +#elif defined(IR_USE_TIMER_TINY0) +#define TIMER_RESET +#define TIMER_ENABLE_PWM (TCCR0A |= _BV(COM0B1)) +#define TIMER_DISABLE_PWM (TCCR0A &= ~(_BV(COM0B1))) +#define TIMER_ENABLE_INTR (TIMSK |= _BV(OCIE0A)) +#define TIMER_DISABLE_INTR (TIMSK &= ~(_BV(OCIE0A))) +#define TIMER_INTR_NAME TIMER0_COMPA_vect + +static void timerConfigkHz(uint16_t frequency) { + const uint16_t pwmval = SYSCLOCK / 2000 / (frequency); + TCCR0A = _BV(WGM00); + TCCR0B = _BV(WGM02) | _BV(CS00); + OCR0A = pwmval; + OCR0B = pwmval * DUTY_CYCLE / 100; +} + +#define TIMER_COUNT_TOP (SYSCLOCK * MICROS_PER_TICK / 1000000) +static void timerConfigNormal() { +#if (TIMER_COUNT_TOP < 256) + TCCR0A = _BV(WGM01); + TCCR0B = _BV(CS00); + OCR0A = TIMER_COUNT_TOP; + TCNT0 = 0; +#else + TCCR0A = _BV(WGM01); + TCCR0B = _BV(CS01); + OCR0A = TIMER_COUNT_TOP / 8; + TCNT0 = 0; +#endif +} + +#define IR_SEND_PIN 1 /* ATtiny85 */ + +#elif defined(IR_USE_TIMER_4809_1) +// ATmega4809 TCB0 +#define TIMER_RESET TCB0.INTFLAGS = TCB_CAPT_bm +#define TIMER_ENABLE_PWM (TCB0.CTRLB |= TCB_CCMPEN_bm) +#define TIMER_DISABLE_PWM (TCB0.CTRLB &= ~(TCB_CCMPEN_bm)) +#define TIMER_ENABLE_INTR (TCB0.INTCTRL = TCB_CAPT_bm) +#define TIMER_DISABLE_INTR (TCB0.INTCTRL &= ~(TCB_CAPT_bm)) +#define TIMER_INTR_NAME TCB0_INT_vect + +static void timerConfigkHz(uint16_t frequency) { + const uint32_t pwmval = (SYSCLOCK / 2000) / (frequency); + TCB0.CTRLB = TCB_CNTMODE_PWM8_gc; + TCB0.CCMPL = pwmval; + TCB0.CCMPH = (pwmval * DUTY_CYCLE) / 100; + TCB0.CTRLA = (TCB_CLKSEL_CLKDIV2_gc) | (TCB_ENABLE_bm); +} + +static void timerConfigNormal() { + TCB0.CTRLB = (TCB_CNTMODE_INT_gc); + TCB0.CCMP = ((SYSCLOCK * MICROS_PER_TICK) / 1000000); + TCB0.INTCTRL = TCB_CAPT_bm; + TCB0.CTRLA = (TCB_CLKSEL_CLKDIV1_gc) | (TCB_ENABLE_bm); +} + +#define IR_SEND_PIN 6 /* Nano Every, Uno WiFi Rev2 */ +//--------------------------------------------------------- +// ESP32 (ESP8266 should likely be added here too) +// + +// ESP32 has it own timer API and does not use these macros, but to avoid ifdef'ing +// them out in the common code, they are defined to no-op. This allows the code to compile +// (which it wouldn't otherwise) but irsend will not work until ESP32 specific code is written +// for that -- merlin +// As a warning, sending timing specific code from an ESP32 can be challenging if you need 100% +// reliability because the arduino code may be interrupted and cause your sent waveform to be the +// wrong length. This is specifically an issue for neopixels which require 800Khz resolution. +// IR may just work as is with the common code since it's lower frequency, but if not, the other +// way to do this on ESP32 is using the RMT built in driver like in this incomplete library below +// https://github.com/ExploreEmbedded/ESP32_RMT +#elif defined(IR_TIMER_USE_ESP32) + +#if ! defined(IR_SEND_PIN) +#define IR_SEND_PIN 4 // can use any pin, no timer restrictions +#endif + +#if ! defined(LEDCHANNEL) +#define LEDCHANNEL 0 // The channel used for PWM 0 to 7 are high speed PWM channels +#endif + +#define TIMER_RESET +#define TIMER_ENABLE_PWM ledcWrite(LEDCHANNEL, DUTY_CYCLE) // we must use channel here not pin number +#define TIMER_DISABLE_PWM ledcWrite(LEDCHANNEL, 0) + +#ifdef ISR +#undef ISR +#endif +#define ISR(f) void IRAM_ATTR IRTimer() + +#elif defined(ARDUINO_ARCH_SAMD) +// use timer 3 hardcoded at this time + +#define IR_SEND_PIN 9 + +#define TIMER_RESET +#define TIMER_ENABLE_PWM // Not presently used +#define TIMER_DISABLE_PWM +#define TIMER_ENABLE_INTR NVIC_EnableIRQ(TC3_IRQn) // Not presently used +#define TIMER_DISABLE_INTR NVIC_DisableIRQ(TC3_IRQn) +#define TIMER_INTR_NAME TC3_Handler // Not presently used +static void timerConfigkHz(uint16_t frequency) { + +} + +#ifdef ISR +#undef ISR +#endif +#define ISR(f) void irs() + +//--------------------------------------------------------- +// Unknown Timer +// +#else +#error "Internal code configuration error, no known IR_USE_TIMER* defined\n" +#endif + +#endif // ! IRremoteBoardDefs_h diff --git a/ModuleTests/testSensorPack/src/private/IRremoteInt.h b/ModuleTests/testSensorPack/src/private/IRremoteInt.h new file mode 100644 index 00000000..f0d3e16d --- /dev/null +++ b/ModuleTests/testSensorPack/src/private/IRremoteInt.h @@ -0,0 +1,126 @@ +//****************************************************************************** +// IRremoteint.h +// IRremote +// Version 2.0.1 June, 2015 +// Copyright 2009 Ken Shirriff +// For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html +// +// Modified by Paul Stoffregen to support other boards and timers +// +// Interrupt code based on NECIRrcv by Joe Knapp +// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 +// Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ +// +// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) +// Whynter A/C ARC-110WD added by Francesco Meschia +//****************************************************************************** + +#ifndef IRremoteint_h +#define IRremoteint_h + +//------------------------------------------------------------------------------ +// Include the Arduino header +// +#include + +// All board specific stuff have been moved to its own file, included here. +#include "IRremoteBoardDefs.h" + +//------------------------------------------------------------------------------ +// Information for the Interrupt Service Routine +// +#if ! defined(RAW_BUFFER_LENGTH) +#define RAW_BUFFER_LENGTH 101 ///< Maximum length of raw duration buffer. Must be odd. +#endif + +/** + * This struct is used to communicate with the ISR (interrupt service routine). + */ +typedef struct { + // The fields are ordered to reduce memory over caused by struct-padding + uint8_t rcvstate; ///< State Machine state + uint8_t recvpin; ///< Pin connected to IR data from detector + uint8_t blinkpin; + uint8_t blinkflag; ///< true -> enable blinking of pin on IR processing + unsigned int rawlen; ///< counter of entries in rawbuf + unsigned int timer; ///< State timer, counts 50uS ticks. + unsigned int rawbuf[RAW_BUFFER_LENGTH]; ///< raw data + uint8_t overflow; ///< Raw buffer overflow occurred +} irparams_t; + +// ISR State-Machine : Receiver States +#define IR_REC_STATE_IDLE 0 +#define IR_REC_STATE_MARK 1 +#define IR_REC_STATE_SPACE 2 +#define IR_REC_STATE_STOP 3 +#define IR_REC_STATE_OVERFLOW 4 + +/** + * Allow all parts of the code access to the ISR data + * NB. The data can be changed by the ISR at any time, even mid-function + * Therefore we declare it as "volatile" to stop the compiler/CPU caching it + */ +#ifdef IR_GLOBAL +volatile irparams_t irparams; +#else +extern volatile irparams_t irparams; +#endif + +//------------------------------------------------------------------------------ +// Defines for setting and clearing register bits +// +#ifndef cbi +#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) +#endif + +#ifndef sbi +#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) +#endif + +//------------------------------------------------------------------------------ +// Pulse parms are ((X*50)-100) for the Mark and ((X*50)+100) for the Space. +// First MARK is the one after the long gap +// Pulse parameters in uSec +// + +/** + * When received, marks tend to be too long and + * spaces tend to be too short. + * To compensate for this, MARK_EXCESS_MICROS is subtracted from all marks, + * and added to all spaces. + */ +#define MARK_EXCESS_MICROS 100 + +/** Relative tolerance (in percent) for some comparisons on measured data. */ +#define TOLERANCE 25 + +/** Lower tolerance for comparison of measured data */ +//#define LTOL (1.0 - (TOLERANCE/100.)) +#define LTOL (100 - TOLERANCE) +/** Upper tolerance for comparison of measured data */ +//#define UTOL (1.0 + (TOLERANCE/100.)) +#define UTOL (100 + TOLERANCE) + +/** Minimum gap between IR transmissions, in microseconds */ +#define _GAP 5000 + +/** Minimum gap between IR transmissions, in MICROS_PER_TICK */ +#define GAP_TICKS (_GAP/MICROS_PER_TICK) + +//#define TICKS_LOW(us) ((int)(((us)*LTOL/MICROS_PER_TICK))) +//#define TICKS_HIGH(us) ((int)(((us)*UTOL/MICROS_PER_TICK + 1))) +#if MICROS_PER_TICK == 50 && TOLERANCE == 25 // Defaults + #define TICKS_LOW(us) ((int) ((us)/67 )) // (us) / ((MICROS_PER_TICK:50 / LTOL:75 ) * 100) + #define TICKS_HIGH(us) ((int) ((us)/40 + 1)) // (us) / ((MICROS_PER_TICK:50 / UTOL:125) * 100) + 1 +#else + #define TICKS_LOW(us) ((int) ((long) (us) * LTOL / (MICROS_PER_TICK * 100) )) + #define TICKS_HIGH(us) ((int) ((long) (us) * UTOL / (MICROS_PER_TICK * 100) + 1)) +#endif + +//------------------------------------------------------------------------------ +// IR detector output is active low +// +#define MARK 0 ///< Sensor output for a mark ("flash") +#define SPACE 1 ///< Sensor output for a space ("gap") + +#endif diff --git a/ModuleTests/testSensorPack/testSensorPack.ino b/ModuleTests/testSensorPack/testSensorPack.ino new file mode 100644 index 00000000..fe2c1766 --- /dev/null +++ b/ModuleTests/testSensorPack/testSensorPack.ino @@ -0,0 +1,303 @@ +// -------------------------------------- +// i2c_scanner +// +// Version 1 +// This program (or code that looks like it) +// can be found in many places. +// For example on the Arduino.cc forum. +// The original author is not know. +// Version 2, Juni 2012, Using Arduino 1.0.1 +// Adapted to be as simple as possible by Arduino.cc user Krodal +// Version 3, Feb 26 2013 +// V3 by louarnold +// Version 4, March 3, 2013, Using Arduino 1.0.3 +// by Arduino.cc user Krodal. +// Changes by louarnold removed. +// Scanning addresses changed from 0...127 to 1...119, +// according to the i2c scanner by Nick Gammon +// http://www.gammon.com.au/forum/?id=10896 +// Version 5, March 28, 2013 +// As version 4, but address scans now to 127. +// A sensor seems to use address 120. +// Version 6, November 27, 2015. +// Added waiting for the Leonardo serial communication. +// +// +// This sketch tests the standard 7-bit addresses +// Devices with higher bit address might not be seen properly. +// +byte testID = 0; + +#include +#include "Adafruit_NeoPixel.h" +#include "sound.h" + +char instruction[] = "---\n1-Touch\t 2-Gesture 3-light\n4-Distns 5-PIR 6-Noise\n*\t *\t *\n16-Old\t\t\n19-New\t 20-Print 21-Sound\n---"; +bool compatible_3V = true; +#include "infrared.h" +#include "pitches.h" +#include "Arduino_APDS9960.h" +// Which pin on the Arduino is connected to the NeoPixels? +// On a Trinket or Gemma we suggest changing this to 1: +#define LED_PIN 10 +#define LED_COUNT 7 // How many NeoPixels are attached to the Arduino? +#define IR_PIN 4 +#define PIR_PIN 8 + + + +int digitalPins[] = { 6, 7 }; +int analogPins[] = { A2, A3 }; +int LightPins = A4; +int VoicePins = A5; +byte pirState = 0; +byte loopCounter = 0; + +#define TARGET_ADDRESS 0x39 + +int notes[] = { + NOTE_C4, + NOTE_D4, + NOTE_E4, + NOTE_F4, +}; + + +byte touchCounter[2]; +byte pirCounter = 0; + +// Declare our NeoPixel strip object: +Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); +// Argument 1 = Number of pixels in NeoPixel strip +// Argument 2 = Arduino pin number (most are valid) +// Argument 3 = Pixel type flags, add together as needed: + +void indicatePass(int passCase = 0) { + strip.setPixelColor(testID, strip.Color(0, 255, 0)); + if (passCase == 1) + strip.setPixelColor(testID + 1, strip.Color(255, 0, 0)); //Conditional pass. The consistency or the device ID doesn't match. + strip.show(); + if (passCase == 1) + playMelody(fail, sizeof(fail) / 2); + if (makeSound) + playMelody(pass, sizeof(pass) / 2); + else + delay(1000); + delay(100); +} + +void indicateFail() { + strip.setPixelColor(testID, strip.Color(255, 0, 0)); + strip.show(); + if (makeSound) + playMelody(fail, sizeof(fail) / 2); + else + delay(1000); +} + +void setup() { + Wire.begin(); + Serial.begin(115200); + Serial.println("Start..."); + Serial.println(instruction); + pinMode(PIR_PIN, INPUT); + pinMode(analogPins[0], INPUT); + pinMode(analogPins[1], INPUT); + while (!Serial) + ; // Leonardo: wait for serial monitor + irrecv.enableIRIn(); // Start the receiver + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255) +} + +void loop() { + read_infrared(); + if (testID < 6) { + strip.clear(); + strip.setPixelColor(testID, strip.Color(0, 0, 255)); + if (testID == 2 || testID == 3) + strip.setPixelColor(4, strip.Color(128, compatible_3V ? 128 : 0, compatible_3V ? 0 : 255)); //新黄 旧紫 + strip.show(); + } + switch (testID) { + case 0: testTouch(); break; + case 1: + if (!loopCounter) + testI2Cdevice(TARGET_ADDRESS); + break; + case 2: testDoubleAnalog(2); break; //light + case 3: testDoubleAnalog(3); break; //distance + case 4: testPIR(); break; + case 5: testNoise(); break; + } + loopCounter = (loopCounter + 1) % 10; +} + +void testTouch() { + bool touched = false; + for (int thisSensor = 0; thisSensor < 2; thisSensor++) { + // get a sensor reading: + int sensorReading = digitalRead(digitalPins[thisSensor]); + if (sensorReading) { + // play the note corresponding to this sensor: + if (makeSound) + tone(BUZZER, notes[thisSensor], 20); + + touchCounter[thisSensor]++; + touched = true; + } + } + if (touched) { + for (int thisSensor = 0; thisSensor < 2; thisSensor++) { + Serial.print(touchCounter[thisSensor]); + Serial.print('\t'); + } + Serial.println(); + } + if (touchCounter[0] > 4 && touchCounter[1] > 4) { + indicatePass(); + touchCounter[0] = touchCounter[1] = 0; + } + if (touchCounter[0] > 30 || touchCounter[1] > 30) { + indicateFail(); + touchCounter[0] = touchCounter[1] = 0; + } + delay(10); +} + +void testPIR() { + int sensorReading = digitalRead(PIR_PIN); + if (sensorReading != pirState) { + if (printValue) { + Serial.print(sensorReading); + Serial.print('\t'); + Serial.println(pirCounter); + } + pirCounter++; + pirState = sensorReading; + if (pirCounter > 1) { + indicatePass(); + pirCounter = 0; + pirState = 0; + } + } +} +bool maxReading[2], minReading[2]; +long consistencyQ = 0; + +void testDoubleAnalog(int test) { + int readMin, readMax, diff; + if (compatible_3V) { + readMin = test == 2 ? 25 : 30; + readMax = test == 2 ? 45 : 600; + diff = test == 2 ? 50 : 15; + } else { + readMin = test == 2 ? 5 : 30; + readMax = test == 2 ? 60 : 900; + diff = test == 2 ? 50 : 15; + } + int sen[2]; + for (int thisSensor = 0; thisSensor < 2; thisSensor++) { + // get a sensor reading: + int sensorReading = sen[thisSensor] = analogRead(analogPins[thisSensor]); + if (sensorReading < readMin) + minReading[thisSensor] = true; + if (sensorReading > readMax) + maxReading[thisSensor] = true; + if (printValue) { + Serial.print(sensorReading); + Serial.print('\t'); + } + if (makeSound) + beep(sensorReading / 20 + 1, 10, 0); + int brightness = 255 * sensorReading / 1024; + strip.setPixelColor(thisSensor + 5, strip.Color(brightness, brightness, brightness)); + strip.show(); + } + if (printValue) + Serial.print(sen[0] - sen[1]); + consistencyQ += (abs(sen[0] - sen[1]) < diff ? 1 : -1); + if (maxReading[0] && minReading[0] && maxReading[1] && minReading[1]) { + if (consistencyQ > 0) { + indicatePass(); + } else { + indicatePass(1); + } + consistencyQ = maxReading[0] = minReading[0] = maxReading[1] = minReading[1] = 0; + } + if (printValue) + Serial.println(); +} + +void testI2Cdevice(int targetAddress) { + bool found = false; + byte error, address; + int nDevices; + + Serial.println("Scanning I2C Scanner..."); + nDevices = 0; + for (address = 1; address < 127; address++) { + // The i2c_scanner uses the return value of + // the Write.endTransmisstion to see if + // a device did acknowledge to the address. + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) { + Serial.print("I2C device found at address 0x"); + if (address < 16) + Serial.print("0"); + Serial.print(address, HEX); + Serial.println(" !"); + nDevices++; + if (address == targetAddress) + found = true; + + } else if (error == 4) { + Serial.print("Unknown error at address 0x"); + if (address < 16) + Serial.print("0"); + Serial.println(address, HEX); + } + } + Serial.print(nDevices); + Serial.println(" devices found\n"); + if (nDevices == 0) + Serial.println("No I2C devices found\n"); + else if (nDevices > 5) + Serial.println("More I2C devices found\n"); + else { + if (found) { + Serial.println("Target I2C device found!\n"); + if (!APDS.begin()) { + Serial.println("Error initializing APDS-9960 sensor!"); + indicatePass(1); //found the gesture but the begin fails + } else + indicatePass(); + } else { + indicateFail(); + } + } +} + +void testNoise() { + int LightData = analogRead(LightPins); + int VoiceData = analogRead(VoicePins); + + // Serial.print(LightData); + // Serial.print('\t'); + // Serial.print(VoiceData); + // if (makeSound) + // beep(sensorReading / 20 + 1, 10, 0); + int noise = map((1024 - VoiceData), 0, 1024, 0, 255); + int brightness = 255 * LightData / 1024; + // Serial.print('\t'); + // Serial.print(brightness); + strip.setPixelColor(5, strip.Color(noise, noise, noise)); + strip.setPixelColor(6, strip.Color(brightness, brightness, brightness)); + strip.show(); + delay(100); + + // Serial.println(); +}