--- /dev/null
+
+PROGRAM=lights
+# SRC=version.c main.c logging.c pwm.c adc.c pwmled.c pattern.c buttons.c \
+# control.c battery.c wdt.c
+SRC=main.c logging.c pwm.c
+OBJ=$(SRC:.c=.o)
+
+
+MCU=attiny25
+AVRDUDE_MCU=$(MCU)
+AVRDUDE_PROGRAMMER=usbasp
+
+CFLAGS=-Wall -Os -mmcu=$(MCU) -DUSE_LOGGING=1 -DF_CPU=1000000UL -std=gnu99
+LDFLAGS=
+AVRDUDE_FLAGS= -p$(AVRDUDE_MCU) -c $(AVRDUDE_PROGRAMMER) -B10
+
+FORMAT=ihex
+
+CC=avr-gcc
+OBJCOPY=avr-objcopy
+OBJDUMP=avr-objdump
+AVRDUDE=avrdude
+
+all: $(PROGRAM).hex $(PROGRAM).eep
+
+program: $(PROGRAM).hex $(PROGRAM).eep
+ $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i -U eeprom:w:$(PROGRAM).eep:i
+
+program_flash: $(PROGRAM).hex
+ $(AVRDUDE) $(AVRDUDE_FLAGS) -U flash:w:$(PROGRAM).hex:i
+
+program_eeprom: $(PROGRAM).eep
+ $(AVRDUDE) $(AVRDUDE_FLAGS) eeprom:w:$(PROGRAM).eep:i
+
+dump_eeprom:
+ $(AVRDUDE) $(AVRDUDE_FLAGS) -U eeprom:r:eeprom.raw:r
+ od -tx1 eeprom.raw
+
+objdump: $(PROGRAM).elf
+ $(OBJDUMP) --disassemble $<
+
+.PRECIOUS : $(OBJ) $(PROGRAM).elf
+
+%.hex: %.elf
+ $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
+
+%.eep: %.elf
+ $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
+ --change-section-lma .eeprom=0 -O $(FORMAT) $< $@
+
+%.elf: $(OBJ)
+ $(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
+
+%.o: %.c lights.h Makefile
+ $(CC) -c $(CFLAGS) $< -o $@
+
+%.s: %.c lights.h Makefile
+ $(CC) -S -c $(CFLAGS) $< -o $@
+
+%.o: %.S
+ $(CC) -c $(CFLAGS) $< -o $@
+
+clean:
+ rm -f $(PROGRAM).hex $(PROGRAM).eep $(PROGRAM).elf *.o *.s eeprom.raw
+
+version.c:
+ ./version.pl > version.c
+
+.PHONY: all clean dump_eeprom program program_flash program_eeprom objdump version.c
+
--- /dev/null
+#ifndef LIGHTS_H__
+#define LIGHTS_H__ 1
+
+#define N_PWMLED_MODES 4
+
+/* logging.c */
+#ifdef USE_LOGGING
+void init_log();
+void log_set_state(unsigned char val);
+void log_flush();
+void log_byte(unsigned char byte);
+void log_word(uint16_t word);
+#else
+void inline init_log() { }
+void inline log_set_state(unsigned char val) { }
+void inline log_flush() { }
+void inline log_byte(unsigned char byte) { }
+void inline log_word(uint16_t word) { }
+#endif
+
+/* adc.c */
+#define PWMLED_ADC_SHIFT 1 /* 1<<1 measurements per single callback */
+extern volatile unsigned char need_battery_adc;
+extern volatile unsigned char need_pwmled_adc;
+extern volatile unsigned char adc_enabled;
+void init_adc();
+void susp_adc();
+void start_next_adc();
+
+/* pwm.c */
+#define PWM_MAX 0xFF
+extern volatile unsigned char pwm_enabled;
+void init_pwm();
+void susp_pwm();
+void pwm_off();
+void pwm_set(uint8_t stride);
+
+/* pwmled.c */
+void init_pwmled();
+void pwmled_adc(uint16_t adcval);
+void pwmled_set_target(unsigned char mode);
+void pwmled_on_off(unsigned char on);
+
+/* pattern.c */
+void init_pattern();
+void patterns_next_tick();
+void led_set_pattern(unsigned char led, unsigned char bits_len,
+ unsigned char bits_start, unsigned char *data);
+void led_set_number_pattern(unsigned char led,
+ unsigned char num, unsigned char inv);
+void pattern_reload();
+
+/* buttons.c */
+void init_buttons();
+void susp_buttons();
+void timer_check_buttons();
+unsigned char buttons_wait_for_release();
+void status_led_on_off(unsigned char on);
+
+/* battery.c */
+void battery_adc();
+void init_battery();
+unsigned char battery_gauge();
+
+/* control.c */
+void init_control();
+void long_press_start();
+void long_press();
+void short_press();
+void brake_on();
+void brake_off();
+void pwmled_pattern_select(unsigned char led);
+void status_led_pattern_select(unsigned char led);
+#define ERR_BATTERY 1
+#define ERR_PWMLED 2
+void set_error(unsigned char err);
+
+/* wdt.c */
+extern volatile uint16_t jiffies;
+void init_wdt();
+void susp_wdt();
+
+/* main.c */
+void power_down();
+
+#endif /* !LIGHTS_H__ */
+
--- /dev/null
+#ifdef USE_LOGGING
+
+#include <avr/io.h>
+#include <avr/eeprom.h>
+
+#include "lights.h"
+
+#define LOG_BUFFER 32
+static unsigned char log_buffer_ee[LOG_BUFFER] EEMEM;
+static unsigned char log_buffer_count;
+static unsigned char log_buffer[LOG_BUFFER];
+static unsigned char log_state EEMEM;
+/* Upper 4 bits are reset count, lower 4 bits are reset reason from MCUSR */
+static unsigned char reboot_count EEMEM = 0;
+static unsigned char can_write_eeprom = 0;
+static uint16_t flushed_end;
+
+void log_set_state(unsigned char val)
+{
+ if (can_write_eeprom)
+ eeprom_write_byte(&log_state, val);
+}
+
+void init_log()
+{
+ unsigned char r_count;
+
+ r_count = eeprom_read_byte(&reboot_count);
+ r_count >>= 4;
+
+ if (r_count < 5) {
+ r_count++;
+ eeprom_write_byte(&reboot_count,
+ (r_count << 4) | (MCUSR & 0xF));
+ MCUSR = 0;
+ can_write_eeprom = 1;
+ } else {
+ //eeprom_write_byte(&log_state, 0xFF);
+ can_write_eeprom = 0;
+ }
+
+ log_set_state(1);
+ log_buffer_count = 0;
+ flushed_end = 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;
+
+ if (!can_write_eeprom)
+ return;
+
+ for (i=flushed_end; i < log_buffer_count; i++) {
+ eeprom_write_byte(&log_buffer_ee[i],
+ log_buffer[i]);
+ }
+
+ flushed_end = i;
+
+ if (flushed_end == LOG_BUFFER)
+ log_set_state(0x42);
+}
+
+#endif
+
--- /dev/null
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+#include <avr/power.h>
+#include <avr/wdt.h>
+
+#include "lights.h"
+
+#if 0
+static void hw_setup()
+{
+ power_all_disable();
+
+// init_battery();
+ init_pwm();
+// init_adc();
+// init_wdt();
+//
+// init_buttons();
+
+// init_pwmled();
+// init_pattern();
+// init_control();
+
+// set_sleep_mode(SLEEP_MODE_IDLE);
+}
+
+static void hw_suspend()
+{
+ susp_pwm();
+ susp_adc();
+ susp_wdt();
+
+ susp_buttons();
+
+ power_all_disable();
+}
+
+void power_down()
+{
+ hw_suspend();
+
+ do {
+ // G'night
+ set_sleep_mode(SLEEP_MODE_PWR_DOWN);
+ sleep_enable();
+ sleep_bod_disable();
+ sei();
+ sleep_cpu();
+
+ // G'morning
+ cli();
+ sleep_disable();
+
+ // allow wakeup by long button-press only
+ } while (!buttons_wait_for_release());
+
+ // ok, so I will wake up
+ hw_setup();
+}
+#endif
+
+
+uint16_t read_adc_sync()
+{
+ uint16_t rv;
+
+ ADCSRA |= _BV(ADSC); // start the conversion
+
+ // wait for the conversion to finish
+ while((ADCSRA & _BV(ADIF)) == 0)
+ ;
+
+ rv = ADCW;
+ ADCSRA |= _BV(ADIF); // clear the IRQ flag
+
+ return rv;
+}
+
+void init_adc()
+{
+ power_adc_enable();
+ ACSR |= _BV(ACD); // but disable the analog comparator
+
+ ADCSRA = _BV(ADEN) // enable
+ // | _BV(ADPS1) | _BV(ADPS0) // CLK/8 = 125 kHz
+ | _BV(ADPS2) // CLK/16 = 62.5 kHz
+ ;
+
+ // Disable digital input on all bits used by ADC
+ DIDR0 = _BV(ADC3D) | _BV(ADC2D);
+
+ ADMUX = _BV(REFS1) | _BV(MUX1) | _BV(MUX0);
+
+ /* Do first conversion and drop the result */
+ read_adc_sync();
+
+ // ADCSRA |= _BV(ADIE); // enable IRQ
+}
+
+int main(void)
+{
+ uint16_t a, p, c, target;
+ int32_t sum;
+ uint8_t micro;
+ init_log();
+
+ log_set_state(3);
+
+ init_pwm();
+ init_adc();
+ // init_pwmled();
+
+ pwm_set(1);
+ // setup_mux(0);
+ // pwmled_on_off(1);
+ // pwmled_set_target(1);
+
+ sum = 0;
+ c = 0;
+ micro = 0;
+ sei();
+
+ target = 0xf0;
+#if 0
+ while(1) {
+ unsigned char p;
+ for (p = 0xd4; p <= 0xd5; p+= 0x1) {
+ unsigned char i, j;
+ uint16_t sum;
+
+ pwm_set(p);
+
+ for (j = 0; j < 12; j++) {
+ sum = 0;
+ for (i = 0; i < 2; i++) {
+ sum += read_adc_sync();
+ }
+ log_byte(sum);
+ for (i = 0; i < 14; i++) {
+ sum += read_adc_sync();
+ }
+ }
+ log_flush();
+ }
+ }
+#endif
+ while (1) {
+ sum += target - read_adc_sync() - (sum >> 7);
+ micro++;
+ if (micro > 7)
+ micro = 0;
+
+ p = ((sum >> 4) + micro) >> 3;
+ if (p > 0xd8)
+ p = 0xd8;
+ pwm_set(p);
+ if (++c > 200) {
+ log_byte(p);
+ log_flush();
+ c = 0;
+ }
+ }
+
+ p = a = c = 0;
+ while (1) {
+ uint16_t i;
+ for (i = 0; i < 25; i++) {
+ uint16_t val;
+ val = read_adc_sync();
+ a += val - (a >> 5);
+ }
+
+ if (a < (42 << 5) && p < 250) {
+ p++;
+ } else if (p > 1 && a > (38 << 5)) {
+ p--;
+ }
+ pwm_set(p);
+ if (++c > 1000) {
+ log_byte(0xbb);
+ // log_word(read_adc_sync());
+ log_word(a);
+ log_word(p);
+ log_flush();
+ c = 0;
+ }
+ }
+
+ while(1) {
+ uint16_t i, t;
+ for (t = 0; t < 3; t++) {
+ pwmled_set_target(t);
+ for (i = 0; i < 1000; i++) {
+ need_pwmled_adc = 1;
+ while (need_pwmled_adc == 1)
+ ;
+ }
+ }
+ }
+
+#if 0
+ hw_setup();
+ power_down();
+
+#if 1
+ while (1) {
+ cli();
+ if (pwm_enabled) {
+ set_sleep_mode(SLEEP_MODE_IDLE);
+ } else if (adc_enabled) {
+ set_sleep_mode(SLEEP_MODE_ADC);
+ } else {
+ set_sleep_mode(SLEEP_MODE_PWR_DOWN);
+ }
+
+ sleep_enable();
+ // keep BOD active, no sleep_bod_disable();
+ sei();
+ sleep_cpu();
+ sleep_disable();
+ }
+#endif
+
+#if 0
+ DDRB |= _BV(PB2);
+ while (1) {
+ PORTB |= _BV( PB2 );
+ _delay_ms(200);
+ PORTB &=~ _BV( PB2 );
+ _delay_ms(200);
+ }
+#endif
+#endif
+}
--- /dev/null
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/power.h>
+#include <util/delay.h>
+#include <util/atomic.h>
+
+#include "lights.h"
+
+/*
+ * Single PWM channel on OC1B (pin PB4 of Tiny45).
+ * Counts from 0 to 0xFF, without OCR1C compare.
+ */
+
+volatile unsigned char pwm_enabled;
+
+static void inline enable_pll()
+{
+ /* Async clock */
+ PLLCSR = _BV(PLLE) | _BV(LSM);
+
+ /* Synchronize to the phase lock */
+ _delay_us(100);
+ while ((PLLCSR & _BV(PLOCK)) == 0)
+ ;
+ PLLCSR |= _BV(PCKE);
+}
+
+void init_pwm()
+{
+ pwm_enabled = 0;
+ power_timer1_enable();
+
+ TCCR1 = _BV(CTC1) | _BV(CS11); // pll_clk/2
+ GTCCR = _BV(COM1B1) | _BV(PWM1B);
+
+ OCR1C = PWM_MAX;
+ OCR1B = 0; // initial stride is 0
+
+ DDRB &= ~(_BV( PB4 ));
+ PORTB &= ~_BV(PB4); // set to zero
+}
+
+void susp_pwm()
+{
+ DDRB &= ~(_BV( PB4 ));
+ PORTB &= ~(_BV( PB4 ));
+ TCCR1 = 0;
+ TIMSK = 0;
+ TIFR = 0;
+
+ PLLCSR &= ~(_BV(PLLE) | _BV(PCKE));
+}
+
+void pwm_off()
+{
+ OCR1B = 0;
+ DDRB &= ~_BV(PB4);
+
+ PLLCSR &= ~(_BV(PLLE) | _BV(PCKE));
+ power_timer1_disable();
+ pwm_enabled = 0;
+}
+
+void pwm_set(uint8_t stride)
+{
+ OCR1B = stride;
+
+ if (!pwm_enabled) {
+ power_timer1_enable();
+ enable_pll();
+ DDRB |= _BV(PB4);
+ pwm_enabled = 1;
+ }
+}