Vendored and trimmed from microsoft/uf2-samdx1, adapted to the actual ATSAMD21G17D (128 KiB flash / 16 KiB RAM), fixing the flash/RAM-size mismatch that the previously commented-out SAM-BA bootloader target had (it assumed a 256 KiB SAMD21G18A). Builds as a standalone PlatformIO environment (bootloader/platformio.ini), no python2/make dependency. Compiles clean, fits in the 8 KiB bootloader region (7292/8192 bytes). Not yet flashed/verified on real hardware.
92 lines
3.4 KiB
C
92 lines
3.4 KiB
C
#ifndef DEVICE_NEOPIXEL_H
|
|
#define DEVICE_NEOPIXEL_H
|
|
|
|
#ifdef BOARD_NEOPIXEL_PIN
|
|
|
|
// The timings are taken from Adafruit's NeoPixel library
|
|
|
|
static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMask,
|
|
const uint8_t *ptr, int numBytes) __attribute__((naked));
|
|
|
|
static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMask,
|
|
const uint8_t *ptr, int numBytes) {
|
|
asm volatile(" push {r4, r5, r6, lr};"
|
|
" add r3, r2, r3;"
|
|
"loopLoad:"
|
|
" ldrb r5, [r2, #0];" // r5 := *ptr
|
|
" add r2, #1;" // ptr++
|
|
" movs r4, #128;" // r4-mask, 0x80
|
|
"loopBit:"
|
|
" str r1, [r0, #4];" // set
|
|
#ifdef SAMD21
|
|
" movs r6, #3; d2: sub r6, #1; bne d2;" // delay 3
|
|
#endif
|
|
#ifdef SAMD51
|
|
" movs r6, #3; d2: subs r6, #1; bne d2;" // delay 3
|
|
#endif
|
|
" tst r4, r5;" // mask&r5
|
|
" bne skipclr;"
|
|
" str r1, [r0, #0];" // clr
|
|
"skipclr:"
|
|
#ifdef SAMD21
|
|
" movs r6, #6; d0: sub r6, #1; bne d0;" // delay 6
|
|
#endif
|
|
#ifdef SAMD51
|
|
" movs r6, #6; d0: subs r6, #1; bne d0;" // delay 6
|
|
#endif
|
|
" str r1, [r0, #0];" // clr (possibly again, doesn't matter)
|
|
#ifdef SAMD21
|
|
" asr r4, r4, #1;" // mask >>= 1
|
|
#endif
|
|
#ifdef SAMD51
|
|
" asrs r4, r4, #1;" // mask >>= 1
|
|
#endif
|
|
" beq nextbyte;"
|
|
" uxtb r4, r4;"
|
|
#ifdef SAMD21
|
|
" movs r6, #2; d1: sub r6, #1; bne d1;" // delay 2
|
|
#endif
|
|
#ifdef SAMD51
|
|
" movs r6, #2; d1: subs r6, #1; bne d1;" // delay 2
|
|
#endif
|
|
" b loopBit;"
|
|
"nextbyte:"
|
|
" cmp r2, r3;"
|
|
" bcs stop;"
|
|
" b loopLoad;"
|
|
"stop:"
|
|
" pop {r4, r5, r6, pc};"
|
|
"");
|
|
}
|
|
|
|
// this assumes the pin has been configured correctly
|
|
static inline void neopixel_send_buffer(const uint8_t *ptr, int numBytes) {
|
|
uint8_t portNum = BOARD_NEOPIXEL_PIN / 32;
|
|
uint32_t pinMask = 1ul << (BOARD_NEOPIXEL_PIN % 32);
|
|
|
|
PINOP(BOARD_NEOPIXEL_PIN, DIRSET);
|
|
|
|
#if (USB_VID == 0x239a) && (USB_PID == 0x0013) // Adafruit Metro M0
|
|
// turn off mux too, needed for metro m0
|
|
PORT->Group[BOARD_NEOPIXEL_PIN / 32].PINCFG[BOARD_NEOPIXEL_PIN % 32].reg =
|
|
(uint8_t)(PORT_PINCFG_INEN);
|
|
#endif
|
|
|
|
PINOP(BOARD_NEOPIXEL_PIN, OUTCLR);
|
|
delay(1);
|
|
|
|
volatile uint32_t *clraddr = &PORT->Group[portNum].OUTCLR.reg;
|
|
|
|
// equivalent to cpu_irq_is_enabled()
|
|
if (__get_PRIMASK() == 0) {
|
|
__disable_irq();
|
|
neopixel_send_buffer_core(clraddr, pinMask, ptr, numBytes);
|
|
__enable_irq();
|
|
} else {
|
|
neopixel_send_buffer_core(clraddr, pinMask, ptr, numBytes);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|