]> mj.ucw.cz Git - misc.git/blob - sphinx/sphinx.c
The Sphinx v0.9
[misc.git] / sphinx / sphinx.c
1 /*
2  *      The Sphinx -- A Device for Asking Riddles at the Entrance to the Garage
3  *
4  *      (c) 2017 Martin Mares <mj@ucw.cz>
5  */
6
7 /*
8  *      Pin assignment (component side view; "*" marks inverted signals)
9  *
10  *                      +-------------------+
11  *                      | RESET*        VCC |
12  *      button*         | PB3           SCK |
13  *      output          | PB4          MISO |
14  *                      | GND      PB0=MOSI |   diagnostic LED*
15  *                      +-------------------+
16  *
17  *      Note that MOSI is used for programming, so the LED is connected
18  *      via jumper, which must be open during programming.
19  */
20
21 #define F_CPU 1200000UL
22
23 #include <avr/interrupt.h>
24 #include <avr/io.h>
25 #include <avr/sleep.h>
26 #include <util/delay.h>
27
28 typedef uint8_t byte;
29 typedef uint16_t u16;
30
31 #define B(x) (1U<<(x))
32
33 static void sleep(uint16_t millisec)
34 {
35         while (millisec) {
36                 _delay_ms(1);
37                 millisec--;
38         }
39 }
40
41 static byte out_pulse;
42 #define OUT_PULSE_WIDTH 100
43 static byte debounce;
44 #define DEBOUNCE_THRESHOLD 3
45 static byte pressed;
46 static u16 press_timer;
47 #define LONG_PRESS 500
48 static byte second_timer;
49 static byte unlocked;
50 #define UNLOCK_SECONDS 20
51
52 ISR(TIM0_COMPA_vect)
53 {
54         if (out_pulse) {
55                 if (!--out_pulse)
56                         PORTB &= ~B(PB4);
57         }
58
59         if (!second_timer) {
60                 second_timer = 100;
61                 if (unlocked)
62                         unlocked--;
63         }
64         second_timer--;
65
66         if (PINB & B(PB3)) {
67                 // Switch open
68                 if (debounce)
69                         debounce--;
70                 else
71                         pressed = 0;
72         } else {
73                 // Switch closed
74                 if (debounce < DEBOUNCE_THRESHOLD)
75                         debounce++;
76                 else
77                         pressed = 1;
78         }
79
80         if (unlocked) {
81                 if (!(second_timer & 7))
82                         PORTB ^= B(PB0);
83                 if (pressed) {
84                         if (out_pulse < 2)
85                                 out_pulse = 2;
86                         PORTB |= B(PB4);
87                         unlocked = UNLOCK_SECONDS;
88                 }
89         } else {
90                 if (!pressed) {
91                         PORTB |= B(PB0);
92                         press_timer = 0;
93                 } else {
94                         PORTB &= ~B(PB0);
95                         if (press_timer < LONG_PRESS) {
96                                 press_timer++;
97                                 if (press_timer == LONG_PRESS) {
98                                         out_pulse = OUT_PULSE_WIDTH;
99                                         PORTB |= B(PB4);
100                                         unlocked = UNLOCK_SECONDS;
101                                 }
102                         }
103                 }
104         }
105 }
106
107 int main(void)
108 {
109         // PB0: output for diagnostic LED
110         DDRB |= B(PB0);
111
112         // PB3: button input, needs pull up
113         PORTB |= B(PB3);
114
115         // PB4: output to control the gate
116         PORTB &= ~B(PB4);
117         DDRB |= B(PB4);
118
119         for (byte i=0; i<5; i++) {
120                 PORTB &= ~B(PB0);
121                 sleep(50);
122                 PORTB |= B(PB0);
123                 sleep(50);
124         }
125
126 #if 0
127         // Copy mode
128         for (;;) {
129                 sleep(10);
130                 if (PINB & B(PB3)) {
131                         PORTB |= B(PB0);
132                         PORTB &= ~B(PB4);
133                 } else {
134                         PORTB &= ~B(PB0);
135                         PORTB |= B(PB4);
136                 }
137         }
138 #endif
139
140         // Set up timer interrupt
141         TCCR0A = 0x02;                  // No OC pins, clear on compare
142         TCCR0B = 0x05;                  // Run at CLK_IO / 1024
143         OCR0A = 11;                     // Approximately once in 10ms
144         TIMSK0 = B(OCIE0A);             // Interrupt on compare
145         sei();
146
147         // Sleep well, my little sphinx
148         for (;;) {
149                 set_sleep_mode(SLEEP_MODE_IDLE);
150                 sleep_mode();
151         }
152 }