2323 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2424 * THE SOFTWARE.
2525 */
26+ #include "hpl_gpio.h"
2627
27- #include "mphalport .h"
28+ #include "py/mphal .h"
2829
2930#include "shared-bindings/neopixel_write/__init__.h"
3031
31- #include "asf/common2/services/delay/delay.h"
32- #include "asf/sam0/drivers/port/port.h"
32+ #include "tick.h"
33+
34+ #ifdef SAMD51
35+ static inline void delay_cycles (uint8_t cycles ) {
36+ uint32_t start = SysTick -> VAL ;
37+ uint32_t stop = start - cycles ;
38+ if (start < cycles ) {
39+ stop = 0xffffff + start - cycles ;
40+ }
41+ while (SysTick -> VAL > stop ) {}
42+ }
43+ #endif
44+
45+ uint64_t next_start_tick_ms = 0 ;
46+ uint32_t next_start_tick_us = 1000 ;
3347
3448void common_hal_neopixel_write (const digitalio_digitalinout_obj_t * digitalinout , uint8_t * pixels , uint32_t numBytes ) {
3549 // This is adapted directly from the Adafruit NeoPixel library SAMD21G18A code:
@@ -38,14 +52,21 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout,
3852 uint32_t pinMask ;
3953 PortGroup * port ;
4054
55+ // This must be called while interrupts are on in case we're waiting for a
56+ // future ms tick.
57+ wait_until (next_start_tick_ms , next_start_tick_us );
58+
4159 // Turn off interrupts of any kind during timing-sensitive code.
4260 mp_hal_disable_all_interrupts ();
4361
62+ #ifdef SAMD21
4463 // Make sure the NVM cache is consistently timed.
4564 NVMCTRL -> CTRLB .bit .READMODE = NVMCTRL_CTRLB_READMODE_DETERMINISTIC_Val ;
65+ #endif
66+
4667
4768 uint32_t pin = digitalinout -> pin -> pin ;
48- port = port_get_group_from_gpio_pin (pin );
69+ port = & PORT -> Group [ GPIO_PORT (pin )]; // Convert GPIO # to port register
4970 pinMask = (1UL << (pin % 32 )); // From port_pin_set_output_level ASF code.
5071 ptr = pixels ;
5172 end = ptr + numBytes ;
@@ -57,31 +78,75 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout,
5778
5879 for (;;) {
5980 * set = pinMask ;
81+ // This is the time where the line is always high regardless of the bit.
82+ // For the SK6812 its 0.3us +- 0.15us
83+ #ifdef SAMD21
6084 asm("nop; nop;" );
85+ #endif
86+ #ifdef SAMD51
87+ delay_cycles (18 );
88+ #endif
6189 if (p & bitMask ) {
90+ // This is the high delay unique to a one bit.
91+ // For the SK6812 its 0.3us
92+ #ifdef SAMD21
6293 asm("nop; nop; nop; nop; nop; nop; nop;" );
94+ #endif
95+ #ifdef SAMD51
96+ delay_cycles (25 );
97+ #endif
6398 * clr = pinMask ;
6499 } else {
65100 * clr = pinMask ;
101+ // This is the low delay unique to a zero bit.
102+ // For the SK6812 its 0.3us
103+ #ifdef SAMD21
66104 asm("nop; nop;" );
105+ #endif
106+ #ifdef SAMD51
107+ delay_cycles (25 );
108+ #endif
67109 }
68110 if ((bitMask >>= 1 ) != 0 ) {
111+ // This is the delay between bits in a byte and is the 1 code low
112+ // level time from the datasheet.
113+ // For the SK6812 its 0.6us +- 0.15us
114+ #ifdef SAMD21
69115 asm("nop; nop; nop; nop; nop;" );
116+ #endif
117+ #ifdef SAMD51
118+ delay_cycles (44 );
119+ #endif
70120 } else {
71121 if (ptr >= end ) break ;
72122 p = * ptr ++ ;
73123 bitMask = 0x80 ;
124+ // This is the delay between bytes. Its similar to the other branch
125+ // in the if statement except its tuned to account for the time the
126+ // above operations take.
127+ // For the SK6812 its 0.6us +- 0.15us
128+ #ifdef SAMD51
129+ delay_cycles (50 );
130+ #endif
74131 }
75132 }
76133
134+ #ifdef SAMD21
77135 // Speed up! (But inconsistent timing.)
78136 NVMCTRL -> CTRLB .bit .READMODE = NVMCTRL_CTRLB_READMODE_NO_MISS_PENALTY_Val ;
137+ #endif
138+
139+ // ticks_ms may be out of date at this point because we stopped the
140+ // interrupt. We'll risk it anyway.
141+ current_tick (& next_start_tick_ms , & next_start_tick_us );
142+ if (next_start_tick_us < 100 ) {
143+ next_start_tick_ms += 1 ;
144+ next_start_tick_us = 100 - next_start_tick_us ;
145+ } else {
146+ next_start_tick_us -= 100 ;
147+ }
79148
80149 // Turn on interrupts after timing-sensitive code.
81150 mp_hal_enable_all_interrupts ();
82151
83- // 50us delay to let pixels latch to the data that was just sent.
84- // This could be optimized to only occur before pixel writes when necessary,
85- // like in the Arduino library.
86- delay_us (50 );
87152}
0 commit comments