Skip to content

Commit 9c67605

Browse files
committed
atmel-samd: Add low level neopixel_write module & function for WS281x/neopixel RGB LEDs.
1 parent 7e08347 commit 9c67605

File tree

5 files changed

+135
-1
lines changed

5 files changed

+135
-1
lines changed

atmel-samd/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,13 @@ SRC_C = \
140140
modmachine_dac.c \
141141
modmachine_pin.c \
142142
modmachine_pwm.c \
143+
modneopixel_write.c \
143144
moduos.c \
144145
modutime.c \
145146
mphalport.c \
146147
pin_named_pins.c \
147148
rom_fs.c \
149+
samdneopixel.c \
148150
storage.c \
149151
uart.c \
150152
asf/common/services/sleepmgr/samd/sleepmgr.c \

atmel-samd/modneopixel_write.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <stdio.h>
2+
3+
#include "py/obj.h"
4+
#include "py/mphal.h"
5+
#include "py/runtime.h"
6+
7+
#include "modmachine_pin.h"
8+
#include "samdneopixel.h"
9+
10+
extern const mp_obj_type_t pin_type;
11+
12+
STATIC mp_obj_t neopixel_write_neopixel_write_(mp_obj_t pin_obj, mp_obj_t buf, mp_obj_t is800k) {
13+
// Convert parameters into expected types.
14+
const pin_obj_t *pin = pin_find(pin_obj);
15+
mp_buffer_info_t bufinfo;
16+
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
17+
// Call platform's neopixel write function with provided buffer and options.
18+
samd_neopixel_write(pin->pin, (uint8_t*)bufinfo.buf, bufinfo.len,
19+
mp_obj_is_true(is800k));
20+
return mp_const_none;
21+
}
22+
STATIC MP_DEFINE_CONST_FUN_OBJ_3(neopixel_write_neopixel_write_obj, neopixel_write_neopixel_write_);
23+
24+
STATIC const mp_rom_map_elem_t neopixel_write_module_globals_table[] = {
25+
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write) },
26+
{ MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write), (mp_obj_t)&neopixel_write_neopixel_write_obj },
27+
};
28+
29+
STATIC MP_DEFINE_CONST_DICT(neopixel_write_module_globals, neopixel_write_module_globals_table);
30+
31+
const mp_obj_module_t neopixel_write_module = {
32+
.base = { &mp_type_module },
33+
.globals = (mp_obj_dict_t*)&neopixel_write_module_globals,
34+
};

atmel-samd/mpconfigport.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,13 @@ typedef long mp_off_t;
108108
extern const struct _mp_obj_module_t machine_module;
109109
extern const struct _mp_obj_module_t uos_module;
110110
extern const struct _mp_obj_module_t utime_module;
111+
extern const struct _mp_obj_module_t neopixel_write_module;
111112

112113
#define MICROPY_PORT_BUILTIN_MODULES \
113114
{ MP_OBJ_NEW_QSTR(MP_QSTR_umachine), (mp_obj_t)&machine_module }, \
114115
{ MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \
115-
{ MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module } \
116+
{ MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \
117+
{ MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write),(mp_obj_t)&neopixel_write_module } \
116118

117119
#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \
118120
{ MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&machine_module }, \

atmel-samd/samdneopixel.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include "asf/common2/services/delay/delay.h"
2+
#include "asf/sam0/drivers/port/port.h"
3+
4+
#include "samdneopixel.h"
5+
6+
void samd_neopixel_write(uint32_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz) {
7+
// This is adapted directly from the Adafruit NeoPixel library SAMD21G18A code:
8+
// https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.cpp
9+
uint8_t *ptr, *end, p, bitMask;
10+
uint32_t pinMask;
11+
PortGroup* port;
12+
13+
// Turn off interrupts of any kind during timing-sensitive code.
14+
irqflags_t flags = cpu_irq_save();
15+
16+
port = port_get_group_from_gpio_pin(pin);
17+
pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code.
18+
ptr = pixels;
19+
end = ptr + numBytes;
20+
p = *ptr++;
21+
bitMask = 0x80;
22+
23+
volatile uint32_t *set = &(port->OUTSET.reg),
24+
*clr = &(port->OUTCLR.reg);
25+
26+
if(is800KHz) {
27+
for(;;) {
28+
*set = pinMask;
29+
asm("nop; nop; nop; nop; nop; nop; nop; nop;");
30+
if(p & bitMask) {
31+
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
32+
"nop; nop; nop; nop; nop; nop; nop; nop;"
33+
"nop; nop; nop; nop;");
34+
*clr = pinMask;
35+
} else {
36+
*clr = pinMask;
37+
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
38+
"nop; nop; nop; nop; nop; nop; nop; nop;"
39+
"nop; nop; nop; nop;");
40+
}
41+
if(bitMask >>= 1) {
42+
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;");
43+
} else {
44+
if(ptr >= end) break;
45+
p = *ptr++;
46+
bitMask = 0x80;
47+
}
48+
}
49+
} else { // 400 KHz bitstream
50+
for(;;) {
51+
*set = pinMask;
52+
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");
53+
if(p & bitMask) {
54+
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
55+
"nop; nop; nop; nop; nop; nop; nop; nop;"
56+
"nop; nop; nop; nop; nop; nop; nop; nop;"
57+
"nop; nop; nop;");
58+
*clr = pinMask;
59+
} else {
60+
*clr = pinMask;
61+
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
62+
"nop; nop; nop; nop; nop; nop; nop; nop;"
63+
"nop; nop; nop; nop; nop; nop; nop; nop;"
64+
"nop; nop; nop;");
65+
}
66+
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
67+
"nop; nop; nop; nop; nop; nop; nop; nop;"
68+
"nop; nop; nop; nop; nop; nop; nop; nop;"
69+
"nop; nop; nop; nop; nop; nop; nop; nop;");
70+
if(bitMask >>= 1) {
71+
asm("nop; nop; nop; nop; nop; nop; nop;");
72+
} else {
73+
if(ptr >= end) break;
74+
p = *ptr++;
75+
bitMask = 0x80;
76+
}
77+
}
78+
}
79+
80+
// Turn on interrupts after timing-sensitive code.
81+
cpu_irq_restore(flags);
82+
83+
// 50ms 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_ms(50);
87+
}

atmel-samd/samdneopixel.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef SAMD_NEOPIXEL_WRITE_H
2+
#define SAMD_NEOPIXEL_WRITE_H
3+
4+
#include <stdint.h>
5+
#include <stdbool.h>
6+
7+
void samd_neopixel_write(uint32_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
8+
9+
#endif

0 commit comments

Comments
 (0)