Skip to content

Commit

Permalink
samd21: Add bit-banging neopixel support
Browse files Browse the repository at this point in the history
To support neopixel devices on an arbitrary GPIO pin, we have to
bit-bang the protocol. DMA doesn't work because the SAMD21G DMAC
engine can't write to the PORT registers due to a missing connection
inside the SoC.

Signed-off-by: Keith Packard <keithp@keithp.com>
  • Loading branch information
keith-packard committed May 14, 2019
1 parent 95920c4 commit 979ec6d
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 0 deletions.
100 changes: 100 additions & 0 deletions samd21/ao-neopixel.c
@@ -0,0 +1,100 @@
/*
* Copyright © 2019 Keith Packard <keithp@keithp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include <ao.h>
#include <ao-snek.h>

void
ao_snek_neopixel_write(void *gpio, uint8_t pin, int npixel, struct snek_neopixel *pixels)
{
volatile uint32_t *outtgl = &(((struct samd21_port *) gpio)->outtgl);
uint32_t value = ((uint32_t) 1 << pin);

while (npixel--) {
int32_t p = pixels->p;
uint8_t bit;
pixels++;

ao_arch_block_interrupts();
for (bit = 0; bit < 24; bit++) {
*outtgl = value;
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();

ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
if (p < 0) {
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();

ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();

ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();

ao_arch_nop();
ao_arch_nop();
ao_arch_nop();

*outtgl = value;
} else {
*outtgl = value;
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();

ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();

ao_arch_nop();
ao_arch_nop();
ao_arch_nop();

}
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();
ao_arch_nop();

ao_arch_nop();

p <<= 1;
}
ao_arch_release_interrupts();
}
}
12 changes: 12 additions & 0 deletions samd21/ao-snek.h
Expand Up @@ -44,4 +44,16 @@ ao_snek_running(bool running);
#define SNEK_CODE_HOOK_START ao_snek_running(true);
#define SNEK_CODE_HOOK_STOP ao_snek_running(false);

struct snek_neopixel {
union {
struct {
uint8_t _extra, b, r, g;
};
uint32_t p;
};
};

void
ao_snek_neopixel_write(void *port, uint8_t pin, int npixel, struct snek_neopixel *pixels);

#endif /* _AO_SNEK_H_ */
1 change: 1 addition & 0 deletions samd21/snek-altos.builtin
Expand Up @@ -29,4 +29,5 @@ eeprom.erase, 0
reset, 0
random.seed, 1
random.randrange, 1
neopixel, 1
#include <ao-snek.h>
1 change: 1 addition & 0 deletions samd21/snek-samd21.defs
Expand Up @@ -24,6 +24,7 @@ SNEK_SAMD21_SRC = \
ao-interrupt.c \
ao-led.c \
ao-timer.c \
ao-neopixel.c \
ao-usb-samd21.c \
ao-tcc-samd21.c \
ao-tc-samd21.c \
Expand Down

0 comments on commit 979ec6d

Please sign in to comment.