/* ATtiny13 1.2MHz */ #define F_CPU 1200000L #include #include #include #include #define ADDRESS_1 8 #define ADDRESS_2 16 #define PANEL_DDR DDRB #define PANEL_PORT_OUT PORTB #define PANEL_L_PIN PB4 #define PANEL_R_PIN PB2 #define PANEL_C_PIN PB3 volatile uint8_t sequence, adc, frequency, mode, frequency_old, mode_old, duration, duty, c, d, i; volatile uint16_t timer_overflow_count, timer_overflow_target=384, delay; ISR(TIM0_OVF_vect) { // Read buttons state in interrupt, using ADC if(++timer_overflow_count > timer_overflow_target) { // check if any button is pressed timer_overflow_target = 384; // by default every 82ms timer_overflow_count = 0; ADCSRA |= (1 << ADSC); // start analog-to-digital conversion while (ADCSRA & (1 << ADSC)); // wait for it to finish adc = ADCH; /* If two buttons are pressed at the same time, reset to default settings */ if(adc < 150) { frequency=1; mode=2; eeprom_update_byte((uint8_t*)ADDRESS_1, frequency); // update EEPROM with defaults eeprom_update_byte((uint8_t*)ADDRESS_2, mode); PANEL_PORT_OUT &= ~( (1 << PANEL_L_PIN) | (1 << PANEL_R_PIN) | (1 << PANEL_C_PIN) ); // and make LC panels transparent _delay_ms(1000); // for one second return; } /* If SWITCH_1 is pressed, change device's occlusions frequency */ if (adc < 176) { frequency ++; timer_overflow_target = 768; // wait 164ms after a button is pressed return; } if (frequency > 4) { frequency = 0; } /* If SWITCH_0 is pressed, change device's eyes occlusion rate */ /* (with eye is occluded longer in each main while loop execution) */ if (adc < 210) { mode++; timer_overflow_target=768; // wait 164ms after a button is pressed return; } if (mode > 4) { mode = 0; } } } void advance_sequence(void) { // This function drives outputs switch(sequence) { case 0: PANEL_PORT_OUT |= (1 << PANEL_L_PIN); PANEL_PORT_OUT &= ~( (1 << PANEL_R_PIN) | (1 << PANEL_C_PIN) ); break; case 1: PANEL_PORT_OUT |= (1 << PANEL_L_PIN) | (1 << PANEL_C_PIN); PANEL_PORT_OUT &= ~(1 << PANEL_R_PIN); break; case 2: PANEL_PORT_OUT |= (1 << PANEL_R_PIN) | (1 << PANEL_C_PIN); PANEL_PORT_OUT &= ~(1 << PANEL_L_PIN); break; case 3: PANEL_PORT_OUT |= (1 << PANEL_R_PIN); PANEL_PORT_OUT &= ~( (1 << PANEL_L_PIN) | (1 << PANEL_C_PIN) ); break; } sequence++; if(sequence>3) sequence=0; } int main(void) { /* PB1 and PB0 as outputs, PWM; LC panels driver pins as outputs*/ PANEL_DDR |= (1 << PB1) | (1 << PB0) | (1 << PANEL_L_PIN) | (1 << PANEL_R_PIN) | (1 << PANEL_C_PIN); /* Use 8-bit timer/counter to generate two 4687.5 Hz waves that drive voltage multiplier (charge pump)*/ TCCR0B |= (1 << CS00); // 8-bit timer/counter prescaler to 1 TCCR0A |= (1 << WGM01) | (1 << WGM00); // set to 'Fast PWM' mode TCCR0A |= (1 << COM0A1) | (1 << COM0A0) | (1 << COM0B1); // OC0A output is in antiphase to OC0B OCR0A = 127; // duty 50% OCR0B = 127; TIMSK0 |= (1 << TOIE0); // enable timer overflow interrupt sei(); /* Set up ADC, default ADC input is PB5/ADC0 */ ADMUX |= (1 << ADLAR); //left adjust result ADCSRA |= (1 << ADPS2) | (1 << ADEN); // prescaler to 16, enable ADC /* Read previous state of device */ frequency = eeprom_read_byte((uint8_t*)ADDRESS_1); mode = eeprom_read_byte((uint8_t*)ADDRESS_2); if (frequency > 4) { frequency = 1; } // if EEPROM stored settings outside of range, set defaults if (mode > 4) { mode = 2; } while (1) { switch(frequency) { // calculate how much time every main while loop execution will take case 0: duration=40; break; case 1: duration=20; break; case 2: duration=13; break; case 3: duration=10; break; case 4: duration=8; break; } d = d + duration; if(d > 100) { // Check approximately every second. // duration is in ms, in one cycle of main while loop duration is executed 10 times, // hence 100 "milliseconds" in a second if((frequency == frequency_old) && (mode == mode_old)) c++; // check if settings are permanent else c= 0; if(c==10) { // after approximately 10 seconds of settings being permanent eeprom_update_byte((uint8_t*)ADDRESS_1, frequency); //start updating EEPROM with stable setting eeprom_update_byte((uint8_t*)ADDRESS_2, mode); c = 0; } frequency_old = frequency; mode_old = mode; d = 0; } duty = mode * 2; // if you multiply duty value by 10 and add 10, // you get percentage of time left eye is occluded in each main while loop execution switch(frequency) { // determines how much time each tenth of main while loop cycle will take case 0: delay=12000; break; case 1: delay=6000; break; case 2: delay=4000; break; case 3: delay=3000; break; case 4: delay=2400; break; } /* Puts 10 blocks of _delay_loop_2 to properly */ /* synchronize eyes occlusions. First blocks */ /* occlude left eye, next blocks occlude right */ /* eye. Each execution of _delay_loop_2 takes */ /* four clock cycles. */ /* -------------------------------------------- */ advance_sequence(); for(i = 0; i <= (duty); i++) _delay_loop_2(delay); advance_sequence(); for(i = 0; i < (9-duty); i++) _delay_loop_2(delay); /* -------------------------------------------- */ } }