diff options
Diffstat (limited to 'boo-stanby-watcher')
-rw-r--r-- | boo-stanby-watcher/Makefile | 39 | ||||
-rw-r--r-- | boo-stanby-watcher/main.c | 307 |
2 files changed, 346 insertions, 0 deletions
diff --git a/boo-stanby-watcher/Makefile b/boo-stanby-watcher/Makefile new file mode 100644 index 0000000..2646d03 --- /dev/null +++ b/boo-stanby-watcher/Makefile @@ -0,0 +1,39 @@ +DEVICE = attiny25 +CLOCK = 1000000 +FUSES = -U lfuse:w:0x62:m -U hfuse:w:0xdd:m + +PROGRAMMER = avrdude +PROGRAMMER += -c buspirate +PROGRAMMER += -P /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A400XJSH-if00-port0 +PROGRAMMER += -p $(DEVICE) -B 4 + +DEFS = -DF_CPU=$(CLOCK)UL +CFLAGS = -Wall -Werror -pedantic -std=c99 +CFLAGS += -O2 -funsigned-bitfields -fpack-struct -fshort-enums +CFLAGS += -mmcu=$(DEVICE) $(DEFS) +#LIBS = -Wl,--relax,--gc-sections,--print-gc-sections,--entry=main +#LIBS = -Wl,--entry=main,--gc-sections,--rela +#LDFLAGS = $(LIBS) + +all: main.hex size + +main.hex: main.c + avr-gcc -o main.elf $? $(CFLAGS) $(LDFLAGS) + avr-objcopy -j .text -j .data -O ihex main.elf main.hex + +f: flash +flash: all + $(PROGRAMMER) -U flash:w:main.hex:i + +fuse: + $(PROGRAMMER) $(FUSES) + +clean: + rm -f *.o *.elf *.hex + +size: + avr-size --mcu=$(DEVICE) -t -A main.elf + avr-size --mcu=$(DEVICE) -C main.elf + avr-nm --size-sort main.elf + + diff --git a/boo-stanby-watcher/main.c b/boo-stanby-watcher/main.c new file mode 100644 index 0000000..5c24c1b --- /dev/null +++ b/boo-stanby-watcher/main.c @@ -0,0 +1,307 @@ +#include <avr/io.h> +#include <avr/interrupt.h> + +/* Pinout used: + * ----- + * reset - MCU - Vcc2 + * module reset (PB3) - MCU - (ADC1) Vcc1 mesurement + * module set (PB4) - MCU - not used + * GND - MCU - (PB0) Amp DC control + * ----- + */ + +// assume computer is shutting down if vcc1 is below this value +static const uint8_t ADC_DOWN_VALUE = 217; + +/* times are in tenth of seconds since ISR is triggered every 0.1s~ + * see init_timer */ +static const uint8_t SLEPT1_TIMEOUT = 10; /* 1s */ +static const uint8_t SLEPT2_TIMEOUT = 35; /* 3.5s */ +static const uint8_t CAPA_TIMEOUT = 10; /* 1s */ + +static void init_adc() +{ + /* this function initialises the ADC + + ADC Notes + + Prescaler + + ADC Prescaler needs to be set so that the ADC input frequency is + between 50 - 200kHz. + + Example prescaler values for various frequencies + + Clock Available prescaler values + --------------------------------------- + 1 MHz 8 (125kHz), 16 (62.5kHz) + 4 MHz 32 (125kHz), 64 (62.5kHz) + 8 MHz 64 (125kHz), 128 (62.5kHz) + 16 MHz 128 (125kHz) + + set prescaler to 128 for mcu running at 8MHz + */ + + ADMUX = + (0 << REFS1) | // Sets ref. voltage to VCC, bit 1 + (0 << REFS0) | // Sets ref. voltage to VCC, bit 0 + (1 << ADLAR) | // left shift result + (0 << REFS2) | // Sets ref. voltage to VCC, bit 2 + (0 << MUX3) | // use ADC1 for input, MUX bit 3 + (0 << MUX2) | // use ADC1 for input, MUX bit 2 + (0 << MUX1) | // use ADC1 for input, MUX bit 1 + (1 << MUX0); // use ADC1 for input, MUX bit 0 + + ADCSRA = + (1 << ADEN) | // Enable ADC + (0 << ADSC) | // Do not start conversion + (0 << ADATE) | // Do not enable Auto Trigger + (0 << ADIF) | // Do not set Interrupt flag + (0 << ADIE) | // Do not set interrupt enable + (1 << ADPS2) | // set prescaler to 16, bit 2 + (0 << ADPS1) | // set prescaler to 16, bit 1 + (0 << ADPS0); // set prescaler to 16, bit 0 +} + +// Value is between Vcc 3.4V (255) and GND (0). +static uint8_t read_adc() +{ + ADCSRA |= (1 << ADSC); // start ADC measurement + while (ADCSRA & (1 << ADSC)); // wait till conversion complete + return ADCH; // value is between 0 (=GND) and 255 (=Vcc) +} + +static void init_timer() +{ + /* timer_resolution = 1 / (clock_speed / prescaler) + * timer_resolution = 1 / (10**6 / 1024) + * + * in ctc mode, target counts: + * + * timer_counts = (target_time / timer_resolution) - 1 + * timer_counts = (0.1 / (1/(10**6/1024))) - 1 + * timer_counts = 96.65625000000001 =~ 97 + * + * Why did we add the extra +1 to our number of timer counts? In CTC mode, + * when the timer matches our desired count it will reset itself to zero. + * This takes one clock cycle to perform, so we need to factor that into + * our calculations. + * + * VG notes: more simply 0 to 97 = 98 values = 98 cycles. + * + * trigger_time = (97+1) * (1/(10**6 / 1024)) =~ 0.100352s + * + * WGM0[2:0] = 010 = CTC + */ + TCCR0A = + (1 << WGM01) | // CTC + (0 << WGM00); // CTC + TCCR0B = + (0 << WGM02) | // CTC + (1 << CS02) | // CS0[2:0] = 101 => prescaler clk/1024 + (0 << CS01) | // CS0[2:0] = 101 => prescaler clk/1024 + (1 << CS00); // CS0[2:0] = 101 => prescaler clk/1024 + OCR0A = 97; + TIMSK = + (1 << OCIE0A) | // enable CTC interrupt for compare match 0A + (0 << OCIE0B) | // disable CTC interrupt for compare match 0B + (0 << TOIE0); // disable interrupt for timer0 overflow + +} + +static volatile uint8_t sleep_time; +static volatile uint8_t capa_time; + +ISR(TIMER0_COMPA_vect) +{ + ++sleep_time; + ++capa_time; +} + +static uint8_t get_capa_time(void) +{ + return capa_time; +} + +static uint8_t get_sleep_time(void) +{ + return sleep_time; +} + +static void reset_capa_time(void) +{ + capa_time = 0; +} + +static void reset_sleep_time(void) +{ + sleep_time = 0; +} + +static void plug_set() +{ + PORTB &= ~(1 << PB3); /* make sure reset line is disabled */ + PORTB |= (1 << PB4); /* activate plug modules set */ +} + +static void plug_unset() +{ + PORTB &= ~(1 << PB4); /* deactivate plug modules set */ +} + +static void amp_set() +{ + PORTB |= (1 << PB0); +} + +static void amp_plug_reset() +{ + /* be sure set line is not active before doing a reset else both coil will + * be energized at the same time and thus the behaviour may be + * unpredictable. */ + plug_unset(); + /* deactivate amp dc control and activate module reset */ + PORTB &= ~(1 << PB0); + PORTB |= (1 << PB3); +} + +static void amp_plug_unreset() +{ + PORTB &= ~(1 << PB3); +} + +static enum states { + ST_1_AMP_PLUG_OFF, + ST_2_PLUG_SET, + ST_3_PLUG_ON, + ST_4_IDLE, /* when plug and amp are on we are in idle state */ + ST_5_AMP_PLUG_RESET, + /* ST_OFF never reached normally :P it only appears in the diagram */ + ST_MAX +} current_state = ST_1_AMP_PLUG_OFF; + +enum events { + EV_1_CAPA_LOADED, + EV_2_SLEPT1, + EV_3_SLEPT2, + EV_4_UNLOADING, /* no more current, capa temp power unloading */ + EV_MAX +}; + +static void s0_e0(void); /* do nothing */ +static void s1_e1(void); +static void s2_e2(void); +static void s2_e4(void); +static void s3_e3(void); +static void s3_e4(void); +static void s4_e4(void); +static void s5_e2(void); + +static void (*const state_table [ST_MAX][EV_MAX])(void) = { + {s1_e1, s0_e0, s0_e0, s0_e0}, + {s0_e0, s2_e2, s0_e0, s2_e4}, + {s0_e0, s0_e0, s3_e3, s3_e4}, + {s0_e0, s0_e0, s0_e0, s4_e4}, + {s0_e0, s5_e2, s0_e0, s0_e0}, +}; + +static void process_event(enum events new_event) +{ + state_table[current_state][new_event](); +} + +static void s0_e0(void) {} + +static void s1_e1(void) /* amp&plug off: capa loaded */ +{ + plug_set(); + reset_sleep_time(); + current_state = ST_2_PLUG_SET; +} + +static void s2_e2(void) /* plug set: slept1 */ +{ + plug_unset(); + reset_sleep_time(); + current_state = ST_3_PLUG_ON; +} + +static void s2_e4(void) /* plug set: unloading */ +{ + amp_plug_reset(); + reset_sleep_time(); + current_state = ST_5_AMP_PLUG_RESET; +} + +static void s3_e3(void) /* plug on: slept2 */ +{ + amp_set(); + current_state = ST_4_IDLE; +} + +static void s3_e4(void) /* plug on: unloading */ +{ + amp_plug_reset(); + reset_sleep_time(); + current_state = ST_5_AMP_PLUG_RESET; +} + +static void s4_e4(void) /* idle: unloading */ +{ + amp_plug_reset(); + reset_sleep_time(); + current_state = ST_5_AMP_PLUG_RESET; +} + +static void s5_e2(void) /* amp&plug reset: slept1 */ +{ + amp_plug_unreset(); + current_state = ST_1_AMP_PLUG_OFF; +} + +int main() +{ + uint8_t adcval; + + cli(); + + init_adc(); + init_timer(); + + // digital output + DDRB |= + (1 << PB0) | // Amp DC control + (1 << PB3) | // module reset + (1 << PB4); // module set + PORTB = 0; // outputs set to low and input not set to internal pull-up + + // first ADC conversion may be inaccurate + // atmel advises to discard the result + adcval = read_adc(); + + sei(); + + for (;;) { /* main loop */ + + adcval = read_adc(); + + if (get_capa_time() >= CAPA_TIMEOUT) { + process_event(EV_1_CAPA_LOADED); + } + + if (get_sleep_time() >= SLEPT1_TIMEOUT) { + process_event(EV_2_SLEPT1); + } + + if (get_sleep_time() >= SLEPT2_TIMEOUT) { + process_event(EV_3_SLEPT2); + } + + if (adcval <= ADC_DOWN_VALUE) { + reset_capa_time(); + process_event(EV_4_UNLOADING); + } + } + + return 0; +} |