diff --git a/LICENSE b/LICENSE index cd16d8e0ea72624118ad314b6e13fa054a87d1dc..d356020d6ac24b6018c1b54088a3299201d22210 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 M. Elizabeth Scott +Copyright (c) 2013 Micah Elizabeth Scott Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 97c2456a28dbe38e983ba9753f3a6ca9fa8f5f06..53b1237f630f208fe70c9622070445c645cae2dc 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,9 @@ This is a work in progress! Things I don't know yet: * How many LEDs will be supported per Teensy board * Maximum frame rates * Specific documentation for the USB protocol + +Prerequisites +------------- + +* The recommended ARM toolchain, from <https://code.launchpad.net/gcc-arm-embedded> +* The Teensy Loader: <http://www.pjrc.com/teensy/loader.html> diff --git a/fc_firmware/fc_firmware.ino b/fc_firmware/fc_firmware.ino new file mode 100644 index 0000000000000000000000000000000000000000..f52b033628f761cebba962b822f84a228621e1c4 --- /dev/null +++ b/fc_firmware/fc_firmware.ino @@ -0,0 +1,33 @@ +/* + * Fadecandy Firmware + * For Arduino with Teensyduino and OctoWS2811. + * + * Copyright <c> 2013 Micah Elizabeth Scott. <micah@scanlime.org> + */ + +#include <OctoWS2811.h> +#include "hcolor.h" + +static const int ledsPerStrip = 64; +static const int ledsTotal = ledsPerStrip * 8; + +DMAMEM int displayMemory[ledsPerStrip * 6]; +int drawingMemory[ledsPerStrip * 6]; +OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, WS2811_GRB | WS2811_800kHz); +HPixelBuffer<ledsTotal> pixbuf; + +void setup() +{ + leds.begin(); + + for (unsigned i = 0; i < ledsTotal; ++i) { + pixbuf.pixels[i].color = HColor16(0x1080, 0x0080, 0x0080); + } +} + +void loop() +{ + pixbuf.pixels[0].color.r = millis() & 0xffff; + + pixbuf.show(leds); +} diff --git a/fc_firmware/hcolor.h b/fc_firmware/hcolor.h new file mode 100644 index 0000000000000000000000000000000000000000..3534da4511d0139226dd267272890d5e9f56fef5 --- /dev/null +++ b/fc_firmware/hcolor.h @@ -0,0 +1,120 @@ +/* + * High dynamic range color library. + * + * Copyright <c> 2013 Micah Elizabeth Scott. <micah@scanlime.org> + * + * This is a high dynamic range (48-bit) color data type, + * and a temporal dithering implementation that's compatible + * with the OctoWS2811 LED driver. + */ + +#pragma once +#include <stdint.h> +#include <algorithm> + +class OctoWS2811; + +/// Basic data type for a high-dynamic-range color. +struct HColor { + uint16_t r, g, b; +}; + +/// Constructor for 16-bit colors +static inline HColor HColor16(uint16_t r, uint16_t g, uint16_t b) { + HColor c = { r, g, b }; + return c; +} + +/// Constructor for 8-bit colors +static inline HColor HColor8(uint8_t r, uint8_t g, uint8_t b) { + HColor c = { + r | (unsigned(r) << 8), + g | (unsigned(g) << 8), + b | (unsigned(b) << 8), + }; + return c; +} + +/// Constructor for 8-bit colors packed into a 24-bit word +static inline HColor HColor8(uint32_t rgb) { + return HColor8( (rgb & 0xFF0000) >> 16, (rgb & 0x00FF00) >> 8, rgb & 0x0000FF ); +} + +/// Constructor for float colors, with clamping. +static inline HColor HColorF(float r, float g, float b) { + HColor c = { + std::min<int>(0xffff, std::max<int>(0, r * 65535.0f + 0.5f)), + std::min<int>(0xffff, std::max<int>(0, g * 65535.0f + 0.5f)), + std::min<int>(0xffff, std::max<int>(0, b * 65535.0f + 0.5f)), + }; + return c; +} + +/// Add two colors, with saturation +static inline HColor operator + (HColor a, HColor b) { + HColor c = { + std::min<int>(0xffff, unsigned(a.r) + unsigned(b.r)), + std::min<int>(0xffff, unsigned(a.g) + unsigned(b.g)), + std::min<int>(0xffff, unsigned(a.b) + unsigned(b.b)), + }; + return c; +} + +/** + * Linear interpolation between two colors. "Alpha" is in 8-bit fixed point. + * Returns c1 if alpha==0, or c2 if alpha==0x100. Values outside this range will extrapolate. + */ +static inline HColor lerp8(HColor c1, HColor c2, int alpha) { + int invA = 0x100 - alpha; + HColor c = { + (c1.r * invA + c2.r * alpha) >> 8, + (c1.g * invA + c2.g * alpha) >> 8, + (c1.b * invA + c2.b * alpha) >> 8, + }; + return c; +} + +/// Floating point linear interpolation, with clamping. +static inline HColor lerp(HColor c1, HColor c2, float alpha) { + float invA = 1.0f - alpha; + HColor c = { + std::min<int>(0xffff, std::max<int>(0, c1.r * invA + c2.r * alpha)), + std::min<int>(0xffff, std::max<int>(0, c1.g * invA + c2.g * alpha)), + std::min<int>(0xffff, std::max<int>(0, c1.b * invA + c2.b * alpha)), + }; + return c; +} + +/// Data type for one display pixel +struct HPixel { + HColor color; + int16_t residual[3]; + + /// Temporal dithering algorithm. Returns a 24-bit RGB color. + uint32_t dither() { + int r16 = color.r + residual[0]; + int g16 = color.g + residual[1]; + int b16 = color.b + residual[2]; + int r8 = std::min<int>(0xff, std::max<int>(0, (r16 + 0x80) >> 8)); + int g8 = std::min<int>(0xff, std::max<int>(0, (g16 + 0x80) >> 8)); + int b8 = std::min<int>(0xff, std::max<int>(0, (b16 + 0x80) >> 8)); + residual[0] = r16 - (r8 | (r8 << 8)); + residual[1] = g16 - (g8 | (g8 << 8)); + residual[2] = b16 - (b8 | (b8 << 8)); + return (r8 << 16) | (g8 << 8) | b8; + } +}; + +/// Data type for a framebuffer of pixels +template <unsigned tCount> +struct HPixelBuffer { + HPixel pixels[tCount]; + + /// Update the entire frame + void show(OctoWS2811 &leds) { + for (unsigned i = 0; i < tCount; ++i) { + leds.setPixel(i, pixels[i].dither()); + } + leds.show(); + } +};