mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Interrupts einbinden


Autor: Majd A. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hey,

da ich nun mit meinem LED-Cube relativ weit bin bzw. alle Lauflichter 
usw fertig programmiert habe arbeite ich nun an einem Interrupt Taster 
für den PIC18F4550 Datasheet: 
https://ww1.microchip.com/downloads/en/devicedoc/39632c.pdf

Nun mein Problem: In der "Skizzierung" des Chips steht das RB0 bis 2 die 
Interrupt Pins sind, wenn man allerdings runter zum Interrupt Kapitel 
geht steht dort das die Pins RB4:RB7 die Interrupt Pins wären. Nun zu 
meiner Frage wonach sollte ich mich jetzt richten? Und wie würde ich in 
folgender .c Datei am besten den Interrupt Befehl einsetzen (Ist bloß 
eine Übungsdatei da ich es erstmal verstehen möchte bevor es in meine 
Hauptdatei reinkommt).

Habe jetzt im PIC-Controller Buch von Günter Schmitt gelesen das man 
theoretisch auch einfach INTCON2bits.INTEDG2 =0; einsetzen kann und 
somit je nach Bedarf eine 0 oder 1 einsetzen aber ich vermute das es bei 
mir anders sein wird als im Buch, daher frage ich mal nach.

Mit freundlichen Grüßen

: Verschoben durch Moderator
Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
RB2:0 sind die Pins zum Auslösen jeweils eines eigenen externen 
Interrupts.
RB7:4 lösen ein Input Change Interrupt aus, das für alle Pins das 
gleiche ist. Da müsste man dann also noch herausfinden, welcher Pin das 
Interrupt ausgelöst hat.

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sebastian R. schrieb:
> RB2:0 sind die Pins zum Auslösen jeweils eines eigenen externen
> Interrupts.
> RB7:4 lösen ein Input Change Interrupt aus, das für alle Pins das
> gleiche ist. Da müsste man dann also noch herausfinden, welcher Pin das
> Interrupt ausgelöst hat.

Also heißt das das ich RB2:0 zum auslösen benutze aber die Interrupts 
auf einen anderen Pin programmieren muss? Und was genau bedeutet Input 
Change Interrupt?

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Sebastian R. schrieb:
>> RB2:0 sind die Pins zum Auslösen jeweils eines eigenen externen
>> Interrupts.
>> RB7:4 lösen ein Input Change Interrupt aus, das für alle Pins das
>> gleiche ist. Da müsste man dann also noch herausfinden, welcher Pin das
>> Interrupt ausgelöst hat.
>
> Also heißt das das ich RB2:0 zum auslösen benutze aber die Interrupts
> auf einen anderen Pin programmieren muss? Und was genau bedeutet Input
> Change Interrupt?

okay was Input change Interrupt bedeutet hat sich erübrigt habe gerade 
was dazu im Internet gefunden

Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Also heißt das das ich RB2:0 zum auslösen benutze aber die Interrupts
> auf einen anderen Pin programmieren muss?

Nein. Wenn alles korrekt konfiguriert ist:

INT0_ISR bei steigender oder fallender Flanke an RB0
INT1_ISR bei steigender oder fallender Flanke an RB1
INT2_ISR bei steigender oder fallender Flanke an RB2

IOC_ISR* bei steigender und fallender Flanke an RB4, 5, 6 oder 7

Also die ersten drei Pins haben jeweils eine eigene Interrupt Service 
Routine, RB7:4 teilen sich eine gemeinsame ISR.


*Interrupt on Change

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sebastian R. schrieb:
> Majd A. schrieb:
>> Also heißt das das ich RB2:0 zum auslösen benutze aber die Interrupts
>> auf einen anderen Pin programmieren muss?
>
> Nein. Wenn alles korrekt konfiguriert ist:
>
> INT0_ISR bei steigender oder fallender Flanke an RB0
> INT1_ISR bei steigender oder fallender Flanke an RB1
> INT2_ISR bei steigender oder fallender Flanke an RB2
>
> IOC_ISR* bei steigender und fallender Flanke an RB4, 5, 6 oder 7
>
> Also die ersten drei Pins haben jeweils eine eigene Interrupt Service
> Routine, RB7:4 teilen sich eine gemeinsame ISR.
>
> *Interrupt on Change

Achso okay dann habe ich das nun auch verstanden, danke dir.
Weißt du noch vllt wie ich es am besten in das Übungsprogramm einbinden 
kann? Bzw wie ich die definiere?

Habe jetzt im Buch gelesen das ich die erstmal mit #pragma interrupt und 
dann den Funktionsnamen, aber habe nicht so ganz verstanden wie ich die 
Interrupts auf die einzelnen Ports (RB0) setze oder wie ich sie am Ende 
aktiviere (Habe da einen Ansatz nämlich das ich irgendwie die Befehle 
aus dem Buch eingebe [INTCON3bits.INT2IF; INTCON3bits.INT1 IF =0; etc.] 
aber kann ja nichts eingeben ohne das ich vorher sowas definiert habe 
usw .

Autor: Peter D. (peda)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Fürs Tasten entprellen und Flanken auswerten nimmt man bewährte Lösungen 
mit Timerinterrupt.
Den externen Interrupt nimmt man nur zum Aufwachen aus dem Sleep bei 
Schaltungen für Batteriebetrieb.

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Fürs Tasten entprellen und Flanken auswerten nimmt man bewährte
> Lösungen
> mit Timerinterrupt.
> Den externen Interrupt nimmt man nur zum Aufwachen aus dem Sleep bei
> Schaltungen für Batteriebetrieb.

Ist ein Timerinterrupt nicht ein Interrupt der nach einer bestimmten 
Zeit aktiv wird, oder habe ich das falsch verstanden?

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Ist ein Timerinterrupt nicht ein Interrupt der nach einer bestimmten
> Zeit aktiv wird

Genau deshalb kann er ja entprellen. Entprellen ist quasi ein Zeitglied 
als Tiefpaß.
Die Flankenerkennung fällt nebenbei mit ab.

Autor: Volker S. (vloki)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Weißt du noch vllt wie ich es am besten in das Übungsprogramm einbinden
> kann? Bzw wie ich die definiere?

Wie das mit dem Pin Interrupt funktioniert, kannst du da nachlesen:
http://www.hs-ulm.de/users/vschilli/Mikrocontroller/uCQ/_downloads/uCquick-X_20180413.pdf 
5.3.2 INTx Interrupt

Das Gerüst für den Interrupthandler deines Compilers hast du ja schon.
Wie ist das mit dem Bootloader? Benutzt ihr den, oder habt ihr ein 
Programmiergerät?

Im verlinkten Dokument wird der neuere XC8 Compiler verwendet, die alte 
Beschreibung für den C18 ist aber noch rudimentär enthalten. (13.4.6 C18 
Interrupts)

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> deshalb kann er ja entprellen. Entprellen ist quasi ein Zeitglied
> als Tiefpaß.
> Die Flankenerkennung fällt nebenbei mit ab.

Das Problem ist aber das ich erst etwas unterbrechen will wenn ich den 
Taster RB0 drücke denn sonst soll das Lauflicht permanent Leuchten bis 
ich es halt wechseln will.

Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Fürs Tasten entprellen und Flanken auswerten nimmt man bewährte Lösungen
> mit Timerinterrupt.
> Den externen Interrupt nimmt man nur zum Aufwachen aus dem Sleep bei
> Schaltungen für Batteriebetrieb.

Das ist korrekt.

So im normalen Betrieb hat das Auswerten von Tastern über ein Interrupt 
keine Vorteile. Eher Nachteile, weil beim Prellen des Tasters das 
Interrupt mehrfach aufgerufen werden kann und dann auch Prozessorzeit 
bei drauf geht.

Majd A. schrieb:
> Habe jetzt im Buch gelesen das ich die erstmal mit #pragma interrupt und
> dann den Funktionsnamen, aber habe nicht so ganz verstanden wie ich die
> Interrupts auf die einzelnen Ports (RB0) setze oder wie ich sie am Ende
> aktiviere (Habe da einen Ansatz nämlich das ich irgendwie die Befehle
> aus dem Buch eingebe [INTCON3bits.INT2IF; INTCON3bits.INT1 IF =0; etc.]
> aber kann ja nichts eingeben ohne das ich vorher sowas definiert habe
> usw .

#Pragmas wirst du dafür vermutlich nicht brauchen.

Und du kannst Dinge eingeben, die du nicht definiert hast, weil sie 
jemand anders sie bereits für dich in der p18cxxx.h definiert hat.

Dort sind auch die ganzen Registernamen, die in deinem Buch auftauchen, 
definiert. Dahinter verstecken sich dann auch nur Registeradressen.

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Volker S. schrieb:
> Majd A. schrieb:
>> Weißt du noch vllt wie ich es am besten in das Übungsprogramm einbinden
>> kann? Bzw wie ich die definiere?
>
> Wie das mit dem Pin Interrupt funktioniert, kannst du da nachlesen:
> 
http://www.hs-ulm.de/users/vschilli/Mikrocontroller/uCQ/_downloads/uCquick-X_20180413.pdf
> 5.3.2 INTx Interrupt
>
> Das Gerüst für den Interrupthandler deines Compilers hast du ja schon.
> Wie ist das mit dem Bootloader? Benutzt ihr den, oder habt ihr ein
> Programmiergerät?
>
> Im verlinkten Dokument wird der neuere XC8 Compiler verwendet, die alte
> Beschreibung für den C18 ist aber noch rudimentär enthalten. (13.4.6 C18
> Interrupts)

Wir arbeiten mit MPLAB IDE und benutzen als Bootloader PICDEM (TM) FS 
USB Demo Tool.

Was genau meinst du mit dem Interrupthandler? Ist das unten das Kapitel 
wo steht was ich nutzen soll oder ist das die "Skizzierung" des Chips?

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achso okay danke ich schaue mir das jetzt die PDF Datei die Volker 
reinschickte vllt werde ich ja daraus schlauer

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sebastian R. schrieb:
> #Pragmas wirst du dafür vermutlich nicht brauchen.

Vermutlich doch.
Interrupthandler sind keine gewöhnlichen Funktionen, sie müssen den 
gesamten Kontext sichern und restoren.
Beim AVR-GCC werden sie z.B. über das Schlüsselwort ISR definiert und 
beim PIC über #pragma.

Autor: Volker S. (vloki)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Wir arbeiten mit MPLAB IDE und benutzen als Bootloader PICDEM (TM) FS
> USB Demo Tool.
Ok, dann ist alles klar ;-)


Majd A. schrieb:
> Was genau meinst du mit dem Interrupthandler?
#pragma code _RESET_INTERRUPT_VECTOR = 0x000800
void _reset (void)
{
    _asm goto _startup _endasm
}
#pragma code

/*#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808
void _high_ISR (void)
{
    ;
}

#pragma code _LOW_INTERRUPT_VECTOR = 0x000818
void _low_ISR (void)
{
    ;
}
*/

Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Das Problem ist aber das ich erst etwas unterbrechen will wenn ich den
> Taster RB0 drücke denn sonst soll das Lauflicht permanent Leuchten bis
> ich es halt wechseln will.

In einem Interrupt kannst/solltest du aber auch nicht mehr machen, als 
ein paar Flags zu setzen.

Wenn du in der main blockierende Funktionen fürs Lauflicht hast, aus 
denen du mit Tastendruck rausspringen willst, hilft dir ein Interrupt 
auch nicht viel.

Jede Schleife, die du in der Main verwendest, müsste dann als 
zusätzliche Abbruchbedingung den Tastendruck haben*.

Ansonsten, wenn dein Lauflicht 10 Sekunden läuft und du danach erst 
guckst, ob der Taster gedrückt wurde (auch wenns im Interrupt passiert 
ist) ändert sich halt erst nach 10 Sekunden etwas. Oder man muss den 
Taster so lange gedrückt halten, bis das Lauflicht durch ist.

Das ist halt die generelle Struktur von deinem Programm. Man sollte dazu 
wirklich verstehen, wozu Interrupts da sind, was sie tun und was sie für 
einen leisten können.


Wenn man auf Tastendruck ein Lauflicht abbrechen und ein nächstes 
starten will, braucht man dafür nicht unbedingt Interrupts. Das 
Lauflicht muss nur entsprechend durch die richtigen Bedingungen 
abbrechbar sein.


Ein Timer, der eine 1ms oder 10ms Zeitbasis erzeugt, ist nie verkehrt. 
Da kann man dann sehr viel (nicht zu viel) machen. Die paar 
Mikrosekunden, die er damit beschäftigt ist, einen Counter hochzuzählen 
und Taster einzulesen, fallen nicht groß auf.

Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Beim AVR-GCC werden sie z.B. über das Schlüsselwort ISR definiert und
> beim PIC über #pragma.

Da haben wir schon mein Problem. Ich habe jahrelang auf AVRs entwickelt 
und versuche das jetzt on the fly auf PIC zu übertragen ;)

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Majd A. schrieb:
>> Was genau meinst du mit dem Interrupthandler?#pragma code
> _RESET_INTERRUPT_VECTOR = 0x000800
> void _reset (void)
> {
>     _asm goto _startup _endasm
> }
> #pragma code
>
> /*#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808


Achso das ist das Grundgerüst was wir von unserem Lehrer bekommen haben, 
könnte ich dann theoretisch nicht einfach diesen Code so umschreiben das 
er für mich auf PORTBbits.RB0 liegen würde statt auf dem jetzigen Reset 
Button? Würde gerne einen S-Plan davon reinschicken aber leider besitze 
ich keine da diese Platine eine Eigeonkonstruktion von unserem Lehrer 
ist, könnte eventuell ein Bild von der Platine reinschicken

Autor: Majd A. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Sebastian R. schrieb:
> Wenn du in der main blockierende Funktionen fürs Lauflicht hast, aus
> denen du mit Tastendruck rausspringen willst, hilft dir ein Interrupt
> auch nicht viel.
>
> Jede Schleife, die du in der Main verwendest, müsste dann als
> zusätzliche Abbruchbedingung den Tastendruck haben*.

Habe es auch Anfangs so versucht ich lade mal die Powerpoint hoch wo ich 
verschiedene Lösungsansätze hatte allerdings hat leider keine wirklich 
geklappt. Hatte es bereits mit den Bedingungen versucht, aber ohne 
Erfolg er ist dann einfach beide Lauflichter durchlaufen und habe es 
halt nicht geschafft eine Bedingung zu programmieren wo er dann das 
aktuelle Lauflicht abbricht und direkt zum neuen geht. Ich lade mal eben 
die richtige Datei hoch die ich bis jetzt habe (Könnte etwas komisch für 
euch aussehen da ich noch ein Anfänger bin) und Lauflicht_6 ist das 
Lauflicht zum testen gewesen.

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Das Problem ist aber das ich erst etwas unterbrechen will

Nö, willst Du nicht. Du willst es abbrechen und das ist nicht die 
Aufgabe eines Interrupts. Der Interrupt kann nur ein Flag setzen, 
welches dann das Lauflicht auswerten muß.

Ein Interrupt unterbricht nur kurz, d.h. er ist für die Applikation 
unsichtbar. Die Applikation macht nach einigen µs genau an der Stelle 
weiter, wo der Interrupt sie unterbrochen hat.

Ein Interrupt kann auch abbrechen über setjmp and longjmp, aber das 
solltest Du dann keinem ernsthaften Programmierer zeigen. Das ist very 
bad style.

Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Hatte es bereits mit den Bedingungen versucht, aber ohne
> Erfolg er ist dann einfach beide Lauflichter durchlaufen und habe es
> halt nicht geschafft eine Bedingung zu programmieren wo er dann das
> aktuelle Lauflicht abbricht und direkt zum neuen geht.

Schau dir mal die Befehle "break" und "continue" an.
Die könnten dir dabei helfen.

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Ein Interrupt unterbricht nur kurz, d.h. er ist für die Applikation
> unsichtbar. Die Applikation macht nach einigen µs genau an der Stelle
> weiter, wo der Interrupt sie unterbrochen hat.

weiß ich dann bescheid danke dachte wenn ich den interrupt setze springt 
er dann komplett aus der Schleife

Sebastian R. schrieb:
> Schau dir mal die Befehle "break" und "continue" an.
> Die könnten dir dabei helfen.

break; habe ich schon versucht aber hat nicht ganz so gut geklappt wie 
eigentlich erwartet. Denn manchmal blieb er dann bei einem Lauflicht 
hängen bzw. das fing dann erst nach einigen Sekunden an es zu 
durchlaufen.

Autor: Volker S. (vloki)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Achso das ist das Grundgerüst was wir von unserem Lehrer bekommen haben,
> könnte ich dann theoretisch nicht einfach diesen Code so umschreiben das
> er für mich auf PORTBbits.RB0 liegen würde statt auf dem jetzigen Reset
> Button?

Nein, weil das gar nichts mit irgendwelchen Resetbuttons zu tun hat.
Das ist nur ein Umleitung des ResetVectors, damit das Programm im 
Bootloader bei einem Reset, bzw beim Anlegen der Versorgungsspannung 
weiß, wo es hin springen muss.

Das Interrupt-Gerüst, ist das auskommentierte darunter. Egal, ob du 
später einen PIN- oder Timer-Interrupt verwendest, der Code dafür muss 
dann da rein!

Überflieg doch einfach mal mein Kapitel über Interrupts, bevor du 
weitermachst. Vielleicht wird es dann klarer.

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Volker S. schrieb:
> Majd A. schrieb:
>> Achso das ist das Grundgerüst was wir von unserem Lehrer bekommen haben,
>> könnte ich dann theoretisch nicht einfach diesen Code so umschreiben das
>> er für mich auf PORTBbits.RB0 liegen würde statt auf dem jetzigen Reset
>> Button?
>
> Nein, weil das gar nichts mit irgendwelchen Resetbuttons zu tun hat.
> Das ist nur ein Umleitung des ResetVectors, damit das Programm im
> Bootloader bei einem Reset, bzw beim Anlegen der Versorgungsspannung
> weiß, wo es hin springen muss.
>
> Das Interrupt-Gerüst, ist das auskommentierte darunter. Egal, ob du
> später einen PIN- oder Timer-Interrupt verwendest, der Code dafür muss
> dann da rein!
>
> Überflieg doch einfach mal mein Kapitel über Interrupts, bevor du
> weitermachst. Vielleicht wird es dann klarer.

Aber Interrupts brauche ich ja nicht mehr, wenn ich dadurch nicht direkt 
aus einer Schleife springen kann. Brauche dann wohl einen neuen Weg der 
mir das direkte unterbrechen ermöglicht allerdings weiß ich jetzt nicht 
mehr was.

Vielleicht wisst ihr ja was

Autor: Volker S. (vloki)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das direkte Unterbrechen nennt man Interrupt?
Kannst natürlich auch die Versorgungsspannung kurz unterbrechen,
dann nennt man es Power-On-Reset ;-)

Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Vielleicht wisst ihr ja was

Ich bin mal ganz kurz ehrlich: Deine jetzige Code-Struktur ist ziemlich 
beschissen.

Man könnte die Zeilen auf etwa 5% dessen reduzieren, in dem man die 
Funktionen mit Parameterübergaben ausstattet und ein bisschen 
abstrahiert.

In dem Augenblick, in dem du dann Schleifen um Funktionsaufrufe bauen 
kannst und nicht mehr alles "diskret" hintereinander schreiben musst, 
wird es eigentlich offensichtlich, wo aus welcher Funktion gesprungen 
werden muss.

Dann kann es in etwa (Pseudocode) so aussehen:

int main( void )
{
   init_uc(); // Init-Zeugs
  
   // Hauptschleife
   while(1)
   {
      // Lauflicht 1, solange keine Taste gedrückt wurde
      while(!tastenflag)
      {
         lauflicht1();
      }

      // Lauflicht 2, solange keine Taste gedrückt wurde
      while(!tastenflag)
      {
         lauflicht2();
      }
   }
}

void lauflicht1( void )
{
   for(int i = 0; i<4*4*4; i++)
   {
      set_single_led(i); // LED an Position i einschalten
      delay(20); // 20ms bis zur nächsten LED warten
      reset_single_led(i); // LED an Position i ausschalten

      // Wurde zwischenzeitlich ein Taster gedrückt?
      if (tastenflag == 1)
      {
         break; // Aus for-Schleife springen
      }
   }
}



Idealerweise müsste die Delay-Funktion dann auch noch so aufgebaut sein, 
dass man sie mit einem Flag verlassen kann, aber wenn der Benutzer 20, 
50 oder 100ms nach einem Tastendruck warten muss, wird ihn das nicht 
stören.

Tastenflag kommt dann idealerweise aus einem Timer-Interrupt, das 
regelmäßig die Tasten einliest und sich um die Entprellung kümmert.

: Bearbeitet durch User
Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Volker S. schrieb:
> Das direkte Unterbrechen nennt man Interrupt?
> Kannst natürlich auch die Versorgungsspannung kurz unterbrechen,
> dann nennt man es Power-On-Reset ;-)

Keine Ahnung, mein Lehrer meinte das ich es mit Interrupt machen soll, 
daher bin ich davon ausgegangen.

Sebastian R. schrieb:
> Deine jetzige Code-Struktur ist ziemlich
> beschissen.

Sagte es ja bereits das ich noch Anfänger bin und mich noch nicht an den 
Parametern etc. rangemacht habe. Habe mir aber überlegt die einzelnen 
Leds zu kürzen in dem ich (void) LED Ebene 1 (Wert1)
dann jenachdem wenn ich ihn benutze immer in die Klammer die Zahl 
brauche die ich benötige, würde mir halt 120 Zeilen sparen.

Sebastian R. schrieb:
> Idealerweise müsste die Delay-Funktion dann auch noch so aufgebaut sein,
> dass man sie mit einem Flag verlassen kann, aber wenn der Benutzer 20,
> 50 oder 100ms nach einem Tastendruck warten muss, wird ihn das nicht
> stören.
>
> Tastenflag kommt dann idealerweise aus einem Timer-Interrupt, das
> regelmäßig die Tasten einliest und sich um die Entprel

werde es mal jetzt so versuchen danke für deine Hilfe

Autor: Volker S. (vloki)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sebastian R. schrieb:
> Ich bin mal ganz kurz ehrlich: Deine jetzige Code-Struktur ist ziemlich
> beschissen.

Oh, das hatte ich noch gar nicht angeschaut. +1 ;-)

Autor: Volker S. (vloki)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> mein Lehrer meinte das ich es mit Interrupt machen soll

Vermutlich meinte er sogar, dass du das mit dem PIN Interrupt machen 
sollst, weil er das möglicherweise für einen Anfänger am einfachsten und 
es als guten Einstieg in das Thema Interrupts hielt!

Jetzt fragst du hier und bekommst leider gesagt, dass man das nicht so 
macht ;-)

: Bearbeitet durch User
Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Volker S. schrieb:
> Majd A. schrieb:
>> mein Lehrer meinte das ich es mit Interrupt machen soll
>
> Vermutlich meinte er sogar, dass du das mit dem PIN Interrupt machen
> sollst, weil er das möglicherweise für einen Anfänger am einfachsten und
> es als guten Einstieg in das Thema Interrupts hielt!
>
> Jetzt fragst du hier und bekommst leider gesagt, dass man das nicht so
> macht ;-)

Jetzt ist leider die Frage wonach ich mich richten sollte. Da ich das 
mit dem Interrupt eh nicht so ganz verstehe versuche ich es mal mit dem 
break; wieder habe ja sonst in der Zeit eh nichts zu tun außer mir den 
Kopf darüber zu zerbrechen.

Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es geht auch erst einmal ohne Interrupt.
Wenn man nur nicht entprellt, kann es passieren, dass dann nicht ein 
Lauflicht, sondern 2 oder 3 übersprungen werden.

Was mir auffällt: Du hast "Taster_x_betaetigt", diese Variable wird aber 
nirgens mit einem Eingangswert beschrieben. Vielleicht funktioniert 
deshalb das ganze auch noch nicht. Genau das Beschreiben könnte man im 
Interrupt machen. Oder man

Die Quick&Dirty-Lösung zum Entprellen ohne Interrupt könnte so aussehen:
...
   // Taster gedrückt?
   if(PORTBbits.RB0 == 1)
   {
      delay(20); // 20ms warten
      
      // Immer noch gedrückt?
      if(PORTBbits.RB0 == 1)
      {
         break; // herausspringen
      }
   }
...

Wenn der Taster gedrückt wurde, kann man nach ein paar Millisekunden 
(wenn das Prellen durch ist) einfach noch einmal gucken, ob der Taster 
immer noch gedrückt ist.

Ist nicht elegant, funktioniert aber erstmal.


Ich denke, für den aller ersten Schritt wäre ein großes Code-Aufräumen 
unerlässlich.

Guck dir einfach mal an, wo du viele gleiche Dinge hintereinander 
machst. Das kannst du alles in Schleifen verpacken.

Das würde schon mal sehr viel an Code einsparen.

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sebastian R. schrieb:
> Was mir auffällt: Du hast "Taster_x_betaetigt", diese Variable wird aber
> nirgens mit einem Eingangswert beschrieben. Vielleicht funktioniert
> deshalb das ganze auch noch nicht. Genau das Beschreiben könnte man im
> Interrupt machen. Oder man

Meinst du mit Eingangswert jetzt den Startwert? Habe halt oben als ich 
die Variable erstellt habe die der 0 zugewiesen.

Sebastian R. schrieb:
> Wenn der Taster gedrückt wurde, kann man nach ein paar Millisekunden
> (wenn das Prellen durch ist) einfach noch einmal gucken, ob der Taster
> immer noch gedrückt ist.
>
> Ist nicht elegant, funktioniert aber erstmal.
>
> Ich denke, für den aller ersten Schritt wäre ein großes Code-Aufräumen
> unerlässlich.
>
> Guck dir einfach mal an, wo du viele gleiche Dinge hintereinander
> machst. Das kannst du alles in Schleifen verpacken.
>
> Das würde schon mal sehr viel an Code einsparen.

Danke werde ich jetzt machen nur muss jetzt noch auf meinen Partner die 
Schulstunde  warten das ich erstmal testen kann ob die Lauflichter 
überhaupt klappen nicht das ich da einen Fehler drin habe. Er muss halt 
jetzt nur noch die Register verbinden

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht kannst Du Dir hier was abschauen:

Beitrag "AVR Sleep Mode / Knight Rider"

Das Lauflichtmuster steht in einer Tabelle und bei jedem Aufruf wird das 
nächste Muster ausgegeben.
Damit ist es auch einfach, zwischen den Aufrufen eine Taste abzufragen 
und das nächste Lauflicht auszuwählen.

Autor: Majd A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Vielleicht kannst Du Dir hier was abschauen:
>
> Beitrag "AVR Sleep Mode / Knight Rider"
>
> Das Lauflichtmuster steht in einer Tabelle und bei jedem Aufruf wird das
> nächste Muster ausgegeben.
> Damit ist es auch einfach, zwischen den Aufrufen eine Taste abzufragen
> und das nächste Lauflicht auszuwählen.

Wie man die Lauflichter auswählen kann im anderen Lauflicht weiß ich ja 
und es klappt auch nur das Problem ist das er dann beide Lauflichter 
gleichzeitig durchführt.

Autor: Volker S. (vloki)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Danke werde ich jetzt machen nur muss jetzt noch auf meinen Partner die
> Schulstunde  warten das ich erstmal testen kann ob die Lauflichter
> überhaupt klappen nicht das ich da einen Fehler drin habe.

Ich schätze, da sind noch einige Fehler drin ;-)

Wenn ihr keinen Schaltplan bekommt, woher willst du wissen, wie der 
Zustand bei gedrückten Schaltern ist? Oft wird der 0!

Oder z.B. das hier:
void LED_1_1  (void)                                //Befehl für die Hilfsfunktion der 1.  LED der ersten Ebene    
{  
  Ebene_1 = 1; Data = 1; Clock = 1; Data = 0;  for (i=0; i<0; i=i+1) {Clock = 1;}     
}
Was soll denn das machen?

Funktioniert vielleicht auch so, aber beim Schreiben solltet ihr die LAT 
Register verwenden und nicht PORT. (#define CLOCK LATBbits.LATB1 usw...)

Wenn das große Aufräumen beginnt, überlegt, ob es nicht einfacher ist, 
die kompletten Daten des Würfels als Puffer im Ram anzulegen (8 Byte, 
bzw. 2 Bytes pro Ebene) und für ein Update immer alles komplett raus zu 
schieben. Für ein geändertes Bild würde so der Puffer verändert, und 
dann wieder komplett raus geschoben. (sehe gerade PeterD hat auch schon 
etwas derartiges erwähnt)

TRISB   = 0x00;                        //Definiert den Port B als Eingang
sollte man auch noch mal überdenken

: Bearbeitet durch User
Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Volker S. schrieb:
> Oder z.B. das hier:void LED_1_1  (void)
> //Befehl für die Hilfsfunktion der 1.  LED der ersten Ebene
> {
>   Ebene_1 = 1; Data = 1; Clock = 1; Data = 0;  for (i=0; i<0; i=i+1)
> {Clock = 1;}
> }Was soll denn das machen?

Das wird nach hinten hin ja auch noch schlimmer::
void LED_1_16  (void)                                //Befehl für die Hilfsfunktion der 16. LED der ersten Ebene
{
  Ebene_1 = 1; Data  = 1; Clock = 1; Data = 0;  for (i=0; i<15; i=i+1) {Clock = 1;}    
}

Insgesamt wird dort 16 mal Clock = 1 gesetzt. Das hat mit druch-clock-en 
eines Schieberegisters nicht wirklich was zu tun.

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Majd A. schrieb:
> Wie man die Lauflichter auswählen kann im anderen Lauflicht weiß ich ja
> und es klappt auch nur das Problem ist das er dann beide Lauflichter
> gleichzeitig durchführt.

Das ist Dein Hauptproblem, es fehlt eine Programmstruktur. Du 
betrachtest jedes Lauflicht als festen monolithischen Block.

Die Steuerung über eine Tabelle erlaubt es, nach jedem Teilschritt zum 
Main zurück zu kehren und z.B. eine andere Tabelle auszuwählen. Der 
Programmablauf wird also nicht mehr durch riesige unteilbare Blöcke 
bestimmt, sondern nur durch eine Zählvariable, welcher Eintrag als 
nächstes ausgegeben werden soll.

Bei einer Textverarbeitung gibt es ja auch nicht millionen Funktionen 
für jedes Wort, sondern nur eine Funktion für Textausgabe.
Dein Stil:
  schreibe_Wie();
  schreibe_geht();
  schreibe_es();
  schreibe_Dir();
Richtig:
  char* text = "Wie geht es Dir?"
  schreibe(text);

: Bearbeitet durch User
Autor: Sebastian R. (sebastian_r569)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Bei einer Textverarbeitung gibt es ja auch nicht millionen Funktionen
> für jedes Wort, sondern nur eine Funktion für Textausgabe.

Wobei es natürlich schon sinnvoll sein kann, gewisse Aufgaben in 
verschiedene Funktionsebenen zu zerlegen. Dadurch kann man die Hardware 
ein bisschen abstrahieren und schafft sich kleine, menschenlesbare 
Hilfsfunktionen.

Dabei sollte man repetive Aufgaben oder ähnliche Aufgaben so gut wie 
möglich abstrahieren. Das heißt nicht, dass es hinterher eine Funktion 
mit 50 Parametern für 120 verschiedene Tätigkeiten gibt, sondern alles 
sinnvoll strukturiert ist.

Bei einem LED-Cube kann ich mir gut ein paar Basis- und 
Debugging-Funktionen vorstellen:

- write_byte(b) - Schreibt 8 Bits übers Schieberegister raus. Dadurch 
kann man die 8 Ausgänge von einem Schieberegister wie ein internen Port 
behandeln.

- select_layer(0...3) - Damit kann man die Ebene auswählen, in der man 
LEDs aktivieren möchte

- write_led(x, y, z, status) - Zum Setzen und Löschen einzelner LEDs. 
Status ist dann 0/1, x, y und z die Koordinaten im Cube.

- write_layer(u_int16t daten, layer) - Alle LEDs aus einem Layer 
gleichzeitig beschreiben, in dem man den Zustand aller LEDs als 
16bit-Wert angibt. Dadurch kann man mit Bitshifting schon viele 
Lauflichter erzeugen

- ...

Die Funktionen bauen ganz grob auf der Funktion darüber auf. Wenn sich 
die Hardware z.B. ändert, muss man nur die write_byte anpassen, und alle 
Funktionen darüber funktionieren wieder wie erwartet.

Aber genau das ist z.B. die Kunst, die man erlernen muss. Welche 
Aufgaben kann man abstrahieren, was kann man in Funktionen packen,...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.