Ich weiß, bestimmt eine dumme Frage: ich arbeite mit einem Tiny2313 - wenn ich einen PCINT auslöse, gibt es da ein Flag Register (ähnlich dem EIFR für den Interrupt) in welchem der ausgelöste PCINT bzw. Pin steht? Im Datenblatt finde ich dazu leider nichts (Seite 61) Vielen Dank schonmal für die Hilfe.
Draco schrieb: > in welchem der > ausgelöste PCINT bzw. Pin steht? Neín. Das gibt es nicht. Das musst du selber verwalten.
Verdammt, ich ahnte es... :/ Da bleibt mir im Grunde garnichts anderes übrig als entweder drei if oder eine Switch/Case Anweisung in die Interruptroutine zu bringen?! Nicht sehr schön und sauber. Ich hab das mal so gelöst auf die schnelle: (Achtung ist MikroC)
1 | volatile int PinChangeHistory = 0xff; |
2 | |
3 | void main() { |
4 | |
5 | // Portsettings:
|
6 | DDRB = 0b11111000; // Set In/Out (in) PB0,PB1,PB2 - for Servosignal |
7 | DDRA = 0b00000011; // Set In/Out (out) PA0,PA1 - Out1,Out2 |
8 | DDRD = 0b00111100; // Set In/Out (out) PD2,PD3,PD4,PD5 - Out3-5 |
9 | |
10 | //Registersettings
|
11 | |
12 | GIMSK = 0b00100000; //Ex Vec: PCINT enabled |
13 | PCMSK = 0b00000111; //PCINT Mask: PCINT0,1,2 enabled |
14 | |
15 | SREG_I_bit = 1; //Global Interrupt enabled |
16 | |
17 | while(1) |
18 | {
|
19 | asm nop; |
20 | }
|
21 | |
22 | }
|
23 | |
24 | void ServoInterrupt() org IVT_ADDR_PCINT |
25 | {
|
26 | int PinChanges; |
27 | |
28 | PinChanges = PINB ^ PinChangeHistory; |
29 | PinChangeHistory = PINB; |
30 | |
31 | if(PinChanges & (1 << PORTB0)) |
32 | {
|
33 | // PCINT0 changed
|
34 | }
|
35 | |
36 | if(PinChanges & (1 << PORTB1)) |
37 | {
|
38 | // PCINT1 changed
|
39 | }
|
40 | |
41 | if(PinChanges & (1 << PORTB2)) |
42 | {
|
43 | // PCINT2 changed
|
44 | }
|
45 | }
|
Gibts da eine sauberere Möglichkeit eventuell?!
Draco schrieb: > volatile int PinChangeHistory = 0xff; > int PinChanges; Als erstes würd ich das (und alles andere was nur 8 Bit benötigt) als uint_8t deklarieren, also grundsätzlich keinesfalls unnötig größer als die native Registergröße der verwendeten CPU. Bedenke daß das ein 8-Bit-Prozessor ist, es erzeugt unnötige Verrenkungen im Code mit 16 Bit herumzuhantieren, da braucht er jedesmal 2 Register statt einem, der erzeugte Code wird spürbar größer und entsprechend langsamer, vor allem in einer ISR willst Du sowenig wie möglich Register verschwenden und auch nicht unnötig Zeit verplempern um unbenutzte Nullen herumzuschieben.
:
Bearbeitet durch User
Bernd K. schrieb: > Draco schrieb: >> volatile int PinChangeHistory = 0xff; >> int PinChanges; > > Als erstes würd ich das (und alles andere was nur 8 Bit benötigt) als > uint_8t deklarieren, also grundsätzlich keinesfalls unnötig größer als > die native Registergröße der verwendeten CPU. Der erzeugte Code wird > spürbar kleiner und entsprechend schneller. Außerden wäre es sinnvoll, PinChangeHistory innerhalb der ISR als static und ohne volatile zu definieren. Die ifs lassen sich (wenn PinChanges auf 8 Bit reduziert wird), in einem AVR sehr effizient über die Skip-Befehle umsetzen. Weiterhin sollte PINB nur genau einmal gelesen werden:
1 | uint8_t pinb = PINB; |
2 | PinChanges = pinb ^ PinChangeHistory; |
3 | PinChangeHistory = pinb; |
Das beugt einem Fehler vor und ist effizienter.
Erstmal vielen Dank Bernd und Rolf, ich habe eure Vorschläge berücksichtigt und bin, zugegeben, sehr überrascht! In MikroC ist uint8_t gleich unsigned short (1 byte). Auch das ich PortB nur einmal auslesen lasse verringert den Flash Bedarf um satte 20 Bytes. Das ist bei solch einem kleinen Tiny schon sehr viel und die kleinen Änderungen bringen den benötigten Platz um satte drei Prozent nach unten. Ich sollte mich da echt mehr einlesen um solche "Fallen" gleich beizeiten auszumerzen. MikroC lässt ein direkte zuweißung in der ISR (uint_8t pinbx = PORTB) nicht zu. Das muss ich separat machen. Auch über den Skip Befehl hab ich mich mal kurz drüber gemacht. So wie ich das gerade verstanden habe, kann ich via ASM mit dem Skip (SBRS, SBIS) den nächsten Befehl überspringen. Aber da muss ich mich erst näher einlesen um dies in die IF besser zu integrieren.
1 | void main() { |
2 | |
3 | // Portsettings:
|
4 | DDRB = 0b11111000; // Set In/Out (in) PB0,PB1,PB2 - for Servosignal |
5 | DDRA = 0b00000011; // Set In/Out (out) PA0,PA1 - Out1,Out2 |
6 | DDRD = 0b00111100; // Set In/Out (out) PD2,PD3,PD4,PD5 - Out3-5 |
7 | |
8 | //Registersettings
|
9 | |
10 | GIMSK = 0b00100000; //Ex Vec: PCINT enabled |
11 | PCMSK = 0b00000111; //PCINT Mask: PCINT0,1,2 enabled |
12 | |
13 | SREG_I_bit = 1; //Global Interrupt enabled |
14 | |
15 | while(1) |
16 | {
|
17 | asm nop; |
18 | }
|
19 | |
20 | }
|
21 | |
22 | void ServoInterrupt() org IVT_ADDR_PCINT |
23 | {
|
24 | static unsigned short PinChangeHistory; |
25 | unsigned short PinChanges; |
26 | unsigned short pinbx; |
27 | |
28 | pinbx = PORTB; |
29 | |
30 | PinChanges = pinbx ^ PinChangeHistory; |
31 | PinChangeHistory = pinbx; |
32 | |
33 | if(PinChanges & (1 << PORTB0)) |
34 | {
|
35 | // PCINT0 changed
|
36 | }
|
37 | |
38 | if(PinChanges & (1 << PORTB1)) |
39 | {
|
40 | // PCINT1 changed
|
41 | }
|
42 | |
43 | if(PinChanges & (1 << PORTB2)) |
44 | {
|
45 | // PCINT2 changed
|
46 | }
|
47 | }
|
Draco schrieb: > ich habe eure Vorschläge berücksichtigt und bin, zugegeben, sehr > überrascht! In MikroC ist uint8_t gleich unsigned short (1 byte). Das ist aber eigentlich nicht konform. short muß laut ISO C eigentlich mindestens 16 Bit breit sein. > MikroC lässt ein direkte zuweißung in der ISR (uint_8t pinbx = PORTB) > nicht zu. Das muss ich separat machen. Hmm, merkwürdig. > Auch über den Skip Befehl hab ich mich mal kurz drüber gemacht. So wie > ich das gerade verstanden habe, kann ich via ASM mit dem Skip (SBRS, > SBIS) den nächsten Befehl überspringen. Ja, und zwar abhängig davon, ob in einem Register ein bestimmtes Bit gesetzt ist oder nicht. Das ist eben für genau solche Fälle, wie du es hier brauchst, effizienter als eine Und-Verknüpfung, ein Vergleich und ein bedingter Sprung. > Aber da muss ich mich erst näher einlesen um dies in die IF besser zu > integrieren. Die muss der Compiler integrieren, nicht du.
Draco schrieb: > In MikroC ist uint8_t gleich unsigned short (1 byte) short ist aber eigentlich mindestens 16 bit, alles andere wäre wirklich schräg und nicht standardkonform, ich glaube da bist Du irgendwo in der Zeile verrutscht. nimm explizit uint8_t (oder notfalls unsigned char). überhaupt würd ich bei 8- und 16-bittern grundsätzlich auf die "schwammigen" typen verzichten und stattdessen nur die posix typen mit den knallhart definierten Bitbreiten nehmen, und auch bei breiteren CPUs schadet das nicht und vermeidet potentiellen Ärger in der Zukunft.
Karl H. schrieb: > Draco schrieb: > >> pinbx = PORTB; > > PINB! > > Du willst vom PINB Register lesen und nicht von PORTB Ja... war im zweiten Listing glaube ich schon korrigiert. Spätestens wenn die Maske leer gewesen wäre, wäre die erste verwunderung wieder da gewesen :D Hat es noch rechtzeitig gesehen. Aber danke schön :)
Ich sehe es grad: war es noch nicht :) daher hier mal korrekt:
1 | ...
|
2 | |
3 | void ServoInterrupt() org IVT_ADDR_PCINT |
4 | {
|
5 | unsigned short PinChanges; |
6 | |
7 | PinChanges = PINB ^ PinChangeHistory; |
8 | PinChangeHistory = PINB; |
9 | |
10 | if(PinChanges & (1 << PINB0)) |
11 | {
|
12 | Timer[0] = TCNT0; |
13 | TCNT0 = 0; |
14 | }
|
15 | |
16 | if(PinChanges & (1 << PINB1)) |
17 | {
|
18 | Timer[1] = TCNT0; |
19 | TCNT0 = 0; |
20 | }
|
21 | |
22 | if(PinChanges & (1 << PINB2)) |
23 | {
|
24 | Timer[2] = TCNT0; |
25 | TCNT0 = 0; |
26 | }
|
27 | |
28 | OutGo = 1; |
29 | }
|
30 | |
31 | ...
|
Draco schrieb: > PinChanges = PINB ^ PinChangeHistory; > PinChangeHistory = PINB; Theoretisch könntest du hier beim 2. Zugriff auf PINB ein anderes Ergebnis bekommen, als beim 1. ten Zugriff. Du willst bei solchen Dingen, das bewusste Register immer nur ein EINZIGES mal auslesen und dann mit diesem garantiert gleichbleibendem Wert weiter operieren.
:
Bearbeitet durch User
STK500-Besitzer schrieb: > Draco schrieb: >> in welchem der >> ausgelöste PCINT bzw. Pin steht? > > Neín. Das gibt es nicht. Das musst du selber verwalten. dachte ich auch schon, aber warum alle PCINT in einer ISR erfassen wollen? ich dachte eher so: ISR(PCINT2_vect) da hast du doch jeden PCINT gleich passend.
Joachim B. schrieb: > STK500-Besitzer schrieb: >> Draco schrieb: >>> in welchem der >>> ausgelöste PCINT bzw. Pin steht? >> >> Neín. Das gibt es nicht. Das musst du selber verwalten. > > dachte ich auch schon, > > aber warum alle PCINT in einer ISR erfassen wollen? > > ich dachte eher so: > > ISR(PCINT2_vect) > > da hast du doch jeden PCINT gleich passend. JAIN PCINT2_vect ist der PinChangeInterrupt eines anderen Port... Beim 2313 gibt es den PinChangeInterrupt "nur" an PortB. Wenn ein anderer AVR z.b. PinChangeInterrupts an mehreren Ports hat (PortC, PortD....) dann hat jeder eigene Port seinen Interrupt Vector. Das hat nichts mit den PortPins (PB1, PB2...PE1,PE2....) zu tun. Karl H. schrieb: > Draco schrieb: > >> PinChanges = PINB ^ PinChangeHistory; >> PinChangeHistory = PINB; > > Theoretisch könntest du hier beim 2. Zugriff auf PINB ein anderes > Ergebnis bekommen, als beim 1. ten Zugriff. > > Du willst bei solchen Dingen, das bewusste Register immer nur ein > EINZIGES mal auslesen und dann mit diesem garantiert gleichbleibendem > Wert weiter operieren. Ja das war noch das alte aus MikroC - ich habe mich entschlossen das im AVR Studio zu machen... moment... so siehts aus:
1 | ISR(PCINT_vect) |
2 | {
|
3 | |
4 | uint8_t pinb = PINB; |
5 | uint8_t pinChanges; |
6 | |
7 | pinChanges = pinb ^ pinChangeHistory; |
8 | pinChangeHistory = pinb; |
9 | |
10 | if(pinChanges & (1<<PINB0)) |
11 | {
|
12 | if(TCNT0 > 100) |
13 | {
|
14 | Timer[0] = TCNT0; |
15 | }
|
16 | TCNT0 = 0; |
17 | }
|
18 | |
19 | if(pinChanges & (1<<PINB1)) |
20 | {
|
21 | Timer[1] = TCNT0; |
22 | TCNT0 = 0; |
23 | }
|
24 | |
25 | if(pinChanges & (1<<PINB2)) |
26 | {
|
27 | Timer[2] = TCNT0; |
28 | TCNT0 = 0; |
29 | }
|
30 | |
31 | outGo = 1; |
32 | }
|
Wobei ich da grad noch am rumexperimentieren bin WO ich am besten den kurzen PWM Nippel rausfiltere, entweder direkt in der ISR bevor er überhaupt übergeben wird oder später in der normalen "Arbeitsroutine" - das ist so nen Zwiespalt: Ich will ja die ISR so schlank wie möglich halten, aber da ist das rausfiltern einfach angenehmer.
Draco schrieb: > Ich will ja die ISR so schlank wie möglich > halten so schlank wie möglich heisst nicht, dass man gar nichts machen darf. Im Zweifel sieht man sich das Assembler Listing an und entscheidet, ob es ok ist, wenn da 10 oder 15 Takte mehr verbraucht werden.
So mal am Rande... da ich die Timer Werte ja in einer Matrix abspeicher, wäre meine Idee nun, warum kann ich nicht gleich die Variable "pinChanges" dazu benutzen die Matrixposition anzulaufen?! Ich hab mir da mal so meine Gedanken gemacht:
1 | // Alter Stand vom ehemaligen Input
|
2 | // z.b.: 10010000
|
3 | |
4 | uint8_t pinChangeHistory; |
5 | |
6 | |
7 | |
8 | // Aktueller Port Stand in die Variable schreiben:
|
9 | // z.b.: 10010001 (Pin0 bekommt input)
|
10 | |
11 | uint8_t pinb = PINB; |
12 | |
13 | |
14 | |
15 | // XOR Anweisung zum setzen der aktiven Bits in beiden Variablen:
|
16 | // z.b.: 10010000 ^ 10010001 = 00000001 in pinChanges
|
17 | |
18 | pinChanges = pinb ^ pinChangeHistory; |
Da steht ja dann eine Dezimal Zahl von "1" bei PinB1 wäre das dann die "2" bei PinB2 die "4" usw.... Kann man das vernünftig und schnell shiften das man daraus eine 0,1,2,3 etc... bekommt?!
Draco schrieb: > Da steht ja dann eine Dezimal Zahl von "1" bei PinB1 wäre das dann die > "2" bei PinB2 die "4" usw.... Kann man das vernünftig und schnell > shiften das man daraus eine 0,1,2,3 etc... bekommt?! nein, da auch in pinChanges eine 3 oder 7 oder 155 oder jede andere Bitkombination aus 8 Bits enthalten sein kann. Nämlich immer dann wenn die PinChange ISR aufgerufen wurde weil sich zum Beispiel gleichzeitig mehrere Eingänge geändert haben. Davon abgesehen steht in pinChanges immer dann eine 1 in den Bits wenn sich der PIN geändert hat. D.h. auch wenn er vorher auf HIGH Pegel war und nun auf LOW Pegel umgeschaltet wurde. Wenn du zB. nur auf einen Pegelwechsel reagieren möchtest wenn der Eingangspin auf HIGH Pegel geändert wurde dann musst du sowohl pinChanges als auch pinb zusammen abfragen, Zb. per UND-Verküpfung beider Variablen.
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.