First commit.
This commit is contained in:
@ -0,0 +1,83 @@
|
||||
#ifndef __INC_CLOCKLESS_ARM_NRF51
|
||||
#define __INC_CLOCKLESS_ARM_NRF51
|
||||
|
||||
#if defined(NRF51)
|
||||
|
||||
#include <nrf51_bitfields.h>
|
||||
#define FASTLED_HAS_CLOCKLESS 1
|
||||
|
||||
#if (FASTLED_ALLOW_INTERRUPTS==1)
|
||||
#define SEI_CHK LED_TIMER->CC[0] = (WAIT_TIME * (F_CPU/1000000)); LED_TIMER->TASKS_CLEAR; LED_TIMER->EVENTS_COMPARE[0] = 0;
|
||||
#define CLI_CHK cli(); if(LED_TIMER->EVENTS_COMPARE[0]) { LED_TIMER->TASKS_STOP = 1; return 0; }
|
||||
#define INNER_SEI sei();
|
||||
#else
|
||||
#define SEI_CHK
|
||||
#define CLI_CHK
|
||||
#define INNER_SEI delaycycles<1>();
|
||||
#endif
|
||||
|
||||
|
||||
#include "../common/m0clockless.h"
|
||||
template <uint8_t DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 75>
|
||||
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
|
||||
typedef typename FastPinBB<DATA_PIN>::port_ptr_t data_ptr_t;
|
||||
typedef typename FastPinBB<DATA_PIN>::port_t data_t;
|
||||
|
||||
data_t mPinMask;
|
||||
data_ptr_t mPort;
|
||||
CMinWait<WAIT_TIME> mWait;
|
||||
public:
|
||||
virtual void init() {
|
||||
FastPinBB<DATA_PIN>::setOutput();
|
||||
mPinMask = FastPinBB<DATA_PIN>::mask();
|
||||
mPort = FastPinBB<DATA_PIN>::port();
|
||||
}
|
||||
|
||||
virtual uint16_t getMaxRefreshRate() const { return 400; }
|
||||
|
||||
virtual void showPixels(PixelController<RGB_ORDER> & pixels) {
|
||||
mWait.wait();
|
||||
cli();
|
||||
if(!showRGBInternal(pixels)) {
|
||||
sei(); delayMicroseconds(WAIT_TIME); cli();
|
||||
showRGBInternal(pixels);
|
||||
}
|
||||
sei();
|
||||
mWait.mark();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
struct M0ClocklessData data;
|
||||
data.d[0] = pixels.d[0];
|
||||
data.d[1] = pixels.d[1];
|
||||
data.d[2] = pixels.d[2];
|
||||
data.s[0] = pixels.mScale[0];
|
||||
data.s[1] = pixels.mScale[1];
|
||||
data.s[2] = pixels.mScale[2];
|
||||
data.e[0] = pixels.e[0];
|
||||
data.e[1] = pixels.e[1];
|
||||
data.e[2] = pixels.e[2];
|
||||
data.adj = pixels.mAdvance;
|
||||
|
||||
typename FastPin<DATA_PIN>::port_ptr_t portBase = FastPin<DATA_PIN>::port();
|
||||
|
||||
// timer mode w/prescaler of 0
|
||||
LED_TIMER->MODE = TIMER_MODE_MODE_Timer;
|
||||
LED_TIMER->PRESCALER = 0;
|
||||
LED_TIMER->EVENTS_COMPARE[0] = 0;
|
||||
LED_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
|
||||
LED_TIMER->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk;
|
||||
LED_TIMER->TASKS_START = 1;
|
||||
|
||||
int ret = showLedData<4,8,T1,T2,T3,RGB_ORDER,WAIT_TIME>(portBase, FastPin<DATA_PIN>::mask(), pixels.mData, pixels.mLen, &data);
|
||||
|
||||
LED_TIMER->TASKS_STOP = 1;
|
||||
return ret; // 0x00FFFFFF - _VAL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // NRF51
|
||||
#endif // __INC_CLOCKLESS_ARM_NRF51
|
@ -0,0 +1,9 @@
|
||||
#ifndef __INC_FASTLED_ARM_NRF51_H
|
||||
#define __INC_FASTLED_ARM_NRF51_H
|
||||
|
||||
// Include the k20 headers
|
||||
#include "fastpin_arm_nrf51.h"
|
||||
#include "fastspi_arm_nrf51.h"
|
||||
#include "clockless_arm_nrf51.h"
|
||||
|
||||
#endif
|
@ -0,0 +1,119 @@
|
||||
#ifndef __FASTPIN_ARM_NRF51_H
|
||||
#define __FASTPIN_ARM_NRF51_H
|
||||
|
||||
#if defined(NRF51)
|
||||
/// Template definition for teensy 3.0 style ARM pins, providing direct access to the various GPIO registers. Note that this
|
||||
/// uses the full port GPIO registers. In theory, in some way, bit-band register access -should- be faster, however I have found
|
||||
/// that something about the way gcc does register allocation results in the bit-band code being slower. It will need more fine tuning.
|
||||
/// The registers are data output, set output, clear output, toggle output, input, and direction
|
||||
#if 0
|
||||
template<uint8_t PIN, uint32_t _MASK, typename _DIRSET, typename _DIRCLR, typename _OUTSET, typename _OUTCLR, typename _OUT> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { _DIRSET::r() = _MASK; }
|
||||
inline static void setInput() { _DIRCLR::r() = _MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { _OUTSET::r() = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { _OUTCLR::r() = _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { _OUT::r() = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { _OUT::r() ^= _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return _OUT::r() | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return _OUT::r() & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &_OUT::r(); }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
};
|
||||
|
||||
#define ADDR(X) *(volatile uint32_t*)X
|
||||
#define NR_GPIO_ADDR(base,offset) (*(volatile uint32_t *))((uint32_t)(base + offset))
|
||||
#define NR_DIRSET ADDR(0x50000518UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x518)
|
||||
#define NR_DIRCLR ADDR(0x5000051CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x51C)
|
||||
#define NR_OUTSET ADDR(0x50000508UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x508)
|
||||
#define NR_OUTCLR ADDR(0x5000050CUL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x50C)
|
||||
#define NR_OUT ADDR(0x50000504UL) // NR_GPIO_ADDR(NRF_GPIO_BASE, 0x504)
|
||||
|
||||
#define _RD32_NRF(T) struct __gen_struct_ ## T { static __attribute__((always_inline)) inline reg32_t r() { return T; }};
|
||||
|
||||
_RD32_NRF(NR_DIRSET);
|
||||
_RD32_NRF(NR_DIRCLR);
|
||||
_RD32_NRF(NR_OUTSET);
|
||||
_RD32_NRF(NR_OUTCLR);
|
||||
_RD32_NRF(NR_OUT);
|
||||
|
||||
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN, \
|
||||
_R(NR_DIRSET), _R(NR_DIRCLR), _R(NR_OUTSET), _R(NR_OUTCLR), _R(NR_OUT)> {};
|
||||
#else
|
||||
|
||||
typedef struct { /*!< GPIO Structure */
|
||||
// __I uint32_t RESERVED0[321];
|
||||
__IO uint32_t OUT; /*!< Write GPIO port. */
|
||||
__IO uint32_t OUTSET; /*!< Set individual bits in GPIO port. */
|
||||
__IO uint32_t OUTCLR; /*!< Clear individual bits in GPIO port. */
|
||||
__I uint32_t IN; /*!< Read GPIO port. */
|
||||
__IO uint32_t DIR; /*!< Direction of GPIO pins. */
|
||||
__IO uint32_t DIRSET; /*!< DIR set register. */
|
||||
__IO uint32_t DIRCLR; /*!< DIR clear register. */
|
||||
__I uint32_t RESERVED1[120];
|
||||
__IO uint32_t PIN_CNF[32]; /*!< Configuration of GPIO pins. */
|
||||
} FL_NRF_GPIO_Type;
|
||||
|
||||
#define FL_NRF_GPIO_BASE 0x50000504UL
|
||||
#define FL_NRF_GPIO ((FL_NRF_GPIO_Type *) FL_NRF_GPIO_BASE)
|
||||
|
||||
template<uint8_t PIN, uint32_t _MASK> class _ARMPIN {
|
||||
public:
|
||||
typedef volatile uint32_t * port_ptr_t;
|
||||
typedef uint32_t port_t;
|
||||
|
||||
inline static void setOutput() { FL_NRF_GPIO->DIRSET = _MASK; }
|
||||
inline static void setInput() { FL_NRF_GPIO->DIRCLR = _MASK; }
|
||||
|
||||
inline static void hi() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTSET = _MASK; }
|
||||
inline static void lo() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUTCLR= _MASK; }
|
||||
inline static void set(register port_t val) __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT = val; }
|
||||
|
||||
inline static void strobe() __attribute__ ((always_inline)) { toggle(); toggle(); }
|
||||
|
||||
inline static void toggle() __attribute__ ((always_inline)) { FL_NRF_GPIO->OUT ^= _MASK; }
|
||||
|
||||
inline static void hi(register port_ptr_t port) __attribute__ ((always_inline)) { hi(); }
|
||||
inline static void lo(register port_ptr_t port) __attribute__ ((always_inline)) { lo(); }
|
||||
inline static void fastset(register port_ptr_t port, register port_t val) __attribute__ ((always_inline)) { *port = val; }
|
||||
|
||||
inline static port_t hival() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT | _MASK; }
|
||||
inline static port_t loval() __attribute__ ((always_inline)) { return FL_NRF_GPIO->OUT & ~_MASK; }
|
||||
inline static port_ptr_t port() __attribute__ ((always_inline)) { return &FL_NRF_GPIO->OUT; }
|
||||
inline static port_t mask() __attribute__ ((always_inline)) { return _MASK; }
|
||||
|
||||
inline static bool isset() __attribute__ ((always_inline)) { return (FL_NRF_GPIO->IN & _MASK) != 0; }
|
||||
};
|
||||
|
||||
|
||||
#define _FL_DEFPIN(PIN) template<> class FastPin<PIN> : public _ARMPIN<PIN, 1 << PIN> {};
|
||||
#endif
|
||||
|
||||
// Actual pin definitions
|
||||
#define MAX_PIN 31
|
||||
_FL_DEFPIN(0); _FL_DEFPIN(1); _FL_DEFPIN(2); _FL_DEFPIN(3);
|
||||
_FL_DEFPIN(4); _FL_DEFPIN(5); _FL_DEFPIN(6); _FL_DEFPIN(7);
|
||||
_FL_DEFPIN(8); _FL_DEFPIN(9); _FL_DEFPIN(10); _FL_DEFPIN(11);
|
||||
_FL_DEFPIN(12); _FL_DEFPIN(13); _FL_DEFPIN(14); _FL_DEFPIN(15);
|
||||
_FL_DEFPIN(16); _FL_DEFPIN(17); _FL_DEFPIN(18); _FL_DEFPIN(19);
|
||||
_FL_DEFPIN(20); _FL_DEFPIN(21); _FL_DEFPIN(22); _FL_DEFPIN(23);
|
||||
_FL_DEFPIN(24); _FL_DEFPIN(25); _FL_DEFPIN(26); _FL_DEFPIN(27);
|
||||
_FL_DEFPIN(28); _FL_DEFPIN(29); _FL_DEFPIN(30); _FL_DEFPIN(31);
|
||||
|
||||
#define HAS_HARDWARE_PIN_SUPPORT
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,150 @@
|
||||
#ifndef __INC_FASTSPI_NRF_H
|
||||
#define __INC_FASTSPI_NRF_H
|
||||
|
||||
#ifdef NRF51
|
||||
|
||||
#ifndef FASTLED_FORCE_SOFTWARE_SPI
|
||||
#define FASTLED_ALL_PINS_HARDWARE_SPI
|
||||
|
||||
// A nop/stub class, mostly to show the SPI methods that are needed/used by the various SPI chipset implementations. Should
|
||||
// be used as a definition for the set of methods that the spi implementation classes should use (since C++ doesn't support the
|
||||
// idea of interfaces - it's possible this could be done with virtual classes, need to decide if i want that overhead)
|
||||
template <uint8_t _DATA_PIN, uint8_t _CLOCK_PIN, uint32_t _SPI_CLOCK_DIVIDER>
|
||||
class NRF51SPIOutput {
|
||||
|
||||
struct saveData {
|
||||
uint32_t sck;
|
||||
uint32_t mosi;
|
||||
uint32_t miso;
|
||||
uint32_t freq;
|
||||
uint32_t enable;
|
||||
} mSavedData;
|
||||
|
||||
void saveSPIData() {
|
||||
mSavedData.sck = NRF_SPI0->PSELSCK;
|
||||
mSavedData.mosi = NRF_SPI0->PSELMOSI;
|
||||
mSavedData.miso = NRF_SPI0->PSELMISO;
|
||||
mSavedData.freq = NRF_SPI0->FREQUENCY;
|
||||
mSavedData.enable = NRF_SPI0->ENABLE;
|
||||
}
|
||||
|
||||
void restoreSPIData() {
|
||||
NRF_SPI0->PSELSCK = mSavedData.sck;
|
||||
NRF_SPI0->PSELMOSI = mSavedData.mosi;
|
||||
NRF_SPI0->PSELMISO = mSavedData.miso;
|
||||
NRF_SPI0->FREQUENCY = mSavedData.freq;
|
||||
mSavedData.enable = NRF_SPI0->ENABLE;
|
||||
}
|
||||
|
||||
public:
|
||||
NRF51SPIOutput() { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
|
||||
NRF51SPIOutput(Selectable *pSelect) { FastPin<_DATA_PIN>::setOutput(); FastPin<_CLOCK_PIN>::setOutput(); }
|
||||
|
||||
// set the object representing the selectable
|
||||
void setSelect(Selectable *pSelect) { /* TODO */ }
|
||||
|
||||
// initialize the SPI subssytem
|
||||
void init() {
|
||||
FastPin<_DATA_PIN>::setOutput();
|
||||
FastPin<_CLOCK_PIN>::setOutput();
|
||||
NRF_SPI0->PSELSCK = _CLOCK_PIN;
|
||||
NRF_SPI0->PSELMOSI = _DATA_PIN;
|
||||
NRF_SPI0->PSELMISO = 0xFFFFFFFF;
|
||||
NRF_SPI0->FREQUENCY = 0x80000000;
|
||||
NRF_SPI0->ENABLE = 1;
|
||||
NRF_SPI0->EVENTS_READY = 0;
|
||||
}
|
||||
|
||||
// latch the CS select
|
||||
void select() { saveSPIData(); init(); }
|
||||
|
||||
// release the CS select
|
||||
void release() { shouldWait(); restoreSPIData(); }
|
||||
|
||||
static bool shouldWait(bool wait = false) __attribute__((always_inline)) __attribute__((always_inline)) {
|
||||
// static bool sWait=false;
|
||||
// bool oldWait = sWait;
|
||||
// sWait = wait;
|
||||
// never going to bother with waiting since we're always running the spi clock at max speed on the rfduino
|
||||
// TODO: When we set clock rate, implement/fix waiting properly, otherwise the world hangs up
|
||||
return false;
|
||||
}
|
||||
|
||||
// wait until all queued up data has been written
|
||||
static void waitFully() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; }
|
||||
static void wait() __attribute__((always_inline)){ if(shouldWait()) { while(NRF_SPI0->EVENTS_READY==0); } NRF_SPI0->INTENCLR; }
|
||||
|
||||
// write a byte out via SPI (returns immediately on writing register)
|
||||
static void writeByte(uint8_t b) __attribute__((always_inline)) { wait(); NRF_SPI0->TXD = b; NRF_SPI0->INTENCLR; shouldWait(true); }
|
||||
|
||||
// write a word out via SPI (returns immediately on writing register)
|
||||
static void writeWord(uint16_t w) __attribute__((always_inline)){ writeByte(w>>8); writeByte(w & 0xFF); }
|
||||
|
||||
// A raw set of writing byte values, assumes setup/init/waiting done elsewhere (static for use by adjustment classes)
|
||||
static void writeBytesValueRaw(uint8_t value, int len) { while(len--) { writeByte(value); } }
|
||||
|
||||
// A full cycle of writing a value for len bytes, including select, release, and waiting
|
||||
void writeBytesValue(uint8_t value, int len) {
|
||||
select();
|
||||
while(len--) {
|
||||
writeByte(value);
|
||||
}
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
// A full cycle of writing a raw block of data out, including select, release, and waiting
|
||||
template<class D> void writeBytes(uint8_t *data, int len) {
|
||||
uint8_t *end = data + len;
|
||||
select();
|
||||
while(data != end) {
|
||||
writeByte(D::adjust(*data++));
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
void writeBytes(uint8_t *data, int len) {
|
||||
writeBytes<DATA_NOP>(data, len);
|
||||
}
|
||||
|
||||
// write a single bit out, which bit from the passed in byte is determined by template parameter
|
||||
template <uint8_t BIT> inline static void writeBit(uint8_t b) {
|
||||
waitFully();
|
||||
NRF_SPI0->ENABLE = 0;
|
||||
if(b & 1<<BIT) {
|
||||
FastPin<_DATA_PIN>::hi();
|
||||
} else {
|
||||
FastPin<_DATA_PIN>::lo();
|
||||
}
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
FastPin<_CLOCK_PIN>::toggle();
|
||||
NRF_SPI0->ENABLE = 1;
|
||||
}
|
||||
|
||||
template <uint8_t FLAGS, class D, EOrder RGB_ORDER> void writePixels(PixelController<RGB_ORDER> pixels) {
|
||||
select();
|
||||
int len = pixels.mLen;
|
||||
while(pixels.has(1)) {
|
||||
if(FLAGS & FLAG_START_BIT) {
|
||||
writeBit<0>(1);
|
||||
}
|
||||
writeByte(D::adjust(pixels.loadAndScale0()));
|
||||
writeByte(D::adjust(pixels.loadAndScale1()));
|
||||
writeByte(D::adjust(pixels.loadAndScale2()));
|
||||
|
||||
pixels.advanceData();
|
||||
pixels.stepDithering();
|
||||
}
|
||||
D::postBlock(len);
|
||||
waitFully();
|
||||
release();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,46 @@
|
||||
#ifndef __LED_SYSDEFS_ARM_NRF51
|
||||
#define __LED_SYSDEFS_ARM_NRF51
|
||||
|
||||
#ifndef NRF51
|
||||
#define NRF51
|
||||
#endif
|
||||
|
||||
#define LED_TIMER NRF_TIMER1
|
||||
#define FASTLED_NO_PINMAP
|
||||
#define FASTLED_HAS_CLOCKLESS
|
||||
|
||||
#define FASTLED_SPI_BYTE_ONLY
|
||||
|
||||
#define FASTLED_ARM
|
||||
#define FASTLED_ARM_M0
|
||||
|
||||
#ifndef F_CPU
|
||||
#define F_CPU 16000000
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <nrf51.h>
|
||||
#include <core_cm0.h>
|
||||
|
||||
typedef volatile uint32_t RoReg;
|
||||
typedef volatile uint32_t RwReg;
|
||||
typedef uint32_t prog_uint32_t;
|
||||
typedef uint8_t boolean;
|
||||
|
||||
#define PROGMEM
|
||||
#define NO_PROGMEM
|
||||
#define NEED_CXX_BITS
|
||||
|
||||
// Default to NOT using PROGMEM here
|
||||
#ifndef FASTLED_USE_PROGMEM
|
||||
#define FASTLED_USE_PROGMEM 0
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_ALLOW_INTERRUPTS
|
||||
#define FASTLED_ALLOW_INTERRUPTS 1
|
||||
#endif
|
||||
|
||||
#define cli() __disable_irq();
|
||||
#define sei() __enable_irq();
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user