From 9c676052333f45c74791ab30cc62b827631523f5 Mon Sep 17 00:00:00 2001 From: Tony DiCola Date: Wed, 5 Oct 2016 12:35:23 -0700 Subject: [PATCH] atmel-samd: Add low level neopixel_write module & function for WS281x/neopixel RGB LEDs. --- atmel-samd/Makefile | 2 + atmel-samd/modneopixel_write.c | 34 +++++++++++++ atmel-samd/mpconfigport.h | 4 +- atmel-samd/samdneopixel.c | 87 ++++++++++++++++++++++++++++++++++ atmel-samd/samdneopixel.h | 9 ++++ 5 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 atmel-samd/modneopixel_write.c create mode 100644 atmel-samd/samdneopixel.c create mode 100644 atmel-samd/samdneopixel.h diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile index 96bd53ae01..01b2540b7a 100644 --- a/atmel-samd/Makefile +++ b/atmel-samd/Makefile @@ -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 \ diff --git a/atmel-samd/modneopixel_write.c b/atmel-samd/modneopixel_write.c new file mode 100644 index 0000000000..024e4b1c50 --- /dev/null +++ b/atmel-samd/modneopixel_write.c @@ -0,0 +1,34 @@ +#include + +#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, +}; diff --git a/atmel-samd/mpconfigport.h b/atmel-samd/mpconfigport.h index 1f72477c77..69fe637669 100644 --- a/atmel-samd/mpconfigport.h +++ b/atmel-samd/mpconfigport.h @@ -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 }, \ diff --git a/atmel-samd/samdneopixel.c b/atmel-samd/samdneopixel.c new file mode 100644 index 0000000000..70b27aee71 --- /dev/null +++ b/atmel-samd/samdneopixel.c @@ -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); +} diff --git a/atmel-samd/samdneopixel.h b/atmel-samd/samdneopixel.h new file mode 100644 index 0000000000..8448d45009 --- /dev/null +++ b/atmel-samd/samdneopixel.h @@ -0,0 +1,9 @@ +#ifndef SAMD_NEOPIXEL_WRITE_H +#define SAMD_NEOPIXEL_WRITE_H + +#include +#include + +void samd_neopixel_write(uint32_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz); + +#endif