Skip to content
Snippets Groups Projects
leds.c 7.07 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include "gpio.h"
    
    #include "portexpander.h"
    
    fleur's avatar
    fleur committed
    #include "max32665.h"
    
    #include <stdint.h>
    #include <string.h>
    
    fleur's avatar
    fleur committed
    #include <stdbool.h>
    #include <stdio.h>
    
    #define NUM_LEDS 15
    
    fleur's avatar
    fleur committed
    #define DEFAULT_DIM_TOP 1
    #define DEFAULT_DIM_BOTTOM 8
    #define MAX_DIM 8
    
    
    static const gpio_cfg_t rgb_dat_pin = {
    	PORT_1, PIN_14, GPIO_FUNC_OUT, GPIO_PAD_NONE
    };
    static const gpio_cfg_t rgb_clk_pin = {
    	PORT_1, PIN_15, GPIO_FUNC_OUT, GPIO_PAD_NONE
    };
    
    fleur's avatar
    fleur committed
    static uint8_t leds[NUM_LEDS][3];
    static uint8_t gamma_table[3][256];
    static uint8_t active_groups;
    static uint8_t bottom_dim; //index 11-14
    static uint8_t top_dim;    //index 0-10
    static bool powersave;
    static long powerup_wait_cycles = 500;
    
    
    /***** Functions *****/
    // *****************************************************************************
    
    typedef struct {
    
    	float r; // a fraction between 0 and 1
    	float g; // a fraction between 0 and 1
    	float b; // a fraction between 0 and 1
    
    } rgb;
    
    typedef struct {
    
    	float h; // angle in degrees
    	float s; // a fraction between 0 and 1
    	float v; // a fraction between 0 and 1
    
    } hsv;
    
    #if 0
    static hsv rgb2hsv(rgb in)
    {
        hsv         out;
        float      min, max, delta;
    
        min = in.r < in.g ? in.r : in.g;
        min = min  < in.b ? min  : in.b;
    
        max = in.r > in.g ? in.r : in.g;
        max = max  > in.b ? max  : in.b;
    
        out.v = max;                                // v
        delta = max - min;
        if (delta < 0.00001)
        {
            out.s = 0;
            out.h = 0; // undefined, maybe nan?
            return out;
        }
        if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
            out.s = (delta / max);                  // s
        } else {
            // if max is 0, then r = g = b = 0
            // s = 0, h is undefined
            out.s = 0.0;
            out.h = 0.0;                            // its now undefined
            return out;
        }
        if( in.r >= max )                           // > is bogus, just keeps compilor happy
            out.h = ( in.g - in.b ) / delta;        // between yellow & magenta
        else
        if( in.g >= max )
            out.h = 2.0 + ( in.b - in.r ) / delta;  // between cyan & yellow
        else
            out.h = 4.0 + ( in.r - in.g ) / delta;  // between magenta & cyan
    
        out.h *= 60.0;                              // degrees
    
        if( out.h < 0.0 )
            out.h += 360.0;
    
        return out;
    }
    #endif
    
    static rgb hsv2rgb(hsv in)
    {
    
    	float hh, p, q, t, ff;
    	long i;
    	rgb out;
    
    	if (in.s <= 0.0) { // < is bogus, just shuts up warnings
    		out.r = in.v;
    		out.g = in.v;
    		out.b = in.v;
    		return out;
    	}
    	hh = in.h;
    	if (hh >= 360.0)
    		hh = 0.0;
    	hh /= 60.0;
    	i  = (long)hh;
    	ff = hh - i;
    	p  = in.v * (1.0 - in.s);
    	q  = in.v * (1.0 - (in.s * ff));
    	t  = in.v * (1.0 - (in.s * (1.0 - ff)));
    
    	switch (i) {
    	case 0:
    		out.r = in.v;
    		out.g = t;
    		out.b = p;
    		break;
    	case 1:
    		out.r = q;
    		out.g = in.v;
    		out.b = p;
    		break;
    	case 2:
    		out.r = p;
    		out.g = in.v;
    		out.b = t;
    		break;
    
    	case 3:
    		out.r = p;
    		out.g = q;
    		out.b = in.v;
    		break;
    	case 4:
    		out.r = t;
    		out.g = p;
    		out.b = in.v;
    		break;
    	case 5:
    	default:
    		out.r = in.v;
    		out.g = p;
    		out.b = q;
    		break;
    	}
    	return out;
    
    }
    
    static void shift(uint8_t data)
    {
    
    	for (int i = 0; i < 8; i++) {
    		if (data & 0x80) {
    			GPIO_OutSet(&rgb_dat_pin);
    		} else {
    			GPIO_OutClr(&rgb_dat_pin);
    		}
    		GPIO_OutClr(&rgb_clk_pin);
    		//TMR_Delay(MXC_TMR0, MSEC(1), 0);
    		GPIO_OutSet(&rgb_clk_pin);
    		//TMR_Delay(MXC_TMR0, MSEC(1), 0);
    		data <<= 1;
    	}
    
    }
    
    static void leds_start(void)
    {
    
    	shift(0x00);
    	shift(0x00);
    	shift(0x00);
    	shift(0x00);
    
    }
    
    static void leds_shift(uint8_t r, uint8_t g, uint8_t b, uint8_t dim)
    {
    
    	shift(0xE0 | (dim & 0x1F));
    	shift(b);
    	shift(g);
    	shift(r);
    
    }
    
    static void leds_stop(void)
    {
    
    	shift(0xFF);
    	shift(0xFF);
    	shift(0xFF);
    	shift(0xFF);
    
    fleur's avatar
    fleur committed
    static uint8_t led_to_dim_value(uint8_t led)
    {
    	return (led < 11) ? top_dim : bottom_dim;
    }
    
    void leds_set_dim_top(uint8_t value)
    {
    	top_dim = (value > MAX_DIM) ? MAX_DIM : value;
    }
    
    void leds_set_dim_bottom(uint8_t value)
    
    fleur's avatar
    fleur committed
    	bottom_dim = (value > MAX_DIM) ? MAX_DIM : value;
    
    fleur's avatar
    fleur committed
    void leds_prep(uint8_t led, uint8_t r, uint8_t g, uint8_t b)
    
    	leds[led][0] = r;
    	leds[led][1] = g;
    	leds[led][2] = b;
    
    fleur's avatar
    fleur committed
    #if 0
    //don't use, is buggy
    void leds_set_autodim(uint8_t led, uint8_t r, uint8_t g, uint8_t b)
    {
        if(led==NUM_LEDS){
            leds_set(led,r,g,b);
            return;
        }
        leds[led][3] = max(r,max(g,b));
        float gain = (float)255/leds[led][3]; //might cause rounding->overflow errors might debug later idk~
        leds[led][0] = (uint8_t)(r*gain);
        leds[led][1] = (uint8_t)(g*gain);
        leds[led][2] = (uint8_t)(b*gain);
    }
    #endif
    
    void leds_prep_hsv(uint8_t led, float h, float s, float v)
    
    	hsv in       = { h, s, v };
    	rgb out      = hsv2rgb(in);
    	leds[led][0] = out.r * 255;
    	leds[led][1] = out.g * 255;
    	leds[led][2] = out.b * 255;
    
    fleur's avatar
    fleur committed
    static bool is_led_on(uint8_t led) // scheduled to be on after next update
    {
    	if (!led_to_dim_value(led)) {
    		return false;
    	}
    	for (int i = 0; i < 3; i++) {
    		if (leds[led][i] != 0) {
    			return true;
    		}
    	}
    	return false;
    }
    
    static uint8_t led_to_group(uint8_t led)
    {
    	if (led == 14) {
    		return 1;
    	} else if (led >= 11) {
    		return 2;
    	}
    	return 3;
    }
    
    static uint8_t
    check_privilege(void) //returns number of hierarchical groups with power
    {
    	for (int i = 0; i < NUM_LEDS; i++) {
    		if (is_led_on(i)) {
    			return led_to_group(i);
    		}
    	}
    	return 0;
    }
    
    static uint8_t power_pin_conversion(uint8_t group)
    {
    	if (group == 2) {
    		return 1;
    	}
    	if (group == 1) {
    		return 2;
    	}
    	return 0;
    }
    
    static void power_all(void)
    {
    	for (int i = 0; i < 3; i++) {
    		portexpander_prep(i, 0);
    	}
    	portexpander_update();
    }
    
    void leds_update_power(void)
    {
    	if (!powersave) {
    		return;
    	}
    	uint8_t new_groups =
    		check_privilege(); //there must be a prettier way to do this but meh
    	if (new_groups == active_groups) {
    		return;
    	}
    	for (int i = 0; i < 3; i++) {
    		if (i < new_groups) {
    			portexpander_prep(power_pin_conversion(i), 0);
    		} else {
    			portexpander_prep(power_pin_conversion(i), 1);
    		}
    	}
    	portexpander_update();
    	if (active_groups < new_groups) {
    		for (int i = 0; i < powerup_wait_cycles; i++) {
    			__NOP();
    		}
    	}
    	active_groups = new_groups;
    }
    
    void leds_powersave(bool eco)
    {
    	powersave = eco;
    	if (!powersave) {
    		power_all();
    	} else {
    		leds_update_power();
    	}
    }
    
    
    void leds_update(void)
    {
    
    	leds_start();
    	for (int i = NUM_LEDS - 1; i >= 0; i--) {
    
    fleur's avatar
    fleur committed
    		leds_shift(
    			gamma_table[0][leds[i][0]],
    			gamma_table[1][leds[i][1]],
    			gamma_table[2][leds[i][2]],
    			led_to_dim_value(i)
    		);
    
    	}
    	leds_stop();
    
    fleur's avatar
    fleur committed
    void leds_flashlight(bool power)
    {
    	portexpander_set(7, (power) ? 0 : 1);
    }
    
    void leds_set_gamma_table(uint8_t rgb_channel, uint8_t table[256])
    {
    	for (int i = 0; i < 256; i++) {
    		gamma_table[rgb_channel][i] = table[i];
    	}
    }
    
    
    void leds_init(void)
    {
    
    	GPIO_Config(&rgb_clk_pin);
    	GPIO_Config(&rgb_dat_pin);
    
    	GPIO_OutSet(&rgb_clk_pin);
    	GPIO_OutClr(&rgb_dat_pin);
    
    	memset(leds, 0, sizeof(leds));
    
    fleur's avatar
    fleur committed
    	powersave  = TRUE;
    	top_dim    = DEFAULT_DIM_TOP;
    	bottom_dim = DEFAULT_DIM_BOTTOM;
    
    	for (int i = 0; i < NUM_LEDS; i++) {
    
    fleur's avatar
    fleur committed
    		for (int j = 0; j < 3; j++) {
    			leds[i][j] = 0;
    		}
    
    fleur's avatar
    fleur committed
    	for (int i = 0; i < 256; i++) {
    		for (int j = 0; j < 3; j++) {
    			int k             = (i * (1 + i) + 255) >> 8;
    			gamma_table[j][i] = (k * (k + 1) + 255) >> 8;
    		}
    
    	}
    	leds_update();