Hallo zusammen, meine Frage bezieht sich auf die von mir erstellen Pläne (siehe Anhang). Ich möchte für mein selbstgebauten Arduino UNO ein Shield bauen und möchte die 6 analogen I/O´s für die Taster verwenden, genauer: die Taster sollen über ISR (Interrupt) eingelesen werden -da es sonst blöd wäre die Taster so lange gedrückt zu haltern bis der uC an der richtigen Stelle angekommen ist- und dadurch Variablen im Programm verändern. Ich kann das am Board nicht testen, da ich das UNO und sein Shield gleichzeitig ätzen möchte und somit beides noch nicht fertig habe -es wäre sehr ärgerlich wenn das Shield um sonst wäre! Kann man das mit den analogen Pins machen oder laufe ich da in die verkehrte Richtung? Die digitalen Pins benötige ich für andere Zwecke, deswegen der Umstand mit den Analogen! Danke im Voraus und frohes neues Jahr 2014 Beste Grüße Max PS: die oben angezeigten Schaltungen sind nicht fertig bsw. die Kondensatoren sind kurzgeschlossen -> Tau = 0 & Kondensatorlebensdauer sinkt stark!
:
Bearbeitet durch User
Wer sagt das du die analogen Eingänge nicht auch digital verwenden kannst? Der Mega328 hat Pinchange-Interrupt.
Danke Hubert. Unterstuetzt der auch die Interrupts auf allen analogen Pins?, denn da bin ich mir unsicher. d.h. wenn die Taste gedrueckt wird, dann soll er das in einer ISR verwerken und Z.B. eine Variable aendern. Hatte i.wo gelesen, dass die Interrubts (ISR) nur auf Port B (digital) funktionieren???
Wenn du einen Mega328 hast, dann gilt das laut Datenblatt für alle I/O. Die "analogen Pin" sind nur dann analog wenn du den jeweiligen ADC für diesen Pin aktivierst. Ansonst sind es normale digitale I/O.
Super, denn ich hatte irrtuemlicherweise i.wo gelesen gehab dass man ueber den Port C (analog) keine Interrupts ausloesen kann -natuerlich erst nach dem ich mein Layout fertig hatte. Sehr gut. Hast mir viel geholfen. Habe ich das richtig verstanden, dass man theoretisch mit jedem Pin der Ports B, C oder D ein Interrupt ausloesen kann?
Ja, schau einfach ins Datenblatt, du wirst die Register ohnehin zum programmieren brauchen.
> Edit: Die Taster sind mit Pull-Up-Widerständen, entprellt (22nF)
Das hätte mein Bäcker nicht besser ausdrücken können
Sehr gut, dann hast du ein guten Bäcker. Aber evtl. kennst du noch die Antwort auf meine Frage, oder du fragst deinen Bäcker? Mal was sinnvolles sagen "Ich"! Und sind wir doch mal ehrlich, sowas versteht jeder :)
Hi >Die Taster sind mit Pull-Up-Widerständen, entprellt (22nF) Meine Pull-Up-Widerstände haben als Einheit irgendwas mit Ohm. 22nF ist etwas mit Farad. Scheint also irgendwie in die Kategorie Kondensator zu gehören. Ein Schaltplan wäre übrigens sinnvoller gewesen. Aber so wie es aussieht schließt du den Kondensator bei jedem Tastendruck kurz. Sehr sinnvolle Maßnahme zur Verkürzung der Lebensdauer der Taster. Die Idee mit Tastern an Interrupts kommt von Anfängern hier öfters. Die Lösung ist allerdings hier http://www.mikrocontroller.net/articles/Entprellung zu finden. MfG Spess
Danke für die Erklärung, leider nichts mit meiner Frage zutun. Aber ich weiß genau was du damit meinst (Kondensator) -diese werden entweder gar nicht eingebaut da ich per Software das machen werde oder ich werde nach was anderem umsehen müssen, aber danke. Dieser ist auch nicht der momentane Stand, aber für das Verständnis reicht es! Danke
:
Bearbeitet durch User
Max Neu schrieb: > da es sonst blöd > wäre die Taster so lange gedrückt zu halten bis der uC an der richtigen > Stelle angekommen ist- Du hast möglicherweise noch eine falsche Vorstellung von der Arbeitsgeschwindigkeit des Prozessors. Tastendrücke sind Ereignisse, die - auf menschliche Zeitdimensionen übertragen - Monate bis Jahre dauern. Allein das Prellen des Kontakts dauert "gefühlte Tage". Das kann man bequem durch "zweimal am Tag nachsehen" erledigen. spess53 schrieb: > Die Idee mit Tastern an Interrupts kommt von Anfängern hier öfters. Die > Lösung ist allerdings hier > http://www.mikrocontroller.net/articles/Entprellung > zu finden. Dem ist nichts hinzuzufügen. Grüße Stefan
:
Bearbeitet durch User
Danke Stefan für die schnelle Aufklärung! Dein schneller Prozessor interessiert mich genauer! Meine Aufgabe: Es soll eine Frage Wort für Wort auf dem LCD-Display ausgegeben werden. Wird die Taste gedrückt (egal zu welchen Zeitpunkt) so wird die Ausgabe unterbrochen und man muss die Frage beantworten. Es darf keiner bevorzugt werden (auch nicht durch den Programmaufbau!). Wird ein kleines Trinkspiel, was auch für mich ein Lerneffekt sein soll -> wird da wahrscheinlich nur ein mal gespielt :). Wie machst du das mit deinem schnellen Prozessor ohne Interrupts o.ä? Edit: Möchte diesen mit C und/oder Assembler programmieren Liebe Grüße Max
:
Bearbeitet durch User
Hallo Max, du hast vermutlich bislang versucht, das Problem sequentiell (eine Aufgabe nach der anderen) zu lösen. Versuche doch mal, das Ganze gedanklich anders anzugehen: 1. Dein Prozessor ist so schnell, dass du seine Arbeitszeit problemlos auf mehrere Aufgaben verteilen kannst und er trotzdem innerhalb deiner Zeitvorgaben bleibt. 2. Daher kannst du ihn von Teilaufgabe zu Teilaufgabe "immer im Kreis herum" scheuchen, ohne dass er durcheinander kommt. Er muss sich nur merken, was er bei jeder Aufgabe jeweils zuletzt gemacht hat. Schon bist du beim typischen Aufbau eines Mikrocontroller-Programms:
1 | Reset: |
2 | Initialisierung Hardware |
3 | Initialisierung Variablen Teilaufgabe 1 |
4 | Initialisierung Variablen Teilaufgabe 2 |
5 | ... |
6 | |
7 | Start: |
8 | Aufgabe 1 was zu tun? |
9 | Ja: Alles machen, was kein Warten bedingt, |
10 | neuen Zustand merken |
11 | |
12 | Aufgabe 2 was zu tun? |
13 | Ja: Alles machen, was kein Warten bedingt, |
14 | neuen Zustand merken |
15 | |
16 | ... |
17 | |
18 | Gehe zu Start |
Das ist eine einfache Form von kooperativem Multitasking: http://www.mikrocontroller.net/articles/Multitasking Das Merken des Zustands der einzelnen Teilaufgabe hat auch einen Namen, das ist nämlich ein "Zustandsautomat" (state machine). http://www.mikrocontroller.net/articles/Statemachine Und wie gesagt: Der im Arduino verbaute ATMega 328p erledigt (pi * Daumen) etwa 14 Mio (Assembler-)Befehle je Sekunde. Der könnte bei der beschriebenen Aufgabe nebenher noch einiges anderes erledigen. Du musst ihn nur lassen... Ist es schon einigermaßen klar geworden, wie es gehen kann? Grüße Stefan
Erstmal, Danke an Stefan. Natürlich, hatte ich das in Betracht gezogen, aber warum soll ich die ISR´s nicht ausnützen? Was ist falsch daran, wenn die Architektur das zulässt! Das macht doch einiges einfacher?!?! Evtl. übersehe ich noch eine Kleinigkeit? Beste Grüße Max
Stefan Wagner schrieb: > Dein Prozessor ist so schnell, dass du seine Arbeitszeit problemlos > auf mehrere Aufgaben verteilen kannst Damit es noch etwas klarer wird: "Teilaufgaben" könnten z.B. sein: - Tastatur abfragen - Zeichen aufs Display ausgeben - serielle Schnittstelle bedienen (wenn du sie benutzen willst) - eine LED an- und ausschalten (blinken lassen) und natürlich - Ergebnisse anderer Teilaufgaben nehmen und darauf aufbauend die Logik des Hauptprogramms abarbeiten Ich nehme mal die LED als Beispiel für den detaillierten Aufbau einer solchen Teilaufgabe. Soll die LED nur gleichlang an- und aus sein, hat die Teilaufgabe die Zustände "LED an" und "LED aus". Diesen Zustand merken wir uns in einer Variable led_state. Dann brauchen wir noch eine Information, wie lange die LED schon in dem jeweiligen Zustand ist (wir wollen ja einen bestimmten Blinktakt). Ich unterstelle jetzt noch, dass wir irgendwo eine Quelle für eine "Systemzeit" mit z.B. 10 ms Auflösung haben (wie das geht kommt später) die wir immer wieder abfragen können. Das nachfolgende Beispiel enthält nur die absolute Grundfunktion.
1 | // Die Systemzeit
|
2 | extern uint16_t systicks(void); |
3 | |
4 | // LED ein- und ausschalten
|
5 | // on==0 ist LED aus
|
6 | extern void led_switch(uint8_t on); |
7 | |
8 | // LED-Zustände
|
9 | #define LED_AUS 0
|
10 | #define LED_AN 1
|
11 | |
12 | #define LED_TAKT 100
|
13 | |
14 | // modulglobale Variable
|
15 | // "static" wird später erklärt
|
16 | static uint8_t led_state; |
17 | static uint16_t led_time; |
18 | |
19 | void led_init(void) |
20 | {
|
21 | led_state = LED_AUS; |
22 | led_switch(LED_AUS); |
23 | }
|
24 | |
25 | void led_task(void) |
26 | {
|
27 | switch(led_state) |
28 | {
|
29 | case LED_AUS: |
30 | if(systicks() == led_time) |
31 | {
|
32 | led_switch(LED_AN); |
33 | led_time = systicks() + LED_TAKT |
34 | }
|
35 | break; |
36 | |
37 | case LED_AN: |
38 | if(systicks() == led_time) |
39 | {
|
40 | led_switch(LED_AUS); |
41 | led_time = systicks() + LED_TAKT |
42 | }
|
43 | break; |
44 | }
|
45 | }
|
46 | |
47 | void set_led(uint8_t newstate) |
48 | {
|
49 | led_state = newstate; |
50 | led_time = systicks() + LED_TAKT |
51 | led_switch(newstate); |
52 | }
|
So könnte das (in etwa) aussehen. Die Technik der Zeitabfrage ist sehr vereinfacht dargestellt, tatsächlich würde man das etwas anders machen. (Aber das ist eine andere Geschichte.) Wichtig ist hier nur, dass das Warten auf den nächsten Zustandswechsel nicht in Form einer Warteschleife abläuft, sondern ein oft wiederholtes kurzes Nachsehen ist. Und dazwischen kann eine Menge anderer Dinge erledigt werden. Grüße Stefan
Max Neu schrieb: > aber warum soll ich die ISR´s nicht ausnützen? > Was ist falsch daran, wenn die Architektur das > zulässt? Man sollte sie sinnvoll nutzen. Sie haben nämlich neben Vorteilen auch gewisse "Fallstricke". Ob man für eine bestimmte Aufgabe einen Interrupt nutzt, hängt in der Regel von der Art der Aufgabe ab. Als Faustregel kann man sagen, dass Interrupts dann notwendig werden, wenn man auf ein Ereignis schneller reagieren muss, als der Prozessor für einen "Programmrundlauf" benötigt. Typische Kandidaten sind Timer oder serielle Schnittstellen (UART, SPI, I2C). Bei einem Timer muss man sehr zeitnah auf den Überlauf reagieren, weil sonst das Zeitraster "aus den Fugen geht". Bei einer seriellen Schnittstelle gehen bei verspäteter Reaktion u.U. Zeichen verloren (Bufferoverflow). Die Eingänge für externe Interrupts braucht man schon etwas seltener. Typisch ist hier z.B. die Information, dass ein per SPI angeschlossener externer Baustein Daten hat, die abzuholen sind. Generell gesagt: Typisch für die Nutzung von Interrupts ist, dass die notwendige Aktion vergleichsweise kurz ist, aber schnell nach dem Ereignis erfolgen muss. Um auf deine ursprüngliche Frage zurück zu kommen: Die Abfrage eines mechanischen Kontakts (Tasters) hat ganz andere Bedingungen. Ein mechanischer Taster ist langsam. Die Setzzeit (Zeit bis der Kontakt sauber geschlossen ist) liegt im Bereich etlicher Millisekunden. Bis dahin erzeugt der Kontakt ein ganzes Bündel an Übergängen. Die minimale Haltezeit bei menschlicher Betätigung ist im Bereich mehrerer hundert Millisekunden. Daraus folgt, dass man sowieso mehrfach abfragen muss, um sicher zwischen Prellen und "wirklich gedrückt" bzw. "wirklich losgelassen" unterscheiden zu können. Da der Kontakt "schnarchlangsam" ist, kann man das nicht nur problemlos "zu Fuß" machen, man muss in der Regel sogar warten, bis man sinnvoll erneut abfragen kann. Innerhalb einer ISR ist das nicht sinnvoll machbar. Zudem würde bei Nutzung eines externen Interrupts das Kontaktprellen zumindest zeitweise eine wahre "Interruptflut" erzeugen und so massiv Rechenzeit "fressen". Beides spricht für die zyklische Abfrage im Rahmen des normalen Programms und gegen die Nutzung eines externen Interrupts*. Grüße Stefan *Wichtig: Es gibt eine Ausnahme davon. Die liegt vor, wenn man den Controller per Tastendruck aus einem Sleep-Modus holen will. Aber sobald er wieder wach ist, sollte man die weitere Abfrage der Tasten besser wieder im Vordergrund machen.
Okay, ursprünglich hatte ich eine andere Frage gestellt. Hier sind diese kurz nochmal: Kann ich die analogen Pins genauso wie die digitalen nutzen? Antwort: JA siehe Antwort von Hubert (Danke) Unterstützen alle analogen Pins die Interrupts (bei ATMEGA328/-P)? Antwort: NEIN (keiner von denen) siehe dazu diese Seite von meinem Prof: http://www.netzmafia.de/skripten/hardware/Arduino/programmierung.html ->habe ich das richtig Verstanden? Bei den Diskussionen haben sich neue Fragen aufgestellt: Betreff: Tastenabfrage ohne ISR?!? Siehe Kommentar von Stefan Wagner (Danke) Damit mein Problem sichtbar für alle wird hier ein Programmbeispiel mit einer LED :D Ein Counter soll die Tastendrücke zählen und die LED, so oft wie die Taste gedrückt worden ist, blinken lassen. _Programm (C):
1 | #define LED_PIN 3 //z.B.
|
2 | #define ISR_PIN 2
|
3 | |
4 | //globale Variable
|
5 | int counter = 0; |
6 | |
7 | //hier die ISR
|
8 | void tastenAbfrage(void) |
9 | {
|
10 | counter++; |
11 | }
|
12 | |
13 | |
14 | void init(void) //so heißt es meine ich beim UNO? |
15 | {
|
16 | pinMode(LED_PIN,OUT); |
17 | //hier wird der Interrupt freigegeben
|
18 | interruptFreigabe(ISR_PIN,RISING_EDGE,&tastenAbfrage); //Wie die Funktion genauer heißt müsste ich nachschauen |
19 | }
|
20 | |
21 | void schleife(void) //so heißt es meine ich beim UNO? |
22 | {
|
23 | while(conter>0) |
24 | {
|
25 | digitalWrite(LED_PIN,1); |
26 | delay(500); |
27 | digitalWrite(LED_PIN,0); |
28 | conter--; |
29 | }
|
30 | }
|
Es geht hierbei ums Verständnis!!! wie würdet ihr das machen ohne ISR? Es soll keine Tastendruck ausgelassen werden Internet sagt zu ISR: Interrupts sind sinnvoll um Vorgänge in Mikrocontrollerprogrammen automatisch ablaufen zu lassen. Sie können ebenfalls dabei helfen Zeitprobleme zu lösen. Typische Aufgaben für ein Interrupt sind z.B. Drehwinkelgeber und Überwachung von Benutzereingaben
:
Bearbeitet durch User
Max Neu schrieb: > Unterstützen alle analogen Pins die Interrupts (bei ATMEGA328/-P)? > Antwort: NEIN (keiner von denen) siehe dazu diese Seite von meinem Prof: > http://www.netzmafia.de/skripten/hardware/Arduino/programmierung.html > ->habe ich das richtig Verstanden? Nein, nicht richtig verstanden. In dem Link habe ich die Antwort auf die schnelle nicht gefunden. Es sind grundsätzlich alle Pin digital, das analoge ist eine Zusatzfunktion und muss erst aktiviert werden. Solange der Pin digital ist, funktioniert auch der Pin-Change-Interrupt. Eine Ausnahme gibt es nur bei der TQPF und MLF32 Version, da ist PC6 und PC7 ausschließlich analog. Eine Tastenabfrage über ISR ist insofern kritisch da durch das Tastenprellen sofort ein zweiter Interrupt ausgelöst wird, der nach beenden des ersten sofort aufgerufen wird. Bei starkem Tastenprellen kann das mehrfach passieren. Daher ist das Pollen der Tasten und unterdrücken des Tastenprellen mittels einer Debounce-Funktion besser. In der Hauptschleife darf dabei natürlich kein delay vorhanden sein.
Okay, danke Hubert. Dann muss ich mich wohl mehr mit Assembler befassen, wollt eigentlich mehr mit "fertigen" C-Funktionen arbeiten wie
1 | ...
|
2 | attachInterrupt(14, myfunction, CHANGE); //Interrupt am analogen Pin |
3 | ...
|
oder so ähnlichen Zeug, ohne Erfolg, da muss ich mich stärker einarbeiten -leider fehlt mir die Zeit hierfür -> aus Erfahrung mit 68HC11. Aber Leiterplattenherstellung war erfolgreich :D Danke nochmal an die Community
Auch wenn es sicherlich auf Dauer nicht verkehrt ist, den Assembler der AVR-Controllerfamilie kennen zu lernen: Nur um Interrupts zu nutzen, brauchst du das nicht. Du kannst den ATMega328 auch in nativem C programmieren. avr-gcc und avrlib unterstützen Interrupt-Serviceroutinen. Du bist also nicht auf das Arduino-Framework festgelegt. Grüße Stefan PS: Die Bibliotheken des Arduino-Framework liegen als C-Quellcode vor. Du kannst sie also auch ansehen und auf eigene Projekte portieren.
Danke an alle Helfer! Um es hier abzuschließen: Hier ein kurzes Programm für alle die es interessiert wie man Pin-Change-Interrupt auf analogen Pin 18 und 19 auslöst. Im Beispiel werden die Tastendrücke über den Pin 19 gezählt und durch das drücken der Taste an Pin 18 blinkt die LED an Pin 3 so oft wie man die Taste an Pin 19 zuvor gedrückt hatte (einfach testen :D )!
1 | #include <avr/interrupt.h> |
2 | #include <avr/io.h> |
3 | |
4 | |
5 | int counter=0; |
6 | boolean los = false; |
7 | |
8 | |
9 | ISR( PCINT1_vect ) |
10 | {
|
11 | if(digitalRead(19)==0) |
12 | counter++; |
13 | if(digitalRead(18)==0) |
14 | los = !los; |
15 | }
|
16 | |
17 | void setup() |
18 | {
|
19 | pinMode(3, OUTPUT); |
20 | //Freigabe der Interrupts an analogen Pins
|
21 | PCICR |= (1 << PCIE1); |
22 | //analogen Pins 12 und 13 für Interrupts freigeben
|
23 | PCMSK1 |= (1 << PCINT12); |
24 | PCMSK1 |= (1 << PCINT13); |
25 | //Interrupts aktivieren -> keine Ahnung ob man das wirklich braucht :)
|
26 | interrupts(); |
27 | }
|
28 | |
29 | void loop() { |
30 | |
31 | while((counter>0)&&los) |
32 | {
|
33 | digitalWrite(3,HIGH); |
34 | delay(500); |
35 | digitalWrite(3,LOW); |
36 | delay(500); |
37 | counter--; |
38 | }
|
39 | |
40 | }
|
Ist bestimmt nicht die schönste Lösung, aber für den Anfang funktioniert es! Es soll ja nur grob zeigen wie es funktioniert!!! PS: Es funktionier auf fast allen I/O-Pins des ATMEGA328, denke ich... Ach ja hier findet man weitere hilfen: http://www.kriwanek.de/arduino/grundlagen/182-pin-change-interrupts-grundlagen.html
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.