atmel-samd: Add low level neopixel_write module & function for WS281x/neopixel RGB LEDs.

This commit is contained in:
Tony DiCola 2016-10-05 12:35:23 -07:00
parent 7e08347d5c
commit 9c67605233
5 changed files with 135 additions and 1 deletions

View File

@ -140,11 +140,13 @@ SRC_C = \
modmachine_dac.c \
modmachine_pin.c \
modmachine_pwm.c \
modneopixel_write.c \
moduos.c \
modutime.c \
mphalport.c \
pin_named_pins.c \
rom_fs.c \
samdneopixel.c \
storage.c \
uart.c \
asf/common/services/sleepmgr/samd/sleepmgr.c \

View File

@ -0,0 +1,34 @@
#include <stdio.h>
#include "py/obj.h"
#include "py/mphal.h"
#include "py/runtime.h"
#include "modmachine_pin.h"
#include "samdneopixel.h"
extern const mp_obj_type_t pin_type;
STATIC mp_obj_t neopixel_write_neopixel_write_(mp_obj_t pin_obj, mp_obj_t buf, mp_obj_t is800k) {
// Convert parameters into expected types.
const pin_obj_t *pin = pin_find(pin_obj);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
// Call platform's neopixel write function with provided buffer and options.
samd_neopixel_write(pin->pin, (uint8_t*)bufinfo.buf, bufinfo.len,
mp_obj_is_true(is800k));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(neopixel_write_neopixel_write_obj, neopixel_write_neopixel_write_);
STATIC const mp_rom_map_elem_t neopixel_write_module_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write), (mp_obj_t)&neopixel_write_neopixel_write_obj },
};
STATIC MP_DEFINE_CONST_DICT(neopixel_write_module_globals, neopixel_write_module_globals_table);
const mp_obj_module_t neopixel_write_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&neopixel_write_module_globals,
};

View File

@ -108,11 +108,13 @@ typedef long mp_off_t;
extern const struct _mp_obj_module_t machine_module;
extern const struct _mp_obj_module_t uos_module;
extern const struct _mp_obj_module_t utime_module;
extern const struct _mp_obj_module_t neopixel_write_module;
#define MICROPY_PORT_BUILTIN_MODULES \
{ MP_OBJ_NEW_QSTR(MP_QSTR_umachine), (mp_obj_t)&machine_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module } \
{ MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \
{ MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write),(mp_obj_t)&neopixel_write_module } \
#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \
{ MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&machine_module }, \

87
atmel-samd/samdneopixel.c Normal file
View File

@ -0,0 +1,87 @@
#include "asf/common2/services/delay/delay.h"
#include "asf/sam0/drivers/port/port.h"
#include "samdneopixel.h"
void samd_neopixel_write(uint32_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz) {
// This is adapted directly from the Adafruit NeoPixel library SAMD21G18A code:
// https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.cpp
uint8_t *ptr, *end, p, bitMask;
uint32_t pinMask;
PortGroup* port;
// Turn off interrupts of any kind during timing-sensitive code.
irqflags_t flags = cpu_irq_save();
port = port_get_group_from_gpio_pin(pin);
pinMask = (1UL << (pin % 32)); // From port_pin_set_output_level ASF code.
ptr = pixels;
end = ptr + numBytes;
p = *ptr++;
bitMask = 0x80;
volatile uint32_t *set = &(port->OUTSET.reg),
*clr = &(port->OUTCLR.reg);
if(is800KHz) {
for(;;) {
*set = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop;");
if(p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;");
*clr = pinMask;
} else {
*clr = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop;");
}
if(bitMask >>= 1) {
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop;");
} else {
if(ptr >= end) break;
p = *ptr++;
bitMask = 0x80;
}
}
} else { // 400 KHz bitstream
for(;;) {
*set = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");
if(p & bitMask) {
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop;");
*clr = pinMask;
} else {
*clr = pinMask;
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop;");
}
asm("nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;"
"nop; nop; nop; nop; nop; nop; nop; nop;");
if(bitMask >>= 1) {
asm("nop; nop; nop; nop; nop; nop; nop;");
} else {
if(ptr >= end) break;
p = *ptr++;
bitMask = 0x80;
}
}
}
// Turn on interrupts after timing-sensitive code.
cpu_irq_restore(flags);
// 50ms delay to let pixels latch to the data that was just sent.
// This could be optimized to only occur before pixel writes when necessary,
// like in the Arduino library.
delay_ms(50);
}

View File

@ -0,0 +1,9 @@
#ifndef SAMD_NEOPIXEL_WRITE_H
#define SAMD_NEOPIXEL_WRITE_H
#include <stdint.h>
#include <stdbool.h>
void samd_neopixel_write(uint32_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
#endif