/* Name: autofeeder.c * Author: Toshi Nagata * Copyright: 2017 (c) Toshi Nagata * License: Open BSD License * * ATtiny2313V * Running with 8 MHz Xtal and 1/8 prescale (= 1 MHz) * PB4: Digit 2 anode * PB3: Digit 1 anode * PB2: Cathode B * PB1: Cathode A * PB0: Cathode G * PD6: Cathode F * PD5: Cathode E * PD4: Cathode D * PD3: Cathode C * PD2: Cathode DP * PD1: LED * PD0: RELAY * ------ A * F / / B * / G / * ------ * E / / C * / / * ------ D .DP */ #include #include #include #include #include #define A 0x01 // Port B #define B 0x80 // Port D; left-shifted by 1 #define C 0x20 // Port D; left-shifted by 1 #define D 0x10 // Port D; left-shifted by 1 #define E 0x08 // Port D; left-shifted by 1 #define F 0x04 // Port B #define G 0x02 // Port B #define DP 0x20 // Port D #define ANODE1 0x08 #define ANODE2 0x10 #define TIMER0_MAX 159 // 1usec * 64* 160 = 10.2 msec #define TIMER1_MAX 15624 // 1usec * 64 * 15625 = 1 sec #define TIMER1_INT 6250 // 1 sec * 6250/15625 = 0.4 sec unsigned long timerACount, timerBCount; unsigned char timer0Count; unsigned char timerInvoked; unsigned char digits; // Bits 7:4 and 3:0 for LED2/1 unsigned char dpoints; // Bits 1 and 0 for LED2/1 unsigned char patTable[12] = { A | B | C | D | E | F, /* 0: ABCDEF- */ B | C, /* 1: -BC---- */ A | B | D | E | G, /* 2: AB-DE-G */ A | B | C | D | G, /* 3: ABCD--G */ B | C | F | G, /* 4: -BC--FG */ A | C | D | F | G, /* 5: A-CD-FG */ A | C | D | E | F | G, /* 6: A-CDEFG */ A | B | C | F, /* 7: ABC--F- */ A | B | C | D | E | F | G, /* 8: ABCDEFG */ A | B | C | D | F | G, /* 9: ABCD-FG */ G, /* -: ------G */ 0 /* blank */ }; void setup() { DDRB = 0b00011111; // PB0-PB7 for output DDRD = 0b01111111; // PD0-PD7 for output // Enable both LEDs // PORTB |= (ANODE1 | ANODE2); // Set interrupt cli(); // Disable interrupt // Set timer0 // WGM02..WGM00 = 0b010 (CTC mode with OCR0A) TCCR0A = (1 << WGM01); TCCR0B = (1 << CS01) | (1 << CS00); // CTC mode, clock source = clk/64 TCNT0 = 0; // Reset timer OCR0A = TIMER0_MAX; // Interrupt every 10 msec // Set timer1 // WGM13..WGM10 = 0b0100 (CTC mode with OCR1A) TCCR1A = 0; TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10); // CTC mode, clock source = clk/64 TCNT1 = TIMER1_MAX - 1; // Reset timer OCR1A = TIMER1_MAX; // Interrupt every 1 second OCR1B = TIMER1_INT; // Enable OCR0A, OCR1A and OCR1B interrupt TIMSK = (1 << OCIE0A) | (1 << OCIE1A) | (1 << OCIE1B); timerACount = timerBCount = 0; timerInvoked = 0; digits = 0xb0; dpoints = 0; set_sleep_mode(SLEEP_MODE_IDLE); sei(); // Enable interrupt } // Interrupt handler (timer1 CTC) ISR(TIMER1_COMPA_vect) { timerInvoked |= 1; timerACount++; } // Interrupt handler (timer1, compare OCR1B) ISR(TIMER1_COMPB_vect) { timerInvoked |= 2; timerBCount++; } // Interrupt handler (timer0 CTC) ISR(TIMER0_COMPA_vect) { timerInvoked |= 4; timer0Count++; } void set_digits(unsigned char c) { if (c < 10) digits = 0xb0 + c; else digits = ((c / 10) << 4) + (c % 10); } #define timerALimit 43200 // 12*3600 #define timerASwitch1 3600 #define timerASwitch2 60 #define timerAMotor 18 /* For testing #define timerALimit 120 #define timerASwitch1 60 #define timerASwitch2 20 #define timerAMotor 7 */ void loop() { int n; while (timerInvoked != 0) { cli(); // Disable interrupt if (timerInvoked & 1) { unsigned int rem_time; timerInvoked &= ~1; PORTD |= 0b00000010; // LED on // Wrap the timerA if necessary if (timerACount >= timerALimit) timerACount -= timerALimit; // Motor on? if (timerACount < timerAMotor) PORTD |= 0b00000001; else PORTD &= 0b11111110;; rem_time = timerALimit - timerACount; if (rem_time >= timerASwitch1) { // Show the remaining hours (4 secs), minutes (1 sec), seconds (1 sec) dpoints = 0; switch (rem_time % 6) { case 0: case 5: case 4: case 3: set_digits(rem_time / 3600); break; case 2: set_digits((rem_time % 3600) / 60); break; case 1: set_digits(rem_time % 60); break; } } else if (rem_time >= timerASwitch2) { // Show the remaining minutes (3 sec), seconds (1 sec) switch (rem_time % 4) { case 0: case 3: case 2: set_digits(rem_time / 60); break; case 1: set_digits(rem_time % 60); break; } dpoints = (rem_time % 2); } else { dpoints = 1; set_digits(rem_time); } } else if (timerInvoked & 2) { timerInvoked &= ~2; dpoints = 0; if (timerACount >= timerAMotor) PORTD &= 0b11111101; // LED off } else if (timerInvoked & 4) { unsigned char m, dp; timerInvoked &= ~4; n = timer0Count % 2; PORTD |= 0b01111100; PORTB |= 0b00000111; if (n == 0) { PORTB &= ~ANODE2; PORTB |= ANODE1 | 0b00000111; m = (digits >> 4) & 0x0f; dp = (dpoints >> 1) & 1; } else { PORTB &= ~ANODE1; PORTB |= ANODE2 | 0b00000111; m = digits & 0x0f; dp = dpoints & 1; } m = patTable[m]; dp = (dp ? DP : 0); PORTD &= ~(((m >> 1) & 0b01111100) | dp); PORTB &= ~(m & 0b00000111); } sei(); // Enable interrupt } // Go to sleep until next interrupt sleep_enable(); sleep_cpu(); sleep_disable(); // Wake up } int main() { setup(); while (1) { loop(); } return 0; /* Not reached */ }