]> www.fi.muni.cz Git - bike-lights.git/commitdiff
lights.c split into more modules
authorJan "Yenya" Kasprzak <kas@fi.muni.cz>
Tue, 28 Aug 2012 13:25:01 +0000 (15:25 +0200)
committerJan "Yenya" Kasprzak <kas@fi.muni.cz>
Tue, 28 Aug 2012 14:58:19 +0000 (16:58 +0200)
Makefile
adc.c [new file with mode: 0644]
lights.c [deleted file]
lights.h [new file with mode: 0644]
logging.c [new file with mode: 0644]
main.c [new file with mode: 0644]
pattern.c [new file with mode: 0644]
pwm.c [new file with mode: 0644]
pwmled.c [new file with mode: 0644]
tmr.c [new file with mode: 0644]

index 99b97938e5db89f661382c25d8152eae3f1bb0e0..989bffbd60e46e740416fc9e7a0292828ecf0f43 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,15 @@
 
-
 PROGRAM=lights
-SRC=lights.c
+SRC=main.c logging.c adc.c pwm.c tmr.c pwmled.c
 OBJ=$(SRC:.c=.o)
 
+
 MCU=attiny861a
 # AVRDUDE_MCU=$(MCU)
 AVRDUDE_MCU=attiny861
 AVRDUDE_PROGRAMMER=usbasp
 
-CFLAGS=-Os -mmcu=$(MCU)
+CFLAGS=-Wall -Os -mmcu=$(MCU) -DUSE_LOGGING=1 -DF_CPU=1000000UL
 LDFLAGS=
 AVRDUDE_FLAGS= -p$(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER)
 
@@ -38,7 +38,6 @@ dump_eeprom:
 objdump: $(PROGRAM).elf
        $(OBJDUMP) --disassemble $<
 
-
 .PRECIOUS : $(OBJ) $(PROGRAM).elf
 
 %.hex: %.elf
@@ -51,10 +50,10 @@ objdump: $(PROGRAM).elf
 %.elf: $(OBJ)
        $(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
 
-%.o: %.c
+%.o: %.c lights.h Makefile
        $(CC) -c $(CFLAGS) $< -o $@
 
-%.s: %.c
+%.s: %.c lights.h Makefile
        $(CC) -S -c $(CFLAGS) $< -o $@
 
 %.o: %.S
diff --git a/adc.c b/adc.c
new file mode 100644 (file)
index 0000000..a37e958
--- /dev/null
+++ b/adc.c
@@ -0,0 +1,79 @@
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#include "lights.h"
+
+static unsigned char adc_mux[] = { // pwmleds should be first
+       // 0: pwmled 0: 1.1V, ADC3 (PA4), single-ended
+       _BV(REFS1) | _BV(MUX1) | _BV(MUX0),
+       // 1: pwmled 1: 1.1V, ADC0,1 (PA0,1), gain 1 or 8
+       _BV(REFS1) | _BV(MUX3) | _BV(MUX2),
+       // 2: pwmled 2: 1.1V, ADC2,1 (PA2,1), gain 1 or 8
+       _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0),
+       // 3: ambient light: 1.1V, ADC4 (PA5), single-ended
+       _BV(REFS1) | _BV(MUX2),
+       // 4: batt voltage: 1.1V, ADC5 (PA6), single-ended
+       _BV(REFS1) | _BV(MUX2) | _BV(MUX0),
+};
+
+#define LAST_ADC (sizeof(adc_mux)/sizeof(char))
+volatile static unsigned char current_adc = LAST_ADC;
+
+static void start_next_adc()
+{
+       while (current_adc > 0) {
+               --current_adc;
+
+               // test if current_adc should be measured
+               if (current_adc < N_PWMLEDS && pwmled_is_on(current_adc))
+                       goto found;
+               // TODO ambient light, battery sense, etc.
+       }
+
+       // all ADCs have been handled
+       current_adc = LAST_ADC;
+       return;
+found:
+       // ADCSRB |= _BV(GSEL); // gain 8 or 32
+       ADMUX = adc_mux[current_adc]; // set up mux, start one-shot conversion
+       ADCSRA |= _BV(ADSC);
+}
+
+void init_adc()
+{
+       ADCSRA = _BV(ADEN)                      // enable
+               | _BV(ADPS1) | _BV(ADPS0)       // CLK/8 = 125 kHz
+               // | _BV(ADPS2)                 // CLK/16 = 62.5 kHz
+               ;
+       ADCSRB |= _BV(GSEL); // gain 8 or 32
+
+       // Disable digital input on all bits used by ADC
+       DIDR0 = _BV(ADC0D) | _BV(ADC1D) | _BV(ADC2D) | _BV(ADC3D)
+               | _BV(ADC4D) | _BV(ADC5D);
+
+       ADCSRA |= _BV(ADSC);
+
+       /* Do first conversion and drop the result */
+       while ((ADCSRA & _BV(ADIF)) == 0)
+               ;
+       ADCSRA |= _BV(ADIF); // clear the IRQ flag
+
+       ADCSRA |= _BV(ADIE); // enable IRQ
+}
+
+ISR(ADC_vect) { // IRQ handler
+       uint16_t adcval = ADCW;
+
+       if (current_adc < N_PWMLEDS)
+               pwmled_adc(current_adc, adcval);
+       // TODO ambient light, battery sense, etc.
+       
+       start_next_adc();
+}
+
+void timer_start_adcs()
+{
+       if (current_adc == LAST_ADC) // Don't start if in progress
+               start_next_adc();
+}
+
diff --git a/lights.c b/lights.c
deleted file mode 100644 (file)
index d05fa1c..0000000
--- a/lights.c
+++ /dev/null
@@ -1,273 +0,0 @@
-#include <avr/io.h>
-#include <avr/eeprom.h>
-#include <util/delay.h>
-#include <avr/sleep.h>
-#include <avr/interrupt.h>
-
-volatile uint16_t adcval;
-unsigned char led_is_on = 0;
-volatile unsigned char adccount = 0;
-
-typedef struct {
-       unsigned char pwmval, expected;
-} led_level_t;
-
-led_level_t led_modes[2] = {
-       { 0x50, 0x38 },
-       { 0x38, 0x04 },
-};
-
-unsigned char led_mode = 0;
-unsigned char led_mode_changed = 0;
-
-static void inline led_on()
-{
-        DDRB |= _BV( PB5 );
-       PORTA |=  _BV( PA0 );
-       led_is_on = 1;
-}
-
-static void inline led_off()
-{
-       led_is_on = 0;
-        DDRB &= ~_BV( PB5 );
-       PORTA &=~  _BV( PA0 );
-//        ADCSRA &= ~(_BV(ADIE) | _BV(ADIF));
-}
-
-/* ------------ Logging/Debugging ----------- */
-
-unsigned char debug_state EEMEM;
-
-static void inline debug_setstate(unsigned char val)
-{
-       eeprom_write_byte(&debug_state, val);
-}
-
-#define LOG_BUFFER 64
-uint16_t log_buffer[LOG_BUFFER] EEMEM;
-unsigned char log_buffer_count;
-uint16_t log_buffer2[LOG_BUFFER];
-volatile unsigned char stop = 0;
-
-static void inline init_log()
-{
-       debug_setstate(1);
-       log_buffer_count = 0;
-}
-
-static void log_word(uint16_t word) {
-       if (log_buffer_count >= LOG_BUFFER)
-               return;
-       
-       // eeprom_write_word(&log_buffer[log_buffer_count], word);
-       log_buffer2[log_buffer_count] = word;
-       log_buffer_count++;
-
-       if (log_buffer_count == LOG_BUFFER) {
-               unsigned char i;
-               for (i=0; i < LOG_BUFFER; i++) {
-                       eeprom_write_word(&log_buffer[i],
-                               log_buffer2[i]);
-               }
-               debug_setstate(0x42);
-       }
-}
-
-/* ------------ Timer ----------- */
-
-volatile uint16_t jiffies = 0;
-
-static void inline init_tmr()
-{
-       TCCR0A = _BV(WGM00);
-       TCCR0B = _BV(CS02) | _BV(CS00); // 1 kHz
-       OCR0A = 12; // 100 Hz
-       TIMSK |= _BV(OCIE0A);
-       DDRA |= _BV( PA0 );
-
-       jiffies = 0;
-}
-
-static void inline tmr_handler()
-{
-       unsigned char c = jiffies & 0x0F;
-       unsigned char c1 = jiffies & 0x7F;
-
-       ++jiffies;
-
-#if 0
-       if (c == 1)
-               led_on();
-
-       if (c == 9)
-               led_off();
-#endif
-
-       if (c == 0x02 || c == 0x08)
-               led_on();
-               
-       if (c == 0x05 || c == 0x0b)
-               led_off();
-
-#if 1
-       if (c1 == 0x10) {
-               led_mode = 0;
-               led_mode_changed = 1;
-               OCR1D = led_modes[led_mode].pwmval;
-       } else if (c1 == 0x70) {
-               led_mode = 1;
-               led_mode_changed = 1;
-               OCR1D = led_modes[led_mode].pwmval;
-       }
-#endif
-
-       ADCSRA |= _BV(ADSC);
-#if 0
-       if (led_is_on && adcval != 0xFFEE) {
-               adcval = 0xFFEE;
-
-               ADCSRA |= _BV(ADIF) | _BV(ADIE) | _BV(ADSC);
-       }
-#endif
-}
-
-ISR(TIMER0_COMPA_vect)
-{
-       tmr_handler();
-}
-
-/* ------------ PWM ----------- */
-
-static void inline init_pwm()
-{
-       /* Async clock */
-       PLLCSR = _BV(LSM) | _BV(PLLE);
-       _delay_ms(1);
-       while (PLLCSR & _BV(PLOCK) == 0)
-               ;
-       PLLCSR |= _BV(PCKE);
-
-       TCCR1C = _BV(COM1D0) | _BV(COM1D1) | _BV(PWM1D);
-       TCCR1A = _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1) | _BV(PWM1A) | _BV(PWM1B);
-       // TCCR1B = 0x80| _BV(CS13) | _BV(CS11);
-       TCCR1B = _BV(7)                         // PWM1X: PWM inversion mode
-               | _BV(CS10)                     // no clock prescaling
-               ;
-       OCR1C = 0xFF;                           // TOP value
-       OCR1D = OCR1B = OCR1A = 0;
-       // OCR1D = 0;
-       DDRB |= _BV( PB5 );
-       PORTB &= ~(_BV( PB5 ) | _BV( PB1 ) | _BV( PB3 ));
-
-       // led_off();
-       // TIMSK |= _BV(TOIE1);
-}
-
-#if 0
-static void inline pwm_handler()
-{
-       // TIMSK &= ~_BV(TOIE1);
-       // OCR1D = pwmval;
-}
-
-ISR(TIMER1_OVF_vect)
-{
-       pwm_handler();
-}
-#endif
-
-/* ------------ A/D Converter ----------- */
-
-static void inline init_adc()
-{
-       ADCSRA = _BV(ADEN)                      // enable
-               | _BV(ADPS1) | _BV(ADPS0)       // CLK/8 = 125 kHz
-               // | _BV(ADPS2)                 // CLK/16 = 62.5 kHz
-               ;
-       ADMUX = _BV(REFS1)                      // 1.1V internal reference
-               | _BV(MUX4)|_BV(MUX3)   // port ADC5-6, gain 1
-               ;
-       // ADCSRB = _BV(REFS2); 
-       ADCSRB |= _BV(GSEL); 
-       // Disable digital input on all bits used by ADC
-       DIDR0 = _BV(ADC5D)|_BV(ADC6D) | _BV(AREFD);
-       ADCSRA |= _BV(ADIE);
-}
-
-static void inline adc_handler()
-{
-       uint16_t new_pwm = led_modes[led_mode].pwmval;
-       uint16_t old_pwm = new_pwm;
-       uint16_t adc_exp = led_modes[led_mode].expected;
-
-       adcval = ADCW;
-       adccount++;
-
-       // log_word(((adcval & 0x3FC) << 6) | pwmval);
-
-       if (!led_is_on)
-               return;
-
-       // ADCSRA &= ~(_BV(ADIE) | _BV(ADIF));
-
-       if (led_mode_changed) {
-               led_mode_changed = 0;
-               goto set_pwm;
-       }
-
-       log_word(((adcval & 0xFF) << 8) | old_pwm);
-
-       if (2*adcval > 5*adc_exp) { // >2.5x expected, lower significantly
-               new_pwm = 2*old_pwm/3;
-       } else if (3*adcval > 4*adc_exp) { // >1.33x expected, lower a bit
-               new_pwm = old_pwm - 1;
-       } else if (4*adcval < 3*adc_exp) { // 0.75x expected, raise a bit
-               new_pwm = old_pwm + 1;
-       }
-
-       if (new_pwm > 0x60) { // odpojeno?
-               new_pwm = 0x60;
-       }
-       if (new_pwm < 2) { // zkrat?
-               new_pwm = 2;
-       }
-
-set_pwm:
-       if (new_pwm != old_pwm) {
-               led_modes[led_mode].pwmval = new_pwm;
-               OCR1D = new_pwm;
-       }
-       // ADCSRA |= _BV(ADSC);
-}
-
-ISR(ADC_vect)
-{
-       adc_handler();
-}
-
-int main(void)
-{
-       _delay_ms(1500);
-       init_log();
-
-       init_pwm();
-       init_adc();
-       init_tmr();
-
-       led_on();
-       debug_setstate(3);
-
-       sei();
-       while (1)
-               ; // sleep_mode();
-
-#if 0
-       while (1) {
-               PORTA |=  _BV( PA0 );
-               _delay_ms(200);
-               PORTA &=~ _BV( PA0 );
-               _delay_ms(200);
-       }
-#endif
-}
diff --git a/lights.h b/lights.h
new file mode 100644 (file)
index 0000000..d6e0cbd
--- /dev/null
+++ b/lights.h
@@ -0,0 +1,45 @@
+#ifndef LIGHTS_H__
+#define LIGHTS_H__ 1
+
+#define N_LEDS 5
+#define N_PWMLEDS 3
+#define N_PWMLED_MODES 4
+
+/* logging.c */
+#ifdef USE_LOGGING
+void log_set_state(unsigned char val);
+void log_init();
+void log_flush();
+void log_byte(unsigned char byte);
+void log_word(uint16_t word);
+#else
+void inline log_set_state(unsigned char val) { }
+void inline log_init() { }
+void inline log_flush() { }
+void inline log_byte(unsigned char byte) { }
+void inline log_word(uint16_t word) { }
+#endif
+
+/* adc.c */
+void init_adc();
+void timer_start_adcs();
+
+/* pwm.c */
+void init_pwm();
+void pwm_on(unsigned char n);
+void pwm_off(unsigned char n);
+void pwm_set(unsigned char n, unsigned char stride);
+
+/* tmr.c */
+extern volatile uint16_t jiffies;
+void init_tmr();
+
+/* pwmled.c */
+void pwmled_init();
+void pwmled_adc(unsigned char n, uint16_t adcval);
+void pwmled_set_mode(unsigned char n, unsigned char mode);
+unsigned char pwmled_is_on(unsigned char n);
+
+
+#endif /* !LIGHTS_H__ */
+
diff --git a/logging.c b/logging.c
new file mode 100644 (file)
index 0000000..04f9515
--- /dev/null
+++ b/logging.c
@@ -0,0 +1,53 @@
+#ifdef USE_LOGGING
+
+#include <avr/io.h>
+#include <avr/eeprom.h>
+
+#include "lights.h"
+
+#define LOG_BUFFER 128
+static unsigned char log_state EEMEM;
+static unsigned char log_buffer_ee[LOG_BUFFER] EEMEM;
+static unsigned char log_buffer_count;
+static unsigned char log_buffer[LOG_BUFFER];
+
+void log_set_state(unsigned char val)
+{
+       eeprom_write_byte(&log_state, val);
+}
+
+void log_init()
+{
+       log_set_state(1);
+       log_buffer_count = 0;
+}
+
+void log_byte(unsigned char byte) {
+       if (log_buffer_count >= LOG_BUFFER)
+               return;
+       
+       // eeprom_write_word(&log_buffer[log_buffer_count], word);
+       log_buffer[log_buffer_count++] = byte;
+
+       if (log_buffer_count == LOG_BUFFER)
+               log_flush();
+}
+
+void log_word(uint16_t word) {
+       log_byte(word & 0xFF);
+       log_byte(word >> 8);
+}
+
+void log_flush() {
+       unsigned char i;
+
+       log_buffer_count = LOG_BUFFER;
+       for (i=0; i < LOG_BUFFER; i++) {
+               eeprom_write_byte(&log_buffer_ee[i],
+                       log_buffer[i]);
+       }
+       log_set_state(0x42);
+}
+
+#endif
+
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..324b884
--- /dev/null
+++ b/main.c
@@ -0,0 +1,36 @@
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+
+#include "lights.h"
+
+int main(void)
+{
+       _delay_ms(1500);
+       log_init();
+
+       init_pwm();
+       init_adc();
+       init_tmr();
+
+       pwmled_init();
+
+       log_set_state(3);
+
+       sei();
+#if 1
+       while (1)
+               ; // sleep_mode();
+#endif
+
+#if 0
+       DDRB |= _BV(PB2);
+       while (1) {
+               PORTB |=  _BV( PB2 );
+               _delay_ms(200);
+               PORTB &=~ _BV( PB2 );
+               _delay_ms(200);
+       }
+#endif
+}
diff --git a/pattern.c b/pattern.c
new file mode 100644 (file)
index 0000000..386f9df
--- /dev/null
+++ b/pattern.c
@@ -0,0 +1,54 @@
+#include "lights.h"
+
+typedef struct {
+       unsigned char mode: 3;
+       unsigned char duration: 5;
+} pattern_t;
+
+static unsigned char led_counters[N_LEDS];
+static pattern_t *led_patterns[N_LEDS];
+
+#define PATTERN_END { 0, 0 }
+pattern_t off_pattern[] = {
+       { 0, 5 },
+       PATTERN_END
+};
+
+pattern_t blink_pattern[] = {
+       { 1, 5 },
+       { 0, 5 },
+       PATTERN_END
+};
+
+void pattern_init()
+{
+       unsigned char i;
+
+       for (i = 0; i < N_LEDS; i++) {
+               led_counters[i] = 0;
+               led_patterns[i] = off_pattern;
+       }
+}
+
+void patterns_next_tick()
+{
+       unsigned char i;
+
+       for (i = 0; i < N_LEDS; i++) {
+               if (led_counters[i] == 0) {
+                       led_patterns[i]++;
+                       if (led_patterns[i]->duration == 0) { // END
+                               led_patterns[i] = pattern_select(i);
+                       }
+                       led_counters[i] = led_patterns[i]->duration;
+                       if (led_patterns[i]->mode == 0) {
+                               led_off(i);
+                       } else {
+                               led_set_level(i, led_patterns[i]->mode - 1);
+                       }
+               }
+
+               led_counters[i]--;
+       }
+}
+
diff --git a/pwm.c b/pwm.c
new file mode 100644 (file)
index 0000000..af4e4fa
--- /dev/null
+++ b/pwm.c
@@ -0,0 +1,72 @@
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+#include "lights.h"
+
+void init_pwm()
+{
+       /* Async clock */
+       PLLCSR = _BV(LSM) | _BV(PLLE);
+
+       /* Synchronize to the phase lock */
+       _delay_ms(1);
+       while ((PLLCSR & _BV(PLOCK)) == 0)
+               ;
+       PLLCSR |= _BV(PCKE);
+
+       TCCR1C = _BV(COM1D0) | _BV(COM1D1) | _BV(PWM1D);
+       TCCR1A = _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1) | _BV(PWM1A) | _BV(PWM1B);
+       TCCR1B = _BV(7)                         // PWM1X: PWM inversion mode
+               | _BV(CS10)                     // no clock prescaling
+               ;
+       OCR1C = 0xFF;                           // TOP value
+
+       OCR1D = OCR1B = OCR1A = 0;              // initial stride is 0
+
+       DDRB  &= ~(_BV( PB1 ) | _BV( PB3 ) | _BV( PB5 )); // tristate it
+       PORTB &= ~(_BV( PB1 ) | _BV( PB3 ) | _BV( PB5 )); // set to zero
+}
+
+void pwm_on(unsigned char n)
+{
+       switch (n) {
+       case 0: DDRB |= _BV(PB1); break;
+       case 1: DDRB |= _BV(PB3); break;
+       case 2: DDRB |= _BV(PB5); break;
+       }
+}
+
+void pwm_off(unsigned char n)
+{
+       switch (n) {
+       case 0: DDRB &= ~_BV(PB1); break;
+       case 1: DDRB &= ~_BV(PB3); break;
+       case 2: DDRB &= ~_BV(PB5); break;
+       }
+}
+
+void pwm_set(unsigned char n, unsigned char stride)
+{
+       switch (n) {
+       case 0: OCR1A = stride; break;
+       case 1: OCR1B = stride; break;
+       case 2: OCR1D = stride; break;
+       }
+}
+
+#if 0
+static void inline pwm_handler()
+{
+       OCR1A = pwmval[0];
+       OCR1B = pwmval[1];
+       OCR1D = pwmval[2];
+       TIMSK &= ~_BV(TOIE1);
+}
+
+ISR(TIMER1_OVF_vect)
+{
+       pwm_handler();
+}
+#endif
+
diff --git a/pwmled.c b/pwmled.c
new file mode 100644 (file)
index 0000000..4861793
--- /dev/null
+++ b/pwmled.c
@@ -0,0 +1,180 @@
+#include <avr/io.h>
+
+#include "lights.h"
+
+static unsigned char pwm_vals[N_PWMLEDS*N_PWMLED_MODES];
+static unsigned char adc_vals[N_PWMLEDS*N_PWMLED_MODES] = {
+       /* pwmled0 */
+       0x04, 0x14, 0x24, 0x38,
+       /* pwmled1 */
+       0x04, 0x14, 0x24, 0x38,
+       /* pwmled2 */
+       0x04, 0x14, 0x24, 0x38,
+};
+
+static unsigned char pwmled_state[N_PWMLEDS];
+#define ST_DISABLED 0
+#define ST_PROBING  1
+#define ST_OFF      2
+#define ST_ON       3
+
+static unsigned char pwmled_mode[N_PWMLEDS];
+
+static unsigned char pwm_probes[N_PWMLEDS];
+
+static void start_probing(unsigned char n)
+{
+       pwmled_state[n] = ST_PROBING;
+       pwm_set(n, 0);
+       pwm_on(n);
+       pwm_probes[n] = 0;
+}
+
+void pwmled_init()
+{
+       unsigned char i;
+
+       for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++)
+               pwm_vals[i] = 0;
+
+       for (i = 0; i < N_PWMLEDS; i++) {
+               start_probing(i);
+       }
+}
+
+unsigned char pwmled_is_on(unsigned char n)
+{
+       unsigned char st = pwmled_state[n];
+       if (st == ST_PROBING || st == ST_ON)
+               return 1;
+       else
+               return 0;
+}
+
+static void inline probing_adc(unsigned char n, uint16_t adcval)
+{
+       unsigned char need_bigger = 0, i;
+       unsigned char *pwm_p = &pwm_vals[n*N_PWMLED_MODES];
+       unsigned char *adc_p = &adc_vals[n*N_PWMLED_MODES];
+       unsigned char pwm = pwm_probes[n];
+
+#if 0
+       log_byte(n);
+       log_byte(0xF4);
+       log_word(adcval);
+#endif
+
+#if 0
+       if (pwm == 0 && adcval > 0) { // non-zero voltage with zero PWM?
+               pwmled_state[n] = ST_DISABLED;
+               log_byte(n);
+               log_byte(0xF0);
+               log_word(adcval);
+               return;
+       }
+#endif
+
+       for (i = 0; i < N_PWMLED_MODES; i++, pwm_p++, adc_p++) {
+               uint16_t adc = *adc_p;
+               if (adc >= adcval) {
+                       *pwm_p = pwm;
+                       need_bigger = 1;
+               }
+       }
+
+#if 0
+       if ((n == 1 && pwm > 0x35) || adcval != 0) {
+               log_byte(n);
+               log_byte(0xF3);
+               log_byte(pwm);
+               log_word(adcval);
+       }
+#endif
+
+       if (!need_bigger) { // successfully probed
+               pwm_off(n);
+               // pwm_set(n, 0);
+               pwmled_state[n] = ST_OFF;
+               log_byte(n);
+               log_byte(0xF1);
+
+               return;
+       }
+
+       if (pwm >= 0x60) { // over the maximum!
+               pwmled_state[n] = ST_DISABLED;
+               log_byte(n);
+               log_byte(0xF2);
+               pwm_off(n);
+               // pwm_set(n, 0);
+               return;
+       }
+
+       // try to increase
+       pwm++;
+       pwm_probes[n] = pwm;
+       pwm_set(n, pwm);
+}
+
+// Feedback loop
+static void inline on_adc(unsigned char n, uint16_t adcval)
+{
+#if 0
+       uint16_t new_pwm = led_modes[led_mode].pwmval;
+       uint16_t old_pwm = new_pwm;
+       uint16_t adc_exp = led_modes[led_mode].expected;
+
+       log_word(((adcval & 0xFF) << 8) | old_pwm);
+
+       if (2*adcval > 5*adc_exp) { // >2.5x expected, lower significantly
+               new_pwm = 2*old_pwm/3;
+       } else if (3*adcval > 4*adc_exp) { // >1.33x expected, lower a bit
+               new_pwm = old_pwm - 1;
+       } else if (4*adcval < 3*adc_exp) { // 0.75x expected, raise a bit
+               new_pwm = old_pwm + 1;
+       }
+
+       if (new_pwm > 0x60) { // odpojeno?
+               new_pwm = 0x60;
+       }
+       if (new_pwm < 2) { // zkrat?
+               new_pwm = 2;
+       }
+
+set_pwm:
+       if (new_pwm != old_pwm) {
+               led_modes[led_mode].pwmval = new_pwm;
+               OCR1D = new_pwm;
+       }
+       // ADCSRA |= _BV(ADSC);
+#endif
+}
+
+void pwmled_adc(unsigned char n, uint16_t adcval)
+{
+       unsigned char i, probing;
+       switch (pwmled_state[n]) {
+       case ST_PROBING:
+               probing_adc(n, adcval);
+
+               probing = 0;
+               for (i = 0; i < N_PWMLEDS; i++)
+                       if (pwmled_state[i] == ST_PROBING)
+                               probing = 1;
+
+               if (!probing) {
+                       for (i = 0; i < N_PWMLEDS; i++)
+                               log_byte(pwmled_state[i]);
+                               
+                       for (i = 0; i < N_PWMLEDS*N_PWMLED_MODES; i++)
+                               log_byte(pwm_vals[i]);
+                       log_flush();
+               }
+               
+               return;
+       case ST_ON:
+               on_adc(n, adcval);
+               return;
+       // WTF am I doing in this function then?
+       }
+}
diff --git a/tmr.c b/tmr.c
new file mode 100644 (file)
index 0000000..b8fb502
--- /dev/null
+++ b/tmr.c
@@ -0,0 +1,25 @@
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#include "lights.h"
+
+volatile uint16_t jiffies;
+
+void init_tmr()
+{
+       TCCR0A = _BV(WGM00);
+       TCCR0B = _BV(CS02) | _BV(CS00); // CLK/1024 = 1 kHz
+       OCR0A = 10; // 100 Hz
+       TIMSK |= _BV(OCIE0A);
+
+       jiffies = 0;
+}
+
+ISR(TIMER0_COMPA_vect)
+{
+       ++jiffies;
+
+       // patterns_next_tick();
+       timer_start_adcs();
+}
+