Forum: Compiler & IDEs PCINT-Pin abwechseln In- und Output


von N. G. (newgeneration) Benutzerseite


Lesenswert?

Hallo an alle,

ich habe eine kleine Frage:
kann ich einen Pin, der als PCINT-Pin festgelegt wurde als Output 
definieren?

ich habe vor, 5 SRF05 im Modus 2(also nur mit einem Pin) zu betreiben. 
Also muss ich zum Auslösen eines Messvorgangs den Pin auf high setzen. 
Um nich meine Zeit mit warten zu vertrödeln, soll das ganze 
Interruptgesteuert umgesetzt werden. Leider ist die Platine dafür schon 
fertig und die Pins damit festgelegt.
uC ist ein mega2560
US0: PB6
US1: PB1
US2: PB7
US3: PB3
US4: PB5
Der Code würde dann ungefähr so aussehen:
Zum Auslösebefehl senden:
1
void startUS(uint8_t num)
2
{
3
    switch(num)
4
    {
5
        case 0:
6
        DDRB |= (1<<US0);
7
        PORTB |= (1<<US0);
8
        //warten
9
        PORTB &= ~(1<<US0);
10
        DDRB &= ~(1<<US0);
11
        break;
12
        case 1:
13
        DDRB |= (1<<US1);
14
        PORTB |= (1<<US1);
15
        //warten
16
        PORTB &= ~(1<<US1);
17
        DDRB &= ~(1<<US1);
18
        break;
19
        case 2:
20
        DDRB |= (1<<US2);
21
        PORTB |= (1<<US2);
22
        //warten
23
        PORTB &= ~(1<<US2);
24
        DDRB &= ~(1<<US2);
25
        break;
26
        case 3:
27
        DDRB |= (1<<US3);
28
        PORTB |= (1<<US3);
29
        //warten
30
        PORTB &= ~(1<<US3);
31
        DDRB &= ~(1<<US3);
32
        break;
33
        case 4:
34
        DDRB |= (1<<US4);
35
        PORTB |= (1<<US4);
36
        //warten
37
        PORTB &= ~(1<<US4);
38
        DDRB &= ~(1<<US4);
39
        break;
40
        default:
41
        break; 
42
    }
43
}
sowie die PCINT0-Interrupt:
1
ISR(PCINT0_vect)
2
{
3
    static uint16_t val[5];
4
    if(PINB & (1<<PB1)) //1 high
5
    {
6
        val[1] = TCNT3;
7
    }
8
    else if(!(PINB & (1<<PB1))) //1 low
9
    {
10
        us[1] = (TCNT3-val[1]);
11
    }
12
    else if(PINB & (1<<PB6)) //0 high
13
    {
14
        val[0] = TCNT3;
15
    }
16
    else if(!(PINB & (1<<PB6))) //0 low
17
    {
18
        us[0] = (TCNT3-val[0]);
19
    }
20
    else if(PINB & (1<<PB7)) //2 high
21
    {
22
        val[2] = TCNT3;
23
    }
24
    else if(!(PINB & (1<<PB7))) //2 low
25
    {
26
        us[2] = (TCNT3-val[2]);
27
    }
28
    else if(PINB & (1<<PB3)) //3 high
29
    {
30
        val[3] = TCNT3;
31
    }
32
    else if(!(PINB & (1<<PB3))) //3 low
33
    {
34
        us[3] = (TCNT3-val[3]);
35
    }
36
    else if(PINB & (1<<PB5)) //4 high
37
    {
38
        val[4] = TCNT3;
39
    }
40
    else if(!(PINB & (1<<PB5))) //4 low
41
    {
42
        us[4] = (TCNT3-val[4]);
43
    }
44
}

ist soweit der Code ok? gerade die Interrupt-Methode?
Geht das so überhaupt?
Bin für alles dankbar.
ich hab sowieso wieder was vergessen, also einfach fragen ;)

von Oliver (Gast)


Lesenswert?

Zitat aus dem Datenblatt:
1
The External Interrupts are triggered by the INT7:0 pin or any of the PCINT23:0 pins. Observe
2
that, if enabled, the interrupts will trigger even if the INT7:0 or PCINT23:0 pins are configured as
3
outputs. This feature provides a way of generating a software interrupt.

Ergo: Ja, aber eine Flanke ist eine Flanke, und löst den Interrupt aus, 
egal, ob die von aussen kommt, oder vom Pin selber erzeugt wird. Willst 
du das nicht, musst du die Interrupts vorher disablen.

Oliver

von N. G. (newgeneration) Benutzerseite


Lesenswert?

Ergo: nur um nochmal sicherzugehen:
Wenn ich das Ding selbst High setze, wird ein Interrupt ausgelößt.
umgehbar mit
1
uint8_t sreg=SREG;
2
case 0:
3
DDRB |= (1<<US0);
4
PORTB |= (1<<US0);
5
SREG=sreg;
6
//warten
7
sreg=SREG;
8
cli();
9
PORTB &= ~(1<<US0);
10
DDRB &= ~(1<<US0);
11
SREG = sreg;
12
break;

von Oliver (Gast)


Lesenswert?

Lass die Finger vom SREG, das brauchst du als C-Programierer nie 
anzufassen.

Lösch das PCINT-Enable-Bit, bevor du den Pin auf Ausgang schaltest, und 
nachdem du zurück auf Eingang geschaltet hast, lösch das zugehörige 
ISR-Flag, bevor du PCINT wieder setzt, und gut ist.

Oliver

von Peter D. (peda)


Lesenswert?

Oliver schrieb:
> Willst
> du das nicht, musst du die Interrupts vorher disablen.

Den Pending-Flags ist es wurscht, ob Interrupts gerade gesperrt sind.
Das wäre auch schlimm, denn dann würde man Interrupts verlieren.

Du mußt erst den Pin als Ausgang pulsen, dann auf Eingang setzen und 
danach darfst Du das Maskenbit freigeben!

Der PORTB ist ungünstig, damit kriegst Du Meßfehler, wenn noch andere 
Interruptquellen zuschlagen können.
Besser sind die ADC-Eingänge, denn die kann man auf die ICU routen.
Damit kriegt man keine Fehler durch die Latenz, die Messungen sind auf 
den CPU-Zyklus genau.

Und laß diese Copy&Paste Monster.
Der Code ist für alle Sensoren gleich, nur die Bitmaske ist eine andere.
Also braucht man nur eine Funktion der man die Bitmaske übergibt.

von Oliver (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Den Pending-Flags ist es wurscht, ob Interrupts gerade gesperrt sind.
> Das wäre auch schlimm, denn dann würde man Interrupts verlieren.

Was man aber durchaus möchte. Wenn der Pin als Ausgang gesetzt ist, und 
dann auch mal "Pingewackel" von sich gibt (sonst müsste man ihn ja nicht 
als Ausgang nutzen), dann setzt das das ISR-Flag. Ob man die so erzeugte 
Interruptanforderung sinnvoll per ISR abarbeiten will oder nicht, hängt 
von der Anwendung ab.

Oliver

von N. G. (newgeneration) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Besser sind die ADC-Eingänge, denn die kann man auf die ICU routen.
> Damit kriegt man keine Fehler durch die Latenz, die Messungen sind auf
> den CPU-Zyklus genau.

ADCs sind leider alle 16 belegt. Und wie gesagt, die Pins sind so 
vorgegeben, die Platine liegt schon fertig vor mir.

Oliver schrieb:
> Lass die Finger vom SREG, das brauchst du als C-Programierer nie
> anzufassen.

Warum? Wenn man einen Block Interruptfest machen will sollte man doch 
das Statusregister sichern bevor man das I-Bit löscht?! Oder hab ich da 
was falsch verstanden?

von Oliver (Gast)


Lesenswert?

Es geht ja nicht darum, einen Block interruptfest zu machen, sondern zu 
verhindern, daß PCINT-Interrupts durch den Ausgang ausgelöst werden. Das 
ist eine andere Aufgabenstellung, die man dadurch löst, daß man genau 
den betroffenen PCINT disabled. Ein Atomic-Block würde nur verindern, da 
der Interrupt innerrhalb des Blocks dazwischenfunkt. Verhindert wird der 
dadurch nicht, er würde dann halt direkt nach Verlassen des Blocks 
ausgeführt.

Zum Thema Automic-Block und SReg hast du prinzipiell recht. Allderings 
hat avr-gcc dafür die entsprechenden ATOMIC - Funktionen, so daß man da 
auch nicht unbedingt selber ans SReg muß.

Deine ersten beiden SReg-Operationen allerings sind unnötig. Lesen des 
Registers hat überhaupt keine Auswirkung auf irgendwas. Beim zweiten 
Block mit dem cli() ist es richtig, der ist ab cli() atomic bis zum 
zurückschreiben des SReg.

Allerdings sollte man bei dem Verfahren schon sicher sein, daß sich in 
dem Atomic-Block keine anderen Flags im SREG verändern, die dan später 
noch gebraucht würden. Denn dann schießt man sich mit dem Vorgehen 
selber ins Knie.

Oliver

von N. G. (newgeneration) Benutzerseite


Lesenswert?

Danke an alle für die Erläuterung, besonders Oliver.

Das mit dem Atimic wusste ich noch nicht, bzw. habe es nie für nötig 
gehlaten. Ist das bei anderen Compilern auch vorhanden?

Mein Problem löst man einfach durch:
1
PCMSK0&= ~(1<<PCINTx);
(Wenn ich es richtig verstanden habe)

Peter Dannegger schrieb:
> Der PORTB ist ungünstig, damit kriegst Du Meßfehler, wenn noch andere
> Interruptquellen zuschlagen können.
Wie war das noch gemeint? auf dem Port liegt nichts anderes, was soll 
die Messfehler verschulden?

Aber erstmal Danke, ihr habt mir sehr, und vor allem schnell geholfen!

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.