@@ -31,6 +31,39 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
31
31
32
32
static void neopixel_send_buffer_core (volatile uint32_t * clraddr , uint32_t pinMask ,
33
33
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
34
67
asm volatile (" push {r4, r5, r6, lr};"
35
68
" add r3, r2, r3;"
36
69
"loopLoad:"
@@ -89,6 +122,7 @@ static void neopixel_send_buffer_core(volatile uint32_t *clraddr, uint32_t pinMa
89
122
"neopixel_stop:"
90
123
" pop {r4, r5, r6, pc};"
91
124
"" );
125
+ #endif
92
126
}
93
127
94
128
static uint64_t next_start_raw_ticks = 0 ;
@@ -109,7 +143,12 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout,
109
143
mp_hal_disable_all_interrupts ();
110
144
111
145
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
112
150
port = & PORT -> Group [GPIO_PORT (pin )]; // Convert GPIO # to port register
151
+ #endif
113
152
pinMask = (1UL << (pin % 32 )); // From port_pin_set_output_level ASF code.
114
153
volatile uint32_t * clr = & (port -> OUTCLR .reg );
115
154
neopixel_send_buffer_core (clr , pinMask , pixels , numBytes );
0 commit comments