Forum: Mikrocontroller und Digitale Elektronik Microchip PIC24 Simple Port I/O, TRISx, LATx, PORTx


von Simon (Gast)


Lesenswert?

Hallo,

möchte ich einen Pin auf 1 setzen so nutze ich:

PORTBbits.PB0 = 1;

das ganze funktioniert auch wenn ich es wieder auf 0 setzen möchte

PORTBbits.PB0 = 0;

Nur was geschieht hier im Hintergrund?



Beispiel:
Ich möchte Pin 1 am PORTB ein und ausschalten.
1
zustand = PORTB;
2
zustand |= 0x01;
3
LATB = zustand;


oder besser so:
1
LATB |= 0x01;

von Hurra (Gast)


Lesenswert?

Das sind Bitfelder, die auf die Portregister gemappt sind. Mit 
PORTBbits.PB0 = 1; setzt man in dem Bitfeld genau das Bit PB0 und damit 
ein Bit im PORTB Register.
Mehr nicht.

Kann man sich im Header ansehen. In MPLABx retsklick -> jump to 
declaration oder so.

Das ist um Längen lesbarer als der UND / ODER Kram.

Was der Compiler damit tut, weiß ich nicht.

von Stefan (Gast)


Lesenswert?

Für den Anfang ist der Befehl LATB |= 0x01; vielleicht
etwas verwirrend für dich.
Wie würdest du denn nur die 3.LED leuchten lassen ?
Ist nicht LATB |= 0x03;

von Simon (Gast)


Lesenswert?

Die PORTBbits sind so deklariert:
1
#define PORTB PORTB
2
extern volatile unsigned int  PORTB __attribute__((__sfr__));
3
typedef struct tagPORTBBITS {
4
  unsigned RB0:1;
5
  unsigned RB1:1;
6
  unsigned RB2:1;
7
  unsigned RB3:1;
8
  unsigned RB4:1;
9
  unsigned RB5:1;
10
  unsigned RB6:1;
11
  unsigned RB7:1;
12
  unsigned RB8:1;
13
  unsigned RB9:1;
14
  unsigned RB10:1;
15
  unsigned RB11:1;
16
  unsigned RB12:1;
17
  unsigned RB13:1;
18
  unsigned RB14:1;
19
  unsigned RB15:1;
20
} PORTBBITS;
21
extern volatile PORTBBITS PORTBbits __attribute__((__sfr__));


Der haken ist schlichtweg das wenn man mit bitmasken arbeitet eine 
struct unbrauchbar wird.

ich mach mal ein Beispiel:
1
  #define LED_RED             (0x08) // Pin 3 ( ja natürlich würde auch ein (1<<3) gehen)
2
  #define LED_GREEN           (0x10) // Pin 4
3
  #define LED_BLUE            (0x20) // Pin 5
4
  #define LED_MASK            (0x38)
5
6
led_On( (LED_GREEN | LED_BLUE) ); // Funktion Aufrufen
7
8
9
void led_On(uint16_t led) {
10
// Hier wird es jetzt spannend, da an diesem port natürlich nur die
11
// pins der LEDs geändert werden sollen, und nicht alle.
12
}

von Michael .. (gismi)


Lesenswert?

Simon schrieb:
> Der haken ist schlichtweg das wenn man mit bitmasken arbeitet eine
> struct unbrauchbar wird.
>

Ich mach dan das einfach so:
1
#define LED_RED            PORTBbits.PB0
2
#define LED_GREEN         PORTBbits.PB1
3
#define LED_BLUE          PORTXYbits.PB10
4
5
LED_GREEN = 1;
6
LED_BLUE = 1;

Und sonst kannst du einfach PORTB nutzen und deine Bitmasken wie bis 
jetzt nutzen.

: Bearbeitet durch User
von Simon (Gast)


Lesenswert?

Das ist sicherlich auch eine Möglichkeit.

Hast du aber eine Dreifarbige LED dann bist du ständig dabei

LED_GREEN = 1;
LED_BLUE = 1;
LED_RED = 1;

LED_GREEN = 0;
LED_BLUE = 0;
LED_RED = 0;

jetzt bringen wir noch eine funktion für das blinken mit rein:

led_Flash(LED_GREEN | LED_BLUE);

oder zwei LEDs abschalten
led_Off(LED_GREEN | LED_BLUE);

klappt doch da wesentlich besser


Meine Idee wäre ja:
ob das so astrein ist?!
1
void led_On(uint16_t led) {
2
    uint16_t x;
3
    x = PORTB;
4
    x &= ~led;
5
    LATB = x;
6
}
7
8
void led_Off(uint16_t led) {
9
    uint16_t x;
10
    x = PORTB;
11
    x |= led;
12
    LATB = x;
13
}

von Michael .. (gismi)


Lesenswert?

Simon schrieb:
> klappt doch da wesentlich besser

Wie man es möchte, repsektive braucht.

Simon schrieb:
> ob das so astrein ist?!

Warum nicht? x kannst du noch wegoptimieren.
und Ansonsten:
http://ww1.microchip.com/downloads/en/DeviceDoc/70000598c.pdf

von Hurra (Gast)


Lesenswert?

Simon schrieb:
> Meine Idee wäre ja:
> ob das so astrein ist?!

Deine Idee funktioniert nur dann, wenn alle LED am PORTB hängen. Der 
darüber angegebene Code ist 100% unabhängig vom Pinning - man kann die 
verschiedenen Ports beliebig mischen, so kann eine LED an PORTF sein, 
die zweite an PORTB und so weiter.

Wie man den Status der LED an die Funktion übergibt, ja dafür gitbs 
einen Million Möglichkeiten, angefangen von Boolschen Variablen bis hin 
zu Strukturen.

Andererseits:
Wenn dir das mit den klassischen Bitmanipulationen gefällt: Es spricht 
nichts dagegen.

von Volker S. (vloki)


Lesenswert?

PORTx sollte man bei Ausgängen eigentlich nie verwenden wenn es ein LATx 
Register gibt!

Wenn man wissen möchte, was im Hintergrund geschieht, dann schaut man 
einfach beim Debuggen in's Disassembly. (Breakpoint an der entsprechnden 
Stelle setzen, Window|Debugging|Disassembly)

von Volker S. (vloki)


Lesenswert?

Michael .. schrieb:
> Ansonsten:
> http://ww1.microchip.com/downloads/en/DeviceDoc/70000598c.pdf

Oh cool, da wird ja tatsächlich im Datasheet erklärt, warum die 
Verwendung von PORT bei Ausgängen wegen des RMW-Effektes keine gute Idee 
ist.
(Figure 10-2: Example of Unintended I/O Behavior)

von Simon (Gast)


Lesenswert?

Naja, nur du hast nur die Möglichkeit die daten von PORT zu lesen.
Beim schreiben gibts vielleicht noch die option ob LAT oder PORT

von Volker S. (vloki)


Lesenswert?

Simon schrieb:
> Naja, nur du hast nur die Möglichkeit die daten von PORT zu lesen.

Michael hat sich extra die Mühe gemacht ein Datenblatt zu verlinken.
Schau doch bitte mal "Figure 10-1: Dedicated Port Structure Block 
Diagram" an.


Simon schrieb:
> Beim schreiben gibts vielleicht noch die option ob LAT oder PORT

Was beides das selbe bewirkt. Es wird immer ins Data Latch geschrieben.
(auch sehr schön im Bildchen zu erkennen)

: Bearbeitet durch User
von Peter C. (peter_c49)


Lesenswert?

Hallo Simon,

wenn du die Ports als ganzes liest/schreibst um einzelne Bit zu 
bearbeiten, verzichtest du auf die BSET und BCLR Instructionen die es in 
den PIC24 gibt.

zb: ein LATBbits.LATB4 = 1; // clock pin high
    wird im Assemblerlisting zu
    000E0A  A882CC     BSET LATB, #4

was genau ein opcode/fetch ist, also genau ein Wort im pic24, schon mal 
platz im flash gespart.
Da es sogar nur einen Befehlszyklus baucht, ist es auch um einige Zyklen 
schneller als die klassische Variante.

mfG
Peter ;-)

von Volker S. (vloki)


Lesenswert?

Peter C. schrieb:
> Da es sogar nur einen Befehlszyklus baucht, ist es auch um einige Zyklen
> schneller als die klassische Variante.

Falls die klassische Variante das mit dem Shift-Geraffel sein soll, dann 
können das anscheinend auch einige Compiler optimal in bsf/bcf Befehle 
übersetzen. Andere vermurksen sogar die Bitfeld Variante. Nachweise oder 
Quellenangabe bleibe ich jetzt mal schuldig ;-)

von Hurra (Gast)


Lesenswert?

Peter C. schrieb:
> Da es sogar nur einen Befehlszyklus baucht, ist es auch um einige Zyklen
> schneller als die klassische Variante.

Wer meint, die paar CPU-Zyklen sparen zu müssen, der ist bei PIC24 
meiner Meinung nach falsch aufgehoben.
Für solche Projekte gibt es kleinere CPUs mit wenig Peripherie.

Ein PIC 24 hat nun aber viel Peripherie. Wenn man Ports schnell toggeln 
muss (nur dann spielt sowas überheupt irgendeine Rolle!), dann sollte 
man das einem Peripherieblock aufhalsen, nicht der CPU. Das ist nicht 
nur viel schneller, sondern auch noch effiezienter - die CPU kann 
derweil idlen sogar schlafen.

Bei ein paar LEDs (um die geht es hier!) spielt das gar keine Rolle. Die 
Toggelt man alle paar 100 ms. Egal wie viele man hat, über 0,0x% 
CPU-Last kommt man damit sowieso nie.
Und nein, eine PWM für LED macht nicht die CPU. Die macht der OC-Timer. 
Während die CPU schläft. Zu einem winzigen Bruchteil des Stromverbrauchs 
des Kerns.

Ich arbeite seit Jahren mit den Dingern, und die Pins die am schnellsten 
vom der CPU getoggelt werden sind höchstens StatusLED - alle 250ms mal 
was tun oder so. Alle anderen Geschichten machen die reichlich 
vorhandenen Peripherieblöcke.

von Klaus (Gast)


Lesenswert?

Hurra schrieb:
> Ich arbeite seit Jahren mit den Dingern, und die Pins die am schnellsten
> vom der CPU getoggelt werden sind höchstens StatusLED - alle 250ms mal
> was tun oder so. Alle anderen Geschichten machen die reichlich
> vorhandenen Peripherieblöcke.

Das ist eigentlich auch richtig. Aber zum Testen, zur Inbetriebnahme 
mach ich vieles gern mal mit Bitbanging. Und wenn das dann gut und 
schnell funktioniert, insbesondere mit den 70MHz der PIC24Exxx, bin ich 
häufig zu faul das jetzt noch mal mit der eingebauten Peripherie zu 
machen.

MfG Klaus

von Hurra (Gast)


Lesenswert?

Klaus schrieb:
> Das ist eigentlich auch richtig. Aber zum Testen, zur Inbetriebnahme
> mach ich vieles gern mal mit Bitbanging. Und wenn das dann gut und
> schnell funktioniert, insbesondere mit den 70MHz der PIC24Exxx, bin ich
> häufig zu faul das jetzt noch mal mit der eingebauten Peripherie zu
> machen.
>
> MfG Klaus

Dann braucht man sich um Effizienz aber wirklich keine Gedanken mehr zu 
machen :-(

Nur: Der PIC24 ist eben ein Stromsparprozessor. Die Stärken des PIC24 
sind ausgefeilte Stromsparmodi und viel Peripherie.

Bitbanging heißt damit, die Stärken zu ignoieren.
Übrig bleibt ein höchst mittelprächtiger Controller. Der Prozessorkern 
ist nicht die starke Seite des PIC24.

Dann ist man mit einem CortexM0 mit wenigen Features beser bedient. Oder 
einem PIC32 von mir aus.

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.