Forum: Mikrocontroller und Digitale Elektronik PCINT: Flag Register für die Pins?


von Draco (Gast)


Lesenswert?

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.

von STK500-Besitzer (Gast)


Lesenswert?

Draco schrieb:
> in welchem der
> ausgelöste PCINT bzw. Pin steht?

Neín. Das gibt es nicht. Das musst du selber verwalten.

von Draco (Gast)


Lesenswert?

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?!

von Bernd K. (prof7bit)


Lesenswert?

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
von Rolf M. (rmagnus)


Lesenswert?

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.

von Draco (Gast)


Lesenswert?

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
}

von Peter D. (peda)


Lesenswert?

Nimm den ATtiny2313A, der hat 3 PCINTs.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

Draco schrieb:

>      pinbx = PORTB;

PINB!

Du willst vom PINB Register lesen und nicht von PORTB

von Draco (Gast)


Lesenswert?

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

von Draco (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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
von Joachim B. (jar)


Lesenswert?

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.

von Draco (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Draco (Gast)


Lesenswert?

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?!

von Hagen R. (hagen)


Lesenswert?

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
Noch kein Account? Hier anmelden.