/* Copyright (c) 2008 Michael Hanselmann * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. */ #include #include #include #include #include #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) /* Table calculation in Python: * gamma = 2.2 * values = [int(round(4096 * (i / 255.0) ** gamma)) for i in xrange(256)] * print ", ".join([str(i) for i in values]) */ /* Gamma 2.2 */ const uint16_t gamma_2_2[256] PROGMEM = { 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 11, 12, 14, 15, 17, 19, 21, 23, 25, 27, 29, 32, 34, 37, 40, 43, 46, 49, 52, 55, 59, 62, 66, 70, 73, 77, 82, 86, 90, 95, 99, 104, 109, 114, 119, 124, 129, 135, 140, 146, 152, 158, 164, 170, 176, 182, 189, 196, 202, 209, 216, 224, 231, 238, 246, 254, 261, 269, 277, 286, 294, 302, 311, 320, 329, 338, 347, 356, 365, 375, 385, 394, 404, 414, 424, 435, 445, 456, 467, 477, 489, 500, 511, 522, 534, 546, 557, 569, 582, 594, 606, 619, 631, 644, 657, 670, 684, 697, 710, 724, 738, 752, 766, 780, 795, 809, 824, 838, 853, 869, 884, 899, 915, 930, 946, 962, 978, 994, 1011, 1027, 1044, 1061, 1078, 1095, 1112, 1130, 1147, 1165, 1183, 1201, 1219, 1238, 1256, 1275, 1293, 1312, 1331, 1351, 1370, 1389, 1409, 1429, 1449, 1469, 1489, 1510, 1530, 1551, 1572, 1593, 1614, 1636, 1657, 1679, 1700, 1722, 1745, 1767, 1789, 1812, 1834, 1857, 1880, 1904, 1927, 1950, 1974, 1998, 2022, 2046, 2070, 2095, 2119, 2144, 2169, 2194, 2219, 2245, 2270, 2296, 2322, 2348, 2374, 2400, 2427, 2453, 2480, 2507, 2534, 2561, 2589, 2616, 2644, 2672, 2700, 2728, 2757, 2785, 2814, 2843, 2872, 2901, 2931, 2960, 2990, 3020, 3050, 3080, 3110, 3141, 3171, 3202, 3233, 3264, 3295, 3327, 3359, 3390, 3422, 3454, 3487, 3519, 3552, 3585, 3618, 3651, 3684, 3717, 3751, 3785, 3819, 3853, 3887, 3921, 3956, 3991, 4026, 4061, 4096 }; /* Gamma 0.8 */ const uint16_t gamma_0_8[256] PROGMEM = { 0, 49, 85, 117, 147, 176, 204, 231, 257, 282, 307, 331, 355, 379, 402, 425, 447, 469, 491, 513, 535, 556, 577, 598, 618, 639, 659, 680, 700, 720, 739, 759, 778, 798, 817, 836, 855, 874, 893, 912, 931, 949, 968, 986, 1004, 1023, 1041, 1059, 1077, 1095, 1113, 1130, 1148, 1166, 1183, 1201, 1218, 1235, 1253, 1270, 1287, 1304, 1321, 1338, 1355, 1372, 1389, 1406, 1423, 1439, 1456, 1473, 1489, 1506, 1522, 1539, 1555, 1572, 1588, 1604, 1620, 1636, 1653, 1669, 1685, 1701, 1717, 1733, 1749, 1765, 1780, 1796, 1812, 1828, 1843, 1859, 1875, 1890, 1906, 1921, 1937, 1952, 1968, 1983, 1999, 2014, 2029, 2045, 2060, 2075, 2090, 2106, 2121, 2136, 2151, 2166, 2181, 2196, 2211, 2226, 2241, 2256, 2271, 2286, 2301, 2316, 2330, 2345, 2360, 2375, 2389, 2404, 2419, 2433, 2448, 2463, 2477, 2492, 2506, 2521, 2535, 2550, 2564, 2579, 2593, 2607, 2622, 2636, 2651, 2665, 2679, 2693, 2708, 2722, 2736, 2750, 2765, 2779, 2793, 2807, 2821, 2835, 2849, 2863, 2877, 2891, 2905, 2919, 2933, 2947, 2961, 2975, 2989, 3003, 3017, 3031, 3045, 3058, 3072, 3086, 3100, 3114, 3127, 3141, 3155, 3169, 3182, 3196, 3210, 3223, 3237, 3251, 3264, 3278, 3291, 3305, 3318, 3332, 3345, 3359, 3372, 3386, 3399, 3413, 3426, 3440, 3453, 3467, 3480, 3493, 3507, 3520, 3533, 3547, 3560, 3573, 3587, 3600, 3613, 3626, 3640, 3653, 3666, 3679, 3693, 3706, 3719, 3732, 3745, 3758, 3771, 3785, 3798, 3811, 3824, 3837, 3850, 3863, 3876, 3889, 3902, 3915, 3928, 3941, 3954, 3967, 3980, 3993, 4006, 4019, 4032, 4045, 4057, 4070, 4083, 4096 }; volatile uint16_t current_red; volatile uint16_t current_green; volatile uint16_t current_blue; /* * Input range: 0-65535 * Output range: 0-4096 */ uint16_t approx(const uint16_t *tbl, uint16_t value) { uint8_t idx = value >> 8; uint16_t first = pgm_read_word(tbl + idx); if (idx == 255) { /* Last entry */ return first; } uint16_t second = pgm_read_word(tbl + idx + 1); uint16_t diff = second - first; uint8_t value_offset = value & 0xff; return first + ((diff * value_offset) >> 8); } void hsv_to_rgb(uint16_t h, uint16_t s, uint16_t v) { uint16_t r = 0, g = 0, b = 0; if (s == 0) { r = g = b = v; } else { uint32_t i, f; uint16_t p, q, t; i = (((uint32_t)h) * 6) & 0xFF0000; f = ((((uint32_t)h) * 6) - i)>>8; i >>= 16; p = 65535 - s * 256; q = 65535 - s * f; t = 65535 - s * (256 - f); switch (i) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } } r = approx(gamma_0_8, r); g = approx(gamma_2_2, g); b = approx(gamma_2_2, b); /* Hack: The red chip of my LEDs is very weak, hence correcting the * other colours a bit. */ g = (g * 2) >> 3; b = (b * 2) >> 3; cli(); current_red = r; current_blue = b; current_green = g; sei(); } ISR(TIMER0_COMPA_vect) { static uint16_t val = 0; static uint8_t cnt = 0; wdt_reset(); if (current_red > 0 && val <= current_red) { PORTB &= ~_BV(PB0); } else { PORTB |= _BV(PB0); } if (current_green > 0 && val <= current_green) { PORTB &= ~_BV(PB1); } else { PORTB |= _BV(PB1); } if (current_blue > 0 && val <= current_blue) { PORTB &= ~_BV(PB2); } else { PORTB |= _BV(PB2); } /* Cheat a bit to get more speed because lower values are more sensible * to changes than larger ones. */ if (cnt < 20) { ++cnt; } val += cnt; if (val > 4096) { cnt = 0; val = 0; } } int main() { wdt_disable(); current_red = 0; current_green = 0; current_blue = 0; DDRB |= _BV(PB2) | _BV(PB1) | _BV(PB0); PORTB |= _BV(PB0) | _BV(PB1) | _BV(PB0); DDRB &= ~_BV(PB3); PORTB |= _BV(PB3); ADMUX = _BV(MUX1); ADCSRA = _BV(ADEN); DIDR0 = _BV(ADC0D) | _BV(ADC1D) | _BV(ADC2D); _delay_ms(1); /* Setup timer */ TCCR0A = _BV(WGM01); TCCR0B = _BV(CS01); /* Prescaler clk/8 */ TCNT0 = 0; OCR0A = 4; TIMSK = _BV(OCIE0A); /* Setup 15ms watchdog */ wdt_reset(); wdt_enable(WDTO_15MS); sei(); uint16_t step = 0; uint16_t adc_value = 0; uint8_t mode = 0; uint8_t loop = 0; for (;;) { if (loop++ == 10) { /* Read potentiometer */ ADCSRA |= _BV(ADSC); while (ADCSRA & _BV(ADSC)); adc_value = ADCL; adc_value += (ADCH & 0b11) << 8; /* Read switch */ mode = PINB & _BV(PB3); loop = 0; } if (mode) { /* Auto mode with speed selection */ step += adc_value >> 1; } else { /* Colour selection mode */ uint16_t new_step = adc_value << 6; /* Change smoothly */ if (new_step < step) { step -= (step - new_step) >> 4; } else { step += (new_step - step) >> 4; } } hsv_to_rgb(step, 255, 0xffff); } } /* vim: set sw=2 sts=2 et foldmethod=marker : */