aboutsummaryrefslogtreecommitdiffstats
path: root/boo-stanby-watcher
diff options
context:
space:
mode:
Diffstat (limited to 'boo-stanby-watcher')
-rw-r--r--boo-stanby-watcher/Makefile39
-rw-r--r--boo-stanby-watcher/main.c307
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;
+}