/* => 
 * PRG: spinne_attiny85_xc8.c
 * Ersteller, Datum: df8iw, 28.03.2026
 * OS: MX Linux 23.6 - Debian GNU/Linux 12.13
 * IDE: MPLAB X IDE v6.30
 * Compiler: XC8 (v3.10)
 * MCU: ATtiny85V
 * Programmiergeraet: Microchip PICkit Basic
 * Program Memory Usage: 326 bytes, 4 % Full (Flash-EEPROM)
 * Data Memory Usage   :  0  bytes, 0 % Full (SRAM)
 * Zweck: Modell einer Spinne mit 2 Augen, Piepser, Rassel.
 * Stromversorgung aus 2xAA.
 * Energieeffizienz: Sleep-Modus Power-Down, Aufwecken mit Tastendruck.
 *                   Ansteuern d. LED mit PWM.
 * Geplant: Aufwecken mit IR
 */

/* ATTINY85: Anschluesse
 *                  +-\/-+
 *       (NC) PB5  1|    |8  VCC (+3V, 2xAA)
 *   (Beeper) PB3  2|    |7  PB2 (Taste aktiv LOW, int. Pullup)
 *   (Rassel) PB4  3|    |6  PB1 (LED + R gegen Masse)
 *            GND  4|    |5  PB0 (LED + R gegen Masse)
 *                  +----+
 */                      

//FUSES =
//{
//	.low = 0x62, // LOW {SUT_CKSEL=INTRCOSC_8MHZ_6CK_14CK_64MS, CKOUT=CLEAR, CKDIV8=SET}
//	.high = 0xFF, // HIGH {BODLEVEL=DISABLED, EESAVE=CLEAR, WDTON=CLEAR, SPIEN=CLEAR, DWEN=CLEAR, RSTDISBL=CLEAR}
//	.extended = 0xFF, // EXTENDED {SELFPRGEN=CLEAR}
//};

//LOCKBITS = 0xFF; // {LB=NO_LOCK}


#define F_CPU 1000000UL      // vor Includes, default (8MHz/ prescaler 8) = 1 MHz

//#include <stdint.h>  // Headerdatei f. Ganzzahldatentypen, mit vorgegebener Breite
#include <avr/io.h>         // AVR MCU spezifische I-O-Definitionen
#include <avr/delay.h>      // Headerdatei f. Berechn. v. delay
#include <avr/interrupt.h>  // Interrupt-Handling
//#include <avr/sleep.h>      // Power Management u. verschiedene Sleep-Modes

// Variablen

/* Deklarieren v. eigenen Funktionen: Prototyping. 
 * Eigentl. Def. im Quelltext unten nach Fkt. main(). 
 */
ISR(INT0_vect);                  // externer Interrupt 0 zum Aufwachen 
void geheSchlafen(void);         // Aufruf Tiefschlaf mit power down
void aufblitzenLED(void);        // wechselseitig an PB0 u. PB1
static void startTimer0(void);   // init PWM
static void stopTimer0(void);    // stop PWM-Ausgabe
void piepBuzzer(void);           // n-mal Piep
void vibrationMotor(void);       // Rasselgeraeusche

int main(void)                   // Hauptschleife 
{
    // alle nicht benutzen Pin auf def. Pegel: HIGH
    DDRB &= ~((1<<PB5)|(1<<PB4)|(1<<PB3)|(1<<PB2)|(1<<PB1)|(1<<PB0));  // alle Bits auf LOW: Eingaenge
    PORTB |=  (1<<PB5)|(1<<PB4)|(1<<PB3)|(1<<PB2)|(1<<PB1)|(1<<PB0);   // alle Eingaenge mit Pullup auf HIGH
    
    DDRB    |= (1<<PB0)|(1<<PB1)|(1<<PB3)|(1<<PB4);   // setze PB0,PB1,PB3,PB4 als Ausgaenge
    PORTB &= ~((1<<PB0)|(1<<PB1)|(1<<PB3)|(1<<PB4));  // setze Ausgaenge auf LOW: kein Strom

    while (1)                     // Endlosschleife
    {
        geheSchlafen();  // dauerhaft i. Power-Down-Mode (Tiefschlaf), 
        // nach Aufwecken mit Taste an PB2:
        piepBuzzer();
        aufblitzenLED();
        piepBuzzer();
        vibrationMotor();
    }
}

// externer Interrupt 0 zum Aufwachen
ISR(INT0_vect)  // ISR kann leer bleiben aber ist notwendig
{
    GIMSK = 0;                     //disable external interrupts (only need one to wake up)
}

// Fkt.: aufrufen Sleep-Mode Power-Down
void geheSchlafen(void)
{
    MCUCR &= ~((1 << ISC01) | (1 << ISC00));  // LOW an PB2/INT0 erzeugt Interrupt
    ADCSRA = 0;                               // deaktiviere ADC + Analogkomparator
    GIMSK |= (1 << INT0);                     // ext. Interrupt EIN
    MCUCR |= (1<<SM1);                        // Power-Down Sleep-Mode
    MCUCR |= (1<<SE);                         // Sleep Enable
    sei();                                    // alle Interrupts EIN
    asm("SLEEP");                             // sleep_cpu();
    MCUCR &= ~(1<<SE);                        // Sleep Disable     
    GIMSK &= ~(1 << INT0);  // ext. Interrupt AUS für den Fall, das LOW an PB2 zu lang
    cli();                                    // alle Interrupts AUS
}

// Fkt.: wechselseitiges Aufblitzen d. Spinnenaugen
void aufblitzenLED(void)  
{
    startTimer0();              // init PWM mit timer0
    for (uint8_t i=0; i<5; i++)  // n-mal aufblitzen
        {     
            OCR0A = 150;         // LED an PB0 EIN 
            _delay_ms(100);
            OCR0A = 0;           // LED an PB0 AUS
            _delay_ms(100);
            OCR0B = 150;         // LED an PB1 EIN 
            _delay_ms(100);
            OCR0B = 0;           // LED an PB1 AUS
            _delay_ms(100);
        }
    stopTimer0();               // stop PWM
}

// Fkt.: PWM zum Dimmen d. LED an OC0A (PB0) und OC0B (PB1). 
static void startTimer0(void)
{   // fast PWM, setze OC0A, OC0B on TOP, clear on compare match
    TCCR0A |= (1<<COM0A1)|(1 << COM0B1)|(1<<WGM01)|(1<<WGM00);
	TCCR0B = (1 << CS01);  // Prescaler 8: PWM-Frequenz: F_CPU/(8*256) = 488 Hz
}

static void stopTimer0(void)
{
    TCCR0B = 0;  // Timer/Counter Control Register B: Timer angehalten
	TCCR0A = 0;  // verhindere auch PWM-Output, so d. LED AUS sind.
}

// Fkt.: Spinne meldet sich durch Piepen
void piepBuzzer(void)
{
    PORTB |= (1<<PB3);  // PB3 geht HIGH
    _delay_ms(50);      // warte 50 ms
    PORTB &= ~(1<<PB3); // PB3 geht LOW
    _delay_ms(50);      // warte 50 ms
}

// Fkt.: Spinne gibt Rasselgeraeusche von sich
void vibrationMotor(void)
{
    PORTB |= (1<<PB4);  // PB4 geht HIGH
    _delay_ms(500);     // warte 500 ms
    PORTB &= ~(1<<PB4); // PB4 geht LOW
    _delay_ms(500);     // warte 500 ms 
}

