Forum: Mikrocontroller und Digitale Elektronik LEDs mit Timer ansteuern


von Oldie (Gast)


Lesenswert?

Guten Mittag meine Freunde, ich habe ein Problem:
Ich habe mir selbstverständlich alle gegebenen Anleitungen durchgelesen, 
leider ist mir das mit dem Timer/Interrupt nicht ganz klar geworden.
ich habe 2 LEDS an meinen AtMega 8 angeschlossen (LED1 =^ PD6; LED2 =^ 
PD5)
sowie einen Taster (taster =^ PD2), nun mein Projekt:
Ich möchte, dass eine LED (PD6) leuchtet, sobald ich den Taster betätigt 
habe. Damit beginnt auch der Timer zu laufen, der nach einpaar Stunden 
(sagen wir 2) die andere LED (PD5) zum Leuchten bringt. Dann wieder 2 
Stunden Pause, und die LED (PD5)soll wieder blinken. Währenddessen soll 
die LED1 ununterbrochen die ganze Zeit leuchten.

Wie würdet ihr das in C schreiben? Ich bin damit leicht überfordert.
Ich bin erfreut über Tipps.
Vielen Dank

: Verschoben durch User
von NichtDeinFreund (Gast)


Lesenswert?

moin
versuch mal rauszubekommen, wie lange du mit einem 16bit Timer bei 
gegebener Taktfreq. des MC, etwas verzögern kannst .
Stichwort:Vorteiler

mfg

von Oldie (Gast)


Lesenswert?

ja, also... wie findet man das heraus?
Ich weiß nicht, inwiefern Overflows, etc.. Zeitangaben sind.

von kopfkratzer (Gast)


Lesenswert?

kopfkratz
Also wenn Du das Datenblatt nicht verstehst und dann noch die Tutorials 
hier samt FAQ Timer von Karl-Heinz, dann solltest Du Dich entweder nach 
einem anderen Hobby umsehen oder Deine Hausaufgabe mit dem Lehrer 
besprechen :-P
Was Du brauchst:
1. Tastenentprellung (PeDa hilft da weiter)
2. Variable die den Tastendruck speichert
3. Timer der Dir die aktuelle Zeit in einer volatile Variable angibt
4. Hauptprogramm das beide Variablen entsprechend auswertet
Und nun ran an den Speck, egal ob in Assembler, BASIC, C, PASCAL oder 
ZZZZZZZ :-P

von Oldie (Gast)


Lesenswert?

kopfkratzer schrieb:
> 2. Variable die den Tastendruck speichert

Okay, wieso muss der Tastendruck als solcher gespeichert werden?
Ich drücke den Taster, eine LED fängt an zu leuchten und der Timer geht 
los. Ab da ist der Taster ja völlig uninteressant, da er nur einmal (zu 
Anfang) betätigt wird/wurde.

> Vorteiler

Könntest du mir ne Beispielrechnung geben, wie ich die Maximaldauer 
errechnen kann?
Danke

von kopfkratzer (Gast)


Lesenswert?

Oldie schrieb:
> kopfkratzer schrieb:
>> 2. Variable die den Tastendruck speichert
>
> Okay, wieso muss der Tastendruck als solcher gespeichert werden?
> Ich drücke den Taster, eine LED fängt an zu leuchten und der Timer geht
> los. Ab da ist der Taster ja völlig uninteressant, da er nur einmal (zu
> Anfang) betätigt wird/wurde.
>
ROFLMAO
Gut dann ist es noch schlimmer als befürchtet.
Woher willst Du denn wissen das der Taster gedrückt wurde ?
Was soll denn passieren wenn während der 2 Stunden der Taster nochmal 
gedrückt wird, wieder von vorne anfangen ?
Und jetzt erkläre mir mal bitte wie Du aus den beiden Timestamps die 
zwei Stunden errechnen willst ?
Achja woher kommen die Timestamps ?
Besser nochmal beim Lehrer nachfragen ;-)

von D. V. (mazze69)


Lesenswert?

Oldie schrieb:
> Ich drücke den Taster

Das denkst du, inwirklichkeit drückst du ihn mehrfach - ganz einfach 
weil er prellt. Falls du über ein Speicher-Scope verfügst, kannst du den 
elektrischen Part eines Tasterdrucks respektive Schalterbetätigung mal 
ansehen. Du wirst dich wundern.

> Könntest du mir ne Beispielrechnung geben, wie ich die Maximaldauer
> errechnen kann?

Man zählt mithilfe eines Timer eine Variable hoch und leitet davon die 
tatsächlich gewünchte Zeit ab.

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

Oldie schrieb:
> Okay, wieso muss der Tastendruck als solcher gespeichert werden?
> Ich drücke den Taster, eine LED fängt an zu leuchten und der Timer geht
> los.

Oldie schrieb:
> sowie einen Taster (taster =^ PD2), nun mein Projekt:
> Ich möchte, dass eine LED (PD6) leuchtet, sobald ich den Taster betätigt
> habe. Damit beginnt auch der Timer zu laufen, der nach einpaar Stunden
> (sagen wir 2) die andere LED (PD5) zum Leuchten bringt. Dann wieder 2
> Stunden Pause, und die LED (PD5)soll wieder blinken. Währenddessen soll
> die LED1 {=PD6} ununterbrochen die ganze Zeit leuchten.

Grundzustand : Keine Taste gedrück, keine LED leuchtet (wahrscheinlich 
ist das der Zustand nach dem Einschalten deines Controllers)

dann wird irgendwann die Taste gedrückt, Den Zustand "Taste ist 
(verlässlich)gedrückt" bekommst du als Ergebniss deiner 
Entprell-Routine.  Das Betä#tigen der Taste führt zum sofortigen und 
immerwährenden Leuchten von LED1, sowie zum "normalen Lampen-Geleuchte", 
welche folgendes macht:

- abwarten, bis eine Zeit t1 abgelaufen ist
- am Ende der Zeit t1 die LED 2 statisch einschalten (so lese ich das 
raus aus deinem post)

Wiederhole bis (unendlich)
- abwarten, bis eine Zeit t2 abgelaufen ist
- LED 2 ausschalten
- abwarten, bis eine Zeit t3 abgelaufen ist
- LED 2 blinkend einschalten

Ob oder was passiert wenn du die Taste nochmals drückst hast du nicht 
angegeben. Daher wird diese hier auch nicht berücksichtigt

: Bearbeitet durch User
von Oldie (Gast)


Lesenswert?

D. V. schrieb:
> Man zählt mithilfe eines Timer eine Variable hoch und leitet davon die
> tatsächlich gewünchte Zeit ab.

Ja, wenn ich jetzt den Vorteiler auf 1024 setze bei einem Takt von 1Mhz, 
wie viel Zeit wäre das so "realZeit"?

kopfkratzer schrieb:
> Und jetzt erkläre mir mal bitte wie Du aus den beiden Timestamps die
> zwei Stunden errechnen willst ?

geht das nicht oder wie?

von Oldie (Gast)


Lesenswert?

Wegstaben Verbuchsler schrieb:
> Grundzustand : Keine Taste gedrück, keine LED leuchtet (wahrscheinlich
> ist das der Zustand nach dem Einschalten deines Controllers)
> dann wird irgendwann die Taste gedrückt, Den Zustand "Taste ist
> (verlässlich)gedrückt" bekommst du als Ergebniss deiner
> Entprell-Routine.  Das Betä#tigen der Taste führt zum sofortigen und
> immerwährenden Leuchten von LED1, sowie zum "normalen Lampen-Geleuchte",
> welche folgendes macht:
> - abwarten, bis eine Zeit t1 abgelaufen ist
> - am Ende der Zeit t1 die LED 2 statisch einschalten (so lese ich das
> raus aus deinem post)
> Wiederhole bis (unendlich)
> - abwarten, bis eine Zeit t2 abgelaufen ist
> - LED 2 ausschalten
> - abwarten, bis eine Zeit t3 abgelaufen ist
> - LED 2 blinkend einschalten

Das ist es zu 100%. Genau das möchte ich erreichen! Also am besten wäre 
es noch, wenn t4 existiert, und nach Ablauf dieser Zeitspanne wird die 
LED 2 komplett abgeschaltet. Wenn ich den Taster nochmals drücke, soll 
eigentlich nichts passieren (möglicherweise Reset des kompletten 
Vorgangs, aber das ist kein Muss)

von kopfkratzer (Gast)


Lesenswert?

Oldie schrieb:
> D. V. schrieb:
>> Man zählt mithilfe eines Timer eine Variable hoch und leitet davon die
>> tatsächlich gewünchte Zeit ab.
>
> Ja, wenn ich jetzt den Vorteiler auf 1024 setze bei einem Takt von 1Mhz,
> wie viel Zeit wäre das so "realZeit"?
>
> kopfkratzer schrieb:
>> Und jetzt erkläre mir mal bitte wie Du aus den beiden Timestamps die
>> zwei Stunden errechnen willst ?
>
> geht das nicht oder wie?

kopftisch
Was steht denn im Datenblatt des Mega8 bei Timer-OverFlow ?
Wie berechnet sich da die Frequenz ?
Nimm einen Taschenrechner und tippe da Deine aktuelle Frequenz und den 
Vorteilerfaktor ein, was für einen Wert bei 1024 hast Du dann ?
Statt Dein angestrebtes Programm empfehle ich Dir erstmal Dich solange 
mit Timer0 zu beschäftigen bis Du in der Lage bist EINE LED im 
ungefähren Sekundentakt blinken zu lassen.
Dazu brauchst Du eine Zählervariable, besser nimm gleich drei für 
Sekunden, Minuten und Stunden.
Du mußt mit der Formel im Datenblatt den ungefähren Sekundenwert 
ausrechnen und dann eine volatile Variable damit in der ISR beschicken.
Die dann auf Minuten und Stunden umzubrechen ist in C trivial, wenn man 
denn weiß wie es geht ...
Also besser in die nächste Bücherei und sich ein C-Buch holen um das 
auch wirklich durchzuarbeiten.
Wenn dann der Groschen wegen modulo&Co. gefallen ist und Du außer "Hallo 
Welt" auch z.B. den Verbrauch Deines Mopeds korrekt ausrechnen kannst 
geht's weiter ;-)
Es führen zwar alle Wege nach Rom das wurde aber nicht an einem Tag 
erbaut :-P

von Oldie (Gast)


Lesenswert?

Also, ich habe eine angestrebte Periodendauer von t=7200s (2h)
Und bei einem Takt von 4Mhz und einem Vorteiler von 1024 wäre die 
längstmögliche Periodendauer t0 = ca. 0,0655s.
Dann bruache ich von t0 insgesamt 101000 Perioden um meine 2 Stunden zu 
erreichen, ist das richtig?

von Oldie (Gast)


Lesenswert?

Aaalso, mein Problem ist folgendes: ich verstehe nicht, wie ich 2 
Stunden im Timer erreiche. Ist es machbar, bei einem Takt von 4 MHZ 
einen prescaler von 1024 zu benutzen und dann irgendwo mitzählen zu 
lassen, wie oft ein Interrupt ausgelöst wurde? Also so: 4Mhz mit 1024er 
Prescaler läuft, dann beginnt er wieder bei 0, wenn ein Durchlauf 
vollbracht ist und simultan dazu wird in irgendeinem Register 
mitgezählt, wie viele Durchläufe gemacht wurden. Und wenn die 
Durchlaufanzahl einer vorher definierten entspricht, wird ein anderes 
Ereignis ausgelöst. ist das möglich?

von Paul Baumann (Gast)


Lesenswert?

Oldie frug:
>ist das möglich?

Na freilich ist das möglich, aber die Ausgangswerte sind nicht so gut
gewählt, um 1Hz zu erzeugen.
Es geht mit hier mit 4MHz los, die vom Vorteiler durch 1024 geteilt 
werden.
Das heißt, daß der eigentliche Zähler dann mit 3906,25 Hz getaktet wird.

Wenn Du Timer 0 benutzt, dann hat der (8-Bit Timer) 256 Zählschritte,
bevor er "einmal rum" ist.

Das heißt nun: Die 3906,25 Hz werden durch 256 geteilt. Das wären dann
15,25... Hz.

Das wird Mumpitz....

Besser ist es, ganzzahlige Verhältnisse zu schaffen: Nimm 256 als 
Vorteiler.
Dann hast Du 4MHz/64= 62500 Hz.

Man muß im Interrupt dann eine Hilfsvariable die Anzahl der Interrupts
zählen lassen und wenn die entsprechende Zahle erreicht ist, eine Aktion 
auslösen:

Hinweis, bevor hier wieder sinnloses Genöle kommt:
Man KANN den Timer auch im CTC-Modus betreiben und braucht ihn dann
nicht vor zu laden. Das dient hier nur zur besseren Verständlichkeit:
1
$regfile = "2313def.dat"                'AT90S2313-Deklarationen
2
$crystal = 4000000                      'Quarz: 4 MHz
3
Dim Umlauf As Byte
4
Dim X As Bit
5
On Timer0 Ontimer0
6
7
Config Timer0 = Timer , Prescale = 64   '4Mhz/64 = 62500 Hz
8
'Timer auf 6 'heißt:'250Schritte                                                               'bis Überlauf
9
Enable Timer0                           'Timer erzeugt 250 Hz
10
11
Enable Interrupts                       'Interrupts global zulassen
12
Timer0 = 6
13
Umlauf = 250                            'Umlauf zählt die Interrupts
14
Config Portd.6 = Output                 'LED an Port D6
15
Portd.6 = 0
16
X = 0
17
18
19
Do
20
 If Umlauf = 0 Then
21
    Umlauf = 250
22
    If X = 0 Then
23
      Portd.6 = 0
24
      X = 1
25
    Else
26
      Portd.6 = 1
27
      X = 0
28
    End If
29
 End If
30
Loop
31
32
'Interrupt-Routine
33
34
Ontimer0:
35
Timer0 = 6                              'Timer sofort neu laden
36
Decr Umlauf
37
38
Return
39
' **** END OF PROGRAM

: Bearbeitet durch User
von Oldie (Gast)


Lesenswert?

Danke! Nur habe ich folgende Fragen: wo kommen die 250 her? Also woher 
weiß ich dass der Timer mit 250 Hz taktet?
Und: Wie funktioniert das mit dem Umlauf genau? Du hast ihn jetzt als 
250 definiert,  heißt das, dass er bis 250 zählt und dann ein Ereignis 
auslöst?  Danke

von Paul Baumann (Gast)


Lesenswert?

>Nur habe ich folgende Fragen: wo kommen die 250 her? Also woher
>weiß ich dass der Timer mit 250 Hz taktet?

Die 250 kommen daher, daß der Timer vom Wert 6 (mit dem er vorgeladen
wurde) aufwärts bis zum Überlauf zählt. Er macht also nicht 256 Ticks,
sondern nur 250.

Warum 250?

Weil 250*250=62500 ist.

Jetzt brauchen wir noch einen "zweiten Mann", der auch bis 250 zählen 
kann.
Das ist die Variable "Umlauf", die aber hier nicht aufwärts gezählt 
wird,
sondern von 250 aus "runterwärts".

>Wie funktioniert das mit dem Umlauf genau? Du hast ihn jetzt als
>250 definiert,  heißt das, dass er bis 250 zählt und dann ein Ereignis
>auslöst?

Im Interrupt wird die Variable "Umlauf", die zu Anfang auch auf 250 
gesetzt
wurde, um 1 dekrementiert. Wenn Umlauf 0 ist, dann hat es 250 mal einen
Interrupt gegeben.

MfG Paul

Wir müssen hier fort, weil das die Codesammlung ist, die normalerweise
nicht zum Fragen-stellen erlaubt ist....

von Karl H. (kbuchegg)


Lesenswert?

Oldie schrieb:
> Danke! Nur habe ich folgende Fragen: wo kommen die 250 her? Also woher
> weiß ich dass der Timer mit 250 Hz taktet?

Weil du dann im Zusammenspiel mit dem Vorteiler auf eine ganzzahlige 
Zahl kommst, wie oft der Timer-Interrupt pro Sekunde ausgelöst wird. Die 
250 hast du frei gewählt, weil das in der Nähe von 256 liegt und sich 
mit einem der Vorteiler schön ausgeht.

> Und: Wie funktioniert das mit dem Umlauf genau? Du hast ihn jetzt als
> 250 definiert,  heißt das, dass er bis 250 zählt und dann ein Ereignis
> auslöst?

Nein.
Von alleine löst da überhaupt nichts aus.

WEnn die ISR 250 mal in der Sekunde aufgerufen wird, dann zählt man eben 
bei jedem Aufruf eine Variable um 1 hoch (oder runter). Ist die Variable 
bei 250 angelangt (bzw bei 0, wenn runter gezählt wird), dann ist 1 
Sekunde vergangen. Denn der Interrupt wird ja 250 mal in der Sekunde 
ausgelöst.
Die Variable ist genau jene 'Umlauf'

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Lies dir das mal durch:
FAQ: Timer
dann dürfte vieles klarer sein.

von Karl H. (kbuchegg)


Lesenswert?

Oldie schrieb:

> Wenn ich den Taster nochmals drücke, soll
> eigentlich nichts passieren (möglicherweise Reset des kompletten
> Vorgangs, aber das ist kein Muss)

Überleg dir das gut!

Wenn bei einem weiteren Tastendruck nichts mehr passieren soll, hast du 
Glück. Du brauchst keine Tastendruckerkennung samt Entprellung. Im 
zweiten Fall benötigst du sie aber.

Auch solltest du mal darüber nachdenken, wie es dann eigentlich weiter 
geht, wenn die Zeiten abgelaufen sind. Was passiert dann? Wie kommt die 
Schaltung wieder in den Grundzustand?

von Oldie (Gast)


Lesenswert?

Also, ich ersetze den Taster durch einen Schließer, sodass bei Drücken 
des Schließers einfach der Stromfluss aktiviert wird. Das sollte alles 
erleichtern.

Karl Heinz schrieb:
> WEnn die ISR 250 mal in der Sekunde aufgerufen wird, dann zählt man eben
> bei jedem Aufruf eine Variable um 1 hoch (oder runter). Ist die Variable
> bei 250 angelangt (bzw bei 0, wenn runter gezählt wird), dann ist 1
> Sekunde vergangen. Denn der Interrupt wird ja 250 mal in der Sekunde
> ausgelöst.
> Die Variable ist genau jene 'Umlauf'

Wenn bei 250 eine Sekunde vergangen ist, dann sind bei 900000 Interrupts 
ja eine Stunde vergangen. Ich wollte am Anfang ja ein Auslösen eines 
Ergebisses bei 1800000 Interrupts. Ich kann aber nicht einfach sagen, 
dass bei 1800000 Interrupts ein Ergebnis ausgelöst werden soll, da der 
Controller nur 255 mal zählen kann, richtig? Wie sorge ich jetzt dafür, 
dass er 1800000 mal zählen kann? Danke

von Karl H. (kbuchegg)


Lesenswert?

Oldie schrieb:

> Wenn bei 250 eine Sekunde vergangen ist, dann sind bei 900000 Interrupts
> ja eine Stunde vergangen. Ich wollte am Anfang ja ein Auslösen eines
> Ergebisses bei 1800000 Interrupts. Ich kann aber nicht einfach sagen,
> dass bei 1800000 Interrupts ein Ergebnis ausgelöst werden soll

Können tust du schon.
Musst halt für die Variable eine anderen Datentyp nehmen.

Aber ...
> , da der
> Controller nur 255 mal zählen kann, richtig? Wie sorge ich jetzt dafür,
> dass er 1800000 mal zählen kann? Danke

Wie machen wir es denn im täglichen Leben?
Wir zählen ja auch nicht die Sekunden seit Mitternacht. Sondern:
60 Sekunden sind 1 Minute
60 Minuten sind 1 Stunde.

Kein Mensch sagt, dass du Zeiten nur in einer einzigen Variablen zählen 
darfst. Nimmst du halt 3 davon. Eine für Sekunden, eine für Minuten, 
eine für Stunden.

(Wobei man Stunden eventuell nicht braucht. 120 Minuten geht gerade 
noch, so dass sich jeder darunter 2 Stunden vorstellen kann)

: Bearbeitet durch User
von Oldie (Gast)


Lesenswert?

Karl Heinz schrieb:
> Kein Mensch sagt, dass du Zeiten nur in einer einzigen Variablen zählen
> darfst. Nimmst du halt 3 davon. Eine für Sekunden, eine für Minuten,
> eine für Stunden.

Hohoho, also schummel ich mich zu den 1800000 Interrupts? :D

Ungefähr so vom logischen:
Wenn 250 Interrupts (1s) ausgelöst, zähle irgendeine Variable (s) hoch 
bis 60.
Wenn s = 60; gehe wieder zurück zu s = 0, aber setze Variable m + 1

Wenn m = 120; aktiviere LED an PortD

So vom logischen?

von Karl H. (kbuchegg)


Lesenswert?

Oldie schrieb:
> Karl Heinz schrieb:
>> Kein Mensch sagt, dass du Zeiten nur in einer einzigen Variablen zählen
>> darfst. Nimmst du halt 3 davon. Eine für Sekunden, eine für Minuten,
>> eine für Stunden.
>
> Hohoho, also schummel ich mich zu den 1800000 Interrupts? :D

Was heißt da schummeln.
Jede Wald und Wiesen Nasenpopel Uhr arbeitet nach diesem Prinzip. So 
funktinoniert nun mal unsere Zeitrechnug. Ich seh keinen Grund, warum 
man dieses Prinzip nicht bernehmen soll. Hat sich seit über 2000 Jahren 
bewährt.
Und nur weil du auf große Zahlen, wie 1.8 Mio Interrupts abfährst, muss 
das nicht für jeden gelten. Ich stell meine Abfrage lieber auf 120 
Minuten ein, als auf 1.8 Mio Interrupts. Das kommt auch besser, wenn man 
mal statt 2 Stunden nur deren anderthalb braucht. Denn das kann ich im 
Kopf rechnen, dass das 90 minuten sind. Wieviele Interrupts das sind, 
ist mir hingegen völlig wurscht. Das ergibt sich sowieso von alleine, 
bis dann irgendwann mal 90 Minuten erreicht sind.
1
....
2
3
volatile uint8_t subCnt;
4
volatile uint8_t Sekunden, Minuten, Stunden, Tage, Wochen, Jahre, Jahrhunderte;
5
volatile uint16_t Jahrtausende, Jahrmillionen;
6
7
ISR( ... )
8
{
9
  subCnt++;
10
11
  if( subCnt == 250 )
12
  {
13
    subCnt = 0;
14
    Sekunden++;
15
16
    if( Sekunden == 60 )
17
    {
18
      Sekunden = 0;
19
      Minuten++;
20
21
      if( Minuten == 60 )
22
      {
23
        Minuten = 0;
24
        Stunden++;
25
26
        if( Stunden == 24 )
27
        {
28
          Stunden = 0;
29
          Tage++;
30
31
          if( Tage == 7 )
32
          {
33
            Tage = 0;
34
            Wochen++;
35
           
36
            if( Wochen == 52 )
37
            {
38
              Wochen = 0;
39
              Jahre++;
40
41
              if( Jahre == 100 )
42
              {
43
                Jahre = 0;
44
                Jahrhunderte++;
45
46
                if( Jahrhunderte == 10 )
47
                {
48
                  Jahrhunderte = 0;
49
                  Jahrtausende++;
50
51
                  if( Jahrtausende == 1000 )
52
                  {
53
                    Jahrtausende = 0;
54
                    Jahrmillionen++;
55
                  }
56
                }
57
              }
58
            }
59
          }
60
        }
61
      }
62
    }
63
  }
64
}

brauchst du es noch länger? Ein paar Stufen mehr gingen noch, bis du auf 
dem Alter des Universums bist.
Immerhin: wenn der letzte Dinosaurier auf den Knopf gedrückt hätte, dann 
würde das klappen, das heute das Licht angeht. 65 Millionen Jahre sind 
damit schon locker drinnen.

: Bearbeitet durch User
von Oldie (Gast)


Lesenswert?

Muchas Gracias! Ich habe nun die Sache mit den Interrupts und der 
Zeitrechnung verstanden. Nur stellt sich mir immer von Code-technisch 
diese Frage: Ich habe nun definiert, nach 2 Stunden ein Ereignis 
ausgelöst werden soll. wie stelle ich das dar?

von Karl H. (kbuchegg)


Lesenswert?

Oldie schrieb:
> Muchas Gracias! Ich habe nun die Sache mit den Interrupts und der
> Zeitrechnung verstanden. Nur stellt sich mir immer von Code-technisch
> diese Frage: Ich habe nun definiert, nach 2 Stunden ein Ereignis
> ausgelöst werden soll. wie stelle ich das dar?

entweder so wie du schreibst, das du eben die Minuten mit 120 
vergleichst, oder was ich immer gerne mache: ich dreh den Spiess um um 
mach eine Countdownuhr, die von einer bestimmten Zeit auf 0 runter zählt 
und dann eben schaltet, wenn 0 erreicht ist. Ist sicherlich 
Geschmackssache, aber ich hab immer das Gefühl, dass ich bei einer 
Countdown-Uhr besser mit mehreren Zeiten im Programm umgehen kann, weil 
der Endpunkt des Countdowns ja immer 0 ist und ich einfach nur 
unterschiedliche Startzeiten zuweisen muss.
1
volatile uint8_t Minuten;
2
volatile uint8_t Sekunden;
3
4
ISR( ... )
5
{
6
  static uint8_t subCnt;
7
8
  subCnt++;
9
  if( SubCnt == 250 )
10
  {
11
    subCnt = 0;
12
    if( Sekunden > 0 )
13
      Sekunden--;
14
15
    else
16
    {
17
      Sekunden = 59;
18
19
      if( Minuten > 0 )
20
        Minuten--;
21
22
      else
23
        // ... Schalte LED aus ...
24
        PORT.....
25
    }
26
  }
27
}
28
29
int main()
30
{
31
  ...
32
33
  while( 1 )
34
  {
35
    if( Taster gedrückt && Minuten == 0 )
36
    {
37
      // ... Schalte LED ein ...
38
      PORT.....
39
40
      // Countdownzeit setzen
41
      cli();
42
      Sekunden = 0;
43
      Minuten = 120;
44
      sei();
45
46
      // jetzt läuft die Uhr und zählt die 120 Minuten runter
47
      // sind die abgelaufen, dann wird die LED wieder abgeschaltet
48
    }
49
  }
50
}

Ob man jetzt den Schaltvorgang in der ISR macht oder im Hauptprogramm 
hängt davon ab, wie kompliziert die abhängige Operation ist. Hier ist 
das einfach, eine LED wird abgeschaltet. Das kann man genausogut in der 
ISR machen. Bei komplizierteren Aktionen, setzt man sich dann eben ein 
Flag, dass die Zeit abgelaufen ist und wertet das dann in der 
Hauptschleife aus.

Dieses Grundrezept kann noch beliebig variiert werden. Zb mt mehreren 
'Zählvariablen' können auch mehrere 'Countdownds' parallel laufen. Zb 
ein 5 Sekunden Countdown, der eine andere LED gleichzeitig blinken 
lässt. Oder mehrere Taster, die 'gleichzeitig' unterschiedliche 
Zeiträume auf unterschiedlichen LEDs realisieren. etc. etc.

Wie gesagt: denk dir was aus! Wie würdest du das ganze mit Papier, 
Bleistift und einem Summer machen, der im Sekundentakt einen Ton von 
sich gibt. Der sekündliche Ton entspricht dem Teil in der ISR
1
ISR( ... )
2
{
3
4
  subCnt++;
5
  if( subCnt == 250 )
6
  {
7
    subCnt = 0;
8
9
    .... hier ist der Sekundentakt
10
11
  }
12
}

und mit den gleichen Strategien, mit denen du in der Realität auch 
operierst, operierst du auch hier. Das ist im wesentlichen nichts 
anderes. Nur eben in einer Programmiersprache ausformuliert. Und geh 
davon aus, dass du ein furchtbar schlechtes Gedächtnis hast. Alles was 
du dir eigentlich merken müsstest (aber wegen des schlechten 
Gedächntisses nicht kannst), musst du dir eben aufschreiben (daher 
Papier und Bleistift). Im Programm ist das dann eben eine Variable. Und 
ja, wenn das bedeutet, dass du dir am Papier notieren musst, ob die LED 
jetzt blinken oder Dauerleuchten soll, dann bedeutet das eben das du das 
'niederschreiben' musst, oder eben in einer Variablen festhalten, was 
die LED jetzt eigentlich tun soll (0 ist blinken, 1 ist Dauerleuchten, 
oder so) und du die Variable 'befragen' musst, was jetzt eigentlich 
passieren soll.
Aber mach dir um Gottes Willen immer vorher die Strategie klar, die du 
anwenden willst. Du kannst dir als 'Ideenlieferant' dich selber nehmen. 
Denn du kannst das alles perfekt im wirklichen Leben. Einfach hinsetzen 
und drauflosprogrammieren hingegen führt normalerweise ins Desaster. So 
gut bist du noch nicht, dass das klappen würde. Erst mal brauchst du 
immer einen Plan. Und am besten skizzierst du dir diesen Plan erst mal 
in einfachen Worten.

: Bearbeitet durch User
von Oldie (Gast)


Lesenswert?

Okay dann mach ich das erstmal komplett in Worten:

Es gibt:
LED1
LED2
Timer0
Schließer

Wenn Schließer aktiviert, dann leuchtet LED1 ununterbrochen.
Parallel dazu startet Timer0.
Der Takt von 4Mhz wird durch einen Prescaler von 64 geteilt.
Das bedeutet: 250 abgelaufene Interrupts ^= 1 Sekunde.
Wenn 1 Interrupts abgelaufen, dann inkrementiere Variable interrupts um 
1.
Wenn Variable Interrupts bei 250, dann inkrementiere Variable Sekunde 
UND
setze Variable Interrupts zurück auf 0, aber lass ihn weiterzählen.
Wenn Sekunde bei 60, setzte zurük auf 0 und inkrementiere Variable 
Minute.
Wenn Minute = 120, dann leuchtet LED2 auf.
Wenn Minute = 140, dann beende leuchten von LED2 + Beende kompletten 
Vorgang.

Ist das so richtig?

von Oldie (Gast)


Lesenswert?

Achja uuund: Kann ich die eine LED dauerhaft leuchten lasse, indem ich 
das einfach in den Code schreibe, oder muss ich das auch irgendwie immer 
wiederholen?

von Dietrich L. (dietrichl)


Lesenswert?

Oldie schrieb:
> Achja uuund: Kann ich die eine LED dauerhaft leuchten lasse, indem ich
> das einfach in den Code schreibe, oder muss ich das auch irgendwie immer
> wiederholen?

Die LEDs sind ja an einem auf "Ausgang" geschalteten Port angeschlossen. 
Zum Ein- bzw. Ausschalten setzt man das entsprechende Port-Bit auf den 
gewünschten Zustand. Der Zustand wird dort gespeichert und bleibt 
solange erhalten, bis er vom Programm geändert (überschrieben) wird. 
Wenn also das Programm am Port nicht "rummacht", ändert sich die LED 
nicht.

Gruß Dietrich

von Oldie (Gast)


Lesenswert?

Danke!

> Wenn Schließer aktiviert, dann leuchtet LED1 ununterbrochen.
> Parallel dazu startet Timer0.
> Der Takt von 4Mhz wird durch einen Prescaler von 64 geteilt.
> Das bedeutet: 250 abgelaufene Interrupts ^= 1 Sekunde.
> Wenn 1 Interrupts abgelaufen, dann inkrementiere Variable interrupts um
> 1.
> Wenn Variable Interrupts bei 250, dann inkrementiere Variable Sekunde
> UND
> setze Variable Interrupts zurück auf 0, aber lass ihn weiterzählen.
> Wenn Sekunde bei 60, setzte zurük auf 0 und inkrementiere Variable
> Minute.
> Wenn Minute = 120, dann leuchtet LED2 auf.
> Wenn Minute = 140, dann beende leuchten von LED2 + Beende kompletten
> Vorgang.

Ist das richtig?

von Karl H. (kbuchegg)


Lesenswert?

Oldie schrieb:
> Danke!
>
>> Wenn Schließer aktiviert, dann leuchtet LED1 ununterbrochen.
>> Parallel dazu startet Timer0.
>> Der Takt von 4Mhz wird durch einen Prescaler von 64 geteilt.
>> Das bedeutet: 250 abgelaufene Interrupts ^= 1 Sekunde.
>> Wenn 1 Interrupts abgelaufen, dann inkrementiere Variable interrupts um
>> 1.
>> Wenn Variable Interrupts bei 250, dann inkrementiere Variable Sekunde
>> UND
>> setze Variable Interrupts zurück auf 0, aber lass ihn weiterzählen.
>> Wenn Sekunde bei 60, setzte zurük auf 0 und inkrementiere Variable
>> Minute.
>> Wenn Minute = 120, dann leuchtet LED2 auf.
>> Wenn Minute = 140, dann beende leuchten von LED2 + Beende kompletten
>> Vorgang.
>
> Ist das richtig?

Du wirst nie weiter kommen, wenn du immer eine Tante zum lulu gehen 
brauchst.
Du hast den µC vor dir, du hast deine Entwicklungsugebung vor dir. 
Probiers aus. Nicht funktionierende Programme gehören genauso dazu, wie 
runterfallen zum radfahr-lernen dazugehört.
Kaputt machen kannst du nichts. Im schlimmsten Fall funktionierts 
einfach nicht. Arbeite in SChritten und setz erst mal kleinere Zeiten 
an. Ob da letzten Endes 2 Minuten steht oder 120, das ist nur eine Zahl. 
Aber 2 Minuten (oder noch kürzer) ist für dich in der Enwicklungsphase 
erlebbar. 120 ist es nicht.

von Oldie (Gast)


Lesenswert?

1
#define F_CPU 4000000UL     /* 4MHz */
2
 
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <avr/interrupt.h>
6
7
// Defines:
8
#define LED1    (1<<PD5)
9
#define LED2    (1<<PD6)
10
11
volatile uint8_t Minuten;
12
volatile uint8_t Sekunden;
13
14
15
int main(void)
16
{
17
  // LED-Pins als Ausgang definieren
18
  DDRD |= LED1 | LED2;
19
 
20
  // LED1 einschalten
21
  PORTD |= LED1;
22
  }
23
{
24
  // Timer 0 konfigurieren
25
  TCCR0 = (1<<CS01) (1<<CS00); // Prescaler 64
26
 
27
  // Overflow Interrupt erlauben
28
  TIMSK |= (1<<TOIE0);
29
 
30
  // Global Interrupts aktivieren
31
  sei();
32
 {
33
  static uint8_t subCnt;
34
35
  subCnt++;
36
  if( SubCnt == 250 )
37
  {
38
    subCnt = 0;
39
    if( Sekunden > 0 )
40
      Sekunden--;
41
42
    else
43
    {
44
      Sekunden = 59;
45
46
      if( Minuten > 0 )
47
        Minuten--;
48
49
      else
50
        // ... Schalte LED aus ...
51
        PORTD &= LED2;
52
    }
53
  }
54
}
55
56
 
57
 while(1)
58
 {
59
 if( Minuten == 0 )
60
    { PORTD |= LED2;
61
      // ... Schalte LED ein ...
62
     
63
64
      // Countdownzeit setzen
65
      cli();
66
      Sekunden = 0;
67
      Minuten = 2;
68
      sei();
69
70
      // Die 2 Minuten laufen doch ab und dann leuchtet die LED oder?
71
    }
72
  }
73
}
74
 }

von Karl H. (kbuchegg)


Lesenswert?

Und was soll dieses Konglomerat aus zusammenkopiertem Code sein?

Wo ist deine ISR?
Welcher Codeteil gehört in die ISR?

Du solltest wirklich erst mal mit was einfacherem anfangen. Das kann ein 
Blinder greifen, dass du mit dieser Aufgabenstellung noch heillos 
überfordert bist. Auch einfaches zusammenkopieren von Code will gelernt 
sein. Solange man nicht versteht, was man da tut, wird das nichts.

: Bearbeitet durch User
von Oldie (Gast)


Lesenswert?

Okay, also ich bin Schritt für Schritt vorgegangen:
Als erstes habe ich ganz stumpf versucht, eine LED leuchten zu lassen.
1
#define F_CPU 1000000UL     /* 1MHz */ 
2
#include <avr/io.h>
3
// Defines:
4
#define LED1    (1<<PD5)
5
#define LED2    (1<<PD6)
6
int main()
7
{
8
  // LED-Pins als Ausgang definieren
9
  DDRD |= LED1 | LED2;
10
 
11
  // LED1 einschalten
12
  PORTD |= LED1; }

Hat funktioniert, ein Schritt nach vorne.

Das mit dem Timer habe ich mittlerweile auch begriffen, nur ist mir 
folgendes immer noch ein Rätsel:
Was bedeutet dieses ISR? Mag nun plump erscheinen, aber diese Frage 
quält mich.
Was hat es damit auf sich? Was macht man damit, bzw. wofür ist es gut?
Danke!

von Dietrich L. (dietrichl)


Lesenswert?

Oldie schrieb:
> Was bedeutet dieses ISR?

- ISR = Interrupt Service Routine
- Interrupt: ein Hardware-Ereignis, dass den Programmablauf unterbricht 
und die ISR anspringt. Am Ende der ISR kehrt der Prozessor wieder an die 
Stelle zurück, wo das Hauptprogramm unterbrochen wurde.
- Bei der hier beschriebener Anwendung ist es der Timer (ein Stück 
selbständig arbeitende Hardware innerhalb des Mikrocontollers), der z.B. 
bei Überlauf den Interrupt auslöst.
- Der Timer wird also am Anfang des Hauptprogramms entsprechend 
konfiguriert (durch Einträge in verschidenen Steuerregistern des 
Timers), dass er z.B. jede Millisekunde eine Interrupt auslöst. Ab 
diesem Zeitpunkt ist der Interrupt aktiv und die ISR wird jede 
Millisekunde abgearbeitet - unabhängig vom Hauptprogramm.

Ansonsten solltest Du z.B. mal hier lesen: 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmieren_mit_Interrupts

Gruß Dietrich

von Oldie (Gast)


Lesenswert?

Okay Leute, ich habe eine Bitte an euch:
Vom Ding her habe ich das ganze jetzt mehr oder weniger verstanden, nur 
bräuchte ich mal einen von euch, der, wenn er die Zeit findet und ein 
bisschen Lust hat, mir diesen Code schreibt, damit ich das ganze 
wirklich nachvollziehen kann, da ich noch Probleme damit habe, die 
einzelnen Abfolgen richtig aufzuschreiben.
>> Atmega8
>> Wenn Schließer aktiviert, dann leuchtet LED1 ununterbrochen.
>> Parallel dazu startet Timer0.
>> Der Takt von 4Mhz wird durch einen Prescaler von 64 geteilt.
>> Das bedeutet: 250 abgelaufene Interrupts ^= 1 Sekunde.
>> Wenn 1 Interrupts abgelaufen, dann inkrementiere Variable interrupts um
>> 1.
>> Wenn Variable Interrupts bei 250, dann inkrementiere Variable Sekunde
>> UND
>> setze Variable Interrupts zurück auf 0, aber lass ihn weiterzählen.
>> Wenn Sekunde bei 60, setzte zurük auf 0 und inkrementiere Variable
>> Minute.
>> Wenn Minute = 120, dann leuchtet LED2 auf.
>> Wenn Minute = 140, dann beende leuchten von LED2 + Beende kompletten
>> Vorgang.

Also eben, dass von Anfang an LED1 (1<<PD5) leuchtet und dass dabei der 
Timer läuft, der dann nach 2 Stunden (kann man dann ja noch ändern in 
weniger) LED2 (1<<PD6) anfängt zu leuchten. Wenn es dann aber zu 140 
Mintuen gekommen ist, geht LED2 wieder aus. LED1 leutet ununterbrochen.

Wie gesagt, wenn mir einer helfen würde, wäre ich extrem begeistert und 
er wäre der netteste Mensch der Welt, ich bräuchte das einfach mal, um 
es nachzuvollziehen. Bitte keine Kommentare á la "Mach's dir selbst du 
BOB", VIELEN DANK!

von Oldie (Gast)


Lesenswert?

Bitte bitte, ich bräuchte das unbedingt!

von Oldie (Gast)


Lesenswert?

Wenn mir jemand das in C schreibt, ich nerve nie wieder! Ich bitte euch 
aus tiefstem Herzen..

von Oldie (Gast)


Lesenswert?

Ich tanze sogar dafür!!!

von Oldie (Gast)


Lesenswert?

Ist hier echt keiner, der das für mich machen würde? :(

von Oldie (Gast)


Lesenswert?

Oder anders: Wieso funktioniert das nicht?
1
#define F_CPU 1000000UL     /* 1MHz */
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
// Defines:
6
#define LED1    (1<<PD5)
7
#define LED2    (1<<PD6)
8
 
9
//Variablen für die Zeit
10
volatile unsigned int millisekunden;
11
volatile unsigned int sekunde;
12
volatile unsigned int minute;
13
volatile unsigned int stunde;
14
 
15
int main(void)
16
{
17
18
// LED-Pins als Ausgang definieren
19
  DDRD |= LED1 | LED2;
20
 
21
  // LED1 einschalten
22
  PORTD |= LED1;
23
  
24
  
25
  // Timer 0 konfigurieren
26
  TCCR0A = (1<<WGM01); // CTC Modus
27
  TCCR0B |= (1<<CS01); // Prescaler 8
28
  // ((1000000/8)/1000) = 125
29
  OCR0A = 125-1;
30
 
31
  // Compare Interrupt erlauben
32
  TIMSK |= (1<<OCIE0A);
33
 
34
  // Global Interrupts aktivieren
35
  sei();
36
 
37
  while(1)
38
  {
39
    if ( minute == 2 )
40
  {
41
  PORTD |= LED2;
42
  
43
  }
44
  }
45
}
46
 
47
/*
48
Der Compare Interrupt Handler 
49
wird aufgerufen, wenn 
50
TCNT0 = OCR0A = 125-1 
51
ist (125 Schritte), d.h. genau alle 1 ms
52
*/
53
ISR (TIMER0_COMPA_vect)
54
{
55
  millisekunden++;
56
  if(millisekunden == 1000)
57
  {
58
    sekunde++;
59
    millisekunden = 0;
60
    if(sekunde == 60)
61
    {
62
      minute++;
63
      sekunde = 0;
64
    }
65
    if(minute == 60)
66
    {
67
      stunde++;
68
      minute = 0;
69
    }
70
    if(stunde == 24)
71
    {
72
      stunde = 0;
73
    }
74
  }
75
}

Ist die LED jetzt nur bei minute == 2 an oder auch danach? Wie sag ich, 
dass er die LED auch danach noch anlassen soll?

von Karl H. (kbuchegg)


Lesenswert?

Oldie schrieb:


> Ist die LED jetzt nur bei minute == 2 an oder auch danach? Wie sag ich,
> dass er die LED auch danach noch anlassen soll?

Aus deinen Vorstudeien solltest du eigentlich schon gelernt haben, dass 
normale Output Port Pins nicht eigenmächtig ihren Zustand ändern.

Wenn du daher hier
1
  PORTD |= LED2;
den Pin auf 1 schaltest, dann bleibt der auch auf 1, weil es im ganzen 
Programm keine Anweisung gibt, die ihn wieder ausschalten würde.

Im übrigen hättest du dir die Frage ganz leicht selbst beantworten 
können. D.h. der Mega hätte sie dir beantwortet. Brenn das Programm 
rein, lass es laufen und sieh nach wie es sich verhält.


Genau deswegen schreibt dir das auch keiner, obwohl es für dir meisten 
eine Kleinigkeit ist. Wir sind hier kein kostenloses Entwicklungsbüro. 
In erster Linie eignest du dir die Kenntnisse an und schreibst das 
Programm. Wenn es momentan noch zu schwer ist, dann schalt 3 Gänge 
zurück und fang mit etwas einfacherem an. Dann erhebt sich auch die 
Frage nicht mehr, ob und wie lange ein Portpin nach dem Setzen auf 1 
auch auf 1 bleibt. Dann WÜSSTEST du nämlich, dass er das tut. Und zwar 
so lange, bis eine Anweisung ausgeführt wird, die den Pin wieder auf 0 
setzt. Gibt es keine, dann bleibt der Pin bis zum St. Nimmerleinstag auf 
1.

: Bearbeitet durch User
von olli (Gast)


Lesenswert?

Warum hast du denn jetzt noch Millisekunden eingebaut?
am besten fängst du jetzt mal an selbst zu Coden und nicht nur 
unverstandenes zusammen zu kopieren.
und wenn es nicht klappt übst du erstmal ganz normale interrupts mit 
LEDs und blinken mit nem Timer und dann geht's weiter.

von Jon D. (shee2e)


Lesenswert?

Hallo,
ich habe mir den Code nur kurz überflogen:
kannst du mir sagen welchen wertebereich unsigned int hat?
(Tip die antwort wird nahe an deimen Problem liegen warum es nicht das 
tutst was du dir gedacht hast.
Und ich schließe mich den anderen an, ganz langsam. Und am besten 
anderen Quellcode nicht abkopieren sondern abschreiben (ich weiß man 
kommt sich blöd vor) und wenn man eine Zeile nicht versteht in der C 
referenz bzw. dem Datenblatt nachgucken.
Mfg
shee2e

von Logiker (Gast)


Lesenswert?

>Und am besten anderen Quellcode nicht abkopieren sondern abschreiben

Ich möchte an dieser Stelle widersprechen. Das entscheidende ist nicht, 
ob der Code nun kopiert oder abgeschrieben wird. Das entscheidende ist, 
dass man einen Code, Ausdruck für Ausdruck, Zeile für Zeile, Schleife 
für Schleife nachvollzieht. Unterläßt man das aber dann ist abschreiben 
oder kopieren gleichermaßen sinnlos.

Der Rat, Code nicht einfach nur zu kopieren, bezieht sich demgemäß im 
Besonderen auf solche Frager hier, die schon Code kopiert haben und das 
Nachvollziehen unterlassen haben.

Man mag darüber verschiedener Meinung sein, ob nicht der Rat den Code 
nachzuvollziehen sinnvoller sei. Aber es ist für den Leser dann sicher 
einfacher einen Code anzusehen und zu erkennen ob der Code verstanden 
worden ist, als die sprachlich an sich schon nicht einfache Beschreibung 
des Fragers zu untersuchen. Deswegen ist der Rat Code lieber von Grund 
auf selbst zu schreiben effektiver.

von Jon D. (shee2e)


Lesenswert?

Hallo,
ja ok kopiert oder abgeschreiben ist eigentlich egal, aber nachvollzogen 
sollte es werden. Da ist das abschreiben für mich besser da ich damals 
das sicher jede Zeile durchgeganen bin und in dem Fall, dass ich es 
nicht verstanden habe, sofort nachgeschlagen habe.
MfG
shee2e

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Jon Doe schrieb:

> sollte es werden. Da ist das abschreiben für mich besser

Ist bei mir ähnlich.
Wenn ich etwas abschreibe, dann bleibt es nicht aus, dass man sich 
während des Schreibens bereits erste Gedanken macht. Und sei es nur, 
dass man die generelle Struktur erkennt bzw. ganz nebenbei anfängt die 
im Programm verwendeten Begriffe bzw. die Nomenklatur sich zu merken.

Den Code durchgehen ist zweifellos unabdingbar. Trotzdem ist abschreiben 
(überhaupt bei so kurzen Programmen) zumindest bei mir in dieser 
Hinsicht wesentlich effektiver als einfach nur kopieren mittels 
Copy&Paste.

von Logiker (Gast)


Lesenswert?

An sich liegt, zumindest meiner Ansicht nach, das Nachvollziehen sowieso 
schon sehr nahe; meint: man sollte sowieso auf die Idee kommen das zu 
tun.

Nun, ich wollte darauf hinaus, das jemand der Code kopiert hat und ihn 
offensichtlich nicht verstanden hat, vermutlich dazu neigt, ihn auch 
dann nicht nachzuvollziehen wenn er ihn abschreibt.

Das jemand wie Du, Karl-Heinz, den Code nachvollzieht, den er 
abschreibt, bezweifle ich dagegen keinen Augenblick.

von zong (Gast)


Lesenswert?

Das Tutorial auf dieser Seite ist imho wirklich sehr gut zum Lernen der 
notwendigsten Dinge. Arbeite doch da erstmal los und du wirst erstaunt 
sein, wie einfach dein Problem nach ein paar verstandenen Grundlagen 
ist...

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.