Skip to content

Commit 98d0bb5

Browse files
committed
NeoPixel code for 8 MHz CPU clock
1 parent 7962fe3 commit 98d0bb5

File tree

1 file changed

+39
-0
lines changed
  • ports/atmel-samd/common-hal/neopixel_write

1 file changed

+39
-0
lines changed

ports/atmel-samd/common-hal/neopixel_write/__init__.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,39 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
3131

3232
static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMask,
3333
const uint8_t *ptr, int numBytes) {
34+
#ifdef SAMD21
35+
// This code is for the CPU running at 8 MHz, no wait states (1 cycle=125ns).
36+
// There's extra 6 cycles (750ns) before each byte, but it doesn't seem to be a problem in practice.
37+
asm volatile(
38+
" push {r4, r5, r6, lr};"
39+
" add r6, r2, r3;" // r6 = end address (ptr + numBytes)
40+
41+
"next_byte:"
42+
" ldrb r5, [r2];" // load byte from ptr
43+
" lsl r5, r5, #24;" // shift left 24 bits
44+
" add r2, #1;" // increment ptr
45+
" movs r4, #8;" // r4 = 8 bits to process
46+
47+
"next_bit:"
48+
" str r1, [r0, #4];" // set pin HIGH
49+
" lsl r5, r5, #1;" // shift left 1 bit
50+
" bcs one_bit_path;" // [1 cycle if not taken, 2 if taken]
51+
52+
"zero_bit_path:"
53+
" str r1, [r0];" // set pin LOW
54+
55+
"one_bit_path:"
56+
" nop;"
57+
" nop;"
58+
" str r1, [r0];" // set pin LOW
59+
" sub r4, #1;" // decrement bit counter
60+
" bne next_bit;" // [1 cycle if not taken, 2 if taken]
61+
" cmp r2, r6;" // compare current ptr to end address
62+
" bcc next_byte;" // loop if not all bytes sent
63+
64+
" pop {r4, r5, r6, pc};" // restore registers and return
65+
);
66+
#else
3467
asm volatile (" push {r4, r5, r6, lr};"
3568
" add r3, r2, r3;"
3669
"loopLoad:"
@@ -89,6 +122,7 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
89122
"neopixel_stop:"
90123
" pop {r4, r5, r6, pc};"
91124
"");
125+
#endif
92126
}
93127

94128
static uint64_t next_start_raw_ticks = 0;
@@ -109,7 +143,12 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout,
109143
mp_hal_disable_all_interrupts();
110144

111145
uint32_t pin = digitalinout->pin->number;
146+
#ifdef SAMD21
147+
// We use PORT_IOBUS, not PORT, so that "str r1, [r0]" takes 1 cycle, not 4.
148+
port = &PORT_IOBUS->Group[GPIO_PORT(pin)]; // Convert GPIO # to port register
149+
#else
112150
port = &PORT->Group[GPIO_PORT(pin)]; // Convert GPIO # to port register
151+
#endif
113152
pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code.
114153
volatile uint32_t *clr = &(port->OUTCLR.reg);
115154
neopixel_send_buffer_core(clr, pinMask, pixels, numBytes);

0 commit comments

Comments
 (0)