/* ANY-LICENSE V1.0 ---------------- You can use these files under any license approved by the Open Source Initiative, preferrably one of the popular licenses, as long as the license you choose is compatible to the dependencies of these files. See http://www.opensource.org/licenses/ for a list of approved licenses. Author: Martin Furter Project: Copyright: 2016 ---------------------------------------------------------------------------- BIG LED STRING ============== Control strings of LED balls containing a WS2811. A remote control with 4 buttons controls the following functions: button press function 1 short next pattern/program 2 short prev pattern/program 3 short function 1 up (speed / color) 4 short function 1 down (speed / color) 1 long on 2 long off 3 long brighter 4 long darker Patterns and Functions: - All white. - All same adjustable color. - All same color slowly changing, speed is adjustable. - Rainbow, static, saturation is adjustable. - Rainbow, slowly changing, speed is adjustable. - Static repeated patterns. - Wave? Wishlist: - save configuration to eeprom (automatically when nothing has been changed anymore for a few seconds) ---------------------------------------------------------------------------- Getting the source code: mkdir -p $HOME/electronics/projects/tins_avr_lib mkdir -p $HOME/electronics/projects/avr_leds cd $HOME/electronics/projects/ svn co http://repos.borg.ch/projects/tins_avr_lib/trunk tins_avr_lib svn co http://repos.borg.ch/projects/avr_leds/trunk avr_leds mkdir $HOME/bin ln -s $HOME/electronics/projects/tins_avr_lib/tmk/tmk.py $HOME/bin/tmk.py Compiling: cd $HOME/electronics/projects/ # edit tmk.xml and check the mcu target tmk.py big-led-string Burning onto the MCU: STK500: avrdude -c stk500 -P /dev/ttyUSB0 -p atmega168 -U flash:w:big-led-string.srec USBASP: avrdude -c usbasp -p atmega168 -U flash:w:big-led-string.srec Terminal mode, f.ex. for setting the fuses: avrdude -c stk500 -P /dev/ttyUSB0 -p atmega168 -t write lfuse 0 0xF7 ---------------------------------------------------------------------------- https://cpldcpu.wordpress.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/ WS2812B WS2812 Cycles tHIin 0 <563ns <500ns <3 tHIin 1 >625ns >563ns >3 tPERin >1063ns >875ns >5 tRESET >9us >11us => SPI 4bit/pulse ~300ns/bit at 18.432MHz (54.25us/clock) SPI divider=8 => 0 pulse: 1000 1=434ns 0=1302ns period=1736ns => 1 pulse: 1110 1=1302ns 0=434ns period=1736ns at 18.432MHz (54.25us/clock) USART in SPI mode divider=6 NEEDS INVERTER !!! => 0 pulse: 0111 1=325ns 0=976ns period=1302ns => 1 pulse: 0001 1=976ns 0=325ns period=1302ns RGB data: R7..R0 G7..G0 B7..B0 PINS Atmega168: DIL SMD Functions I/O Usage 5 1 PD3 OC2B O DEBUG_PIN_4 6 2 PD4 I Receiver D0 8 3 GND - 7 4 VCC - 5 GND - 6 VCC - 9 7 PB6 XTAL1 I crystal 10 8 PB7 XTAL2 O crystal 11 9 PD5 OC0B I Receiver D1 12 10 PD6 OC0A I Receiver D2 13 11 PD7 AIN1 I Receiver D3 14 12 PB0 ICP1 O DEBUG_PIN_5 15 13 PB1 OC1A O DEBUG_PIN_6 16 14 PB2 OC1B O DEBUG_PIN_7 17 15 PB3 MOSI/OC2A I ISP 18 16 PB4 MISO O ISP 19 17 PB5 SCK I ISP 20 18 AVCC - - 19 ADC6 - 21 20 AREF - 22 21 GND - - 22 ADC7 - 23 23 PC0 ADC0 O DEBUG_PIN_0 24 24 PC1 ADC1 O DEBUG_PIN_1 25 25 PC2 ADC2 O DEBUG_PIN_2 26 26 PC3 ADC3 O Multiplexer A0 27 27 PC4 ADC4 O Multiplexer A1 28 28 PC5 ADC5 O Multiplexer A2 1 29 PC6 /RESET I ISP 2 30 PD0 RXD - 3 31 PD1 TXD O WS2811 data output 4 32 PD2 O DEBUG_PIN_3 */ /*----------------------------------------------------------------------------*/ // {{{ includes #define F_CPU 18432000 #include #include #include #include #include #include #include #include #include #include // }}} includes /*----------------------------------------------------------------------------*/ // {{{ configuration #define LIST_SIZE(x) ( sizeof(x) / sizeof(x[0]) ) // debugging pins #define DEBUG_PIN_0 C,0 #define DEBUG_PIN_1 C,1 #define DEBUG_PIN_2 C,2 #define DEBUG_PIN_3 D,2 #define DEBUG_PIN_4 D,3 #define DEBUG_PIN_5 B,0 #define DEBUG_PIN_6 B,1 #define DEBUG_PIN_7 B,2 #define DEBUG_PIN_012(x) PORTC = (PORTC & 0xF8) | ((x) & 0x07) // set timer overflow rate to 100Hz #define TIMER1_RATE 100 // minimum number of ticks to count as short press #define BUTTON_SHORT_MIN 5 // minimum number of ticks to count as long press #define BUTTON_LONG_MIN 70 // number of ticks until retrigger of long press #define BUTTON_LONG_RETRIGGER (255-20) // 0x00 for high active buttons, 0x0F for low active #ifndef LOW_ACTIVE #define LOW_ACTIVE 0x00 #endif // 0: normal order of buttons, 1: reverse order of buttons #ifndef SWAPPED_INPUTS #define SWAPPED_INPUTS 1 #endif // order of the color components: 0=GRB ("standard"), 1=RGB (R and G swapped) #ifndef COLORS_RGB #define COLORS_RGB 1 #endif #if COLORS_RGB == 0 #define WS2811_SEND(r,g,b) ws2811_send(r,g,b) #else // COLORS_RGB != 0 #define WS2811_SEND(r,g,b) ws2811_send(g,r,b) #endif // minimum brightness (must be at least 4) // lower values than 64 lead to strange behaviour. why? +++++ #define BRIGHTNESS_MIN 64 // delay between changing persistend data and saving it to eeprom #define SAVE_DELAY 30 // default speed for color wheels after initializing #define DEFAULT_SPEED 3 typedef struct { uint8_t length; } strip_t; const strip_t strips[] PROGMEM = { { 40 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }; typedef struct { uint8_t r; uint8_t g; uint8_t b; } rgb_t; const rgb_t static_colors[] PROGMEM = { { 0xFF, 0x00, 0xFF }, { 0x00, 0x00, 0xFF }, { 0x00, 0xFF, 0xFF }, { 0x00, 0xFF, 0x00 }, { 0xFF, 0xFF, 0x00 }, { 0xFF, 0x00, 0x00 }, }; #define STATIC_COLOR_COUNT LIST_SIZE( static_colors ) #if 0 const rgb_t temperated[] PROGMEM = { { 0xFF, 0xDF, 0xDF }, // { 0xFF, 0xE3, 0xDF }, // { 0xFF, 0xE7, 0xDF }, // { 0xFF, 0xEB, 0xDF }, { 0xFF, 0xEF, 0xDF }, // { 0xFF, 0xF3, 0xDF }, // { 0xFF, 0xF7, 0xDF }, // { 0xFF, 0xFB, 0xDF }, { 0xFF, 0xFF, 0xDF }, // { 0xFF, 0xFF, 0xE3 }, // { 0xFF, 0xFF, 0xE7 }, // { 0xFF, 0xFF, 0xEB }, { 0xFF, 0xFF, 0xEF }, // { 0xFF, 0xFF, 0xF3 }, // { 0xFF, 0xFF, 0xF7 }, // { 0xFF, 0xFF, 0xFB }, { 0xFF, 0xFF, 0xFF }, // { 0xFB, 0xFB, 0xFF }, // { 0xF7, 0xF7, 0xFF }, // { 0xF3, 0xF3, 0xFF }, { 0xEF, 0xEF, 0xFF }, // { 0xEB, 0xEB, 0xFF }, // { 0xE7, 0xE7, 0xFF }, // { 0xE3, 0xE3, 0xFF }, { 0xDF, 0xDF, 0xFF } }; #else const rgb_t temperated[] PROGMEM = { { 0xFF, 0x8F, 0x8F }, { 0xFF, 0xCF, 0x8F }, { 0xFF, 0xFF, 0x8F }, { 0xFF, 0xFF, 0xCF }, { 0xFF, 0xFF, 0xFF }, { 0xCF, 0xCF, 0xFF }, { 0x8F, 0x8F, 0xFF } }; #endif #define TEMPERATED_COUNT LIST_SIZE( temperated ) #define TEMPERATED_DEFAULT (TEMPERATED_COUNT/2) const rgb_t static_pattern_red[] PROGMEM = { { 0xFF, 0x00, 0xFF }, { 0xFF, 0x00, 0x00 }, { 0xFF, 0xBB, 0xBB }, { 0xFF, 0x88, 0x00 }, { 0xFF, 0xFF, 0x00 }, }; const rgb_t static_pattern_ch[] PROGMEM = { { 0xFF, 0x00, 0x00 }, { 0xFF, 0xFF, 0xFF } }; const rgb_t static_pattern_blue[] PROGMEM = { { 0x00, 0x00, 0xFF }, { 0xFF, 0xFF, 0xFF }, { 0x00, 0xFF, 0xF0 }, { 0x88, 0xFF, 0x00 }, { 0x00, 0xFF, 0x00 } }; typedef struct { const rgb_t* pattern; uint8_t length; } static_pattern_list_t; #define STATIC_PATTERN_LIST_ENRTY(x) { x, LIST_SIZE(x) } const static_pattern_list_t static_pattern_list[] PROGMEM = { STATIC_PATTERN_LIST_ENRTY( static_pattern_red ), STATIC_PATTERN_LIST_ENRTY( static_pattern_ch ), STATIC_PATTERN_LIST_ENRTY( static_pattern_blue ) }; #define STATIC_PATTERN_COUNT LIST_SIZE( static_pattern_list ) // }}} configuration /*----------------------------------------------------------------------------*/ // {{{ program list // program "none": generic "do nothing" functions void none_init(); void none_begin(); uint8_t none_param( uint8_t up ); void none_next(); // program "off" void off_update(); // program "all_white" void all_white_init(); void all_white_begin(); uint8_t all_white_param( uint8_t up ); void all_white_update(); typedef struct { uint8_t state; } all_white_t; // program "static_color" void static_color_init(); void static_color_begin(); uint8_t static_color_param( uint8_t up ); void static_color_update(); typedef struct { uint8_t state; } static_color_t; // program "color_wheel" void color_wheel_init(); void color_wheel_begin(); uint8_t color_wheel_param( uint8_t up ); void color_wheel_next(); void color_wheel_update(); typedef struct { uint8_t speed; } color_wheel_t; // program "static_rainbow" void static_rainbow_init(); uint8_t static_rainbow_param( uint8_t up ); void static_rainbow_update(); typedef struct { uint8_t saturation; } static_rainbow_t; // program "rainbow_wheel" void rainbow_wheel_init(); uint8_t rainbow_wheel_param( uint8_t up ); void rainbow_wheel_next(); void rainbow_wheel_update(); typedef struct { uint8_t speed; } rainbow_wheel_t; // program "static_pattern" void static_pattern_init(); uint8_t static_pattern_param( uint8_t up ); void static_pattern_update(); typedef struct { uint8_t pattern; } static_pattern_t; typedef struct { void (*init)(); void (*begin)(); uint8_t (*param)( uint8_t up ); void (*next)(); void (*update)(); } program_t; const program_t program_list[] PROGMEM = { { none_init, none_begin, none_param, none_next, off_update }, { all_white_init, all_white_begin, all_white_param, none_next, all_white_update }, { static_color_init, static_color_begin, static_color_param, none_next, static_color_update }, { color_wheel_init, color_wheel_begin, color_wheel_param, color_wheel_next, color_wheel_update }, { static_rainbow_init, none_begin, static_rainbow_param, none_next, static_rainbow_update }, { rainbow_wheel_init, color_wheel_begin, rainbow_wheel_param, rainbow_wheel_next, rainbow_wheel_update }, { static_pattern_init, none_begin, static_pattern_param, none_next, static_pattern_update } }; #define PROGRAM_COUNT 7 #define STRUCT_VERSION 1 typedef struct { uint8_t version; uint8_t length; all_white_t all_white; static_color_t static_color; color_wheel_t color_wheel; static_rainbow_t static_rainbow; rainbow_wheel_t rainbow_wheel; static_pattern_t static_pattern; uint16_t checksum; } program_data_t; // }}} program list /*----------------------------------------------------------------------------*/ // {{{ state struct { // flag which is set by timer overflow interrupt volatile uint8_t timer_overflow; // centi seconds uint8_t centisecs; // seconds uint8_t secs; // brightness of the LEDs uint8_t brightness; // counters for short button press uint8_t button_short[4]; // counters for long button press uint8_t button_long[4]; // flag which is set when the LEDs are switched on uint8_t switched_on; // flag which is set when the (new) program has to be initialized uint8_t begin_program; // flag which is set when the LEDs have to be updated uint8_t update_leds; // delay counter for the next function of the programs uint16_t counter; // current program program_t program; // current program number uint8_t program_nr; // stuct containing program dependent persistent data program_data_t program_data; // time at which data has to be saved to eeprom or 0xFF if nothing changed uint8_t data_save_secs; // stuct containing program dependent temporary data struct { uint8_t state; rgb_t color; rgb_t color0; rgb_t color1; } tmp; } state; // }}} state /*----------------------------------------------------------------------------*/ // {{{ timer 1 overflow ISR( TIMER1_OVF_vect ) { state.timer_overflow = true; } // }}} timer 1 overflow /*----------------------------------------------------------------------------*/ // {{{ calc_data_sum uint16_t calc_data_checksum() { xorshift16_init( 0x1234 ); for( uint8_t i=0; i> 8; } } // }}} add_brightness /*----------------------------------------------------------------------------*/ // {{{ select_strip void select_strip( uint8_t strip ) { PORTC = (PORTC & 0x38) | ((strip & 7) << 3); } // }}} select_strip /*----------------------------------------------------------------------------*/ // {{{ leds_set_all void leds_set_all( uint8_t r, uint8_t g, uint8_t b ) { uint8_t strip; uint8_t length; uint8_t i; for( strip=0; strip<8; strip++ ) { length = pgm_read_byte( &strips[strip].length ); if( length == 0 ) { break; } select_strip( strip ); for( i=0; istate = TEMPERATED_DEFAULT; } void all_white_begin() { all_white_t* const data = &state.program_data.all_white; memcpy_P( &state.tmp.color, &temperated[data->state], sizeof(state.tmp.color) ); } uint8_t all_white_param( uint8_t up ) { all_white_t* const data = &state.program_data.all_white; uint8_t changed = false; if( up ) { if( data->state < (TEMPERATED_COUNT-1) ) { data->state++; changed = true; } } else { if( data->state > 0 ) { data->state--; changed = true; } } if( changed ) { memcpy_P( &state.tmp.color, &temperated[data->state], sizeof(state.tmp.color) ); state.update_leds = true; } return changed; } void all_white_update() { leds_set_all_br( state.tmp.color.r, state.tmp.color.g, state.tmp.color.b ); } // }}} program "all_white" /*----------------------------------------------------------------------------*/ // {{{ program "static_color" void static_color_init() { static_color_t* const data = &state.program_data.static_color; data->state = 0; } void static_color_begin() { static_color_t* const data = &state.program_data.static_color; memcpy_P( &state.tmp.color, &static_colors[data->state], sizeof(state.tmp.color) ); } uint8_t static_color_param( uint8_t up ) { static_color_t* const data = &state.program_data.static_color; if( up ) { data->state++; if( data->state >= STATIC_COLOR_COUNT ) { data->state = 0; } } else { data->state--; if( data->state >= STATIC_COLOR_COUNT ) { data->state = STATIC_COLOR_COUNT - 1; } } memcpy_P( &state.tmp.color, &static_colors[data->state], sizeof(state.tmp.color) ); state.update_leds = true; return true; } void static_color_update() { leds_set_all_br( state.tmp.color.r, state.tmp.color.g, state.tmp.color.b ); } // }}} program "static_color" /*----------------------------------------------------------------------------*/ // {{{ program "color_wheel" void color_wheel_init() { color_wheel_t* const data = &state.program_data.color_wheel; data->speed = DEFAULT_SPEED; } void color_wheel_set_counter( uint8_t speed ) { state.counter = ((uint16_t)1) << speed; } void color_wheel_do_begin( uint8_t speed ) { state.tmp.state = 0; state.tmp.color.r = 0xFF; state.tmp.color.g = 0x00; state.tmp.color.b = 0x00; color_wheel_set_counter( speed ); } void color_wheel_begin() { color_wheel_t* const data = &state.program_data.color_wheel; color_wheel_do_begin( data->speed ); } uint8_t color_wheel_change_speed( uint8_t up, uint8_t* pspeed ) { uint8_t speed = *pspeed; if( up ) { if( speed >= 14 ) { return false; } speed++; } else { if( speed <= 0 ) { return false; } speed--; } *pspeed = speed; color_wheel_set_counter( speed ); return true; } uint8_t color_wheel_param( uint8_t up ) { color_wheel_t* const data = &state.program_data.color_wheel; return color_wheel_change_speed( up, &data->speed ); } void color_wheel_calc_next( uint8_t speed ) { // calculate next color switch( state.tmp.state ) { case 0: state.tmp.color.g++; if( state.tmp.color.g == 0xFF ) { state.tmp.state++; } break; case 1: state.tmp.color.r--; if( state.tmp.color.r == 0x00 ) { state.tmp.state++; } break; case 2: state.tmp.color.b++; if( state.tmp.color.b == 0xFF ) { state.tmp.state++; } break; case 3: state.tmp.color.g--; if( state.tmp.color.g == 0x00 ) { state.tmp.state++; } break; case 4: state.tmp.color.r++; if( state.tmp.color.r == 0xFF ) { state.tmp.state++; } break; case 5: state.tmp.color.b--; if( state.tmp.color.b == 0x00 ) { state.tmp.state = 0; } break; default: state.tmp.state = 0; state.tmp.color.r = 0xFF; state.tmp.color.g = 0x00; state.tmp.color.b = 0x00; break; } state.update_leds = true; color_wheel_set_counter( speed ); } void color_wheel_next() { color_wheel_t* const data = &state.program_data.color_wheel; color_wheel_calc_next( data->speed ); } void color_wheel_update() { leds_set_all_br( state.tmp.color.r, state.tmp.color.g, state.tmp.color.b ); } // }}} program "color_wheel" /*----------------------------------------------------------------------------*/ // {{{ program "static_rainbow" void static_rainbow_init() { static_rainbow_t* const data = &state.program_data.static_rainbow; data->saturation = 8; } uint8_t static_rainbow_param( uint8_t up ) { static_rainbow_t* const data = &state.program_data.static_rainbow; if( data->saturation > 8 ) { data->saturation = 0; } if( up ) { if( data->saturation < 8 ) { data->saturation++; } } else { if( data->saturation > 0 ) { data->saturation--; } } state.update_leds = true; return true; } void static_rainbow_update_strips( uint8_t j ) { uint8_t strip; uint8_t length; uint8_t i; for( strip=0; strip<8; strip++ ) { length = pgm_read_byte( &strips[strip].length ); if( length == 0 ) { break; } select_strip( strip ); for( i=state.tmp.state; length>0; length--,i++ ) { uint8_t r, g, b; switch( j ) { default: r = state.tmp.color0.r; g = state.tmp.color0.g; b = state.tmp.color0.b; j = 6; break; case 1: r = state.tmp.color1.r; g = state.tmp.color1.g; b = state.tmp.color1.b; break; case 2: r = state.tmp.color0.g; g = state.tmp.color0.b; b = state.tmp.color0.r; break; case 3: r = state.tmp.color1.g; g = state.tmp.color1.b; b = state.tmp.color1.r; break; case 4: r = state.tmp.color0.b; g = state.tmp.color0.r; b = state.tmp.color0.g; break; case 5: r = state.tmp.color1.b; g = state.tmp.color1.r; b = state.tmp.color1.g; break; } j--; WS2811_SEND( r, g, b ); } ws2811_wait(); } } void static_rainbow_update() { static_rainbow_t* const data = &state.program_data.static_rainbow; uint8_t satval = data->saturation; if( satval > 8 ) { satval = 0; } else { satval = 31 * (8 - satval); } state.tmp.color0.r = add_brightness( 0xFF ); state.tmp.color0.g = add_brightness( satval ); state.tmp.color0.b = add_brightness( satval ); state.tmp.color1.r = add_brightness( 0xFF ); state.tmp.color1.g = add_brightness( 0xFF ); state.tmp.color1.b = add_brightness( satval ); static_rainbow_update_strips( 0 ); } // }}} program "static_rainbow" /*----------------------------------------------------------------------------*/ // {{{ program "rainbow_wheel" void rainbow_wheel_init() { rainbow_wheel_t* const data = &state.program_data.rainbow_wheel; data->speed = DEFAULT_SPEED; } void rainbow_wheel_begin() { rainbow_wheel_t* const data = &state.program_data.rainbow_wheel; color_wheel_do_begin( data->speed ); } uint8_t rainbow_wheel_param( uint8_t up ) { rainbow_wheel_t* const data = &state.program_data.rainbow_wheel; return color_wheel_change_speed( up, &data->speed ); } void rainbow_wheel_next() { rainbow_wheel_t* const data = &state.program_data.rainbow_wheel; color_wheel_calc_next( data->speed ); } void rainbow_wheel_update() { state.tmp.color0.r = add_brightness( state.tmp.color.r ); state.tmp.color0.g = add_brightness( state.tmp.color.g ); state.tmp.color0.b = add_brightness( state.tmp.color.b ); state.tmp.color1.b = add_brightness( ~state.tmp.color.r ); state.tmp.color1.r = add_brightness( ~state.tmp.color.g ); state.tmp.color1.g = add_brightness( ~state.tmp.color.b ); static_rainbow_update_strips( 0 ); } // }}} program "rainbow_wheel" /*----------------------------------------------------------------------------*/ // {{{ program "static_pattern" void static_pattern_init() { static_pattern_t* const data = &state.program_data.static_pattern; data->pattern = 0; } uint8_t static_pattern_param( uint8_t up ) { static_pattern_t* const data = &state.program_data.static_pattern; if( up ) { data->pattern++; if( data->pattern >= STATIC_PATTERN_COUNT ) { data->pattern = 0; } } else { data->pattern--; if( data->pattern >= STATIC_PATTERN_COUNT ) { data->pattern = STATIC_PATTERN_COUNT - 1; } } state.update_leds = true; return true; } void static_pattern_update() { static_pattern_t* const data = &state.program_data.static_pattern; static_pattern_list_t pattern; const rgb_t* led; uint8_t n; uint8_t strip; uint8_t length; memcpy_P( &pattern, &static_pattern_list[data->pattern], sizeof(pattern) ); led = pattern.pattern; n = pattern.length; for( strip=0; strip<8; strip++ ) { length = pgm_read_byte( &strips[strip].length ); if( length == 0 ) { break; } select_strip( strip ); while( length > 0 ) { length--; // WS2811 order is G R B #if COLORS_RGB == 0 ws2811_send_byte( add_brightness( pgm_read_byte( &led->g ) ) ); #endif ws2811_send_byte( add_brightness( pgm_read_byte( &led->r ) ) ); #if COLORS_RGB != 0 ws2811_send_byte( add_brightness( pgm_read_byte( &led->g ) ) ); #endif ws2811_send_byte( add_brightness( pgm_read_byte( &led->b ) ) ); n--; if( n == 0 ) { led = pattern.pattern; n = pattern.length; } else { led++; } } ws2811_wait(); } } // }}} program "static_pattern" /*----------------------------------------------------------------------------*/ // {{{ set_program void set_program( uint8_t program_nr ) { if( state.switched_on ) { if( program_nr < 1 ) { program_nr = PROGRAM_COUNT - 1; } else if( program_nr >= PROGRAM_COUNT ) { program_nr = 1; } state.program_nr = program_nr; } else { program_nr = 0; } memcpy_P( &state.program, &program_list[program_nr], sizeof(state.program) ); // DEBUG_PIN_012( ~program_nr ); state.begin_program = true; } // }}} set_program /*----------------------------------------------------------------------------*/ // {{{ short_button_press void short_button_press( uint8_t button, uint8_t* changed ) { if( !state.switched_on ) { return; } switch( button ) { case 0: // next pattern/program set_program( state.program_nr + 1 ); // ++++ changed? break; case 1: // prev pattern/program set_program( state.program_nr - 1 ); // ++++ changed? break; case 2: // function 1 up (speed / color) if( state.program.param( 1 ) ) { *changed = true; } break; case 3: // function 1 down (speed / color) if( state.program.param( 0 ) ) { *changed = true; } break; } } // }}} short_button_press /*----------------------------------------------------------------------------*/ // {{{ long_button_press uint8_t long_button_press( uint8_t button, uint8_t* changed ) { uint8_t counter; uint8_t brightness; counter = 255; switch( button ) { case 0: // switch on state.switched_on = true; set_program( state.program_nr ); break; case 1: // switch off state.switched_on = false; set_program( state.program_nr ); break; case 2: // brighter if( !state.switched_on ) { break; } brightness = state.brightness; if( brightness < 255 ) { brightness = (brightness * 146 + 96) >> 7; if( brightness < state.brightness ) { brightness = 255; } state.brightness = brightness; state.update_leds = true; } if( brightness < 255 ) { counter = BUTTON_LONG_RETRIGGER; } break; case 3: // darker if( !state.switched_on ) { break; } brightness = state.brightness; if( brightness > BRIGHTNESS_MIN ) { state.brightness = (brightness * 224) >> 8; state.update_leds = true; } if( brightness > BRIGHTNESS_MIN ) { counter = BUTTON_LONG_RETRIGGER; } break; } return counter; } // }}} long_button_press /*----------------------------------------------------------------------------*/ // {{{ read_buttons void read_buttons() { uint8_t pin; uint8_t counter; uint8_t button; uint8_t changed = false; pin = (PIND >> 4) ^ LOW_ACTIVE; #if SWAPPED_INPUTS != 0 pin = ((pin >> 2) & 3) | ((pin & 3) << 2); pin = ((pin >> 1) & 5) | ((pin & 5) << 1); #endif for( button=0; button<4; button++,pin>>=1 ) { counter = state.button_short[button]; if( pin & 0x01 ) { // button is pressed, counting up if( counter < BUTTON_SHORT_MIN ) { state.button_short[button] = counter + 1; } else { counter = state.button_long[button]; if( counter < 255 ) { counter++; if( counter == BUTTON_LONG_MIN || counter == 255 ) { counter = long_button_press( button, &changed ); } state.button_long[button] = counter; } } } else { // button is not pressed, counting down if( counter > 0 ) { counter--; state.button_short[button] = counter; if( counter == 0 ) { // short counter reached zero if( state.button_long[button] < BUTTON_LONG_MIN ) { // it was not a long press so process the short press short_button_press( button, &changed ); } // reset long counter state.button_long[button] = 0; } } } } if( changed ) { state.data_save_secs = state.secs + SAVE_DELAY; } } // }}} read_buttons /*----------------------------------------------------------------------------*/ // {{{ tick void tick() { // update time state.centisecs++; if( state.centisecs >= 100 ) { AVRPIN_TOGGLE( DEBUG_PIN_7 ); state.centisecs = 0; state.secs++; if( state.secs >= 60 ) { state.secs = 0; } } // read the buttons and execute commands if needed read_buttons(); // calculate next LED state if needed if( state.begin_program ) { state.begin_program = false; state.update_leds = true; state.counter = 0; state.program.begin(); } else if( state.counter > 0 ) { state.counter--; if( state.counter == 0 ) { state.program.next(); } } // update LEDs if needed if( state.update_leds ) { state.update_leds = false; state.program.update(); } // save data to eeprom if needed if( state.data_save_secs == state.secs ) { state.data_save_secs = 0xFF; save_data_to_eeprom(); } } // }}} tick /*----------------------------------------------------------------------------*/ // {{{ initialize void initialize( void ) { // initialize structures memset( &state, 0, sizeof(state) ); state.brightness = 255; state.switched_on = false; state.program_nr = 1; state.begin_program = true; state.data_save_secs = 0xFF; set_program( 1 ); // initialize outputs DDRB = 0x07; DDRC = 0x3F; DDRD = 0x0E; // initialize debug pins to high AVRPIN_SET( DEBUG_PIN_0 ); AVRPIN_SET( DEBUG_PIN_1 ); AVRPIN_SET( DEBUG_PIN_2 ); AVRPIN_SET( DEBUG_PIN_3 ); AVRPIN_SET( DEBUG_PIN_4 ); AVRPIN_SET( DEBUG_PIN_5 ); AVRPIN_SET( DEBUG_PIN_6 ); AVRPIN_SET( DEBUG_PIN_7 ); // load configuration if( !load_data_from_eeprom() ) { for( uint8_t i=0; i