Simple neopixel (WS2812) library, tuned for stm32 (L432) at 80 MHz Should be compatible with any stm32, different clock speed may require timing adjustments in neopixel.c
Revision 0:a81364d9a67b, committed 2017-03-21
- Comitter:
- MightyPork
- Date:
- Tue Mar 21 21:17:08 2017 +0000
- Commit message:
- initial
Changed in this revision
diff -r 000000000000 -r a81364d9a67b colorspace.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/colorspace.cpp Tue Mar 21 21:17:08 2017 +0000 @@ -0,0 +1,174 @@ +#include "mbed.h" +#include "colorspace.h" +#include "math.h" + +#define max(a,b) ((a)>(b)?(a):(b)) +#define min(a,b) ((a)>(b)?(b):(a)) + +static float threeway_max(float a, float b, float c) { + return max(a, max(b, c)); +} + + +static float threeway_min(float a, float b, float c) { + return min(a, min(b, c)); +} + + +static float hue2rgb(float p, float q, float t) { + if(t < 0) t += 1; + if(t > 1) t -= 1; + if(t < 1/6.0f) return p + (q - p) * 6 * t; + if(t < 1/2.0f) return q; + if(t < 2/3.0f) return p + (q - p) * (2/3.0f - t) * 6; + return p; +} + + +void hsl2rgb(const FloatHSL *hsl, FloatRGB *rgb) +{ + float h = hsl->h; + float s = hsl->s; + float l = hsl->l; + + if (s == 0) { + rgb->r = rgb->g = rgb->b = l; // achromatic + } else { + float q = l < 0.5f ? l * (1 + s) : l + s - l * s; + float p = 2 * l - q; + rgb->r = hue2rgb(p, q, h + 1/3.0f); + rgb->g = hue2rgb(p, q, h); + rgb->b = hue2rgb(p, q, h - 1/3.0f); + } +} + + +void hsv2rgb(const FloatHSV *hsv, FloatRGB *rgb) +{ + float r, g, b; + + int i = floor(hsv->h * 6); + float f = hsv->h * 6 - i; + float p = hsv->v * (1 - hsv->s); + float q = hsv->v * (1 - f * hsv->s); + float t = hsv->v * (1 - (1 - f) * hsv->s); + + switch(i % 6) { + case 0: + r = hsv->v; + g = t; + b = p; + break; + case 1: + r = q; + g = hsv->v; + b = p; + break; + case 2: + r = p; + g = hsv->v; + b = t; + break; + case 3: + r = p; + g = q; + b = hsv->v; + break; + case 4: + r = t; + g = p; + b = hsv->v; + break; + case 5: + r = hsv->v; + g = p; + b = q; + break; + } + + rgb->r = r; + rgb->g = g; + rgb->b = b; +} + +void rgb2hsl(const FloatRGB *rgb, FloatHSL *hsl) +{ + float rd = rgb->r; + float gd = rgb->g; + float bd = rgb->b; + float max = threeway_max(rd, gd, bd); + float min = threeway_min(rd, gd, bd); + float h, s, l = (max + min) / 2.0f; + + if (max == min) { + h = s = 0; // achromatic + } else { + float d = max - min; + s = l > 0.5f ? d / (2 - max - min) : d / (max + min); + if (max == rd) { + h = (gd - bd) / d + (gd < bd ? 6 : 0); + } else if (max == gd) { + h = (bd - rd) / d + 2; + } else if (max == bd) { + h = (rd - gd) / d + 4; + } + h /= 6; + } + hsl->h = h; + hsl->s = s; + hsl->l = l; +} + + + +/** Convert from HSV to HSL (this is possibly wrong) */ +void hsv2hsl(const FloatHSV *hsv, FloatHSL *hsl) +{ + float l = (2 - hsv->s) * hsv->v / 2.0f; + float s = hsv->s; + + if (l != 0) { + if (l == 1) { + s = 0; + } else if (l < 0.5f) { + s = s * hsv->v / (l * 2); + } else { + s = s * hsv->v / (2 - l * 2); + } + } + + hsl->h = hsv->h; + hsl->s = s; + hsl->l = l; +} + +/** Convert from HSL to HSV */ +void hsl2hsv(const FloatHSL *hsl, FloatHSV *hsv) +{ + float sat = hsv->s * ((hsl->l < 0.5f) ? hsl->l : (1 - hsl->l)); + hsv->s = 2*sat / (hsl->l + sat); + hsv->h = hsl->h; + hsv->v = hsl->l + sat; +} + + +uint32_t hsl2hex(const FloatHSL *hsl) +{ + FloatRGB rgb; + hsl2rgb(hsl, &rgb); + return rgb2hex(&rgb); +} + +uint32_t hsv2hex(const FloatHSV *hsv) +{ + FloatRGB rgb; + hsv2rgb(hsv, &rgb); + return rgb2hex(&rgb); +} + + +uint32_t rgb2hex(FloatRGB *rgb) +{ + return ((int)floor(rgb->r*255 + 0.5f) << 16) | ((int)floor(rgb->g*255 + 0.5f) << 8) | ((int)floor(rgb->b*255 + 0.5f)); +} +
diff -r 000000000000 -r a81364d9a67b colorspace.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/colorspace.h Tue Mar 21 21:17:08 2017 +0000 @@ -0,0 +1,64 @@ +#ifndef HSL_H +#define HSL_H + +// +// Utilities for working with colors in different color spaces +// + +#include "mbed.h" + +// all values 0 to 1 + +// adapted from: https://github.com/ratkins/RGBConverter + +struct FloatRGB { + float r; + float g; + float b; +}; + + +struct FloatHSL { + float h; + float s; + float l; +}; + + +struct FloatHSV { + float h; + float s; + float v; +}; + + +// --- Converting to RGB hex --- + +/** Convert HSL to RGB hex */ +uint32_t hsl2hex(const FloatHSL *hsl); + +/** Convert HSV to RGB hex */ +uint32_t hsv2hex(const FloatHSV *hsv); + +/** Convert RGB to RGB hex */ +uint32_t rgb2hex(FloatRGB *rgb); + + +// --- Itner-space conversion functions --- + +/** Convert HSL to RGB */ +void hsl2rgb(const FloatHSL *hsl, FloatRGB *rgb); + +/** Convert RGB to HSL */ +void rgb2hsl(const FloatRGB *rgb, FloatHSL *hsl); + +/** Convert from HSV to HSL */ +void hsv2hsl(const FloatHSV *hsv, FloatHSL *hsl); + +/** Convert from HSL to HSV */ +void hsl2hsv(const FloatHSL *hsl, FloatHSV *hsv); + +/** Convert HSV to RGB ("handy" algo) */ +void hsv2rgb(const FloatHSV *hsv, FloatRGB *rgb); + +#endif /* HSL_H */ \ No newline at end of file
diff -r 000000000000 -r a81364d9a67b neopixel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/neopixel.cpp Tue Mar 21 21:17:08 2017 +0000 @@ -0,0 +1,91 @@ +#include "mbed.h" +#include "neopixel.h" + +NeoPixelOut::NeoPixelOut(PinName pin) : DigitalOut(pin) +{ + normalize = false; + global_scale = 1.0f; +} + +// The timing should be approximately 800ns/300ns, 300ns/800ns +void NeoPixelOut::byte(register uint32_t byte) +{ + for (int i = 0; i < 8; i++) { + gpio_write(&gpio, 1); + + // duty cycle determines bit value + if (byte & 0x80) { + // one + for(int j = 0; j < 6; j++) __nop(); + + gpio_write(&gpio, 0); + for(int j = 0; j < 2; j++) __nop(); + } + else { + // zero + for(int j = 0; j < 2; j++) __nop(); + + gpio_write(&gpio, 0); + for(int j = 0; j < 5; j++) __nop(); + } + + byte = byte << 1; // shift to next bit + } + +} + +void NeoPixelOut::send(Pixel *colors, uint32_t count, bool flipwait) +{ + // Disable interrupts in the critical section + __disable_irq(); + + Pixel* rgb; + float fr,fg,fb; + for (int i = 0; i < count; i++) { + rgb = colors++; + fr = (int)rgb->r; + fg = (int)rgb->g; + fb = (int)rgb->b; + + if (normalize) { + float scale = 255.0f/(fr+fg+fb); + fr *= scale; + fg *= scale; + fb *= scale; + } + + fr *= global_scale; + fg *= global_scale; + fb *= global_scale; + + if (fr > 255) fr = 255; + if (fg > 255) fg = 255; + if (fb > 255) fb = 255; + if (fr < 0) fr = 0; + if (fg < 0) fg = 0; + if (fb < 0) fb = 0; + + // Black magic to fix distorted timing + #ifdef __HAL_FLASH_INSTRUCTION_CACHE_DISABLE + __HAL_FLASH_INSTRUCTION_CACHE_DISABLE(); + #endif + + byte((int)fg); + byte((int)fr); + byte((int)fb); + + #ifdef __HAL_FLASH_INSTRUCTION_CACHE_ENABLE + __HAL_FLASH_INSTRUCTION_CACHE_ENABLE(); + #endif + } + + __enable_irq(); + + if (flipwait) flip(); +} + + +void NeoPixelOut::flip(void) +{ + wait_us(50); +}
diff -r 000000000000 -r a81364d9a67b neopixel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/neopixel.h Tue Mar 21 21:17:08 2017 +0000 @@ -0,0 +1,69 @@ +#ifndef NEOPIXEL_H +#define NEOPIXEL_H +#include "mbed.h" + +/* +// Example + +NeoPixelOut npx(D12); + +int main() { + wait(0.2); // wait for HSE to stabilize + + npx.global_scale = 1.0f; // Adjust brightness + npx.normalize = true; // Equalize brightness to make r + g + b = 255 + + Pixel strip[6]; + strip[0].hex = 0xFF0000; + strip[1].hex = 0xFFFF00; + strip[2].hex = 0x00FF00; + strip[3].hex = 0x00FFFF; + strip[4].hex = 0x0000FF; + strip[5].hex = 0xFF00FF; + + npx.send(strip, 6); + + while(1); +} +*/ + + + +/** + * @brief Struct for easy manipulation of RGB colors. + * + * Set components in the xrgb.r (etc.) and you will get + * the hex in xrgb.num. + */ +union Pixel { + /** Struct for access to individual color components */ + struct __attribute__((packed)) { + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t a; // unused + }; + + /** RGB color as a single uint32_t */ + uint32_t hex; +}; + + +class NeoPixelOut : DigitalOut { +private: + void byte(uint32_t b); + +public: + bool normalize; + float global_scale; + + NeoPixelOut(PinName pin); + + void send(Pixel *colors, uint32_t count, bool flipwait=true); + + /** Wait long enough to make the colors show up */ + void flip(void); +}; + + +#endif /* NEOPIXEL_H */ \ No newline at end of file