diff --git a/libs/microbit/buffer.cpp b/libs/microbit/buffer.cpp index 843b8076..5abba58f 100644 --- a/libs/microbit/buffer.cpp +++ b/libs/microbit/buffer.cpp @@ -12,6 +12,11 @@ namespace BufferMethods { ManagedBuffer(buf).setByte(off, v); } + //% + uint8_t *getBytes(Buffer buf) { + return buf->payload; + } + /** Returns the length of a Buffer object. */ //% property int length(Buffer s) { diff --git a/libs/neopixel/kind.json b/libs/neopixel/kind.json new file mode 100644 index 00000000..1192d770 --- /dev/null +++ b/libs/neopixel/kind.json @@ -0,0 +1,20 @@ +{ + "name": "neppixel", + "description": "AdaFruit NeoPixel driver for micro:bit", + "files": [ + "neopixel.ts", + "sendbuffer.asm" + ], + "testFiles": [ + "neotest.ts" + ], + "microbit": { + "config": { + "MICROBIT_BLE_ENABLED": "0" + } + }, + "public": true, + "dependencies": { + "microbit": "file:../microbit" + } +} diff --git a/libs/neopixel/neopixel.ts b/libs/neopixel/neopixel.ts new file mode 100644 index 00000000..96118f36 --- /dev/null +++ b/libs/neopixel/neopixel.ts @@ -0,0 +1,88 @@ +namespace neopixel { + + //% shim=sendBufferAsm + function sendBuffer(buf: Buffer, pin: DigitalPin) { + } + + class Strip { + buf: Buffer; + pin: DigitalPin; + brightness: number; + + length() { + return this.buf.length / 3 + } + + /** + * Set the brightness of the strip, 0-255. + */ + setBrigthness(brightness: number): void { + this.brightness = brightness; + } + + /** + * Set the pin where the neopixel is connected, defaults to P0. + */ + setPin(pin: DigitalPin): void { + this.pin = pin; + pins.digitalWritePin(this.pin, 0) + basic.pause(50) + } + + /** + * Turn off all LEDs. + */ + clear(): void { + this.buf.fill(0); + } + + /** + * Shift LEDs forward. + */ + shift(off: number): void { + this.buf.shift(-off * 3) + } + + /** + * Shift LEDs forward. + */ + rotate(): void { + this.buf.rotate(-3) + } + + display() { + basic.pause(1) + sendBuffer(this.buf, this.pin); + } + + /** + * Set give LED to a given color (range 0-255 for r, g, b) + */ + setPix(ledoff: number, r: number, g: number, b: number): void { + ledoff = ledoff * 3; + let br = this.brightness; + if (br < 255) { + r = (Math.clamp(0, 255, r) * br) >> 8; + g = (Math.clamp(0, 255, b) * br) >> 8; + b = (Math.clamp(0, 255, b) * br) >> 8; + } + let buf = this.buf; + buf[ledoff + 0] = Math.clamp(0, 255, g); + buf[ledoff + 1] = Math.clamp(0, 255, r); + buf[ledoff + 2] = Math.clamp(0, 255, b); + } + } + + /** + * Create a new NeoPixel driver for `numleds` LEDs. + * @params numleds number of leds in the strip, eg: 24,30,60,64 + */ + export function create(numleds: number): Strip { + let strip = new Strip(); + strip.buf = pins.createBuffer(numleds * 3); + strip.setBrigthness(255) + strip.setPin(DigitalPin.P0) + return strip; + } + +} diff --git a/libs/neopixel/neotest.ts b/libs/neopixel/neotest.ts new file mode 100644 index 00000000..29695c56 --- /dev/null +++ b/libs/neopixel/neotest.ts @@ -0,0 +1,92 @@ +basic.showLeds(` + # . . . . + . . . . . + . . # . . + . . . . . + . . . . # + `) +console.log("Start") + +// Create a NeoPixel driver - specify the number of LEDs: +let strip = neopixel.create(24); + +// If your strip is not at P0, specify the pin. +strip.setPin(DigitalPin.P0) + +// Brightness defaults to 255 (very bright); 0 is completely off. +strip.setBrigthness(20) + +// Set pixels - pixel number, followed by red, green and blue values, between 0 and 255. +strip.setPix(0, 255, 255, 255); +strip.setPix(1, 0, 255, 255); +strip.setPix(2, 255, 0, 255); +strip.setPix(3, 255, 255, 0); + +console.log("Send!") + +// Send the image to the strip. +strip.display(); +basic.pause(500); + +console.log("Sent!") + +// Green light travelling the strip: +for (let l = 0; l < strip.length(); l++) { + strip.clear(); + strip.setPix(l, 0, 255, 0); + strip.display(); + basic.pause(100); +} + +let special = false; +let numcol = 0; +input.onButtonPressed(Button.A, () => { + special = true; + let r = 0; + let g = 0; + let b = 0; + if (numcol == 0) { + r = 255; + g = 255; + b = 255; + } else if (numcol == 1) { + r = 255; + } else if (numcol == 2) { + b = 255; + } else if (numcol == 3) { + r = 255; + g = 255; + } else if (numcol == 4) { + r = 10; + g = 10; + b = 10; + } + numcol = (numcol + 1) % 5; + for (let k = 0; k < 7; k++) { + strip.setPix(k, r, g, b); + } + strip.display(); +}); + +control.inBackground(() => { + for (let j = 0; j < 2000000; j++) { + if (special) { + basic.pause(100); + continue; + } + let r1 = (0 + j * 2) & 63; + let g1 = (20 + j * 1) & 63; + let b1 = (30 + j * 3) & 63; + for (let i = 0; i < strip.length(); i++) { + strip.setPix(i, (r1 - i) * 20, (g1 - i) * 20, (b1 - i) * 20); + } + strip.display() + basic.pause(60); + } +}); + +control.inBackground(() => { + while (true) { + basic.showString("XMAS!", 150); + } +}); diff --git a/libs/neopixel/sendbuffer.asm b/libs/neopixel/sendbuffer.asm new file mode 100644 index 00000000..98b5202f --- /dev/null +++ b/libs/neopixel/sendbuffer.asm @@ -0,0 +1,62 @@ + push {r4,r5,r6,r7,lr} + + mov r4, r0 ; save buff + mov r6, r1 ; save pin + + mov r0, r4 + bl buffer::count + mov r5, r0 + + mov r0, r4 + bl buffer::cptr + mov r4, r0 + + ; setup pin as digital + mov r0, r6 + movs r1, #0 + bl micro_bit::digitalWritePin + + ; load pin address + mov r0, r6 + + ldr r0, [r0, #8] ; get mbed DigitalOut from MicroBitPin + ldr r1, [r0, #4] ; r1-mask for this pin + ldr r2, [r0, #16] ; r2-clraddr + ldr r3, [r0, #12] ; r3-setaddr + + cpsid i ; disable irq + + b .start + +.nextbit: ; C0 + str r1, [r3, #0] ; pin := hi C2 + tst r6, r0 ; C3 + bne .islate ; C4 + str r1, [r2, #0] ; pin := lo C6 +.islate: + lsrs r6, r6, #1 ; r6 >>= 1 C7 + bne .justbit ; C8 + + ; not just a bit - need new byte + adds r4, #1 ; r4++ C9 + subs r5, #1 ; r5-- C10 + bcc .stop ; if (r5<0) goto .stop C11 +.start: + movs r6, #0x80 ; reset mask C12 + nop ; C13 + +.common: ; C13 + str r1, [r2, #0] ; pin := lo C15 + ; always re-load byte - it just fits with the cycles better this way + ldrb r0, [r4, #0] ; r0 := *r4 C17 + b .nextbit ; C20 + +.justbit: ; C10 + ; no nops, branch taken is already 3 cycles + b .common ; C13 + +.stop: + str r1, [r2, #0] ; pin := lo + cpsie i ; enable irq + + pop {r4,r5,r6,r7,pc}