Forum: Mikrocontroller und Digitale Elektronik ATtiny2313 Sleep Mode, aber wie?


von Erikson D. (nevadakirin)


Lesenswert?

Hallo liebes Forum!

Ich habe mich gerade hier angemeldet, da ich hier schon einige Beiträge 
gelesen habe und finde, dass das hier ein sehr professionelles Forum 
ist!

Vorab, ich bin kein Profiprogrammierer, sondern ein Anfänger, also seit 
mir bitte nicht böse, falls ich nicht gleich verstehe was gemeint ist 
und, dass ich euch so ein einfaches Programm zumute.

Zu meinem Problem:

Ich baue als Projektarbeit einen Schneckenzaun, welcher zwei Leitungen 
hat. Die Leitungen sollen den Schnecken ganz leichte Stromimpulse geben, 
sodass sie sich vom Grünzeug fernhalten. Die Impulse sollen 100ms dauern 
und der Abstand zwischen jedem Impuls soll eine Sekunde dauern. Also 
sprich, 100ms Stromimpuls auf Leitung A -> eine Sekunde Pause (da soll 
der µC schlafen) -> 100ms Stromimpuls auf Leitung B -> eine Sekunde 
Pause -> usw.
Dies wird mit dem µC ATtiny2313 angesteuert. Da, dass ganze Projekt 
solarbetrieben läuft, sollte es möglichst Stromsparend sein und deshalb 
verwende ich die Sleep-Funktion des µC. Ich habe, das auch schon 
programmiert, nur funktioniert es leider nicht wie erwüscht...

Ich hoffe es ist kein Problem, wenn ich hier meinen Programmcode 
einfüge.

Programm:
1
/*
2
 * Schneckenzaun.c
3
 *
4
 * Created: 29.04.2015 10:58:30
5
 *  Author: Erik
6
 */ 
7
8
Hauptprogramm:
9
10
#define F_CPU 1000000 
11
#include <avr/io.h>
12
#include <avr/sleep.h>
13
#include <util/delay.h>
14
#include <avr/interrupt.h>
15
#define OUT1 0
16
#define OUT2 1
17
#define OUTDDR DDRB
18
#define OUTPORT PORTB
19
#include "Interrupt.h"
20
int Phase = 0;
21
22
int main(void)
23
{
24
    OUTDDR |= ((1<<OUT1) | (1<<OUT2)); // Ausgänge definieren
25
    OUTPORT &=~ ((1<<OUT1) | (1<<OUT2)); //Leitung A und B auf Low  
26
  
27
    while(1)
28
    {
29
        set_sleep_mode(SLEEP_MODE_IDLE); //Funktion spart 70% Strom
30
  sleep_enable(); //Sleep Modus zulassen
31
  sei(); //Interrupts zulassen
32
        TimerInit(); //Funktionsaufruf
33
  sleep_cpu(); // ATtiny2313 schlafen legen
34
    
35
        if((Phase == 1))
36
    {
37
            OUTPORT |= (1<<OUT1); // setze Leitung A auf High  
38
            OUTPORT &=~ (1<<OUT1); // setze Leitung A auf Low
39
                  Phase = 0; // Hilfsvariable wird auf null gesetzt
40
    }
41
    else if((Phase == 0))
42
    {
43
            OUTPORT |= (1<<OUT2); //setze Leitung B auf High
44
            OUTPORT &=~ (1<<OUT2); //setze Leitung B auf Low
45
            Phase = 1; //Hilfsvariable wird auf eins gesetzt
46
    }
47
    }
48
}
C-File:
1
#include <avr/interrupt.h>
2
#include <avr/io.h>
3
#include <avr/sleep.h>
4
5
void TimerInit()
6
{
7
  //Prescaler auf 1024 setzen (laut Datenblatt), Bitfolge 00000101
8
  //TCCR0B |= ((1<<CS00)|(1<<CS02));
9
  //TCCR0B &=~ (1<<CS01); 
10
  TCNT1 = 0xFFFFFFFFFFFFFFFF - 1000;
11
  TIMSK |= 0x01;
12
} 
13
14
ISR(INT0_vect)
15
{
16
  sleep_enable();
17
  TCNT1 = 0xFFFFFFFFFFFFFFFF - 100;
18
  sleep_disable();
19
  cli();
20
}
Ich bedanke mich vorab, für eure Antworten!
mfG Erik

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

1
void TimerInit()
2
{
3
  //Prescaler auf 1024 setzen (laut Datenblatt), Bitfolge 00000101
4
  //TCCR0B |= ((1<<CS00)|(1<<CS02));
5
  //TCCR0B &=~ (1<<CS01); 
6
  TCNT1 = 0xFFFFFFFFFFFFFFFF - 1000;
7
  TIMSK |= 0x01;
8
}


dem Timer ist kein Vorteiler zugeordnet. Daher läuft der auch nicht. 
Läuft der Timer nicht, generiert er auch keine Interrupts.


halte dich bitte an das Schema
1
...
2
3
int main()
4
{
5
6
   Initialisieren und Konfigurieren der Hardware
7
8
   sei();   // letzte Aktion vor der Hauptschleife
9
   while( 1 )
10
   {
11
      Programmlogik unter Verwendung der bereits im Initialisierungsteil
12
      konfigurierten Hardwarekomponenten
13
   }
14
}

nicht alles so Kraut und Rüben durcheinander.

Wenn der Timer einmal eingestellt ist, dann braucht der nicht dauernd 
neu eingestellt werden.
Ein einmal eingestellter Sleep Modus braucht auch nicht dauernd neu 
eingestellt werden.

Wenn es in einer ISR nichts zu tun gibt, dann kann die ruhig leer 
bleiben. Man muss da nicht mit Gewalt irgendwelche Statements 
reinquetschen.

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

Erikson Dietrich schrieb:
> TIMSK |= 0x01;

Wo ist die ISR dazu?

Um die Kleinigkeiten kümmern wir uns später.

mfg.

von Karl H. (kbuchegg)


Lesenswert?

Ausserdem würde ich vorschlagen, erst mal alles ohne die 
Sleep-Geschichte zu machen.

Erst mal muss der Rest stimmen. Die Ergänzung, dass der µC sich in den 
Zwischenphasen, in denen es nichts zu tun gibt aufs Ohr legt, wird zum 
Schluss nachgerüstet. Aber erst mal muss dein Timer samt zugehörigen 
Aktionen funktionieren.

FAQ: Timer

Im Moment verzettelst du dich, indem du dem sleep viel zu viel 
Aufmerksamkeit schenkst. Das ist in deinem Programm der Nebenschauplatz 
und keineswegs das Wichtigste.

: Bearbeitet durch User
von Erikson D. (nevadakirin)


Lesenswert?

@Karl Heinz:

Okay, ich verstehe!
Ich habe in meiner Funktion (TimerInit()), nun den Prescaler gesetzt, 
also meinen Programmcode, wieder freigegeben und das Hauptprogramm habe 
ich folgendermaßen geändert:
1
Hauptprogramm
2
3
int main(void)
4
{
5
   OUTDDR |= ((1<<OUT1) | (1<<OUT2)); // Ausgänge definieren
6
   OUTPORT &=~ ((1<<OUT1) | (1<<OUT2)); //Leitung A und B auf Low
7
   set_sleep_mode(SLEEP_MODE_IDLE); // Funktion spart 70% Strom
8
   sleep_enable(); //Sleep Modus zulassen
9
   TimerInit(); //Funktionsaufruf
10
   sei(); //Interrupts zulassen
11
  
12
    while(1)
13
    {  
14
        sleep_cpu(); // ATTin2313 schlafen legen
15
    
16
        if((Phase == 1))
17
    {
18
      OUTPORT |= (1<<OUT1); // setze Leitung A auf High  
19
      OUTPORT &=~ (1<<OUT1); // setze Leitung A auf Low
20
      Phase = 0; // Hilfsvariable wird auf null gesetzt
21
    }
22
    else if((Phase == 0))
23
    {
24
      OUTPORT |= (1<<OUT2); //setze Leitung B auf High
25
      OUTPORT &=~ (1<<OUT2); //setze Leitung B auf Low
26
      Phase = 1; //Hilfsvariable wird auf eins gesetzt
27
    }
28
    }
29
}

C-File:
1
void TimerInit()
2
{
3
    //Prescaler auf 1024 setzen (laut Datenblatt), Bitfolge 00000101
4
    TCCR0B |= ((1<<CS00)|(1<<CS02));
5
    TCCR0B &=~ (1<<CS01); 
6
    TCNT1 = 0xFFFFFFFFFFFFFFFF - 1000;
7
    TIMSK |= 0x01;
8
} 
9
10
ISR(INT0_vect)
11
{
12
  
13
}

@Thomas Eckmann

Hallo!
Die ISR befindet sich gleich darunter, nur wird sie nie aufgerufen, wenn 
ich das Programm simuliere, nur weiß ich leider nicht warum, da ich doch 
mit TIMSK auf die ISR zuweise, oder etwa nicht?

von Karl H. (kbuchegg)


Lesenswert?

Erikson Dietrich schrieb:

> Die ISR befindet sich gleich darunter,

Nope.

Das stehr EINE Isr.
Aber die falsche.

Wie der Name schon sagt, ist das die ISR die für den externen Interrupt 
0 zuständig wäre (wenn du den freigegeben hättest).

Die Namen der ISR müssen schon zu den von dir freigegebenen Interrupts 
passen! Dein Tiny kann einige verschiedene Interrupts generieren. Für 
jeden ist eine eigene ISR zuständig.

Nochmal
FAQ: Timer

> da ich doch mit TIMSK auf die ISR zuweise

du weisst du überhaupt nichts zu.
Du setzt das Bit im TIMSK, welches den Overflow Interrupt vom Timer 
freigibt. Und das würdest du besser so schreiben
1
  TIMSK |= ( 1 << TOIE0 );

Lies TOIE0 als *T*imer *O*verflow *I*nterrupt *E*nable für den Timer 0

Wird das Bit gesetzt, dann ist der entsprechende Interrupt freigegeben. 
Wie die zugehörige ISR heissen muss, ist dir vorgegeben.
1
ISR( TIMER0_OVF_vect )       // Overflow Interrupt Vector
2
{
3
   ....
4
}

da strckt wieder der Name drinnen: OVF wie Overflow

: Bearbeitet durch User
von Dave C. (dave_chappelle)


Lesenswert?

Erikson Dietrich schrieb:
> TCNT1 = 0xFFFFFFFFFFFFFFFF - 1000;

Bin ich der einzige den das irritiert?

von Karl H. (kbuchegg)


Lesenswert?

Und noch ein gut gemeinter Rat unter uns Klosterschwestern.

Lass solche Kommentare
1
      Phase = 0; // Hilfsvariable wird auf null gesetzt
einfach weg. Die sind ... einfach nur peinlich. Wenn ich im Code nicht 
sehe, dass da eine Variable auf 0 gesetzt wird, dann brauch ich auch 
keinen Kommentar, der mir genau dasselbe sagt.

Generell: Kommentare sollen nicht das in Worten beschreiben, was ich 
ohnehin im Code lesen kann. Kommentaree sollen das Beschreiben, was ich 
im Code eben NICHT direkt sehen kann. Ein Kommentar, der nicht 
geschrieben werden braucht (und auch nicht wird), ist ein guter 
Kommentar! Wenn immer möglich, soll der Code sein eigener Kommentar 
sein. Das lässt sich oft durch eine ordentliche Wahl von Variablennamen 
(oder sonstigen Namen) erreichen.

Das hier
1
      OUTPORT |= (1<<OUT2); //setze Leitung B auf High

eine Ausgangsleitung ganz offensichtlich auf High gesetzt wird, das seh 
ich im Code auch. Das brauchst du mir nicht beschreiben.
Aber:
Was bewirkt das?
Geht dann ein Licht an, ertönt eine Sirene, wird die Feuerwehr dadurch 
benachrichtigt, .... ? Das alles interessiert mich, nur steht es weder 
im Code noch steht es im Kommentar.

Jetzt könnte man zb schreiben
1
      OUTPORT |= (1<<OUT2); // Sirene einschalten
oder
1
      OUTPORT |= (1<<OUT2); // Fehlerlampe einschalten
oder
.... (also einen entsprechenden Kommentar dazu geben)

man kann aber auch durch die Wahl der makronamen zb schreiben
1
      OUTPORT |= (1 << SIRENE);
oder
1
      OUTPORT |= (1 << ERROR_LED);
oder
....

und braucht plötzlich die Komentare gar nicht mehr, weil im Code SELBER 
steht, was da geschaltet wird. Genau das meint man mit: der Code ist 
sein eigener Kommentar. Wenn du die Wahl hast zwische "ich kann meinen 
Code so formulieren, dass das Wesentliche direkt im Code steht" und "ich 
schreibs in einem Kommentar dazu", dann sollst du immer die Code 
Variante favorisieren.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Dave Chappelle schrieb:
> Erikson Dietrich schrieb:
>> TCNT1 = 0xFFFFFFFFFFFFFFFF - 1000;
>
> Bin ich der einzige den das irritiert?

:-)
Schreib es dem jugendlichen Leichtsinn zu. Ein
1
  TCNT1 = -1000;
hätte es auch getan.

von Dave C. (dave_chappelle)


Lesenswert?

Karl Heinz schrieb:
> TCNT1 = -1000;

Aber die 0xFFFFFFFFFFFFFFFF ist ja viel zu gross für das Register? Und 
0xFFFFFFFFFFFFFFFF -1000 ist ja auch immer noch viel zu gross für das 
Register, gibt das dann nicht einfach eine 0?

Sorry kann kein Assembler weiss darum nicht genau wie der Prozessor da 
vorgeht.

von Erikson D. (nevadakirin)


Lesenswert?

@Karl Heinz

Sorry, aber ich habe deinen zweiten Post nicht gesehen.
Ich habe, den SleepMode mal auskommentiert und mir dass zum Timer 
durchgelesen, was du mir als Link hinterlassen hast. Soweit verstehe ich 
den Timer, und ich habe es auch so geändert, sodass mir auf meine ISR 
zugewiesen wird. Nur mein Problem ist, dass wenn ich die Funktion im 
Hauptprogramm aufrufe und ich simuliere, ich nicht weiß wo ich sehe wann 
der Timer den Overflow macht und die ISR auslöst. Wenn ich den SleepMode 
weglasse, kann ich simulieren, also einmal ist diese Leitung an und dann 
diese, aber ich bekomme es nicht hin, dass eine Sekunde Abstand 
dazwischen ist und dass eine Leitung nur 100ms eingeschalten bleibt.

Bitte habt Geduld mit mir, ich weiß, dass das für euch Kinderkram ist..
mfG Erik

von Karl H. (kbuchegg)


Lesenswert?

Dave Chappelle schrieb:
> Karl Heinz schrieb:
>> TCNT1 = -1000;
>
> Aber die 0xFFFFFFFFFFFFFFFF ist ja viel zu gross für das Register? Und
> 0xFFFFFFFFFFFFFFFF -1000 ist ja auch immer noch viel zu gross für das
> Register, gibt das dann nicht einfach eine 0?

Alles was zu gross wird, wird einfach abgeschnitten.

von Erikson D. (nevadakirin)


Lesenswert?

Karl Heinz schrieb:
> TCNT1 = 0xFFFFFFFFFFFFFFFF - 1000;

Ja das hier hat mein Lehrer gesagt, ich verstehe es selbst nicht ganz. 
Er meinte ich brauche ein 16Bit großes Register und soll es 2^16 stellen 
und einfach -1000 schreiben, weil der Timer so angeblich pro Sekunde ein 
Interrupt auslöst, aber ganz Blick ich da nicht durch.

Und das mit den Kommentaren werde ich noch ändern!

mfG Erik

von Dave C. (dave_chappelle)


Lesenswert?

Karl Heinz schrieb:
> Alles was zu gross wird, wird einfach abgeschnitten

Mir war bewusst, dass diese Subtraktion in einem 8 Bitter nicht so (ohne 
weiteres) ausgeführt werden kann, deshalb ging ich davon aus, dass das 
Ergebnis einfach 0 beträgt. Ich wusste nicht, dass zu grosse Zahlen in 
jedem Zusammenhang ganz ausgeblendet werden.

Dann ist diese Subtraktion eigentlich komplett überflüssig?

von Karl H. (kbuchegg)


Lesenswert?

Erikson Dietrich schrieb:

> Nur mein Problem ist, dass wenn ich die Funktion im
> Hauptprogramm aufrufe und ich simuliere,

Tip.
Zum simulieren im Einzelschrittbetrieb ist es sinnvoll, als Vorteiler 
die 1 zu wählen (und nicht die 1024). Dann macht der simulierte µC bei 
jedem Druck auf F10 genau einen Befehl.

> ich nicht weiß wo ich sehe wann
> der Timer den Overflow macht

Im Einzelschrittbetrieb kannst du dir in der Simulation das TCNT0 
Register ansehen. Es wird sukzessive hochzählen, jedesmal wenn du einen 
Schritt machst.

> und die ISR auslöst.

Breakpoint auf den Anfang der ISR setzen.

(und wenn ich mich recht erinnere, gibt es im Simulator vom Atmel Studio 
irgendwo eine Konfigurationsoption mit der man Breakpoints in ISR 
erlauben muss)

> Wenn ich den SleepMode
> weglasse, kann ich simulieren,

Ja. Lass den erst mal weg.
Wie gesagt: der kann erst zum Schluss eingebaut werden. Jetzt 
interessiert dich erst mal nur der Timer. Also konzentrier dich auch auf 
den.

> also einmal ist diese Leitung an und dann
> diese, aber ich bekomme es nicht hin, dass eine Sekunde Abstand
> dazwischen ist

Logisch.
Bis jetzt hast du ja nichts, was vom Timer abhängt.

Tip:
Wenn dein Timer alle 10ms einen Overflow generiert, dann ist nach dem 
100ten Aufruf der ISR genau 1 Sekunde vergangen, weil 10ms * 100 nun mal 
1000ms, also 1 Sekunde ergibt.

> und dass eine Leitung nur 100ms eingeschalten bleibt.

Bei zb 10ms dauert es daher 10 ISR Aufrufe, bis 100ms vergangen sind.

:-)

Vergiss die Zeiten.
Die Zeiten interessieren dich nur insofern, als du durch die Timer 
Konfiguration eine 'Basiszeit' hast, von der du weisst in welchen 
zeitlichen Abständen die ISR aufgerufen wird. Alles weiteren Zeiten sind 
dann einfach nur: Wieviele ISR Aufrufe müssen abgezählt werden, damit 
die von mir gewünschte Zeitdauer erreicht wird.

Ist im Grunde eine Variation von:
Von meinem Wasserhahn weiss ich, dass er alle 2 Sekunden tropft. Wenn 
ich daher 5 Minuten abwarten muss, zähle ich 60 / 2 * 5 = 150 Tropfen. 
Dann sind die 5 Minuten vergangen.
Nur das es bei dir nicht Tropfen sondern Aufrufe der Overflow ISR sind. 
Aber das Prinzip ist dasselbe.

von Karl H. (kbuchegg)


Lesenswert?

1
void TimerInit()
2
{
3
    //Prescaler auf 1024 setzen (laut Datenblatt), Bitfolge 00000101
4
    TCCR0B |= ((1<<CS00)|(1<<CS02));
5
    TCCR0B &=~ (1<<CS01); 
6
    TCNT1 = 0xFFFFFFFFFFFFFFFF - 1000;

Im übrigen solltest du dich auch mal mit dir selbst einigen, welchen 
Timer du jetzt eigentlich benutzen willst.
Den Timer 0 oder den Timer 1

von Erikson D. (nevadakirin)


Lesenswert?

Das Prinzip des Timers hab ich verstanden :)
Aber um den Vorteiler auf eins zu setzen, muss ich meine Konfiguration 
umändern oder weglassen, weil ich im Datenblatt kein Bitmuster dazu 
finde... aber wenn ich jetzt so darüber nachdenke, 1000/1 = 1000 
(haha...)

Ja mein Lehrer hat gesagt, ich soll den Timer1 nehmen, weil er ein 2^16 
Bitmuster hat, da blicke ich selbst nicht durch...

Und wenn ich den Haltepunkt jetzt bei der ISR setze und F10 drücke und 
aus der ISR raus möchte, kommt der Fehler "unavaileble when debuggee is 
running" und ich kann nicht mehr weiter.

mfG Erik

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Erikson Dietrich schrieb:
> Das Prinzip des Timers hab ich verstanden :)
> Aber um den Vorteiler auf eins zu setzen, muss ich meine Konfiguration
> umändern oder weglassen, weil ich im Datenblatt kein Bitmuster dazu
> finde...

Wo schaust du denn?

> Ja mein Lehrer hat gesagt, ich soll den Timer1 nehmen, weil er ein 2^16
> Bitmuster hat, da blicke ich selbst nicht durch...

OK. Ich hab jetzt mal den Timer 0 genommen.

Vergiss mal dein Programm. Das ist schon etwas konfus in die falsche 
Richtung
1
#define F_CPU 1000000
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
6
#define OUT1 0
7
#define OUT2 1
8
#define OUTDDR DDRB
9
#define OUTPORT PORTB
10
11
// Anzahl ISR Aufrufe, die abgewartet werden müssen
12
// DIe sich daraus ergebende Zeit errechnet sich aus der Timereinstellung und dieser Anzahl
13
#define ON_TIME     3  
14
#define OFF_TIME    5
15
16
#define IDLE_PHASE     0
17
#define WAIT_ON_PHASE  1
18
volatile uint16_t timeCnt;
19
uint8_t           phase;
20
21
ISR( TIMER0_OVF_vect )
22
{
23
  timeCnt--;
24
  
25
  if( timeCnt == 0 )
26
  {
27
    // die voreingestellte Zeit ist abgelaufen
28
  
29
    switch( phase ) {
30
      case IDLE_PHASE:
31
        OUTPORT |= (1<<OUT1);
32
        phase = WAIT_ON_PHASE;
33
        timeCnt = ON_TIME;
34
        break;
35
      
36
      case WAIT_ON_PHASE: 
37
        OUTPORT &= ~(1<<OUT1);
38
        phase = IDLE_PHASE;
39
        timeCnt = OFF_TIME;
40
        break;
41
      
42
      default:
43
        phase = IDLE_PHASE;
44
    }
45
  }
46
}
47
48
int main(void)
49
{
50
  OUTDDR |= ((1<<OUT1) | (1<<OUT2)); // Ausgänge definieren
51
  OUTPORT &=~ ((1<<OUT1) | (1<<OUT2)); //Leitung A und B auf Low
52
53
  TCCR0B |= ( 1 << CS00);                  // zum simulieren: Vorteiler 1
54
//  TCCR0B |= ((1<<CS00)|(1<<CS02));
55
  TIMSK |= ( 1 << TOIE0 );
56
  
57
  phase = IDLE_PHASE;
58
  timeCnt = ON_TIME;
59
  
60
  sei();
61
  
62
  while(1)
63
  {
64
  }
65
}

> Und wenn ich den Haltepunkt jetzt bei der ISR setze und F10 drücke und
> aus der ISR raus möchte, kommt der Fehler "unavaileble when debuggee is
> running" und ich kann nicht mehr weiter.

Welche Entwicklungsumgebung verwendest du?


(Obiges Programm schaltet den AUsgang OUT1 in einem bestimmten Takt.
Die Basiszeit für die ISR Aufrufe wird wie üblich durch die Timer 
Einstellung gemacht, so dass die ISR in bestimmten zeitlichen Abständen 
aufgerufen wird.
In der ISR ist eine sog. Statemachine, die die Anzahl der Aufrufe 
abzählt. Die jeweiligen Anzahlen werden hierdurch
1
#define ON_TIME     3  
2
#define OFF_TIME    5
festgelegt.

Alle Zahlen sind momentan auf eine Verwendung im Simulator ausgelegt. 
Für den realen Tiny, der mit 1Mhz läuft, muss man den Vorteiler 
entsprechend höher stellen und dann eben die jeweiligen Anzahl an 
abzuwartenden ISR Aufrufen ausrechnen um auf die gewünschten Zeiten zu 
kommen.

Wenn du dir im Simulator einen Breakpoint auf den Anfang der ISR setzt 
und mittels F10 das Programm durchsteppst, musst du regelmässig immer 
wieder in der ISR landen. Dort siehst du dann, wie sich der timeCnt 
Zähler erniedrigt und wenn er bei 0 angekommen ist (die erforderliche 
Anzahl an Aufrufen also beisammen ist), dann wird
* der Ausgang entsprechend geschaltet
* die jeweils neue abzuwartende Zeit nach timeCnt geladen

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Erikson Dietrich schrieb:
> Karl Heinz schrieb:
>> TCNT1 = 0xFFFFFFFFFFFFFFFF - 1000;
>
> Ja das hier hat mein Lehrer gesagt, ich verstehe es selbst nicht ganz.

Wenn man es ganz genau nimmt, dann macht man das auch nicht so, sondern 
man benutzt den CTC Modus vom Timer.

Aber eigentlich zettelt er hier einen Nebenschauplatz an.
Denn den Schnecken ist es ziemlich Wurscht, ob sie jetzt 0.1 oder doch 
0.1005 (beispielsweise) Sekunden bestromt werden. Ich glaube nicht, dass 
die eine Uhr dabei haben, die Zeiten stoppen und sich sagen, dass sie 
den Zaun ignorieren können, wenn diese nicht auf die µs genau stimmen.

Typisch Lehrer eben.

: Bearbeitet durch User
von Erikson D. (nevadakirin)


Lesenswert?

Ich habe im Datenblatt des ATtiny2313 reingeschaut, aber anscheinend 
habe ich es übersehen.

Ich habe mir das Programm was du geschrieben hast mal kopiert und bin es 
durchgegangen, aber wenn ich zum TIMSK komme hüpt er trotzdem nicht in 
die ISR und bei sei() haut es ihn wieder auf.

Ich verwende Atmel Studio 6.1

von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Erikson Dietrich schrieb:
> Ich habe im Datenblatt des ATtiny2313 reingeschaut, aber anscheinend
> habe ich es übersehen.
>
> Ich habe mir das Programm was du geschrieben hast mal kopiert und bin es
> durchgegangen, aber wenn ich zum TIMSK komme hüpt er trotzdem nicht in
> die ISR und bei sei() haut es ihn wieder auf.

Das beim sei() stimmt schon. In der Hauptschleife ist ja nichts.

Aber du musst im Atmel Studio unter
"Tools" "Options" / "Tools" die Behandlung von ISR Aufrufen während des 
Single Steppens einschalten.
Irgend ein Schlaumeier bei Atmel hat gedacht, das wäre eine gute Idee, 
wenn man das per Default abschaltet.

: Bearbeitet durch User
von Erikson D. (nevadakirin)


Lesenswert?

Ahh okay, jetzt komm ich in die ISR rein, Danke! :)
Falls ich das jetzt richtig verstanden habe, muss ich die ON- und OFF 
Time so konfigurieren, dass die ON-Time 100ms beträgt und die OFF Time 
eine Sekunde.
Also den Vorteiler so einstellen und meine Leitung OUT2 muss ich auch 
noch einbauen und wenn das funktioniert, geht das Programm soweit, dass 
wir schon den SleepMode konfigurieren können?

PS: Meinem Lehrer ist es glaub ich egal, dass der Takt jetzt nicht genau 
eine Sekunde dauert, nur sollte er nicht zwei Sekunden z.B. dauern.

mfG Erik

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Erikson Dietrich schrieb:
> Ahh okay, jetzt komm ich in die ISR rein, Danke! :)
> Falls ich das jetzt richtig verstanden habe, muss ich die ON- und OFF
> Time so konfigurieren, dass die ON-Time 100ms beträgt und die OFF Time
> eine Sekunde.
> Also den Vorteiler so einstellen

Damit gehts los.

Dein µC läuft mit 1Mhz.
Spielen wir mal ein bischen mit den Zahlen.

Der Timer 0 hat Vorteiler (Datenblatt 
http://www.atmel.com/images/doc2543.pdf, Seite 77) von 1, 8, 64, 256, 
1024

Was wir wollen ist eine Zeit für die Aufrufe der ISR, die 100ms 
möglichst ganzzahlig teilt und dabei möglichst gross ist. Wobei wir das 
auch nicht überbewerten wollen, denn wie gesagt: den Schnecken ist die 
genaue Zeit egal.

Spielen wir mal. Bei einem Vorteiler von x erfolgen die ISR Aufrufe in 
welchen Abständen?
Der Timer kriegt in einer Sekunde Pulse 1000000/x. Mit jedem Puls zählt 
er 1 weiter und wenn er bei 256 angelangt ist, erfolgt der Overflow.
D.h. in 1 Sekunde gibt es wieviele Overflows
1
Vorteiler   Berechnung             Anzahl Ovf pro Sekunde    Zeit zwischen 2 Overflows
2
3
    1          1000000 / 1 / 256     3906.25                 0.000256
4
    8          1000000 / 8 / 256      488.28125              0.002048
5
   64          1000000 / 64 / 256      61.03515625           0.016384
6
  256          1000000 / 256 / 256     15.2587890625         0.065536
7
 1024          1000000 / 1024 / 256     3.814697265625       0.262144

Ein Vorteiler von 1024 wäre schon etwas zu lang (ausser man macht 
Vorladen des Timers, aber das wollen wir nicht wirklich machen).

Was ist mit den anderen Zeiten. Gibt es eine Zeit, die 0.1 Sekunden gut 
teilt? Bzw. wie sehen die Teiler aus?
1
Zeit                                  Teiler
2
0.000256     0.1 / 0.000256           390.6
3
0.002048     0.1 / 0.002048            48.8
4
0.016384     0.1 / 0.016384             6.1
5
0.065536     0.1 / 0.065536             1.5

Die 6.1 sind schon recht nahe an einer ganzan Zahl drann. Wenn wir 6 ISR 
Aufrufe abzählen, dann würde das eine Zeit von 0.098304 Sekunden 
ergeben. Bei entsprechend 61 Aufrufen wären das dann 0.999424 Sekunden.

Ich würde mal sagen: nahe genug drann. 0.098 ist praktisch gesehen 0.1 
Sekunden (also 100ms) und 0.999424 ist praktisch 1.0. Zumal der Tiny ja 
auch nicht mit exakt 1Mhz läuft.

Das sind also die Zahlen die wir nehmen:
Vorteiler 64
Für die Einschaltzeit 6 ISR Aufrufe
Für die Pausenzeit 61 ISR Aufrufe

Also:
enstprechend das Programm ändern, den Vorteiler einstellen, die Werte 
eintragen. An den OUT Pin eine LED anschliessen (oder sonstwas, mit dem 
man den Zustand des Ausgangs sichtbar machen kann), das geänderte 
Programm in den Tiny brennen und nachsehen, ob der Ausgang das 
geforderte Zeitverhalten hat.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Erikson Dietrich schrieb:

> dass
> wir schon den SleepMode konfigurieren können?

Was hast du nur immer mit deinem Sleep Mode.
Der interessiert zur Zeit nicht die Bohne. Das ist einfach nur eine 
entsprechende Konfiguration in der Init Sektion und ein Aufruf von 
sleep_cpu in der Hauptschleife.
Da die ganze Funktionalität von der ISR erledigt wird, ist das nicht 
mehr. Der Timer löst einen Interrupt aus, der Tiny wacht auf, bearbeitet 
die ISR, geht zurück zur Hauptschleife und geht wieder schlafen.

Viel wichtiger ist, dass die Programmlogik funktioniert!

: Bearbeitet durch User
von Erikson D. (nevadakirin)


Lesenswert?

Ich habe das Programm jetzt folgendermaßen geändert:
1
#define F_CPU 1000000
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
6
#define OUT1 0
7
#define OUT2 1
8
#define OUTDDR DDRB
9
#define OUTPORT PORTB
10
11
// Anzahl ISR Aufrufe, die abgewartet werden müssen
12
// Die sich daraus ergebende Zeit errechnet sich aus der Timereinstellung und dieser Anzahl
13
#define ON_TIME     6
14
#define OFF_TIME    61
15
16
#define IDLE_PHASE     0
17
#define WAIT_ON_PHASE  1
18
volatile uint16_t timeCnt;
19
uint8_t           phase;
20
int HelpPhase = 0;
21
22
ISR( TIMER0_OVF_vect )
23
{
24
  timeCnt--;
25
  
26
  if( timeCnt == 0 )
27
  {
28
    // die voreingestellte Zeit ist abgelaufen
29
    
30
    switch( phase ) {
31
      case IDLE_PHASE:
32
      if(HelpPhase == 0)
33
      {
34
        OUTPORT |= (1<<OUT1);
35
        HelpPhase = 1;
36
      }
37
      else if(HelpPhase == 1)
38
      {
39
        OUTPORT |= (1<<OUT2);
40
        HelpPhase = 0;
41
      }
42
      phase = WAIT_ON_PHASE;
43
      timeCnt = ON_TIME;
44
      break;
45
      
46
      case WAIT_ON_PHASE:
47
      OUTPORT &= ~(1<<OUT1);
48
      OUTPORT &= ~(1<<OUT2);
49
      phase = IDLE_PHASE;
50
      timeCnt = OFF_TIME;
51
      break;
52
      
53
      default:
54
      phase = IDLE_PHASE;
55
    }
56
  }
57
}
58
59
60
int main(void)
61
{
62
  OUTDDR |= ((1<<OUT1) | (1<<OUT2)); // Ausgänge definieren
63
  OUTPORT &=~ ((1<<OUT1) | (1<<OUT2)); //Leitung A und B auf Low
64
65
  TCCR0B &=~ (1 << CS02);              // zum simulieren: Vorteiler 1
66
    TCCR0B |= ((1<<CS00)|(1<<CS01));
67
  TIMSK |= ( 1 << TOIE0 );
68
  
69
  phase = IDLE_PHASE;
70
  timeCnt = ON_TIME;
71
  
72
  sei();
73
  
74
    while(1)
75
    {
76
    
77
  }
78
}

Naja, das blöde ist, ich kann das Programm nur mit der Simulation 
testen, da ich keine Platine oder irgendetwas habe um es wirklich zu 
testen, dass ist ja das blöde...

mfG Erik

von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Erikson Dietrich schrieb:

>       case IDLE_PHASE:
>       if(HelpPhase == 0)
>       {
>         OUTPORT |= (1<<OUT1);
>         HelpPhase = 1;
>       }
>       else if(HelpPhase == 1)
>       {
>         OUTPORT |= (1<<OUT2);
>         HelpPhase = 0;
>       }

Das schaltet aber im Sekundentakt abwechselnd eine der beiden Leitungen 
auf 1.
Ist das gewollt?

> Naja, das blöde ist, ich kann das Programm nur mit der Simulation
> testen, da ich keine Platine oder irgendetwas habe um es wirklich zu
> testen, dass ist ja das blöde...

Im Simulator kann man zumindest mal die Zeiten kontrollieren.
Dort gibt es eine Stoppuhr.
Setz dir einen Breakpoint auf die bewussten Port Manipulationen und sieh 
an der Stoppuhr nach, ob die Zeiten hinkommen.
Darauf achten, dass im Simulator die Frequency auf 1Mhz steht (direkt 
darüber)

von Erikson D. (nevadakirin)


Lesenswert?

Jap, es ist gewollt, dass mir Sekundenweise einmal die und einmal diese 
Leitung für 100ms eingeschalten wird. Quasi so, dass die Schnecken jede 
Sekunde einmal von der und einmal von dieser Leitung eine gewischt 
bekommen.

Ich weiß, ich verlange viel, aber ich sehe nirgends diese Stoppuhr, wo 
finde ich die?

mfG Erik :)

: Bearbeitet durch User
von Erikson D. (nevadakirin)


Lesenswert?

Ich weiß, ich sollte keinen Doppelpost machen, aber ich habe diesen 
Processor nicht.. Bei mir in der Leiste stehen IO View, Solution 
Explorer und Properties, aber nicht mehr.

mfG Erik

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.