Forum: Mikrocontroller und Digitale Elektronik Taster-Interrupt Fragen


von batty m. (battyman)


Lesenswert?

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

von Helge A. (besupreme)


Lesenswert?

Bau dir einen interrupt handler, der irgendeinen Merker (ein bit) setzt.

Das wertest du dann im Hauptprogramm aus, wenns dir in den Kram paßt.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von batty m. (battyman)


Lesenswert?

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:
1
#include <avr/io.h>
2
#include <stdint.h>
3
#include <avr/interrupt.h>
4
#define F_CPU 8000000UL
5
#include <util/delay.h>
6
7
#define KEY_DDR         DDRC
8
#define KEY_PORT        PORTC
9
#define KEY_PIN         PINC
10
#define KEY0            0
11
#define KEY1            1
12
#define KEY2            2
13
#define ALL_KEYS        (1<<KEY0 | 1<<KEY1 | 1<<KEY2)
14
15
#define REPEAT_MASK     (1<<KEY1 | 1<<KEY2)    // repeat: key1, key2
16
#define REPEAT_START    50                     // after 500ms
17
#define REPEAT_NEXT     20            // every 200ms
18
19
#define LED_DDR         DDRD
20
#define LED_PORT        PORTD
21
#define LED0            0
22
#define LED1            1
23
#define LED2            2
24
25
volatile uint8_t key_state;                                // debounced and inverted key state:
26
// bit = 1: key pressed
27
volatile uint8_t key_press;                                // key press detect
28
29
volatile uint8_t key_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
  static uint8_t ct0, ct1, rpt;
4
  uint8_t i;
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.  -.-

von Uwe (de0508)


Lesenswert?

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

von batty m. (battyman)


Lesenswert?

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.

von batty m. (battyman)


Lesenswert?

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
1
^=
die beiden in Verbindung.

von batty m. (battyman)


Lesenswert?

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 :-)

von m.n. (Gast)


Lesenswert?

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.

von Dieselwolf (Gast)


Lesenswert?

Batty schrieb:
>Auch wenns nun i-wie sich doof liest.

Das ist wahr: "i-wie" liest sich irgendwie doof.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von batty m. (battyman)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

> 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.

von batty m. (battyman)


Lesenswert?

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:
1
...
2
TCCR0B = (1<<CS02);         // divide by 256
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
...
muss ich nicht noch eigentlich folgendes dazu schreiben:
1
TIFR0 |= 1<<TOV0;
???

von Karl H. (kbuchegg)


Lesenswert?

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.

von batty m. (battyman)


Lesenswert?

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.000 Hz / 1024 * 0,01 + 0,5
3
      = 976,5625 Hz * 0,01 + 0,5
4
      = 9,77 Hz + 0,5
5
      = 10,27 Hz (* ^-1 s)
6
      = 97,4 ms  ->  rund 100ms und nicht 10ms
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.000 Hz / 1024 * 0,01 + 0,5
3
      = 7812,5 Hz * 0,01 + 0,5
4
      = 78,125 Hz + 0,5
5
      = 78,625 Hz (* ^-1 s)
6
      = 12,7 ms  ->  rund 15ms

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.000 Hz / 1024 Prescaler / 256 Zählsteps
2
           = 976,5625 Hz / 256
3
           = 3,815 Hz (* ^-1 s)
4
           = 262,14 ms
5
6
ISR-Aufruf = 8.000.000 Hz / 1024 Prescaler / 256 Zählsteps
7
           = 7812,5 Hz / 256
8
           = 30,518 Hz (* ^-1 s)
9
           = 32,8 ms
10
11
ISR-Aufruf = 8.000.000 Hz / 256 Prescaler / 256 Zählsteps
12
           = 31250 Hz / 256
13
           = 122,07 Hz (* ^-1 s)
14
           = 8,19 ms

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

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von batty m. (battyman)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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"
19
20
          b * c
21
    x = ---------
22
            a
23
24
25
---
26
27
   3 Äpfel   ......   5 Euro
28
   7 Äpfel   ......   x Euro
29
  ---------------------------
30
        5 * 7 
31
    x = ------ = 11.66 Euro
32
          3

: Bearbeitet durch User
von batty m. (battyman)


Lesenswert?

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
...
2
while(1)
3
{
4
// 1.) --------------------------------------------------
5
    if( get_key_short( 1<<KEY1 ))
6
      LED_PORT ^= 1<<LED1;
7
 
8
    if( get_key_long( 1<<KEY1 ))
9
      LED_PORT ^= 1<<LED2;
10
 
11
// 2.) --------------------------------------------------
12
    // single press and repeat
13
 
14
    if( get_key_press( 1<<KEY2 ) || get_key_rpt( 1<<KEY2 ))
15
    {
16
      uint8_t i = LED_PORT;
17
 
18
      i = (i & 0x07) | ((i << 1) & 0xF0);
19
      if( i < 0xF0 )
20
        i |= 0x08;
21
      LED_PORT = i;      
22
    }
23
}
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
5
6
// Step a.)---------------------------------------------
7
8
  i = (0000 0000 & 0000 0111) | (0000 0000 & 1111 0000)
9
    = 0000 0000
10
11
  if ( 0x00 < 0xf0)       // -> wahr!
12
  
13
  i = 0x00 | 0x08 = 0x08  // -> 0000 1000
14
  LED_Port = i            // -> LED3 geht AN
15
16
// Step b.)---------------------------------------------
17
18
  i = (0000 1000 & 0000 0111) | (0001 0000 & 1111 0000)
19
    = 0001 0000
20
21
  if ( 0x10 < 0xf0)       // -> wahr!
22
  
23
  i = 0x10 | 0x08 = 0x18  // -> 0001 1000
24
  LED_Port = i            // -> LED3 geht AUS
25
                          // -> LED4 geht AN (verständlich)
26
27
// Step c.)---------------------------------------------
28
29
  i = (0001 1000 & 0000 0111) | (0011 0000 & 1111 0000)
30
    = 0011 0000
31
32
  if ( 0x30 < 0xf0)       // -> wahr!
33
  
34
  i = 0x30 | 0x08 = 0x38  // -> 0011 1000
35
  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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von batty m. (battyman)


Lesenswert?

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

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.