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

Dependents:   test_led_rgb

Files at this revision

API Documentation at this revision

Comitter:
MightyPork
Date:
Tue Mar 21 21:17:08 2017 +0000
Commit message:
initial

Changed in this revision

colorspace.cpp Show annotated file Show diff for this revision Revisions of this file
colorspace.h Show annotated file Show diff for this revision Revisions of this file
neopixel.cpp Show annotated file Show diff for this revision Revisions of this file
neopixel.h Show annotated file Show diff for this revision Revisions of this file
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