Forum: Mikrocontroller und Digitale Elektronik Ereignis innerhalb einer Zeit


von Heinz der Heizer (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Leute!

Ich habe vor, mir eine Standheizung für mein Auto mittels ATmega8 und 
einem Handy zu bauen.
Das Handy wird an den PinB-1 angeschlossen.
Sobald jemand zwei mal innerhalb 10 sec. (warte_sec()) auf diesem Handy 
anruft, soll die Heizung (PinB-0) eingeschaltet werden und nach einer 
gewünschten Zeit (warte_minuten()) wieder ausgeschaltet werden.

Ich habe bei dem C-Programm das Problem, dass nicht abgefragt wird, ob 
bereits schon einmal innerhalb der 10 sec. angerufen hat.

Im Anhang findet ihr das gesamte Programm.


Könntet ihr mir helfen?


Vielen Dank!

von Heinz der Heizer (Gast)


Lesenswert?

Nachtrag:

Der Prescaler war hier in meinem Upload falsch eingestellt. er müsste 
auf (CS00)|(CS02) stehen, damit er auf 1024 kommt. Das hat aber nicht 
zur Lösung beigetragen.

von Matthias L. (Gast)


Lesenswert?

Wie wäre es mit einer sauberen Lösung?
Eine Schrittkette:

Schritt1: Heizung aus
Transition1: Handyempfang => Schritt2

Schritt2: Heizung aus
Transition1: Handyempfang => Schritt3
Transition2: Timeout 10sek => Schritt1

Schritt3: Heizung ein
Transition1: Timeout x Minuten => Schritt1


So wird das sauber gelöst.
Unsetzung siehe mein Beitrag (Datum: 23.01.2008 11:36)
Beitrag "Programm bzw. Ablaeufe steuern"

von Marcus W. (blizzi)


Lesenswert?

Das Programm compiliert bei mir gar nicht.

Du misshandelst den Timer:
Einen Timeroverflow fragt man gewöhnlich per Interrupt ab und das 
Overflowflag wird nicht per Hand gelöscht sondern automatisch wenn deine 
Interruptroutine ausgeführt wird.

return 2;  <--- return ist keine variable, die du später in
if ((AnrufAnzahl>1) && (return == 2))     abfragen kannst

Ich würde dir raten dich noch einmal in
- Timer und
- Funktionen und Rückgabewerte
einzulesen.

Kannst du den Code so überhaupt compilieren?

Der Vorschlag von Matthias ist langfristig gesehen auch sehr hilfreich, 
wenn dein Projekt mal größer wird blickst du sonst garnicht mehr durch.

Gruß Marcus

von Heinz der Heizer (Gast)


Lesenswert?

Das Projekt wird nicht größer werden, mehr als die genannte Aufgabe soll 
der AVR garnicht machen...

Ja ich kann den Code so kompilieren. Wie mache ich denn dann den 
Rückgabewert für die Funktion, anstelle des "return 2"?


Wieso muss ich unbedingt mit Interrupts arbeiten? Das wird doch wohl 
auch ohne gehen ?!


Danke

von Karl H. (kbuchegg)


Lesenswert?

Dein Programm ist ziemlich mies, und ehrlich gesagt hab ich
da jetzt keine Lust, das gross auf Interrupt Betrieb umzustellen.
Das grundelgende Handwerkszeug dazu findest du im GCC_Tutorial
hier.

Aber: Zu deinem generellen Code Stil
Wenn du 'while' meinst, dann schreib auch 'while'.
So was:
  for(;zuende==0;)
ist maximal ein Schuss ins eigene Knie. Und wenn du dich heute
nicht ins eigene Knie schiesst, dann eben morgen.

  while( zuende == 0 )
  {
  }

PS: Die andere 'while'-Schleife in deinem Code sieht mehr als
suspekt aus.
Aber wie gesagt: Besser wäre es, wenn du auf Interrupt umstellst.

von Heinz der Heizer (Gast)


Lesenswert?

ja, wie karl-heinz schon sagt, "groß" auf Interrupt-Betrieb 
umstellen....

Mir ist wiederum der Interrupt suspekt. Ich muss zugeben, dass ich sie 
noch nie verwendet habe.

Die for-Schleife ist zwar nicht elegant, aber die Funktion deiner 
genannten while-Schleife müsste sie auch machen oder?

Aber wie würde es denn OHNE Interrupts funktionieren?


Danke

von Björn (Gast)


Lesenswert?

wie macht man denn eig. dann einen rückgabe-wert, wenn das von mir 
programmierte "return" nicht klappen soll?!


danke

von Heinz der Heizer (Gast)


Lesenswert?

bin übrigens nicht DER Björn mit dem Lichtschrankenzeug. ich bin ein 
anderer^^ nur zum Klarstellen ;)

von Heinz der Heizer (Gast)


Lesenswert?

jetzt mal eine konkrete Frage:

Geht mein Projekt nur mit einem Interrupt-gesteuertem Programmablauf, 
oder würde es auch ohne gehen?

danke

von Stefan O. (stefano)


Lesenswert?

Björn wrote:
> wie macht man denn eig. dann einen rückgabe-wert, wenn das von mir
> programmierte "return" nicht klappen soll?!
>
>
> danke


Du hast eine Funktion mit dem Funktionsprototypen in der Form:

Rueckgabedatentyp Funktionsname(Eingabedatentyp Variablenbezeichnung);


konkretes Beispiel:
1
int zaehleZweiDazu(int);

Der Funktionscode deiner Funktion:
1
int zaehleZweiDazu(int eingabewert)
2
{
3
 return eingabewert + 2;
4
}
Nun moechtest du angenommen die Funktion im Hauptprogramm verwenden:
1
void main(void)
2
{
3
 int zahl = 0;   // Eine Variable anlegen und auf 0 setzen
4
 
5
 zahl = zaehleZweiDazu(3);  
6
/* An dieser stelle wird die Funktion mit dem "Parameter" 3 aufgerufen. Die Zahl 3 wird an dieFunktion übergeben. Die Funktion zählt dieser Zahl 2 hinzu und gibt das Ergebniss (5) mit return zurück.*/
7
8
// Die Variable Zahl hat jetzt den Wert 5!
9
10
}

von Heinz der Heizer (Gast)


Angehängte Dateien:

Lesenswert?

ok jetzt von der Funktionsrückgabewert abgesehen, hier mal der 
Code,soweit ich ihn nun habe , könntet ihr mal einen Blick draufwerfen 
ob es so funktionieren würde?



Danke

von Heinz der Heizer (Gast)


Lesenswert?

das Abfragen des Rückgabewerts klappt aber trotzdem nicht:

1
if ((AnrufAnzahl==2) && (warte_sec()==1))   //wenn Anrufzahl=2 UND die Funktion warte_sec() den Rückgabewert 1 liefert...
2
      {
3
        AnrufAnzahl=0;
4
        PORTB = 0xFF;
5
        warte_minuten(1);
6
        PORTB = 0;
7
      }


Compiler meldet : ../standheizung.c:53: error: too few arguments to 
function `warte_sec'


Danke

von Karl H. (kbuchegg)


Lesenswert?

Heinz der Heizer wrote:
> das Abfragen des Rückgabewerts klappt aber trotzdem nicht:

Fehlermeldungen sind dazu da, dass man sie auch liest.
OK. Manchmal sind Fehlermeldungen auch mal verwirrend, aber
nicht in diesem Fall.

Was sagt der Compiler:

> Compiler meldet : ../standheizung.c:53: error: too few arguments to
> function `warte_sec'

Wenn man das mal Wort für Wort übersetzt.
Zu wenige Argumente an Funktion 'warte_sec'

Argumente sind die Dinger, die in eine Funktion hineingehen.
Also schaun wir mal. Hier ist der Aufruf

(warte_sec()==1

An warte_sec wird nichts übergeben.

Wie sieht die Funktion aus?

int warte_sec( int i )

Aha. warte_sec möchte ein Argument haben. Du übergibst aber keines.
Daher: Zuwenig Argumente

von Heinz der Heizer (Gast)


Lesenswert?

hier mal der jetzige Code:

das Programm funktioniert natürlich immer noch nicht....
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
5
//----------------------------------------------------------------------------------
6
7
short warte_sec(int);
8
9
short warte_sec(int i)
10
{
11
  TCNT0 = 0;
12
  TCCR0 |=  (1<<CS00)|(1<<CS02);                // Prescaler auf 1024
13
  int overflow=0;
14
  short zuende=0;
15
16
  for(;zuende==0;)
17
  {
18
    if (TIFR &(1<<TOV0))                   // wenn generell Überlauf, erhöhe Variable "uberlauf" um eins
19
      {
20
      while(overflow<(4*i))
21
      {
22
          overflow++;
23
          //TIFR &= ~(1<<TOV0);                  // Flag wieder auf NULL setzen
24
        }
25
    zuende=1;
26
    }
27
                            // Liefere 1 zurück, wenn gewünschte sec-Zeit verstrichen
28
  }
29
return 1;  
30
}
31
//----------------------------------------------------------------------------------
32
void warte_minuten(int y)
33
{
34
  int i;
35
  for(i=0;i<y;i++)
36
  warte_sec(60);
37
}
38
//----------------------------------------------------------------------------------
39
40
void main (void)
41
{
42
  short AnrufAnzahl=0;
43
  DDRC = 0xFF;        //PortB als Ausgang
44
  DDRB = 0x00;        //PortC als Eingang
45
  PORTC = 0;
46
47
  for(;;)
48
  {  
49
    if (PINB==0x01)
50
    {
51
      AnrufAnzahl++;                //erhöhe beim Anrufen AnrufAnzahl    
52
      warte_sec(10);                    
53
      if ((AnrufAnzahl==2) && (warte_sec(2)==1))  //wenn innerhalb der 10 sec. ein weiterer Anruf getätigt wird, dann schalte Heizung ein
54
      {
55
        AnrufAnzahl=0;
56
        PORTC = 0x00;
57
        warte_minuten(1);
58
        PORTC = 0xFF;
59
      }
60
    }
61
62
}
63
}

Wo liegt noch der Fehler?


Danke

von Richard B. (rbrose)


Lesenswert?

Ich würde bei den Kommentaren Anfangen ;-)
1
  DDRC = 0xFF;        //PortB als Ausgang
2
  DDRB = 0x00;        //PortC als Eingang

von Karl H. (kbuchegg)


Lesenswert?

Warum schaust du dir nicht einfach mal an, wie das mit den
Interrupts funktioniert. Das hat doch so alles keinen
wirklichen Sinn.

Als Anfang:

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
vvolatile uint8_t g_HaveOverflow;
5
6
ISR( TIMER2_OVF_vect )   // wird aufgerufen, wenn der Timer
7
                         // den Overflow generiert
8
{
9
  g_HaveOverflow = 1;
10
}
11
12
int main()
13
{
14
  TCCR0 |=  (1<<CS00)|(1<<CS02);                // Prescaler auf 1024
15
16
  TIMSK = ( 1 << TOIE0 );       // Interrupt bei Overflow erlauben
17
18
  sei();    // Interrupts generell zulassen  
19
20
  while( 1 ) {
21
22
    if( g_HaveOverflow == 1) {   // der Timer hat einen Overflow ausgelöst
23
                                 // als Folge davon wurde die ISR
24
                                 // aufgerufen. In der ISR wurde die Variable
25
                                 // g_HaveOverflow auf 1 gesetzt.
26
      cli();
27
      g_HaveOverflow = 0;        // Den Overflow wieder scharfstellen
28
      sei();
29
30
      ....
31
    }
32
  }
33
}

Wenn ich jetzt hier keinen Tippfehler gemacht habe, wird in der
Hauptschleife der Code im Bereich .... jedesmal ausgeführt, wenn
der Timer einen Overflow hat. Von dort weg kannst du dann zb.
mitzählen wie oft das passiert und einen Sekundenpuls zu generieren.

von Heinz der Heizer (Gast)


Lesenswert?

Danke für den Interrupt-Anfang, aber ist ein Interrupt für den 
Tastendruck nicht wichtiger, als ein Interrupt wenn ein Überlauf 
stattfand?

Schade nur, dass im AVR-GCC-Tutorial nicht steht, dass IN der 
ISR-Funktion die Anweisung steht, die ausgeführt wird, wenn der 
Interrupt ausgelöst wird.

Danke

von Karl H. (kbuchegg)


Lesenswert?

Heinz der Heizer wrote:
> Danke für den Interrupt-Anfang, aber ist ein Interrupt für den
> Tastendruck nicht wichtiger, als ein Interrupt wenn ein Überlauf
> stattfand?

Der Witz ist der, dass du in derselben ISR beides machen kannst.

Oder meinst du einen Interrupt der ausgelöst wird, wenn eine
Taste gedrückt wird?
1) ist das sowieso keine gute Idee. Tasten prellen und müssen
   entprellt werden. Das kann man mit der PeDa Methode für
   Tastenentprellen wunderbar in einem Timer Overflow Interrupt
   machen.

2) Wer sagt, dass es nur eine ISR geben kann. Es gibt viele
   verschiedene Interrupts. Für jeden einzelnen kann man eine
   ISR angeben. Es ist nicht ungewöhnlich, wenn es in einem
   Programm mehrere ISR gibt.
   Genau aus dem Grund steht ja auch das TIMER0_OVF_vect bei
1
      ISR( TIMER0_OVF_vect )
   Dadurch wird diese ISR gekennzeichnet als: Wird aufgerufen
   wenn beim Timer 0 ein Overflow auftritt.

>
> Schade nur, dass im AVR-GCC-Tutorial nicht steht, dass IN der
> ISR-Funktion die Anweisung steht, die ausgeführt wird, wenn der
> Interrupt ausgelöst wird.

Na ja.
Das wäre dann in etwa so, als ob man extra betonen muss, dass
man in C innerhalb von Funktionen auch Anweisungen stehen haben
kann, die dann ausgeführt werden, wenn die Funktion aufgerufen
wird.
Eine ISR ist auch nichts anderes als eine Funktion, mit 3 Unter-
schieden:
* die Syntax der Funktionsdefinition ist ein wenig anders
* Der Aufruf der Funktion erfolgt nicht explizit durch einen
  Funktionsaufruf im Programm, sondern durch die Hardware indem
  irgendein Ereignis (in dem Fall: Timer ist übergelaufen)
  eintritt
* Da es keinen expliziten Funktionsaufruf gibt, kann man auch
  keine Argumente und keinen Returnwert haben. Jegliche Kommunikation
  mit der ISR muss über globale Variablen erfolgen (die dann auch
  volatile sein muessen)

Das wars. Mit diesen Punkten hast du den C-spezifischen Teil der
Interrupts erledigt. Der Rest ist Datenblattstudium: Welches
Bit in welchem Register muss wie gesetzt werden, damit welcher
Interrupt ausgelöst wird.

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger wrote:

>
1
> #include <avr/io.h>
2
> #include <avr/interrupt.h>
3
> 
4
> vvolatile uint8_t g_HaveOverflow;
5
> 
6
> ISR( TIMER2_OVF_vect )   // wird aufgerufen, wenn der Timer
7
>                          // den Overflow generiert

Sorry, Copy&Paste Fehler.
Das muss natürlich
1
ISR( TIMER0_OVF_vect )

heissen. Du benutzt ja den Timer 0 und nicht den Timer 2

von Heinz der Heizer (Gast)


Lesenswert?

Hallo nochmal!


Jetzt habe ich mal was mit Interrupt probiert, funktioniert aber auch 
nicht...-Wie solls auch anders sein .


Hier mal der Code
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
volatile uint8_t gTimeCounter=0;
5
6
ISR( TIMER2_OVF_vect )   // wird aufgerufen, wenn der Timer
7
                         // den Overflow generiert
8
{
9
   gTimeCounter++;
10
}
11
12
int main()
13
{
14
15
DDRB = 0xFF;
16
DDRD = 0x00;
17
18
19
  TCCR0 |=  (1<<CS00)|(1<<CS02);                // Prescaler auf 1024
20
21
  
22
  sei();    // Interrupts generell zulassen  
23
24
  while( 1 ) {
25
26
     while(!(PIND == 0xFE));
27
       TIMSK = ( 1 << TOIE0 );       // Interrupt bei Overflow erlauben
28
    
29
   while( gTimeCounter < 10000)
30
   {
31
    if(PIND == 0xFE)
32
    {
33
       PORTB = 0x00;
34
         TIMSK = ~( 1 << TOIE0 );       // Interrupt bei Overflow sperren
35
         gTimeCounter=0;
36
     TIMSK = ( 1 << TOIE0 );  
37
    }
38
    while( gTimeCounter < 10000)
39
    {
40
    PORTB = 0xFF; 
41
      }
42
    
43
   }
44
45
    if( gTimeCounter == 1) {   // der Timer hat einen Overflow ausgelöst
46
                                 // als Folge davon wurde die ISR
47
                                 // aufgerufen. In der ISR wurde die Variable
48
                                 // g_HaveOverflow auf 1 gesetzt.
49
      cli();
50
      gTimeCounter = 0;        // Den Overflow wieder scharfstellen
51
      sei();
52
53
 
54
    }
55
  }
56
}

von Heinz der Heizer (Gast)


Lesenswert?

Habt ihr eine Lösung wie man den Interrupt so setzt, dass während der 10 
sec. der Controller auf erneute Tastendrücke wartet und diese auswertet?

Danke

von Karl H. (kbuchegg)


Lesenswert?

Heinz der Heizer wrote:
> Hallo nochmal!
>
>
> Jetzt habe ich mal was mit Interrupt probiert, funktioniert aber auch
> nicht...-Wie solls auch anders sein .

Hast du die Korrektur gesehen?

> [c]
>
>
>
> #include <avr/io.h>
> #include <avr/interrupt.h>
>
> volatile uint8_t gTimeCounter=0;
>
> ISR( TIMER2_OVF_vect )   // wird aufgerufen, wenn der Timer
>                          // den Overflow generiert


Du arbeitest mit Timer 0, nicht Timer 2.
Daher muss die ISR TIMER0_OVF_vect heissen und nicht TIMER2_OVF_vect

von Heinz der Heizer (Gast)


Lesenswert?

ja ich habe die Änderung vorgenommen, hat aber auch nix gebracht....

von Karl H. (kbuchegg)


Lesenswert?

Mit welcher Taktfrequenz läuft dein µC eigentlich?

Ich kann dir leider jetzt dein Programm nicht komplett
fertig machen. Bin selber im Stress.
Aber ich kann dir ein Programm schreiben, welches bei einer 0 am
Pin D0 den Ausgang PB0 auf 1 setzt, eine gewisse Wartezeit abwartet
und danach den PB0 wieder auf 0 setzt.

Damit die Zeit aber einigermassen stimmt, brauch ich die Taktfrequenz
des µC.

von Heinz der Heizer (Gast)


Lesenswert?

Also meine Taktfrequenz ist bei 1 MHz, da ich damit am leichtesten auf 
eine Sekunde komme.

Echt nett von dir, dass du dich für mich einsetzt, ich erläutere jedoch 
nochmal kurz, was das Programm machen soll.

Wenn ein H-Pegel an PinD0 anliegt, soll das Programm prüfen, ob nach 
einer gewünschten Zeit ein weiterer H-Pegel anliegt. Wenn dies der Fall 
ist, soll PortB0 für eine bestimmte Zeit auf H gesetzt werden, danach 
wieder auf 0 zurückkehren und von vorne beginnen...


Vielen Dank!

von Karl H. (kbuchegg)


Lesenswert?

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
volatile uint16_t g_Delays;
5
volatile uint8_t  g_TimeReached;
6
7
ISR( TIMER0_OVF_vect )
8
{
9
  if( g_Delays > 0 ) {
10
    g_Delays--;
11
12
    if( g_Delays == 0 )
13
      g_TimeReached = 1;
14
}
15
16
int main()
17
{
18
  DDRD = 0x00;     // PortD auf Eingang
19
  DDRB = 0xFF;     // PortB auf Ausgang
20
21
  TCCR0 |=  (1<<CS00)|(1<<CS02);                // Prescaler auf 1024
22
  TIMSK = ( 1 << TOIE0 );       // Interrupt bei Overflow erlauben
23
24
  sei();    // Interrupts generell zulassen  
25
26
  while( 1 ) {
27
28
    if( ( PIND & ( 1 << PD0 ) == 0 )    // PinD0 auf 0 ?
29
      PORTB = 0x01;
30
31
      g_TimeReached = 0;
32
      g_Delays = 300;       // ********
33
    }
34
35
    if( g_TimeReached == 1 ) {
36
      PORTB = 0x00;
37
    }
38
  }
39
}


Die mit // ******* markierte Zeile muesste angepasst werden.
Keine Anhnung wie lange 300 Overflows auf deinem µC bei einem
Vorteiler von 1024 dauern. Hängt von deiner Taktfrequenz ab.


Der Code war noch nicht im Simulator. Sollte aber ziemlich stimmen.

von Heinz der Heizer (Gast)


Lesenswert?

ich habe noch paar Sachen wie Klammern-Setzen verbessert:

er meldet nun "undefined reference to 'main' ". Was bedeutet das?

Hier nochmal der "verbesserte" Code:

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
volatile uint16_t g_Delays;
5
volatile uint8_t  g_TimeReached;
6
7
ISR( TIMER0_OVF_vect )
8
{
9
  if( g_Delays > 0 ) {
10
    g_Delays--;
11
12
    if( g_Delays == 0 )
13
      g_TimeReached = 1;
14
}
15
16
int main()
17
{
18
  DDRD = 0x00;     // PortD auf Eingang
19
  DDRB = 0xFF;     // PortB auf Ausgang
20
21
  TCCR0 |=  (1<<CS00)|(1<<CS02);                // Prescaler auf 1024
22
  TIMSK = ( 1 << TOIE0 );       // Interrupt bei Overflow erlauben
23
24
  sei();              // Interrupts generell zulassen  
25
26
  while( 1 ) {
27
28
   if( ( PIND & ( 1 << PD0 ) == 0 ))    // PinD0 auf 0 ?
29
   {
30
    PORTB = 0x01;
31
32
      g_TimeReached = 0;
33
      g_Delays = 300;       // ********
34
    }
35
  }
36
37
    if( g_TimeReached == 1 ) {
38
      PORTB = 0x00;
39
    }
40
  }
41
}


Danke fürs Posten , das ging ja irre schnell!

von Der Leser (Gast)


Lesenswert?

Vielleicht sind meine Augen nicht mehr die Besten, aber ich glaube zum 
schluß ist eine } zuviel

von Heinz der Heizer (Gast)


Lesenswert?

Nee, Die klammer ist nicht zuviel, dann meldet er "syntax error at end 
of input"...

von Johannes M. (johnny-m)


Lesenswert?

Da ist nicht nur eine Klammer zu viel! Da stimmt alles Mögliche mit 
der Klammerung nicht. In der ISR ist eine offene Klammer, zu der es in 
der ISR keine geschlossene gibt. Dementsprechend ist main für den 
Compiler in die ISR eingebettet, und das haut nicht hin!

Gewöhne Dir generell eine saubere Einrückung an. Dann passiert sowas 
nicht. Also ungefähr so:
1
int main()
2
{
3
    while(1)
4
    {
5
        if(bedingung)
6
        {
7
            machwas();
8
            undnochwas();
9
        }
10
        else
11
        {
12
            machwasanderes();
13
            undvielleichtnochwasanderes();
14
        }
15
    }
16
}
Grundsätzlich sollten der Übersichtlichkeit halber Klammern, die 
zusammengehören, immer direkt untereinander stehen.

von Johannes M. (johnny-m)


Lesenswert?

So, ich hab Dein Programm mal neu formatiert. Jetzt müsste zumindest die 
Klammerei passen.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
volatile uint16_t g_Delays;
5
volatile uint8_t  g_TimeReached;
6
7
ISR( TIMER0_OVF_vect )
8
{
9
    if( g_Delays > 0 )
10
        g_Delays--;
11
12
    if( g_Delays == 0 )
13
        g_TimeReached = 1;
14
}
15
16
int main()
17
{
18
    DDRD = 0x00;     // PortD auf Eingang
19
    DDRB = 0xFF;     // PortB auf Ausgang
20
21
    TCCR0 |=  (1<<CS00)|(1<<CS02);                // Prescaler auf 1024
22
    TIMSK = ( 1 << TOIE0 );       // Interrupt bei Overflow erlauben
23
24
    sei();              // Interrupts generell zulassen  
25
26
    while( 1 ) 
27
    {
28
        if( ( PIND & ( 1 << PD0 ) == 0 ))    // PinD0 auf 0 ?
29
        {
30
            PORTB = 0x01;
31
            g_TimeReached = 0;
32
            g_Delays = 300;       // ********
33
        }
34
35
        if( g_TimeReached == 1 ) 
36
            PORTB = 0x00;
37
    }
38
}
Und auf die Weise sieht man immer sofort, wenn irgendwo eine Klammer 
zu viel ist! Alle mir bekannten Editoren machen die Einrückungen 
automatisch, so dass man sich da praktisch keine Gedanken machen muss.

von Heinz der Heizer (Gast)


Lesenswert?

ok danke erstmal fürs formatieren, ich werds dann vll heut abend noch 
austesten....


gruß

von Heinz der Heizer (Gast)


Lesenswert?

Ähm ist das nun der Code von Karl-Heinz Buchegger, welcher - wie ich 
geschrieben habe - die gewünschte Zeit wartet und dementsprechend den 
Port-PinD auf H-Pegel setzt?

Oder ist das der:

Karl-Heinz Buchegger wrote: Aber ich kann dir ein Programm schreiben, 
welches bei einer 0 am
Pin D0 den Ausgang PB0 auf 1 setzt, eine gewisse Wartezeit abwartet
und danach den PB0 wieder auf 0 setzt.



Vielen Dank!

von Marcus W. (blizzi)


Lesenswert?

Der Code ist doch wirklich schön übersichtlich.
Glaubst du nicht du könntest mal versuchen den selber zu analysieren?
Dabei lernt man unheimlich viel, vielleicht brauchst du dann auch nicht 
bei jedem kleinsten Schritt eine Hilfestellung.

Falls du einen der Befehle nicht verstehst und nicht in den Tutorials 
findest kannst du gerne nachfragen.

von Heinz der Heizer (Gast)


Lesenswert?

vielmehr meinte ich eigentl. ob ich den Code von Karl-Heinz so umbauen 
kann, dass er meinen Ansprüchen gerecht wird.

von Marcus W. (blizzi)


Lesenswert?

Heinz der Heizer wrote:
> vielmehr meinte ich eigentl. ob ich den Code von Karl-Heinz so umbauen
> kann, dass er meinen Ansprüchen gerecht wird.

Klar kannst du das.
Der Code zeigt dir ein funktionierendes Beispiel wie man mit einem Timer 
arbeitet. Du brauchst jetzt nur noch deine gewünschte Funktionen in den 
Code einbauen.

von Karl H. (kbuchegg)


Lesenswert?

Johannes M. wrote:
> Da ist nicht nur eine Klammer zu viel! Da stimmt alles Mögliche mit
> der Klammerung nicht.

Das muss ich auf meine Kappe nehmen. Ich war unter Zeitdruck und
habe den Code direkt im Forumseditor runtergetippt. Da sind
dann wohl einige Klammern auf der Strecke geblieben.

Wenn er sich allerdings an der Einrückung orientiert hätte und
ein bischen analysiert hätte was wohl die Idee hinter so manchem
Codestück ist, dann sollte man das aber schon sehen können, wo
welche Klammer fehlt :-)

Und ja. Der Code ist nicht als Vorstufe zu deiner Lösung zu
sehen sondern als Anschungsbeispiel, wie man mit einem Timer
Zeitsteuerungen machen kann. Wenn du verstanden hast, wie das
funktioniert, ist dein eigentliches Problem nicht mehr so schwer.

von Heinz der Heizer (Gast)


Lesenswert?

Leider ist mir das mit den Interrupts noch nicht so klar. Die einzelnen 
Befehle vielleicht schon, aber wie ich das jetzt speziell auf mein 
Problem, bzw Programm, anwenden kann, verstehe ich noch nicht.

von Kanzler Gorkon (Gast)


Lesenswert?

Wenn Du es (bis jetzt) noch nicht verstanden hast, dann schlage ich Dir 
(gut gemeint) vor, dass Du Dich zuerst einmal mit den Sachen 
beschäftigst, die Du verstehst und Dich dann selbstständig und 
schrittweise an Deine Problemstellung annäherst.

von Heinz der Heizer (Gast)


Lesenswert?

Das würde ich ja gerne, aber ich stehe nunmal vor dieser 
Problemstellung, und komme mit meinem bisherigen Wissensstand nicht 
weiter. Zudem ist mir ja nahegelegt worden, dass hier Interrupts 
unverzichtbar sind, ergo stecke ich momentan in einer Sackgasse :)

von Marcus W. (blizzi)


Lesenswert?

Heinz der Heizer wrote:
> Das würde ich ja gerne, aber ich stehe nunmal vor dieser
> Problemstellung, und komme mit meinem bisherigen Wissensstand nicht
> weiter. Zudem ist mir ja nahegelegt worden, dass hier Interrupts
> unverzichtbar sind, ergo stecke ich momentan in einer Sackgasse :)

Wir sind hier keine Dienstleister, die dir eine funktionierende Software 
schreiben. Sowas kostet in der Regel Geld.
Hier bekommst du nur Tipps und Hilfestellungen, wie du selber deine 
Software schreiben kannst.

> Leider ist mir das mit den Interrupts noch nicht so klar.

Was ist unklar?
Das die Interruptroutine jedesmal aufgerufen wird, wenn der Interrupt 
ausgelöst wird is doch klar oder? Und in deinem Fall benutzt du den 
Timer-Overflow Interrupt, der wird also jedes mal aufgerufen, wenn der 
Timer überläuft.

> aber wie ich das jetzt speziell auf mein
> Problem, bzw Programm, anwenden kann, verstehe ich noch nicht.

Tja, Programmieren muss man erlernen. Dazu braucht man Erfahrung. Das 
Handwerkszeug haben wir dir erklärt. Du weißt jetzt wie du eine LED per 
Knopfdruck eine bestimmte Zeit lang leuchten lassen kannst.
Wie man jetzt die LED erst beim zweiten Knopfdruck leuchten lassen kann 
ist jetzt deine Aufgabe.

Vom Prinzip is der Ablauf so:

Signal kommt an (High), dann wartest du bis es wieder auf Low geht.
Im Anschluss startest du einen Timer und lässt ihn z.B. 10 Sekunden 
laufen.
Damit schaltest du deine Schaltung quasi für 10 Sekunden scharf.
Wenn dann jemand anruft bevor der Timer überläuft wird die Heizung 
angeschalten. (Ein Timer steuert dann im Anschluss wie lange die Heizung 
anbleiben soll)
Läuft der Timer vorher über "entschärft" er die Schaltung wieder und die 
Heizung geht nicht an.

Auch wenn du jetzt vor dieser Problemstellung stehst:
Wenn du es nicht hinbekommst muss deine Problemstellung eben warten und 
du musst mit kleineren Sachen anfangen. Programmieren lernt man nicht 
von heute auf morgen. Dein Problem ist wirklich Programmiertechnisch, 
das hat nichts mit Elektronik oder Mikrocontrollern zu tun.
Die Alternative ist natürlich sich jemand zu suchen der dir für nen 
Kasten Bier die Software schreibt, das is vielleicht ne Sache von 15 
Minuten.

von Karl H. (kbuchegg)


Lesenswert?

Das nächste Problem, um das du dich kümmern musst ist die Erkennung
des Anrufsignals. Es reicht nicht, wenn du nur die Information hast,
dass der Eingang eine 0 aufweist. Denn diese 0 wird ein paar
Millisekunden da stehen. Du musst die Flanke erkennen, wann
das Signal den Pegel wechselt!

Und von dort weg ist es dann eigentlich einfach.
Kommt eine Flanke herein gibt es 3 Möglichkeiten

* Läuft die 10 Sekunden Zeitmessung bereits und ist die noch
  nicht abgelaufen, dann war das die zweite Flanke
  -> der Ausgang wird geschaltet und eine zweite Zeitmessung
     startet nach deren Ablauf der Ausgang wieder in seine
     Ruheposition zurückkommt
     Die 10 Sekunden Zeitmessung wird abgestellt

* Läuft die 10 Sekunden Zeitmessung noch nicht und läuft auch
  die zweite Zeitmessung noch nicht, dann war das die erste Flanke
  -> die 10 Sekunden Zeitmessung wird gestartet

* Läuft die 10 Sekunden Zeitmessung noch nicht, wohl aber die
  zweite Zeitmessung, so liegt der Fall vor, dass während der
  Heizperiode ein weiterer Anruf kam. Du musst entscheiden
  was in diesem Fall passieren soll. Zb. Könnte man die Heizung
  dann vorzeitig abschalten
  -> Ausgang in den Ruheszustand zurückschalten
     zweite Zeitmessung abschalten.

Zusätzlich gibt es noch die Fälle

* die 10 Sekunden laufen ab, ohne dass das Eintreffen einer
  zweiten Flanke die Heizung eingeschaltet hat.
  -> Es muss nichts weiter gemacht werden. Die 10 Sekunden
     Zeitmessung schaltet sich ab

* die zweite Zeitmessung, die die Heizperiode steuert läuft ab
  -> der Heizausgang wird wieder in seinen Ruhezustand zurückgesetzt.
     die zweite Zeitmessung schaltet sich ab.


Du musst anfangen in Form von "Ereignis tritt ein" zu denken und
nicht in Form von "erst mach dies, dann mach das"

von Heinz der Heizer (Gast)


Lesenswert?

Also, jetzt habe ich den Code mal soweit es ging auf meine Bedürfnisse 
angepasst:

Folgendes macht das Programm (im Simulator) bisher, so wie es sein soll: 
Wenn ich PIND0 zweimal drücke, dann kriegt PortB H-Pegel. Ansonsten 
nicht.
Außerdem schaltet er dann den PortB wieder nach einer gewissen Zeit aus. 
-Soweit sogut!

Nun habe ich noch das Problem, dass ich nicht weiß, ob er mit meinem 
Code die Zeit berücksichtigt, in der die Taste mind. 2x gedrückt werden 
soll.

Außerdem weiß ich nicht, wie ich "AnrufAnzahl" auf NULL setzen kann, 
damit nach dem Ausschalten des PortB der Mikrocontroller auf erneute 
Tastendrücke wartet.
Bisher muss ich nach Ausschalten von PortB lediglich EINMAL PinD 
betätigen, damit PortB wieder aktiv wird.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
volatile uint16_t g_Delays;
5
volatile uint8_t  g_TimeReached;
6
7
8
ISR( TIMER0_OVF_vect )
9
{
10
    if( g_Delays > 0 )
11
        g_Delays--;
12
13
    if( g_Delays == 0 )
14
  {
15
        g_TimeReached = 1;
16
  }
17
}
18
19
int main()
20
{
21
    DDRD = 0x00;     // PortD auf Eingang
22
    DDRB = 0xFF;     // PortB auf Ausgang
23
24
  int AnrufAnzahl=0;
25
26
  
27
    TCCR0 |=  (1<<CS00);                // Prescaler auf 1024
28
    TIMSK = ( 1 << TOIE0 );       // Interrupt bei Overflow erlauben
29
30
    sei();              // Interrupts generell zulassen  
31
32
    while( 1 ) 
33
    {
34
        if( (AnrufAnzahl == 0) &&( PIND & ( 1 << PD0 ) == 1 ))    // PinD0 auf 0 ?
35
        {
36
      AnrufAnzahl = 1;
37
      PIND = 0;
38
    }
39
        if ((AnrufAnzahl == 1) && ( PIND & ( 1 << PD0 ) == 1 ))
40
    {
41
      AnrufAnzahl=2;
42
            g_TimeReached = 0;
43
            g_Delays = 30;       // ********
44
        }  
45
    if (AnrufAnzahl == 2)
46
    {  
47
      PORTB = 0xFF;
48
      g_Delays = 20;
49
      //g_Delays--;
50
      AnrufAnzahl =0;
51
    }    
52
    
53
54
        if( g_TimeReached == 1 ) 
55
            PORTB = 0x00;
56
      
57
    }
58
}


Vielen Dank!

von Karl H. (kbuchegg)


Lesenswert?

Ach was solls. Mein Service Techniker in Hongkong ist mit dem
Programmupdate glücklich und ich hab ein bischen Zeit.

Aber versprich mir, dass du den Code studierst und nicht einfach
nur in einen µC brennst und gut ists.

Der Code überwacht den Pin PD0.
Kommt dort ein 0 Signal an, dann beginnt eine Torzeit von 10
Sekunden (einstellbar mit TIME_1) zu laufen. Gleichzeitig wird
eine Kontrollled eingeschaltet um anzuzeigen, dass die Schaltung
scharf ist und auf den zweiten Puls wartet. Kommt innerhalb
dieser Zeit ein zweites 0 Signal, dann wird die Kontrolled wieder
abgeschaltet, die Heizung eingeschaltet und die Heizzeit
(einstellbar mit TIME_2) beginnt.
Nach Ablauf der Heizzeit wird die Heizung wieder abgeschaltet.
Kommt innerhalb der Heizzeit ein weiterer Impuls, so wird die
Heizung vorzeitig abgeschaltet.

PORTB:    Heizung         am Pin HEATER_PIN
          Kontrolled      am Pin LED_PIN

PORTD:    Signaleingang   am Pin INPUT_PIN

Programm ist auf einem Mega16 getestet. Die Makros LED_ON, LED_OFF,
HEATER_ON, HEATER_OFF musst du anpassen, je nachdem ob ein gesetztes
Bit die Led/Heizung ein oder aus schaltet. Ich habs aber einfach
gemacht den jeweiligen Zustand zu verstehen. Im Moment sind alle
Ausgänge so benutzt, dass eine 0 die jeweilige Funktionalität
(Heizung, Led) einschaltet.

Ob du den Pullup am PortD brauchst, musst du selbst eintscheiden.
Im Moment ist der auskommentiert.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
// Taktfrequenz des Controllers
5
#define F_CPU  1000000
6
7
// wo sind Heizelement und Kontrollampe am Port B angeschlossen
8
#define HEATER_PIN   PB0
9
#define LED_PIN      PB1
10
11
// an welchem Eingang am Port D kommt das Signal herein
12
#define INPUT_PIN    PD0
13
14
#define TIME_1   10   /* 10 Sekunden - Torzeit  */
15
#define TIME_2   60   /* 60 Sekunden - Heizzeit */
16
17
#define WAIT_10    F_CPU / 1024 * TIME_1 / 256
18
#define WAIT_HEAT  F_CPU / 1024 * TIME_2 / 256
19
20
#define SET_BIT(p,b)    p |= ( 1 << b )
21
#define CLR_BIT(p,b)    p &= ~( 1 << b )
22
23
#define LED_ON     CLR_BIT( PORTB, LED_PIN )
24
#define LED_OFF    SET_BIT( PORTB, LED_PIN )
25
#define HEATER_ON  CLR_BIT( PORTB, HEATER_PIN )
26
#define HEATER_OFF SET_BIT( PORTB, HEATER_PIN )
27
28
volatile uint16_t g_10SecTimer;      // verbleibende Wartezeit - 10 Sekunden
29
volatile uint16_t g_HeatTimer;       // verbleibende Wartezeit - Heizzeit
30
31
volatile uint8_t  PrevInput;         // der vorhergehende Zustand des Input Pins
32
volatile uint8_t  Signal;            // 1 wenn ein Anruf erkannt wurde
33
34
ISR( TIMER0_OVF_vect )
35
{
36
  if( g_10SecTimer > 0 )
37
    g_10SecTimer--;
38
39
  if( g_HeatTimer > 0 )
40
    g_HeatTimer--;
41
42
  // kann am Eingangspin eine Flanke 1->0 erkannt werden?
43
  uint8_t Input = PIND & ( 1 << INPUT_PIN );
44
  if( PrevInput != Input && Input == 0 )
45
    Signal = 1;
46
  PrevInput = Input;
47
}
48
49
int main()
50
{
51
  DDRB = ( 1 << HEATER_PIN ) | ( 1 << LED_PIN );
52
53
  DDRD = 0x00;
54
//  PORTD = ( 1 << INPUT_PIN );   // ev. Pullup einschalten
55
56
  TCCR0 = ( 1 << CS02 ) | ( 1 << CS00 );
57
  TIMSK = ( 1 << TOIE0 );
58
59
  // zur Zeit läuft kein Timer
60
  g_10SecTimer = 0;
61
  g_HeatTimer = 0;
62
 
63
  PrevInput = 1;
64
  Signal = 0;
65
66
  LED_OFF;
67
  HEATER_OFF;
68
69
  sei();
70
71
  while( 1 ) {
72
73
    if( Signal ) {                   // Am Eingang ist ein Signal gekommen
74
      cli();
75
76
      Signal = 0;
77
78
      if( g_10SecTimer > 0 ) {       // läuft der 10 Sekunden Timer noch?
79
        g_10SecTimer = 0;            // 10 Sekunden abschalten
80
81
        HEATER_ON;                   // Heizung ein
82
        g_HeatTimer = WAIT_HEAT;     // Heizzeit beginnt
83
      }
84
85
      else {                         // 10 Sekunden laufen nicht
86
87
        if( g_HeatTimer > 0 ) {      // läuft die Heizung bereits?
88
          HEATER_OFF;                // Wenn ja, dann vorzeitig abschalten
89
          g_HeatTimer = 0;           // und Timer stoppen
90
        }
91
92
        else {
93
          g_10SecTimer = WAIT_10;    // Starte das 10 Sekunden Intervall
94
          LED_ON;                    // Kontrolllampe an
95
        }
96
      }
97
98
      sei();
99
    }
100
101
    if( g_HeatTimer == 0 )           // wenn die Heizzeit nicht läuft
102
      HEATER_OFF;                    // die Heizung auf jeden Fall abschalten
103
104
    if( g_10SecTimer == 0 )          // wenn die Torzeit nicht läuft
105
      LED_OFF;                       // die Kontrolllampe auf jeden Fall abschalten
106
  }
107
}

von Heinz der Heizer (Gast)


Lesenswert?

ok. Vielen Dank!

Echt nett von dir, dass du dir so eine Arbeit machst. Es hätte auch eine 
Verbesserung meines bereits geschriebenen Codes gereicht.

Ich versuche deinen Code zu verstehen.


Danke!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.