MezData-Logo Creative Commons License 390 Lösungen Button :MEZ-ENTWICKLUNG: Das BruScheiKa Projekt

Die Lokation

BruScheiKa bedeutet Brunzen Scheissen Kaufen. Früher war in dem Klohäuschen ein Kiosk integriert. [ Bildergalerie ]

Die Funktion der Elektronik

Angeschlossene Komponenten

Zwei Figuren mit steuerbaren Augen, Mund und zwei Händen. 3 Münzprüfer mit je zwei Sensoren für unterschiedliche Münzen. An jedem Münzprüfer befindet sich eine LED

Blockschaltbild

ISP-Programmieradapter

MISO 1 6 Pol 2 VTG (VCC)
SCK 3 4 MOSI
Reset 5 6 GND

Belegung Stecker IR-Empfänger

1 2 3
Gnd + Signal

Belegung Stecker Servos

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
S1 F1
Augen
S2 F1
Mund
S3 F1
HandR
S4 F1
HandL
S5 F2
Augen
S6 F2
Mund
S7 F2
HandR
S8 F2
HandL
NC NC
- + S - + S - + S - + S - + S - + S - + S - + S

+ - S2 + - S4 + - S6 + - S8
2 4 6 8 10 12 14 16 18 20 22 24 26
1 3 5 7 9 11 13 15 17 19 21 23 25
- S1 + - S3 + - S5 + - S7 +

Belegung Stecker Münzer

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
M1 M2 M3 NC
Gnd Sk Sg LED NC GND Sk Sg LED NC GND Sk Sg LED NC

LED und Sensoren gegen GND

Sk LED Gnd Sg NC Sk LED
2 4 6 8 10 12 14 16
1 3 5 7 9 11 13 15
Gnd Sg NC Sk LED GND Sg

Belegung Stecker CD-Spieler

1 2 3 4 5 6 7 8 9 10 11 12 13 14
CD1 CD2 CD3 CD4 CD5 CD6 NC NC
- + - + - + - + - + - +
+ + + + + +
2 4 6 8 10 12 14
1 3 5 7 9 11 13
- - - - - -

Belegung Stecker Relais

1 2 3 4 5 6
+5V R1 R2 R3 NC NC

Relais mit Freilaufdiode gegen +5V

µController ATmega16

ATmega644
Belegung Bedeutung Pin             Pin Bedeutung Belegung
M1 LED Gn Links (XCK/T0) PB0 1 A
T
M
E
G
A

6
4
4
40 PA0 (ADC0) Audio Li
M2 LED Rt Rechts (T1) PB1 2 39 PA1 (ADC1) Audio Re
M1 Sk (INT2/AIN0) PB2 3 38 PA2 (ADC2) CD1
M1 Sg / LCD-RS (OC0/AIN1) PB3 4 37 PA3 (ADC3) CD2
M2 Sk / LCD-D4 (SS) PB4 5 36 PA4 (ADC4) CD3
M2 Sg / LCD-D5 (MOSI) PB5 6 35 PA5 (ADC5) CD4
M3 Sk / LCD-D6 (MISO) PB6 7 34 PA6 (ADC6) CD5
M3 Sg / LCD-D7 (SCK) PB7 8 33 PA7 (ADC7) CD6
10K Ohm -> VCC /RESET 9 32 AREF
VCC 10 31 GND
GND 11 30 AVCC
XTAL2 12 29 PC7 (TOSC2) Servo 8
XTAL1 13 28 PC6 (TOSC1) Servo 7
(RXD) PD0 14 27 PC5 (TDI) Servo 6
(TXD) PD1 15 26 PC4 (TDO) Servo 5
IR-Empfänger (INT0) PD2 16 25 PC3 (TMS) Servo 4
LCD-E (INT1) PD3 17 24 PC2 (TCK) Servo 3
M3 LED (OC1B) PD4 18 23 PC1 (SDA) Servo 2
Relais1 (OC1A) PD5 19 22 PC0 (SCL) Servo 1
Relais2 (ICP1) PD6 20 21 PD7 (OC2) Relais3

Teilfunktionen

8 Servos ansteuern

Die Pulsweite soll in 10 µs Servo-Schritten verstellbar sein. Hier die naheliegenden Kombinationen:

CPU Frequenz Vorteiler µs pro Zahl Bewertung
1 MHz 1:1 1 Int. Oszillator ist vorkalibriert, Standard, Multiplikation mit 10 notwendig
4 MHz 1:8 2 Mehraufwand bei Kalibrierung, Multiplikation mit 5 notwendig
8 MHz 1:8 1 Mehraufwand bei Kalibrierung, Multiplikation mit 10 notwendig
Gewählt 8 MHz mit Vorteiler 1:8, zum Multiplizieren mit 10 gibt es ja beim Mega16 praktische Befehle.

Um die 20 ms = 20 000 µs zu zählen brauchen wir den 16 Bit Timer1. Betriebsart Clear Timer on Compare Mode . Der Timer zählt von 0 bis zu einem einstellbaren TOP Wert und springt dann wieder auf 0. Ein Ausgang kann beim Sprung von TOP auf 0 gesetzt und bei Erreichen eines einstellbaren Wertes OCR1A wieder rückgesetzt werden.

ICR1 als TOP weil nicht verändert mit Wert 19 999 für 20 ms Wiederholungsintervall, OCR1A = Servo-Schritt * 10. Bei Erreichen von OCR1A wird ein Interrupt ausgelöst.

Einstellungen bei Initialisierung

Register Bit 7 6 5 4 3 2 1 0 Beschreibung
TCCR1A Bedeutung COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
Wert 0 0 0 0 0 0 0 0

Waveform Generation Mode: CTC mit ICR1 als TOP

TCCR1B Bedeutung ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10
Wert 0 0 0 1 1 0 1 0 Vorteilung Timer mit CPU-CLK / 8
TIMSK Bedeutung OCIE2 TOIE2 TICE1 OCIE1A OCIE1B TOIE1 OCIE0 TOIE0 Input Capture Interrupt Enable (ICF1)
Wert 0 0 1 1 0 0 0 0 Output Compare Interrupt Enable

Audio in Mundbewegung wandeln

Einstellungen bei Initialisierung

Register Bit 7 6 5 4 3 2 1 0 Beschreibung
ADMUX Bedeutung REFS1 REFS0 ADLAR MUX4 MUX3 MUX2 MUX1 MUX0 Interne 2,56V Referenzspannung, Wert ist linksbüdig in ADCH:ADCL
Wert 1 1 1 0 0 0 0 0 Linker Kanal an PC0 (ADC0)
ADCSRA Bedeutung ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0 Wandler an, Auto Trigger Enable, Interrupt frei
Wert 1 1 1 1 1 1 1 1 Vorteiler auf CLK /1 28
SFIOR Bedeutung ADTS2 ADTS1 ADTS0 - ACME PUD PSR2 PSR10 Free Running Mode
Wert 0 0 0

Handbewegungen erzeugen

Ablaufsteuerung des Automaten Münzeinwurf

Ablauf bei grosser Münze Prellen des Sensors am Anfang

IR Empfänger

OUT 1 Oben
(Linse)
GND 2
VCC 3

Gebräuchliche Codes[IR Code Knowledgebase] [SFH 5110-38]

 

Verbesserungen ToDo

  1. Redesign der Servoansteuerung, Problem mit negativen Zahlen und Nullpunkt
  2. Schwätzo soll schneller Servos bewegen
  3. IR Empfang
  4. 6 Kanäle
  5. Während Vorstellung eingeworfene Münzen sollen neue Sequenz starten

Prototyp Platine mit LCD-Anzeige

Typischer Anschlußbelegung eines Displays [LCD-Modul am AVR] [AVR-GCC-Tutorial/LCD-Ansteuerung]

Pin Bezeichnung Beschreibung Belegung Steckerpin
1 GND Masse GND 1
2 VCC Spannungsversorgung +5V +5V 2
3 VEE Kontrast Poti 0..0,5V GND
4 RS Register Select, 1=Daten schreiben / 0=Kommando senden. RS 3
5 R/W 1=Read / 0=Write zum lesen / schreiben in das Display RAM GND
6 Enable Fallende Flanke -> Übertragen des Kommandos oder der Daten, H-Pegel -> Lesen von Daten aus dem Display E 4
7 DB0 Datenbus Bit0 LSB NC
8 DB1 Datenbus Bit1 NC
9 DB2 Datenbus Bit2 NC
10 DB3 Datenbus Bit3 NC
11 DB4 Datenbus Bit4 D4 5
12 DB5 Datenbus Bit5 D5 6
13 DB6 Datenbus Bit6 D6 7
14 DB7 Datenbus Bit7 MSB D7 8
GND 9
IR-Sensor 10
+5V E D5 D7 Sensor
2 4 6 8 10
1 3 5 7 9
GND RS D4 D6 GND

Schlimmer C-Quellcode

Quellcode [BruScheiKa.c]
// BruScheiKa V 1.1 beta (c) Oliver Mezger 12.5.2010
#include <avr/io.h>        // Definitionen laden
#include <util/delay.h>    // Delay-Bibliothek laden
#include <avr/interrupt.h>
#include "lcd-routines.h"
#include <stdlib.h>

#define HardWareTest 0
#define vuTest 1

// Zuordnungen zu Ports
#define RelaisIn        PIND
#define RelaisOut        PORTD
#define RelaisDdr        DDRD
#define MuenzIn            PINB
#define MuenzOut        PORTB
#define MuenzDdr        DDRB
#define CDIn            PINA
#define CDOut            PORTA
#define CDDdr            DDRA
#define ServosIn        PINC
#define ServosOut        PORTC
#define ServosDdr        DDRC

// Automatenspezifische Einstellungen

#define F1Augen    0
#define F1Mund     1
#define F1Rumpf 2
#define F1Hand     3
#define F2Augen 4
#define F2Mund     5
#define F2Rumpf    6
#define F2Hand    7

#define M1Led 0 // an Muenz
#define M2Led 1 // an Muenz
#define M3Led 4 // an Relais
#define M1Sk 2
#define M1Sg 3
#define M2Sk 4
#define M2Sg 5
#define M3Sk 6
#define M3Sg 7

#define CD1 2
#define CD2 3
#define CD3 4
#define CD4 5
#define CD5 6
#define CD6 7

#define Relais1 5  // Summe an sobald Automat laueft
#define Relais2 6  // Tisch hebt sich links
#define Relais3 7  // Tisch hebt sich rechts

// Figur 1 Mann links aus Sicht der Zuschauer
#define F1AugenMitte 150
#define F1AugenRechts 199
#define F1AugenLinks 101

#define F1MundAuf 102  
#define F1MundZu 142  

#define F1RumpfVorne 100
#define F1RumpfHinten 200

#define F1HandOben 100
#define F1HandUnten 200
// Figur 2 Frau rechts aus Sicht der Zuschauer
#define F2AugenMitte 150
#define F2AugenRechts 200
#define F2AugenLinks 100

#define F2MundAuf 118
#define F2MundZu 152

#define F2RumpfRechts 200
#define F2RumpfLinks 100

#define F2HandOben 100
#define F2HandUnten 200

#define anfangWarten 15 // 25 Sekunden warten bis Sprache kommt
#define schnauzAus 10 // 10 Sekunden bis Auschalten nach Stille
// Reihenfolge der Sequenzen {tisch,kindertraum,geleitschutz}
const unsigned char R2an[] ={108,0,0};  // Tisch links
const unsigned char R2aus[] ={120,0,0};
const unsigned char R3an[] ={0,0,116};  // Tisch rechts
const unsigned char R3aus[] ={0,0,123};

typedef enum {stop,mitte,rechts,links,vorne,hinten,oben,unten,bewegen,bewegen2} bewegungen;

//volatile unsigned char test = 3;
volatile unsigned char sprachSituation=0; //links,rechts
volatile bewegungen F1Zustand[3] ={bewegen,bewegen,bewegen}; //Augen,Neigung,Hand
volatile bewegungen F2Zustand[3] ={mitte,stop,stop}; //Augen,Drehung,Hand

volatile unsigned char servosAn=0,aktServo=0, servo[8]={150,150,150,150,150,150,150,150}; //100 = 1000ms,200 = 2000ms

volatile unsigned int systemZeit =0; // Inkrement alle 20ms durch Timer1
volatile unsigned int sekundenUhr = 0; // Inkrement jede Sekunde
volatile unsigned char lebensTakt = 0; // Takt fr Lebenssteuerung der Figuren
volatile unsigned char keyEnter = 0,keyExit =0; // Ergebnisse Muenzpruefung
volatile char ausTimer = 0;

void setF1(bewegungen a,bewegungen b, bewegungen c){
  F1Zustand[0] = a;
  F1Zustand[1] = b;
  F1Zustand[2] = c;
}

void F1leben(){ // Mann Links
  const unsigned char F1RumpfSpeed = 1;
  const unsigned char F1HandSpeed =2;
  const bewegungen Augenbewegung[] ={mitte,mitte,mitte,links,links,mitte,rechts,mitte,mitte,rechts,links,links,links}; // max 16 Eintraege

  static unsigned char augenposition = 0;
  static unsigned char richtungen = 0; // Bit 0 Rumpf, Bit 1 Hand
  bewegungen n;
  unsigned char i;
  if (F1Zustand[0] == bewegen){
    augenposition++;
    i = augenposition >>4;
    if (i >= sizeof(Augenbewegung)){
      augenposition = 0;
      i =0;
    }
    n = Augenbewegung[i];
  }
  else
    n = F1Zustand[0];
  switch(n){
    case stop:
      break;
    case mitte:
      servo[F1Augen] = F1AugenMitte;
      break;
    case rechts:
      servo[F1Augen] = F1AugenRechts;
      break;
    case links:
      servo[F1Augen] = F1AugenLinks;
      break;
    default:;  
  }
  switch(F1Zustand[1]){ // Rumpf
    case stop:
      break;
    case vorne: if (F1RumpfVorne > servo[F1Rumpf]) servo[F1Rumpf]+=F1RumpfSpeed;
            else if (F1RumpfVorne < servo[F1Rumpf]) servo[F1Rumpf]-=F1RumpfSpeed;
      break;
    case hinten: if (F1RumpfHinten > servo[F1Rumpf]) servo[F1Rumpf]+=F1RumpfSpeed;
            else if (F1RumpfHinten < servo[F1Rumpf]) servo[F1Rumpf]-=F1RumpfSpeed;
      break;
    case bewegen:
      if (richtungen&1){
        if (F1RumpfVorne > servo[F1Rumpf]) servo[F1Rumpf]+=F1RumpfSpeed;
        else if (F1RumpfVorne < servo[F1Rumpf]) servo[F1Rumpf]-=F1RumpfSpeed;
        else richtungen &= 0b11111110;
      }
      else{
        if (F1RumpfHinten > servo[F1Rumpf]) servo[F1Rumpf]+=F1RumpfSpeed;
        else if (F1RumpfHinten < servo[F1Rumpf]) servo[F1Rumpf]-=F1RumpfSpeed;
        else richtungen |= 1;
      }
      break;
    default:;
  }
  switch(F1Zustand[2]){ // Hand
    case stop:
      break;
    case oben: if (F1HandOben > servo[F1Hand]) servo[F1Hand]+=F1HandSpeed;
            else if (F1HandOben < servo[F1Hand]) servo[F1Hand]-=F1HandSpeed;
      break;
    case unten: if (F1HandUnten > servo[F1Hand]) servo[F1Hand]+=F1HandSpeed;
            else if (F1HandUnten < servo[F1Hand]) servo[F1Hand]-=F1HandSpeed;
      break;
    case bewegen:
      if (richtungen&2){
        if (F1HandOben > servo[F1Hand]) servo[F1Hand]+=F1HandSpeed;
        else if (F1HandOben < servo[F1Hand]) servo[F1Hand]-=F1HandSpeed;
        else richtungen &= 0b11111101;
      }
      else{
        if (F1HandUnten > servo[F1Hand]) servo[F1Hand]+=F1HandSpeed;
        else if (F1HandUnten < servo[F1Hand]) servo[F1Hand]-=F1HandSpeed;
        else richtungen |= 2;
      }
      break;
    default:;
  }
}

void setF2(bewegungen a,bewegungen b, bewegungen c){
  F2Zustand[0] = a;
  F2Zustand[1] = b;
  F2Zustand[2] = c;
}

void F2leben(){
  const unsigned char F2RumpfSpeed = 2;
  const unsigned char F2HandSpeed =2;
  const bewegungen Augenbewegung[] ={mitte,mitte,links,rechts,rechts,mitte,mitte,rechts,links}; // max 15 Eintraege

  static unsigned char augenposition = 0;
  static unsigned char richtungen = 0; // Bit 0 Rumpf, Bit 1 Hand
  bewegungen n;
  unsigned char i;
  if (F2Zustand[0] == bewegen){ // Augen
    augenposition++;
    i = augenposition >>4;
    if (i >= sizeof(Augenbewegung)){
      augenposition = 0;
      i =0;
    }
    n = Augenbewegung[i];
  }
  else
    n = F2Zustand[0];
  switch(n){
    case stop:
      break;
    case mitte:
      servo[F2Augen] = F2AugenMitte;
      break;
    case rechts:
      servo[F2Augen] = F2AugenRechts;
      break;
    case links:
      servo[F2Augen] = F2AugenLinks;
      break;
    default:;  
  }
  switch(F2Zustand[1]){ // Rumpf
    case stop:
      break;
    case rechts: if (F2RumpfRechts > servo[F2Rumpf]) servo[F2Rumpf]+=F2RumpfSpeed;
            else if (F2RumpfRechts < servo[F2Rumpf]) servo[F2Rumpf]-=F2RumpfSpeed;
      break;
    case links: if (F2RumpfLinks > servo[F2Rumpf]) servo[F2Rumpf]+=F2RumpfSpeed;
            else if (F2RumpfLinks < servo[F2Rumpf]) servo[F2Rumpf]-=F2RumpfSpeed;
      break;
    case bewegen:
     if (richtungen&1){
       if (F2RumpfRechts > servo[F2Rumpf]) servo[F2Rumpf]+=F2RumpfSpeed;
       else if (F2RumpfRechts < servo[F2Rumpf]) servo[F2Rumpf]-=F2RumpfSpeed;
       else richtungen &= 0xfe;
     }
     else{
       if (F2RumpfLinks > servo[F2Rumpf]) servo[F2Rumpf]+=F2RumpfSpeed;
       else if (F2RumpfLinks < servo[F2Rumpf]) servo[F2Rumpf]-=F2RumpfSpeed;
       else richtungen |= 1;
     } 
     break;
    default:;
  }
  switch(F2Zustand[2]){  // Hand
    case stop:
      break;
    case oben: if (F2HandOben > servo[F2Hand]) servo[F2Hand]+=F2HandSpeed;
            else if (F2HandOben < servo[F2Hand]) servo[F2Hand]-=F2HandSpeed;
      break;
    case unten: if (F2HandUnten > servo[F2Hand]) servo[F2Hand]+=F2HandSpeed;
            else if (F2HandUnten < servo[F2Hand]) servo[F2Hand]-=F2HandSpeed;
      break;
    case bewegen:
     if (richtungen&2){
       if (F2HandOben > servo[F2Hand]) servo[F2Hand]+=F2HandSpeed;
       else if (F2HandOben < servo[F2Hand]) servo[F2Hand]-=F2HandSpeed;
       else richtungen &= 0b11111101;
     }
     else {
       if (F2HandUnten > servo[F2Hand]) servo[F2Hand]+=F2HandSpeed;
       else if (F2HandUnten < servo[F2Hand]) servo[F2Hand]-=F2HandSpeed;
       else richtungen |= 2;
     }
    default:;
  }
}
void vuMeter(unsigned char n){ // Anzeige des Pegels
  int vu;
  if (n<8){
    if (n<2){
      if (n<1)
        vu = 0;
      else
        vu = 1;
    }
    else{
     if (n<4)
       vu = 3;
     else
       vu = 7;
    }
  }
  else{
    if (n<16)
      vu = 15;
    else if (n<32)
      vu = 31;
    else
      vu = 63;
  }
  vu = ~vu;
  CDOut = (CDOut & 0b00000111) | (vu<<3); // Bit 7..3
}

#define anzahlSamples 40
#define sprechSchwelle 4
#define servoInkrement 5
#define defaultAudio 5
unsigned char maxAudioF1 = defaultAudio;
unsigned char maxAudioF2 = defaultAudio;

ISR(ADC_vect){
  static unsigned char wert = 0, i=0;
  unsigned char n;
  n = ADCH; // ADC auslesen
  if (i<=1); // Messung verwerfen
  else if (i<anzahlSamples){ // Maximum aus den Messungen
    if (n>wert) wert = n;
  }else if (i==anzahlSamples){ // Ausgeben F1 und Kanal umschalten
    // Achtung es muss gelten Mundzu > Mundauf
    if (wert > maxAudioF1) maxAudioF1 = wert;
    if (wert > (maxAudioF1>>3)){
      n = servo[F1Mund] - servoInkrement;
      if (n<F1MundAuf) servo[F1Mund]=F1MundAuf;
      else servo[F1Mund]= n;
      sprachSituation |= 1;
      ausTimer =0;
    }
    else {
      n = servo[F1Mund] + servoInkrement;
      if (n>F1MundZu) servo[F1Mund]=F1MundZu;
      else servo[F1Mund]= n;
    }
    if (vuTest) vuMeter(wert);
    wert = 0;
    ADMUX |= 1; // Kanal 1
  }
  else if (i<=anzahlSamples+2); // Messung verwerfen
  else if (i<2*anzahlSamples){ // Maximum aus 18..23 
    if (n>wert) wert = n;
  }else{ // Ausgeben F2 und Kanal umschalten
    // Achtung es muss gelten Mundzu > Mundauf
    if (wert > maxAudioF2) maxAudioF2 = wert;
    if (wert > (maxAudioF2>>3)){
      n = servo[F2Mund] - servoInkrement;
      if (n<F2MundAuf) servo[F2Mund]=F2MundAuf;
      else servo[F2Mund]= n;
      sprachSituation |= 2;
      ausTimer = 0;
    }
    else {
      n = servo[F2Mund] + servoInkrement;
      if (n>F2MundZu) servo[F2Mund]=F2MundZu;
      else servo[F2Mund]= n;
    }
    if (vuTest) vuMeter(wert);
    wert = 0;
    ADMUX &= 0xfe; // Kanal 0
    i = 0;
  }
  i++;
}
/*ISR(ADC_vect){
  static unsigned char wert = 0, i=0;
  char n;
  n = ADCH; // ADC auslesen
  if (i<=1); // Messung verwerfen
  else if (i<anzahlSamples){ // Maximum aus den Messungen 1..5
    if (n>wert) wert = n;
  }else if (i==anzahlSamples){ // Ausgeben und Kanal umschalten
    // Achtung es muss gelten Mundzu > Mundauf
    if (wert >sprechSchwelle){
      n = servo[F1Mund] - servoInkrement;
      if (n<F1MundAuf) servo[F1Mund]=F1MundAuf;
      else servo[F1Mund]= n;
      sprachSituation |= 1;
      ausTimer =0;
    }
    else {
      n = servo[F1Mund] + servoInkrement;
      if (n>F1MundZu) servo[F1Mund]=F1MundZu;
      else servo[F1Mund]= n;
    }
    if (vuTest) vuMeter(wert);
    wert = 0;
    ADMUX |= 1; // Kanal 1
  }
  else if (i<=anzahlSamples+2); // Messung verwerfen
  else if (i<2*anzahlSamples){ // Maximum aus 18..23 
    if (n>wert) wert = n;
  }else{ // Ausgeben und Kanal umschalten
    // Achtung es muss gelten Mundzu > Mundauf
    if (wert >sprechSchwelle){
      n = servo[F2Mund] - servoInkrement;
      if (n<F2MundAuf) servo[F2Mund]=F2MundAuf;
      else servo[F2Mund]= n;
      sprachSituation |= 2;
      ausTimer = 0;
    }
    else {
      n = servo[F2Mund] + servoInkrement;
      if (n>F2MundZu) servo[F2Mund]=F2MundZu;
      else servo[F2Mund]= n;
    }
    if (vuTest) vuMeter(wert);
    wert = 0;
    ADMUX &= 0xfe; // Kanal 0
    i = 0;
  }
  i++;
}
*/
ISR(TIMER1_CAPT_vect){  // ISR wird alle 20ms aufgerufen
  static unsigned char su=0,lt=0; // interne Zaehler fuer Takte
  if (servosAn){
    ServosOut = 1; // Servo1 an
    OCR1A= servo[0]*10+TCNT1; // Zeitmarke setzen
  }
  systemZeit++;
  su++;
  lt++;
  if (su >= 50){
    su=0;
    sekundenUhr++;
    ausTimer++;
  }
  if (lt >= 3){
    lt =0;
    lebensTakt++;
  }
  aktServo =0; // wieder Start mit Servo 0
}

ISR(TIMER1_COMPA_vect){  // ISR fuer die Flankensteuerung der Servos
  if (servosAn && aktServo < 8){
    aktServo++;
    ServosOut = 1 << aktServo; // neuen Servo an
    //OCR1A= (((servo[aktServo]<<1)+servo[aktServo])<<1)+1000+TCNT1; // Zeitmarke setzen
    OCR1A= servo[aktServo]*10+TCNT1; // Zeitmarke setzen
  }
}

void keyCheck(){
  static unsigned char keyOld = 0; // alter Zustand
  unsigned char keyTest,tmp;
  keyEnter = 0, keyExit = 0;
  keyTest = ~MuenzIn >> 2 & 0b00111111;// Einlesen und zurechtschieben
  if (keyOld != keyTest){  // hat sich was getan
    _delay_ms(5); // Prellen abwarten
    tmp = (~MuenzIn >> 2)&0b00111111; // nochmal Einlesen und zurechtschieben
    if (tmp == keyTest){  // ist es stabil?
      keyEnter = (~keyOld) & keyTest; // steigende Flanke !alt und neu
      keyExit = keyOld & (~keyTest);  // fallende Flanke alt und !neu
      keyOld = keyTest;
    }
  }
}
void testeHardware(){
  unsigned int altSekundenUhr =0;
  char komponente =0;
  while(1){
    if (altSekundenUhr != sekundenUhr){ // warten bis Sekunde rum ist
      altSekundenUhr = sekundenUhr;
      switch(komponente){
        case 0: MuenzOut |= 1<<M1Led;
          break;
        case 1: MuenzOut &= ~(1<<M1Led);
                MuenzOut |= 1<<M2Led;
          break;
        case 2: MuenzOut &= ~(1<<M2Led);
                RelaisOut |= 1<<M3Led;
          break;
        case 3: RelaisOut &= ~(1<<M3Led);
                RelaisOut |= 1<<Relais1;
          break;
        case 4: RelaisOut &= ~(1<<Relais1);
                RelaisOut |= 1<<Relais2;
          break;
        case 5: RelaisOut &= ~(1<<Relais2);
                RelaisOut |= 1<<Relais3;
          break;
        case 6: RelaisOut &= ~(1<<Relais3);
                CDOut &= ~(1<<CD1);
          break;
        case 7: CDOut |= 1<<CD1;
                CDOut &= ~(1<<CD2);
          break;
        case 8: CDOut |= 1<<CD2;
                CDOut &= ~(1<<CD3);
          break;
        case 9: CDOut |= 1<<CD3;
                CDOut &= ~(1<<CD4);
          break;
        case 10:CDOut |= 1<<CD4;
                CDOut &= ~(1<<CD5); 
          break;
        case 11:CDOut |= 1<<CD5;
                CDOut &= ~(1<<CD6);
          break;    
        case 12:CDOut |= 1<<CD6;
          break;                  
        default: komponente = -1;
     }
     komponente++;
    }
  }
}
int main(){
  CDDdr = 0b11111100;
  CDOut = 0b11111100; // Steuerung mit negativer logik
  ServosDdr = 0xff;        // Alle auf Ausgang
  MuenzDdr = 0b00000011; //
  RelaisDdr = 0b11110000; // drei Realais und M3Led
  ADMUX = 0b11100000; // Interne Ref, linksbuendig, ADC1
  ADCSRA = 0b11111111;  // ADWandler Free Running, Interrupt frei CLK/128
  ICR1 = 19999;            // Signal alle 20 ms
  TCCR1A = 0b00000000;    // Timer1 CTC Mode
  TCCR1B = 0b00011010;    // Keine Vorteilung Timer mit CPU-CLK / 8
  TIMSK =  0b00110000;    // ICF1 und OC1A Interrupt Enable
  lcd_init();
  lcd_string("BruScheiKa");
  sei();        // globale Interruptfreigabe
  unsigned char altLebensTakt =0;
  unsigned int altSekundenUhr =0;
  unsigned char n=0,f1Laber=0,f2Laber=0;
  if (HardWareTest) testeHardware();
  typedef enum {blinken,anfang,startdelay,vorstellung,ende} automatenstatustyp;
  automatenstatustyp automatenstatus = blinken;
  typedef enum {tisch,kindertraum,geleitschutz} sequenztyp;
  const char sqt1[] = "Tisch";
  const char sqt2[] ="Kindertraum";
  const char sqt3[] ="Geleitschutz";
  const char *sequenzName[] = {sqt1,sqt2,sqt3};
  sequenztyp sequenz = tisch;
schedul:
  while(automatenstatus == blinken){        // Warten auf Geld
    servosAn=0;
    keyCheck(); // Muenzeinwurf ueberpruefen
    if (keyEnter){
      if (keyEnter&0b00000011) sequenz = tisch; // Muenze an M1
      else if (keyEnter&0b00001100) sequenz = kindertraum; // Muenze an M2
      else if (keyEnter&0b00110000) sequenz = geleitschutz; // Muenze an M3
      automatenstatus = anfang;
      lcd_clear();
      lcd_string("Anfang ");
      lcd_string(sequenzName[sequenz]);
      break;
    }
    if (altSekundenUhr != sekundenUhr){ // Blinken der LED
      altSekundenUhr = sekundenUhr;
      if (sekundenUhr&1){
        n = ~MuenzIn>>2; // Muenzer einlesen
        if ((n & 0b11) ==0) // keine Verstopfung bei M1
          MuenzOut |= 1;
        if ((n & 0b1100) ==0) // keine Verstopfung bei M2
          MuenzOut |= 2;
        if ((n & 0b110000) ==0) // keine Verstopfung bei M3
          RelaisOut |= 1<<M3Led;
      }
      else{  // LED ausschalten
        MuenzOut &= ~(3); // M1Led und M2Led
        RelaisOut &= ~(1<<M3Led);
      }
    }

  }
  if (automatenstatus == anfang){  // Anfang
    servosAn=1;
    altSekundenUhr=sekundenUhr=0;
    automatenstatus = startdelay;
    RelaisOut |= 1<<Relais1;  // Summenrelais an
    switch (sequenz){
      case tisch:
        CDOut &= ~(1<<CD1); // CD1 an negative Logik
        break;
      case kindertraum:
        CDOut &= ~(1<<CD2); // CD2 an negative Logik
        //CDOut &= ~(1<<CD1); // CD1 an negative Logik
        break;
      case geleitschutz:
        CDOut &= ~(1<<CD3); // CD3 an negative Logik
        //CDOut &= ~(1<<CD1); // CD1 an negative Logik
        break;
    }
  }
  while(automatenstatus == vorstellung || automatenstatus == startdelay){
    if (automatenstatus == startdelay){
      if (sekundenUhr > anfangWarten){
        automatenstatus = vorstellung;
        ausTimer = 0;
        maxAudioF1 = defaultAudio; // Lautstaerke auf Standard setzen
        maxAudioF2 = defaultAudio;
        lcd_clear();
        lcd_string("Vorstellung ");
        //lcd_string(sequenzName[sequenz]);
      }
    }
    else if (ausTimer > schnauzAus) automatenstatus = ende;   // wenn ruhe dann Ende
    if (altSekundenUhr != sekundenUhr){ // bei sekundenwechsel
      altSekundenUhr = sekundenUhr;
      if (R2an[sequenz]>0 && sekundenUhr == R2an[sequenz]) RelaisOut |= 1<<Relais2;
      if (R2aus[sequenz]>0 && sekundenUhr == R2aus[sequenz]) RelaisOut &= ~(1<<Relais2);
      if (R3an[sequenz]>0 && sekundenUhr == R3an[sequenz]) RelaisOut |= 1<<Relais3;
      if (R3aus[sequenz]>0 && sekundenUhr == R3aus[sequenz]) RelaisOut &= ~(1<<Relais3);

    }
    if (altLebensTakt !=lebensTakt){
      altLebensTakt =lebensTakt;
      if (automatenstatus == startdelay || lebensTakt&1)
        RelaisOut |= 1<<M3Led;
      else
        RelaisOut &= ~(1<<M3Led);
      //MuenzOut = (MuenzOut & 0b11111100) | sprachSituation;
      if (sprachSituation &1) f1Laber = 15; // wenn 1 dann Delay setzen
      else if (f1Laber>0){ // Nachlauf zum ueberbruecken von Sprechpausen
        f1Laber--;
        sprachSituation |= 1;
      }
      if (sprachSituation &2) f2Laber = 15; // wenn 1 dann Delay setzen
      else if (f2Laber>0){
        f2Laber--;
        sprachSituation |= 2;
      }
      MuenzOut = (MuenzOut & 0b11111100) | sprachSituation;
      switch (sprachSituation){
        case 0:        // keiner spricht
          setF1(bewegen,bewegen,bewegen); // Augen Rumpf (vorne hinten) Hand
          setF2(bewegen,bewegen,bewegen); // Augen Rumpf (rechts links) Hand
          break;
        case 1:        // links spricht Mann sitzt links
          setF1(bewegen,bewegen,bewegen);
          setF2(bewegen,rechts,bewegen);
          break;
        case 2:        // rechts spricht Frau steht rechts
          setF1(mitte,hinten,unten);
          setF2(bewegen,links,stop);
          break;
        case 3:        // beide sprechen
          setF1(rechts,bewegen,bewegen);
          setF2(links,links,stop);
          break;
      }
      sprachSituation = 0;
      F1leben();
      F2leben();
    }
  }
  if (automatenstatus == ende){  // Ende der Vorstellung
    servosAn=0;
    ServosOut=0; // wirklich ausschalten
    automatenstatus = blinken;
    RelaisOut &= 0b00011111;  // Summenrelais aus
    CDOut = 0b11111100; // alle CDs aus Steuerung mit negativer logik
    char Buffer[20];
    lcd_clear();
    lcd_string("AudioF1 ");
    itoa(maxAudioF1, Buffer, 10 );
    lcd_string( Buffer );
    lcd_setcursor( 0, 2 );
    lcd_string("AudioF2 ");
    itoa(maxAudioF2, Buffer, 10 );
    lcd_string( Buffer );
  }
  goto schedul;
  return 0;
}