Skip to content

Commit

Permalink
Merge pull request FastLED#993 from PaulZC/Support_for_SparkFun_Artem…
Browse files Browse the repository at this point in the history
…is__Apollo3

Adding support for the SparkFun Artemis (Ambiq Micro Apollo3 Blue)
  • Loading branch information
kriegsman authored May 25, 2020
2 parents 4e5f325 + b07e106 commit cfce255
Show file tree
Hide file tree
Showing 9 changed files with 550 additions and 3 deletions.
5 changes: 5 additions & 0 deletions fastspi.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public NRF52SPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif

#if defined(FASTLED_APOLLO3) && defined(FASTLED_ALL_PINS_HARDWARE_SPI)
template<uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
class SPIOutput : public APOLLO3HardwareSPIOutput<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER> {};
#endif

#if defined(SPI_DATA) && defined(SPI_CLOCK)

#if defined(FASTLED_TEENSY3) && defined(ARM_HARDWARE_SPI)
Expand Down
3 changes: 3 additions & 0 deletions led_sysdefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#elif defined(__AVR__)
// AVR platforms
#include "platforms/avr/led_sysdefs_avr.h"
#elif defined(ARDUINO_ARCH_APOLLO3)
// Apollo3 platforms (e.g. the Ambiq Micro Apollo3 Blue as used by the SparkFun Artemis platforms)
#include "platforms/apollo3/led_sysdefs_apollo3.h"
#else
//
// We got here because we don't recognize the platform that you're
Expand Down
38 changes: 35 additions & 3 deletions lib8tion.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
#if !defined(__AVR__)
#include <string.h>
// for memmove, memcpy, and memset if not defined here
#endif
#endif // end of !defined(__AVR__)

#if defined(__arm__)

Expand All @@ -195,8 +195,31 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
// Generic ARM
#define QADD8_C 1
#define QADD7_C 1
#endif
#endif // end of defined(FASTLED_TEENSY3)

#define QSUB8_C 1
#define SCALE8_C 1
#define SCALE16BY8_C 1
#define SCALE16_C 1
#define ABS8_C 1
#define MUL8_C 1
#define QMUL8_C 1
#define ADD8_C 1
#define SUB8_C 1
#define EASE8_C 1
#define AVG8_C 1
#define AVG7_C 1
#define AVG16_C 1
#define AVG15_C 1
#define BLEND8_C 1

// end of #if defined(__arm__)

#elif defined(ARDUINO_ARCH_APOLLO3)

// Default to using the standard C functions for now
#define QADD8_C 1
#define QADD7_C 1
#define QSUB8_C 1
#define SCALE8_C 1
#define SCALE16BY8_C 1
Expand All @@ -213,6 +236,7 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
#define AVG15_C 1
#define BLEND8_C 1

// end of #elif defined(ARDUINO_ARCH_APOLLO3)

#elif defined(__AVR__)

Expand Down Expand Up @@ -274,7 +298,9 @@ Lib8tion is pronounced like 'libation': lie-BAY-shun
#define QMUL8_AVRASM 0
#define EASE8_AVRASM 0
#define BLEND8_AVRASM 0
#endif
#endif // end of !defined(LIB8_ATTINY)

// end of #elif defined(__AVR__)

#else

Expand Down Expand Up @@ -811,6 +837,9 @@ template<class T, int F, int I> class q {
#ifdef FASTLED_ARM
int operator*(int v) { return (v*i) + ((v*f)>>F); }
#endif
#ifdef FASTLED_APOLLO3
int operator*(int v) { return (v*i) + ((v*f)>>F); }
#endif
};

template<class T, int F, int I> static uint32_t operator*(uint32_t v, q<T,F,I> & q) { return q * v; }
Expand All @@ -820,6 +849,9 @@ template<class T, int F, int I> static int16_t operator*(int16_t v, q<T,F,I> & q
#ifdef FASTLED_ARM
template<class T, int F, int I> static int operator*(int v, q<T,F,I> & q) { return q * v; }
#endif
#ifdef FASTLED_APOLLO3
template<class T, int F, int I> static int operator*(int v, q<T,F,I> & q) { return q * v; }
#endif

/// A 4.4 integer (4 bits integer, 4 bits fraction)
typedef q<uint8_t, 4,4> q44;
Expand Down
2 changes: 2 additions & 0 deletions platforms.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#include "platforms/esp/8266/fastled_esp8266.h"
#elif defined(ESP32)
#include "platforms/esp/32/fastled_esp32.h"
#elif defined(ARDUINO_ARCH_APOLLO3)
#include "platforms/apollo3/fastled_apollo3.h"
#else
// AVR platforms
#include "platforms/avr/fastled_avr.h"
Expand Down
185 changes: 185 additions & 0 deletions platforms/apollo3/clockless_apollo3.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#ifndef __INC_CLOCKLESS_APOLLO3_H
#define __INC_CLOCKLESS_APOLLO3_H

FASTLED_NAMESPACE_BEGIN

#if defined(FASTLED_APOLLO3)

// Clockless support for the SparkFun Artemis / Ambiq Micro Apollo3 Blue
// Uses SysTick to govern the pulse timing

//*****************************************************************************
//
// Code taken from Ambiq Micro's am_hal_systick.c
// and converted to inline static for speed
//
//! @brief Get the current count value in the SYSTICK.
//!
//! This function gets the current count value in the systick timer.
//!
//! @return Current count value.
//
//*****************************************************************************
__attribute__ ((always_inline)) inline static uint32_t __am_hal_systick_count() {
return SysTick->VAL;
}

#define FASTLED_HAS_CLOCKLESS 1

template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 50>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
typedef typename FastPin<DATA_PIN>::port_t data_t;

CMinWait<WAIT_TIME> mWait;

public:
virtual void init() {
// Initialize everything

// Configure DATA_PIN for FastGPIO (settings are in fastpin_apollo3.h)
FastPin<DATA_PIN>::setOutput();
FastPin<DATA_PIN>::lo();

// Make sure the system clock is running at the full 48MHz
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);

// Make sure interrupts are enabled
//am_hal_interrupt_master_enable();

// Enable SysTick Interrupts in the NVIC
//NVIC_EnableIRQ(SysTick_IRQn);

// SysTick is 24-bit and counts down (not up)

// Stop the SysTick (just in case it is already running).
// This clears the ENABLE bit in the SysTick Control and Status Register (SYST_CSR).
// In Ambiq naming convention, the control register is SysTick->CTRL
am_hal_systick_stop();

// Call SysTick_Config
// This is defined in core_cm4.h
// It loads the specified LOAD value into the SysTick Reload Value Register (SYST_RVR)
// In Ambiq naming convention, the reload register is SysTick->LOAD
// It sets the SysTick interrupt priority
// It clears the SysTick Current Value Register (SYST_CVR)
// In Ambiq naming convention, the current value register is SysTick->VAL
// Finally it sets these bits in the SysTick Control and Status Register (SYST_CSR):
// CLKSOURCE: SysTick uses the processor clock
// TICKINT: When the count reaches zero, the SysTick exception (interrupt) is changed to pending
// ENABLE: Enables the counter
// SysTick_Config returns 0 if successful. 1 indicates a failure (the LOAD value was invalid).
SysTick_Config(0xFFFFFFUL); // The LOAD value needs to be 24-bit
}

virtual uint16_t getMaxRefreshRate() const { return 400; }

protected:

virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
mWait.wait();
if(!showRGBInternal(pixels)) {
sei(); delayMicroseconds(WAIT_TIME); cli();
showRGBInternal(pixels);
}
mWait.mark();
}

template<int BITS> __attribute__ ((always_inline)) inline static void writeBits(register uint32_t & next_mark, register uint8_t & b) {
// SysTick counts down (not up) and is 24-bit
for(register uint32_t i = BITS-1; i > 0; i--) { // We could speed this up by using Bit Banding
while(__am_hal_systick_count() > next_mark) { ; } // Wait for the remainder of this cycle to complete
// Calculate next_mark (the time of the next DATA_PIN transition) by subtracting T1+T2+T3
// SysTick counts down (not up) and is 24-bit
next_mark = (__am_hal_systick_count() - (T1+T2+T3)) & 0xFFFFFFUL;
FastPin<DATA_PIN>::hi();
if(b&0x80) {
// "1 code" = longer pulse width
while((__am_hal_systick_count() - next_mark) > (T3+(3*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::lo();
} else {
// "0 code" = shorter pulse width
while((__am_hal_systick_count() - next_mark) > (T2+T3+(4*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::lo();
}
b <<= 1;
}

while(__am_hal_systick_count() > next_mark) { ; }// Wait for the remainder of this cycle to complete
// Calculate next_mark (the time of the next DATA_PIN transition) by subtracting T1+T2+T3
// SysTick counts down (not up) and is 24-bit
next_mark = (__am_hal_systick_count() - (T1+T2+T3)) & 0xFFFFFFUL;
FastPin<DATA_PIN>::hi();
if(b&0x80) {
// "1 code" = longer pulse width
while((__am_hal_systick_count() - next_mark) > (T3+(2*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::lo();
} else {
// "0 code" = shorter pulse width
while((__am_hal_systick_count() - next_mark) > (T2+T3+(4*(F_CPU/24000000)))) { ; }
FastPin<DATA_PIN>::lo();
}
}

// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
// gcc will use register Y for the this pointer.
static uint32_t showRGBInternal(PixelController<RGB_ORDER> pixels) {

// Setup the pixel controller and load/scale the first byte
pixels.preStepFirstByteDithering();
register uint8_t b = pixels.loadAndScale0();

cli();

// Calculate next_mark (the time of the next DATA_PIN transition) by subtracting T1+T2+T3
// SysTick counts down (not up) and is 24-bit
// The subtraction could underflow (wrap round) so let's mask the result to 24 bits
register uint32_t next_mark = (__am_hal_systick_count() - (T1+T2+T3)) & 0xFFFFFFUL;

while(pixels.has(1)) { // Keep going for as long as we have pixels
pixels.stepDithering();

#if (FASTLED_ALLOW_INTERRUPTS == 1)
cli();

// Have we already missed the next_mark?
if(__am_hal_systick_count() < next_mark) {
// If we have exceeded next_mark by an excessive amount, then bail (return 0)
if((next_mark - __am_hal_systick_count()) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) { sei(); return 0; }
}
#endif

// Write first byte, read next byte
writeBits<8+XTRA0>(next_mark, b);
b = pixels.loadAndScale1();

// Write second byte, read 3rd byte
writeBits<8+XTRA0>(next_mark, b);
b = pixels.loadAndScale2();

// Write third byte, read 1st byte of next pixel
writeBits<8+XTRA0>(next_mark, b);
b = pixels.advanceAndLoadAndScale0();

#if (FASTLED_ALLOW_INTERRUPTS == 1)
sei();
#endif
}; // end of while(pixels.has(1))

// Unfortunately SysTick relies on an interrupt to reload it once it reaches zero
// and having interrupts disabled for most of the above means the interrupt doesn't get serviced.
// So we had better reload it here instead...
am_hal_systick_load(0xFFFFFFUL);

sei();
return (1);
}

};


#endif

FASTLED_NAMESPACE_END

#endif
8 changes: 8 additions & 0 deletions platforms/apollo3/fastled_apollo3.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef __INC_FASTLED_APOLLO3_H
#define __INC_FASTLED_APOLLO3_H

#include "fastpin_apollo3.h"
#include "fastspi_apollo3.h"
#include "clockless_apollo3.h"

#endif
Loading

0 comments on commit cfce255

Please sign in to comment.