Hey ho liebe Community.
ich bin ma wieder am Erstellen von Beispielprogrammen und mehr und mehr
zu verstehen, was so alles mit nem µC geht und vor allem wie ich meine
praktischen Fragenstellungen und Aufgaben lösen kann.
Ich hangel mich ja immer von einem Punkt zum anderen und bau mir immer
verzweigte Progs auf.
Diesmal habe ich nun aber das Problem, dass ich diverse Texte als String
auf dem LCD (EA W204) ausgebe. Damit ich die Anzeige überhaupt lesen
kann gebe ich den µC mit der Delay-Funktion eine Auszeit.
Problem:
Jetzt möchte ich gerne einen taster bedienen, der am Ende der Anzeige
eine Aktion ausführt. Also mein nicht lösbares Problem liegt im Handling
mit dem taster und der Anzeige-Delay-Auszeit.
Ich würde gerne das so haben, das zwar mein Text auf dem LCD ausgegeben
wird, jedoch der µC auch mit bekommt, dass ich den Taster (hängend an
PC0) gedrückt hatte. Denn wenn dieser nicht als Gedrückt detektiert
wurde, fängt der Anzeigetext wieder von an und ich habe wieder keine
Möglichkeit der tastererkennung.
Ich hab mir das so vergestellt, dass ich beim Drücken des Tasters einen
Zähler hoch zähle. Ist bei Abfrage der Zähler Null passiert nix, wurde
dieser in der Zeit jedoch zu Eins soll xxx passieren.
Könntet ihr mir zu meinem Problem Hilfestellung und Tipps geben wie ich
meine Überschneidungszeit-Probleme lösen kann?
Vielen lieben Dank, LG
Du nimmst einen Timer, der Interrupts von rund 10ms erzeugt. In der
Interruptroutine wertest Du den PCx-Eingang aus und entprellst ihn. Ist
die Taste hinreichend lang gedrückt gewesen (50-100ms) schreibst Du in
eine globale Variable (volatile uint8_t taste) den Wert 1. Das läuft im
Hintergrund ab.
Zum gewünschten Zeitpunkt liest Du diese Variable, bearbeitest den Wert
0 oder 1 und setzt sie wieder auf 0 zurück.
batty man schrieb:> Damit ich die Anzeige überhaupt lesen> kann gebe ich den µC mit der Delay-Funktion eine Auszeit.
Warum?
Sobald sich ein Text ändert, gibst Du ihn aus.
Oder Du gibst immer wieder den gleichen Text aus, solange nichts anderes
zu tun ist.
Du darfst bloß nicht das LCD ständig löschen, sondern überschreibst
einfach den alten Text mit dem neuen Text.
Das Löschen läßt das LCD stark flackern und das sieht unprofessionell
aus.
Danke schön.
Ich sitze weiterhin daran und habe mir zum Entprellen des Tasters ran
gewagt. Dabei bin ich hier im Forum auf eine Ausführung von Peter
Dannegger gestoßen und habe versucht dessen ISR und Tastenentprellung zu
verstehen. Das wäre dann ja so in etwa das, was ich auch bräuchte.
Ich habe gleich versucht einige Einzelheiten auf meinen ATmega2560 zu
übertragen. Jedoch funzt keine LED vom STK600, wodurch ich davon
ausgehe, dass der Code so nicht passt. Nun habe ich versucht alle Steps
Schritt für Schritt zu verstehen, also wann er was warum macht.
Sein Bsp. läuft mit F_CPU 1000000UL und meines mit "echten" F_CPU
8000000UL, da ich dafür in den Fuses den Haken bei CKDIV8 rausgenommen
habe. Dazu benutze ich das Optimierungslevel "Os". Weitere Angleichung
waren die Ports, da ich für die Taster PortC und für die LEDs PortD in
Benutzung habe. Damit sieht es bei mir nun so aus:
volatileuint8_tkey_state;// debounced and inverted key state:
26
// bit = 1: key pressed
27
volatileuint8_tkey_press;// key press detect
28
29
volatileuint8_tkey_rpt;// key long press and repeat
Dazu habe ich folgende Fragen:
1
#define REPEAT_START 50 // after 500ms
2
#define REPEAT_NEXT 20 // every 200ms
Wenn ich nun die 8-fache Frequenz benutze müsste ich doch ansich einfach
nur um das 8-fache die Zählzahl erhöhen. Das funktioniert so aber nicht,
da die Deklaration nur bis 255 zulässig ist. 400 würde das demnach
übersteigen. Nun dachte ich mir, dass das alles nicht so schlimm sei,
dann ist mein Code eben um das 8-fache schneller wie eben das Bsp..
weiter:
1
ISR(TIMER0_OVF_vect)// every 10ms
2
{
3
staticuint8_tct0,ct1,rpt;
4
uint8_ti;
5
6
TCNT0=(uint8_t)(int16_t)-(F_CPU/1024*10e-3+0.5);// preload for 10ms
7
8
i=key_state^~KEY_PIN;// key changed ?
9
ct0=~(ct0&i);// reset or count ct0
10
ct1=ct0^(ct1&i);// reset or count ct1
11
i&=ct0&ct1;// count until roll over ?
12
key_state^=i;// then toggle debounced state
13
key_press|=key_state&i;// 0->1: key press detect
14
15
if((key_state&REPEAT_MASK)==0)// check repeat function
16
rpt=REPEAT_START;// start delay
17
if(--rpt==0){
18
rpt=REPEAT_NEXT;// repeat delay
19
key_rpt|=key_state&REPEAT_MASK;
20
}
21
}
hierbei liegt mein Verständnisproblem weiterhin in den Zeitenvorgaben,
denn:
1
TCNT0=(uint8_t)(int16_t)-(F_CPU/1024*10e-3+0.5);// preload for 10ms
1. Was passiert bzw. warum schreibt man "(uint8_t)(int16_t)" das so
hintereinander? Was soll denn da drin stehen?
2. Wenn ich (angenommen F_CPU = 1000000) das errechne kommt 486 raus.
486^-1 wäre das in Sekunden ja entsprechend. Jedoch sind das nicht die
10ms.
Was macht eigentlich der Operator "Exklusiv-Oder Gleich":
1
key_state^=i;
Könnte mir das einer mal bitte genauer erklären. Mit den logischen
Operanden habe ich leider noch etwas Verständnisprobleme und deshalb
auch noch nicht ganz verstanden wann welcher gewisse Vor-/Nachteile hat.
Danke, ich denke die Fragen reichen erstmal, auch wenn wohl nach dieser
Klärung meine LEDs bei Tastendruck noch nicht leuchten. -.-
XOR == es ist schön wenn man suchen könnte.
Sorry, das sind Basis und wenn die dir nicht bekannt sind, solltest Du
die PeDa Routine als Blackbox verwenden, denn sie funktioniert.
Hilfe kommt aber: http://de.wikipedia.org/wiki/XOR-Gatter
Peter Dannegger schrieb:> batty man schrieb:>> Damit ich die Anzeige überhaupt lesen>> kann gebe ich den µC mit der Delay-Funktion eine Auszeit.>> Warum?>> Sobald sich ein Text ändert, gibst Du ihn aus.> Oder Du gibst immer wieder den gleichen Text aus, solange nichts anderes> zu tun ist.>
Naja, meine ausgegebenen Texte sind jeweils, aufgrund der Zeichenlänge,
in Laufform. Damit ändert sich alle 300ms der ausgegebene Text. Während
der Daly-Zeit von 300ms kann ich mit dem Taster bisher keine weitere
Aktion nach dem Durchlauf von wegen noch während des Durchlaufes
ausführen.
Also der erste Durchlauftext soll solange laufen wie ich Taster0 nicht
gedrückt habe/hatte. Habe ich den Taster gedrückt soll der erste Text
nie wieder angezeigt und dafür der Zweite Text ausgegeben werden.
Mittlerweile habe ich auch dein (für mich) heftig komplexen Code mit bis
zu 8-Tastern und deren Entprellung versucht zu verstehen. Hehe, leider
noch völlig ohne Erfolg. LEDs leuchten bei kurzen oder langen
Drückzeiten nicht. I-wo ist n heftiger Umsetzungfehler. Sorry, meine
Transferleistung ist aufgrund des nicht ganz verstandenen ziemlich
ungenügend.
Uwe S. schrieb:> XOR == es ist schön wenn man suchen könnte.>> Sorry, das sind Basis und wenn die dir nicht bekannt sind, solltest Du> die PeDa Routine als Blackbox verwenden, denn sie funktioniert.>> Hilfe kommt aber: http://de.wikipedia.org/wiki/XOR-Gatter
Sorry, ich meinte speziell
batty man schrieb:> Was macht eigentlich der Operator "Exklusiv-Oder Gleich":>
1
>key_state^=i;
2
>
Ist es richtig verstanden, wenn ich das auch wie folgt scheiben würde?:
1
key_state=key_state^i;
Denn dann habe ich dies wenigstens verstanden ;-) Ich Ver-ExclusivOder
"kay_state mit i (Alles was als Ergibnis ungerade ist wird zu 1, gerade
zu 0) und übergebe anschließend den neuen Wert meiner Variablen über.
Das mit den Kurzschreibweisen bedarf etwas Übung um es logisch und
sofort zu sehen.
Offen bleiben für mich jedoch die weiteren Fragen, in Bezug auf meiner
8MHz Taktfrequenz.
1
#define REPEAT_START 50 // after 500ms
2
#define REPEAT_NEXT 20 // every 200ms
>Wenn ich nun die 8-fache Frequenz benutze müsste ich doch ansich einfach>nur um das 8-fache die Zählzahl erhöhen. Das funktioniert so aber nicht,>da die Deklaration nur bis 255 zulässig ist. 400 würde das demnach>übersteigen. Nun dachte ich mir, dass das alles nicht so schlimm sei,>dann ist mein Code eben um das 8-fache schneller wie eben das Bsp..
und
1
TCNT0=(uint8_t)(int16_t)-(F_CPU/1024*10e-3+0.5);// preload for 10ms
>1. Was passiert bzw. warum schreibt man "(uint8_t)(int16_t)" das so>hintereinander? Was soll denn da drin stehen?>2. Wenn ich (angenommen F_CPU = 1000000) das errechne kommt 486 raus.>486^-1 wäre das in Sekunden ja entsprechend. Jedoch sind das nicht die>10ms.
Also ich bau mir eine ISR mit dem Timer0 (8-Bit). Diese ISR ist als
Overflow deklariert, was zur Folge hat, dass immer ein Zählen von 0-255
und danach ein Ergeignis kommt. Damit das in sinnigene Bahnen läuft muss
ich vorher meine Taktfrequenz reduzieren, sonst habe ich all 32µs ein
Auslösen. In diesem Fall sollen es aber z.B alle 10ms sein. Demnach muss
ich mir was logisches einfallen lassen, was ein Divisionsergebnis von
100Hz ergibt.
Ist das soweit richtig? Sorry nach, i-wie bin ich langsam bei allen
etwas verunsichert das wirklich richtig verstanden zu haben. Auch wenns
nun i-wie sich doof liest. Denn ich kann die "TCNT0"-Rechnung nicht
wirklich nachvollziehen.
LG :-)
batty man schrieb:> Ist das soweit richtig? Sorry nach, i-wie bin ich langsam bei allen> etwas verunsichert das wirklich richtig verstanden zu haben. Auch wenns> nun i-wie sich doof liest. Denn ich kann die "TCNT0"-Rechnung nicht> wirklich nachvollziehen.
Der obige Code ist 'write-only'. Dabei geht es nicht darum, dass man ihn
gut kommentiert einfach nachvollziehen kann, sondern darum, die
Optimierung dem dummen Compiler möglichst wegzunehmen. Auch hofft man
wohl, durch kurze nichtssagende Variablen, die
Ausführungsgeschwindigkeit zu erhöhen :-)
Schreib Dir Deinen eigenen Code, den Du auch selber gut nachvollziehen
kannst. Davon hast Du mehr und lernst es auch noch.
Konzentriere Dich zunächst auf das PC0-Bit und baue keine
Eierlegendewollmilchsau.
batty man schrieb:> hierbei liegt mein Verständnisproblem weiterhin in den Zeitenvorgaben,> denn:>
1
>TCNT0=(uint8_t)(int16_t)-(F_CPU/1024*10e-3+0.5);// preload for
2
>10ms
3
>
Die kannst du rauswerfen, wenn deine ISR ungefär in diesem Zeitraster
aufgerufen wird. Das ist nicht zeitkritisch. Ob die ISR alle 5ms
aufgerufen wird, oder alle 20ms ist Jacke wie Hose.
D.h. hier hast du Spielraum. Wenn du diese ISR auch noch für andere
Dinge benutzen willst, dann ist das aus Sicht des Entprellcodes kein
Problem. Du richtest dir die ISR auf den Zeitraum ein, den du brauchst
und dann schaut man weiter, ob das für den Entprellcode im Zeitfenster
liegt und überlegt sich, was man macht, wenn man da grob draussen ist.
> 1. Was passiert bzw. warum schreibt man "(uint8_t)(int16_t)" das so> hintereinander? Was soll denn da drin stehen?
Da steht gar nichts drinnen. Das sind Typcasts und die Reihenfolge soll
einfach nur sicherstellen, dass beim Runterrechnen des Zahlenwertes in
eine 8 Bit Zahl keine unerwünschten Konvertierungen erfolgen.
Aber wie gesagt: du kannst diese ganze Vorladerei auch lassen, wenn die
ISR AUfrufe im richtigen Zeitbereich erfolgen.
> 2. Wenn ich (angenommen F_CPU = 1000000) das errechne kommt 486 raus.> 486^-1 wäre das in Sekunden ja entsprechend. Jedoch sind das nicht die> 10ms.
Egal.
Dein Zeit-'Spielraum' erstreckt sich von ca. 5ms bis ca. 20ms. Und wenn
es 30ms sind, ist es auch kein Beinbruch. Das ist alles insofern nicht
zeitkritisch, als es eben nicht auf die Millisekunde genau sein muss.
Oh danke.
Supi, dann benutze ich meine 8MHz und hau die mit n Prescaler von 256
runter. Wenn ich damit einmal 256 Schritte zähle, bekomme ich eine
Aufrufzeit von gut 8,2ms. Wenn mein Rechengedanke stimmt ist das doch
toll.
Schade ist nur, dass ich den Rechengedanken des werten Hr. Dannegger
nicht nach voll ziehen kann. Diese Rechnung einfach nur angewandt macht
leider nicht die
>//preload for 10ms
.
Naja, das Tasten entprellen wird auch noch so ein Spaß für sich ;-)
Wenn ich beim Lesen doch bloß nicht so Begriffstutzig wäre, sorry. Ich
will nur nicht die echt gut gemachten Tuts und Codes einfach so nehmen.
Ich habe sie dann ja nicht verstanden (trotz mehrmaligen lesen und zur
Hilfenahme des 2560 DS) - bringt mir also gar nichts und ich will es
lernen und nicht nur abkopieren, denn dann kann ich mir auch eigene
Gedanken und Lösungen für weitere Aufgabenstellungen erarbeiten. :D ala
Gib Mann Fisch oder lehre ihm zu fischen!
Step für Step wirds besser (Ansichtssache) und vor allem komm ich
langsam mit dem Datenblatt mehr klar. Danke für die Geduld mit mir
batty man schrieb:> Schade ist nur, dass ich den Rechengedanken des werten Hr. Dannegger> nicht nach voll ziehen kann. Diese Rechnung einfach nur angewandt macht> leider nicht die>>//preload for 10ms
Ich denke, du hast übersehen, dass er in seinem Code einen Vorteiler von
1024 benutzt und das dieser Vorteiler hier
1
F_CPU/1024*10e-3+0.5);
wieder auftaucht.
Mit diesem Wissen ist die Formel wieder nachvollziehbar. Leider gibt es
keine einfache Möglichkeit, wie man die Prescaler Bits mit einem
eventuellen Faktor in Berechnungen verknüpfen kann.
> Naja, das Tasten entprellen wird auch noch so ein Spaß für sich ;-)
Nicht wirklich.
PeDa Code rein und die Sache ist gegessen.
Ich steh immer noch auf dem Standpunkt: dieser Code funktioniert so gut,
den muss man nicht verstehen. Du verwendest ja auch eine sqrt Funktion
wenn du sie brauchst, könntest selber aber wahrscheinlich keine
effiziente schreiben.
Ich hab im Artikel
Entprellung
mal eine kurze Beschreibung des Kern-Elements (die Bitoperationen)
versucht. Das ist aber nicht so einfach zu beschreiben. So richtig
würdigen kann man den Code allerdings erst, wenn man ihn verstanden hat
(und ich hab auch ein paar Stunden dafür gebraucht, bis ich die
Bitoperationen in allen Details intus hatte). Bis dahin: einfach
verwenden, so wie man eine sqrt Funktionalität oder eine exp()
Funktionialität aus der C-Standardlib auch einfach verwendet.
> Supi, dann benutze ich meine 8MHz und hau die mit n Prescaler> von 256 runter. Wenn ich damit einmal 256 Schritte zähle, bekomme> ich eine Aufrufzeit von gut 8,2ms.
Passt.
> Wenn mein Rechengedanke stimmt
Er stimmt.
Karl Heinz schrieb:> Ich denke, du hast übersehen, dass er in seinem Code einen Vorteiler von> 1024 benutzt und das dieser Vorteiler hier>
1
>F_CPU/1024*10e-3+0.5);
2
>
> wieder auftaucht.
Mhh, doch. An dieser Stelle von TCCR0. Jedoch ist 1Mhz/1024 ca 1ms.
(Und ich glaub jetzt kommt mir gerade ein rechen-lesefehler. Für mich
stand bis so eben 10 mal Euler hoch -3. Uuuuhhh man, mit 10 mal 10 hoch
-3 ist alles gut - ergo 10,27, wobei ich mir nun gerade nicht sicher bin
dass dies in ms sein soll, mhhh)
1
...
2
TCCR0=(1<<CS02)|(1<<CS00);// divide by 1024
3
TCNT0=(uint8_t)(int16_t)-(F_CPU/1024*10e-3+0.5);// preload for 10ms
4
TIMSK|=1<<TOIE0;// enable timer interrupt
5
sei();
6
...
In meinem Fall wäre das TCCR0B-Register und für meine 8,2ms:
batty man schrieb:> Mhh, doch. An dieser Stelle von TCCR0. Jedoch ist 1Mhz/1024 ca 1ms.> (Und ich glaub jetzt kommt mir gerade ein rechen-lesefehler. Für mich> stand bis so eben 10 mal Euler hoch -3. Uuuuhhh man, mit 10 mal 10 hoch> -3 ist alles gut
LOL
Mach dir nichts draus. Ich hab auch erst mal gestutzt und mich gefragt,
wie da jetzt aus der vermeintlichen 1 Millisekunde plötzlich 10 werden
:-)
Blackout haben wir alle mal.
> muss ich nicht noch eigentlich folgendes dazu schreiben:>
1
>TIFR0|=1<<TOV0;
2
>
???
Kannst du machen, wenn du willst.
Es spielt aber keine Rolle, ob der Timer Interrupt nach Aktivierung
einmalig aufgerufen wird oder nicht. Die Entprellung kommt deswegen
nicht aus dem Tritt.
TOV0 in TIFR0 ist ja nur das Bit, welches das Ereignis 'Es ist ein
Overflow aufgetreten' registriert.
So, nach dem ich nun diverse Stunden rum probiert habe folgendes. Der
Code von PeDa funzt echt supi. Cooles Teil. Um alles vereinzelt zu
verstehen habe ich nun immer nach und nach gewisse Teile geändert und
eben learning by doing. Das mit dem Taster2 und dem LED-Wechsel muss ich
auf n Blatt Papier mit 0en und 1en nachrechnen. Dann kommt die Logik
schon von ganz allein. Aufhalten tue ich mich aber leider doch noch mal
mit der Zeit und dessen Berechnung, auch wenn wie gesagt alles mit 8MHz
sehr gut läuft. Wenn ich bloß n Teiler von 256 nehme kann ich gar nicht
so schnell drücken um Einzelereignisse zu erzeugen. Hihi
Aber:
1.)
1
TCNT0=F_CPU/1024*10*10^-3+0,5
2
=1.000.000Hz/1024*0,01+0,5
3
=976,5625Hz*0,01+0,5
4
=9,77Hz+0,5
5
=10,27Hz(*^-1s)
6
=97,4ms->rund100msundnicht10ms
würde ja auch mehr Sinn machen denn wenn ich nun anstatt 1Mhz eben 8MHz
benutze, würde auch die Rechendauer verkürzt und nicht verlängert
werden:
1
TCNT0=F_CPU/1024*10*10^-3+0,5
2
=8.000.000Hz/1024*0,01+0,5
3
=7812,5Hz*0,01+0,5
4
=78,125Hz+0,5
5
=78,625Hz(*^-1s)
6
=12,7ms->rund15ms
Ohne das ganze einfach gerechnet und mit der Berücksichtigung der
Zählsteps wär das dann ja so (was sich mathematisch doch auch selbst
erklärt):
2.)
1
ISR-Aufruf=1.000.000Hz/1024Prescaler/256Zählsteps
2
=976,5625Hz/256
3
=3,815Hz(*^-1s)
4
=262,14ms
5
6
ISR-Aufruf=8.000.000Hz/1024Prescaler/256Zählsteps
7
=7812,5Hz/256
8
=30,518Hz(*^-1s)
9
=32,8ms
10
11
ISR-Aufruf=8.000.000Hz/256Prescaler/256Zählsteps
12
=31250Hz/256
13
=122,07Hz(*^-1s)
14
=8,19ms
Echt entschuldigung jedoch schnall ich die Gedankengänge der ertsen
Rechnung nicht. Zweiteres erklärt sich ja von selbst und zeigt auch die
zuerwartenen zeitereignisse.
Kann es auch sein, dass ich:
1
#define REPEAT_START 50 // after 500ms
2
#define REPEAT_NEXT 20 // every 200ms
diese vergessen habe bei den Berechnungen zuberücksichtigen. Ich gehe
nämlich auch davon aus, dass diese nicht 500ms und 200ms sind.
-.- Sorry, aber so einwenig muss es einleuchtend sein. Bei der
Wurzelrechnung verstehe ich zumindesten warum ich diese benutzen muss,
auch wenn ich sie nicht vereinfacht aufstellen kann.
Ich sehe einfach meinen Denkfehler nicht, da ich dem Code-Ersteller
seinen Gedankenpfad nicht folge leisten kann. Ääääähhhh
batty man schrieb:> Echt entschuldigung jedoch schnall ich die Gedankengänge der ertsen> Rechnung nicht.
Wenn der Timer frei laufen dürfte, dann würde er in 1 Sekunde bis
1000000 zählen, weil er ja mit 1Mhz getaktet wird.
er wird aber nicht mit 1Mhz getaktet, sondern wegen dem Vorteiler von
1024 nur mit einem 1/1024-tel davon.
Wie weit könnte der Zähler daher in 1 Sekunde zählen.
Offenbar bis 1000000 / 1024 = 976
Jetzt wollen wir ihn aber nicht 1 Sekunde lang zählen lassen, sondern
nur 0.01 Sekunden.
Wie weit kommt er dann?
Er kommt bis 976 * 0.01 = 9.76
Die 0.5 sind dann einfach nur noch eine Rundungskorrektur, so dass
Ergebnisse mit Nachkommastellen kleiner x.5 zu x werden und welche mit
Nachkommastellen größer gleich x.5 zu x + 1 werden.
Das Ergebnis ist daher 10
Gegenprobe:
Wenn der Timer mit 1Mhz laufen würde, dann würde die Erhöhung des Timers
um 1 genau 1/1000000 Sekunden dauern.
Mit einem Vorteiler von 1024, das haben wir schon gesehen, zählt der
Timer in 1 Sekunde bis 976.5625. 1 Timer-'Tick' dauert daher 0,001024
Sekunden oder etas mehr als 1 Millisekunde. Um daher 10 Millisekunden
abzuwarten, müss man knapp 10 Timer-'Ticks' abwarten.
10 derartige Ticks dauern 10.24 Millisekunden
9 derartige Ticks würden 9.24 Millisekunden dauern.
10 ist daher näher am richtigen Ergebnis auch wenn keine der beiden
Zahlen exakte 10 Millisekunden ergibt.
Karl Heinz schrieb:> Er kommt bis 976 * 0.01 = 9.76>> Die 0.5 sind dann einfach nur noch eine Rundungskorrektur, so dass> Ergebnisse mit Nachkommastellen kleiner x.5 zu x werden und welche mit> Nachkommastellen größer gleich x.5 zu x + 1 werden.>> Das Ergebnis ist daher 10>>> Gegenprobe:> Wenn der Timer mit 1Mhz laufen würde, dann würde die Erhöhung des Timers> um 1 genau 1/1000000 Sekunden dauern.> Mit einem Vorteiler von 1024, das haben wir schon gesehen, zählt der> Timer in 1 Sekunde bis 976.5625. 1 Timer-'Tick' dauert daher 0,001024> Sekunden oder etas mehr als 1 Millisekunde. Um daher 10 Millisekunden> abzuwarten, müss man knapp 10 Timer-'Ticks' abwarten.> 10 derartige Ticks dauern 10.24 Millisekunden> 9 derartige Ticks würden 9.24 Millisekunden dauern.>> 10 ist daher näher am richtigen Ergebnis auch wenn keine der beiden> Zahlen exakte 10 Millisekunden ergibt.
D.h., dass ich mit den 0,01 schon die Umrechnung von Herz gegen Sekunde
rechne und damit eine einheitenlose Zahl (Faktor zum generieren von
10ms) erhalte. Das mit den 0,5 hab ich verstanden, denn das habe ich
auch bei der Ausgabe meines ADC-Spannungswertes auf dem LCD benötigt. :D
Wenn ich nun meine 8MHz verwende, würde dieser eben in 10ms auf den
Zählerwert 78 kommen, richtig? Demnach würde der µC immer 78 Ticks
warten bis er wieder einen Interrupt auslöst. So zusagen das Feintuning.
batty man schrieb:> Wenn ich nun meine 8MHz verwende, würde dieser eben in 10ms auf den> Zählerwert 78 kommen, richtig?
Genau. Denn dann läuft ja auch der Timer 8 mal so schnell. Ergo muss er
weiter zählen, um dafür wieder die gleiche Zeit zu brauchen.
> Demnach würde der µC immer 78 Ticks> warten bis er wieder einen Interrupt auslöst.
Nicht ganz.
78 Timer-Ticks (also 78 mal weiterzählen des Timers um 1).
1 Timer Tick, das sind aber im gewählten Beispiel 1024 CPU-Taktzyklen
(wegen dem Vorteiler von 1024).
D.h. während die 78 Timer Ticks ablaufen, macht die CPU 78*1024, oder
gleich 79872 Takte. Über den Daumen sind das rund 75-tausend Befehle.
D.h. alle 75000 Befehle wird die CPU mal kurz unterbrochen um sich um
die Tasten zu kümmern. Wie stark daher die CPU "Belastung" durch die
Tastenprüfung ist, kannst du dir daher leicht ausrechnen :-) Selbst bei
"wohlwollend" schlechter Bewertung des ISR Codes mit 100 Taktzyklen,
belastet das daher die CPU nicht mehr als mit 0.12%. In der Praxis
völlig irrelevant, zumal die angenommenen 100 Taktzyklen völlig
überzogen sind.
batty man schrieb:> D.h., dass ich mit den 0,01 schon die Umrechnung von Herz gegen Sekunde> rechne und damit eine einheitenlose Zahl (Faktor zum generieren von> 10ms) erhalte.
Ich denke, so könnte man das sagen.
Persönlich rechne ich das ganze eher selten mit Einheiten.
Ich überleg mir das ganze in einem 2 Schritt verfahren
* wie schnell läuft der Timer wirklich?
Dazu brauch ich die AVR Taktfrequenz und den eingestellten Vorteiler.
Taktfrequenz geteilt durch Vorteiler ergibt mir, wie schnell der Timer
wirklich läuft
* ... wobei ich das auffasse als: wie weit zählt er in 1 Sekunde.
Der Rest ist dann ganz einfach nur der berühmt berüchtigte Dreisatz aus
der Mathematik. Wenn ich im ersten Schritt ermittelt habe, dass der
Timer in 1 Sekunde bis c_max zählen kann, dann:
1
c_max ..... 1 (Sekunde)
2
c ..... t
3
--------------------------------
4
5
c_max * t
6
c = ------------
7
1
8
9
bzw.
10
1 * c
11
t = --------
12
c_max
je nachdem, was ich wissen will: die Zeit zu einem Zählerstand, oder den
Zählerstand zu einer bestimmten Zeit.
Damit kann ich eigentlich so gut wie alles lösen, ohne mir die Formeln
aus dem Datenblatt merken zu müssen. Ich leite sie mir einfach jedesmal
neu her. Das geht schneller, als im Datenblatt die Formel zu suchen.
Den Dreisatz muss man halt intus haben. Aber den braucht man alle Nase
lang in der Programmierung und auch im täglichen Leben. Es schadet daher
nicht, wenn man den im Schlaf beherrscht.
1
Einschub:
2
3
Dreisatz: Prinzip: je mehr - desto mehr
4
So wie in: 3 Äpfel kosten 5 Euro. Wieviel kosten 7 Äpfel?
5
(je mehr Äpfel, desto mehr kosten sie)
6
7
a .... b
8
c .... x
9
----------------
10
11
x = ?
12
13
14
"Zahl über x, mal Stumpf durch Spitz"
15
(der 'Spitz' eines Baumes ist oben, während der Baumstumpf unten ist.
16
c ist also der Stumpf, a der Spitz)
17
18
"Zahl über x (sprich: iks), mal Stumpf durch Spitz"
Hehe :D
Jo danke, ich denke mal das nach den ausführlichen Erklärungen einiges
klar geworden ist. Das Problem ist oft ja nur, wenn ich den Gedankengang
des Programmierers nicht kenne ist es schwierig den Code nach zu
vollziehen. Und dann bin ich ab und zu Begriffstutzig beim Lesen der
Schreibweisen. Man fuchst sich aber von mal zu mal mehr rein und dann
ists beim nächsten mal auch drin.
Wenn ich dann ein ähnliches Bsp/Code sehe begreif ich schneller dessen
Funktion und Ablauf da die Transferleistung dann machbar ist, da ich das
geschriebene für mich auch interpretieren kann.
Ich habe mich nun dazu mit der main-Routine auseinander gesetzt.
1.) ist ansich klar.
ich kann auch ausfürhlicher schreiben zu LED_PORT = LED_Port ^ 1 (auf
Pin1). Wird die Ver-ExclusivOder-ung gerade kommt 0 -> LED AUS, für
ungerade 1 -> LED AN (also die Negation des aktuellen Zustandes).
Warum der Taster weiß wann ich lange oder nur kurz drücke ist mir aus
den darüber stehenden Code-Zeilen (ISR und Hiflscodes) noch nicht
ersichtlich. Gehe aber fast davon aus, dass es der letzte Teil der ISR
ist??.
2.) habe ich extra für mich leere Blätter genommen und zum Einen eine
Hex-Binär-Tabelle angefertig um schneller ablesen zu können.
Anschließend habe ich dann den Code Schritt für Schritt in Binärform
aufgedröselt um es nachzuvollziehen was im Einzelnen dort passiert. Das
es funzt sehe ich ja am Board nur wie war für mich die Frage.
Also hier als kurzes aber hoffentlich ausreichendes Bsp. (ich habe das
bis Step h gemacht und danach ist es eine wiederholte Schleife):
1
//nach erstmaligen Einschalten des µCs
2
//LED_PORT = 0
3
//damit wird an i auch eine 0 übergeben - für 8-Bit acht Nullen
4
//beim zweiten i-Baustein der Ver-Oderung Bit-Shifting um eine Stelle nach links
LED_Port=i// -> LED3 *bleibt* AUS --- WARUM?? !!!!
36
// -> LED4 AUS + LED5 AN
Wozu macht man die erste Ver-Und-ung? Die ist doch immer Null im
Ergebnis egal welchen wert i annimmt.
1
...(i&0x07)|...
Das ich mit dem Bit-Shifting und der Ver-Und-ung immer die nächst höhere
Bit-Stelle auf 1 ziehe und damit die entsprechende LED auch einschalte
ist gerafft. Das ich die vorherige Bit-Stelle mit dem erhalt der 1 die
LED ausschalte und auch für weitere Schritte auslasse ist leider gar
nicht von mir geschnallt worden. Wo im Code häng ich fest bzw. was habe
ich nicht wirklich berücksichtigt um diesen Sachverhalt zu verstehen???
Bitte um weitere Hilfe
batty man schrieb:> Wozu macht man die erste Ver-Und-ung?
Damit die 3 Bits so bleiben wie sie sind und alle anderen gesichert auf
0 kommen.
> Die ist doch immer Null
Nicht notwendigerweise. Es hängt davon ab, wie sie vorher standen, wenn
sie vom LED_PORT gelesen werden.
Generell:
In main() sind notgedrungenerweise 2 Dinge enthalten:
das eine ist die Tastenabfrage
das andere ist, dass aufgrund einer gedrückten Taste irgendwas passieren
soll, damit sich in einem Testprogramm auch etwas tut, wenn man eine
Taste drückt.
Leg daher den 'was bewirkt ein Tastendruck' Code nicht auf die
Goldwaage. Denn das ist NICHT der Code um den es im Artikel geht.
Vielleicht hatte der Autor an diesen 3 Port Bits andere LED. Vielleicht
hatte er dort auch 3 Taster hängen und wollte sicherstellen, dass die
zugehörigen Pullup-Widerstände eingeschaltet bleiben. Was auch immer.
Das ist nur Demo-Code der beweirken soll, dass sich etwas tut und man
erkennen kann, dass eine gedrückte Taste auch tatsächlich korrekt
erkannt wurde. Mehr nicht. Für die eigentliche Tastenerkennung ist das
vollkommen uninteressant, was dann ein Tastendruck auslöst. Das ist in
deinem Code sowieso je nach Aufgabenstellung anders. Für dich wichtig
ist in diesem Artikel und in dem in main gezeigten Code
* was muss ich am ANfang von main machen um die ganze Sache mal ins
Rollen
zu bringen? Muss ich die Portpins auf Eingang schalten? Muss ich die
Pullups einschalten? Wie wird der Timer initialisiert?
* was muss ich in der Hauptschleife tun, um eine gedrückte Taste zu
erkennen. Und das sind einfach nur die Aufrufe der get_.... Funktionen.
Karl Heinz schrieb:> Vielleicht hatte der Autor an diesen 3 Port Bits andere LED.
Hat er offensichtlich
1
#define LED_DDR DDRA
2
#define LED_PORT PORTA
3
#define LED0 0
4
#define LED1 1
5
#define LED2 2
das main schaltet mit kurzen bzw. langen Tastendrücken von KEY1 die LEDS
an den Pins 1 und 2. Und mit den restlichen Pins vom LED_PORT (an denen
offensichtlich auch LED hängen) und einem Tastendruck an KEY2
veranstaltet er ein Lauflicht, wobei die 3 'fixen' LED nicht beeinflusst
werden sollen.
Aber wie gesagt: das hat nichts mit den Tasten an sich zu tun. Das ist
nur eine Funktionalität, die von Tastendrücken ausgelöst wird.
Alles klar, wollt nur trotzdem die Logik dessen gleich mit verstehen. So
kann man sich viele eigene Bemerkungen/Kommentare rein schreiben und als
Beispielcode gleich abspeichern. Sonst fang ich bei Bedarf in 2 Jahren
wieder an zu über legen was da eigentlich alles so schönes passiert ;-)
Gut, aber das Mitbekommen des µC ob langer oder kurzer Tastendruck wäre
am Ende meiner Fragen dann doch noch ziemlich interessant. Wäre ja dann
cool für Enter und Return für spätere Programmebenen (auch wenn das:
warum was wo funzt - ebenfalls für die eigentliche Funktion irrelevant
ist). Echt hammer funktional der Code - n TOP - an dieser Stelle noch
mal. Wie lange überlegt man eigentlich bei solch einer Code-Erstellung.
Steht man da auch so am White-Board oder mit n Zettelhaufen und
durchspielt einmal die Varianten und Abläufe (so wie ich jetzt für das
Nachvollziehen)??
LG