Forum: Compiler & IDEs Port zum Teil parallel ändern


von Steven (. (ovular) Benutzerseite


Lesenswert?

Hallo zusammen!

Ich würde gerne recht effektiv 3 Pins eines Ports parallel setzen.
Möchte LEDs ansteuern aber den restlichen Port unberührt lassen.

Grund für das parallele Setzen ist ein 2x5 LED_Array.
Ich werde es dann mal vorstellen wenn es soweit funktioniert.


erst versuchte ich es mit:
1
temp &= 0x03;   //temp vorbereiten
2
PORTA &= ~0x03; //pins clearen
3
PORTA |= temp;  //pins setzen
natürlich bekommt man hier bei hohen Frequenzen ein zu geringes 
Tastverhältnis.

Ganz gut geht es mit:
1
PORTA = (PORTA&~0x03)|temp

Gibt es da noch was sinnvolleres?

Gruß Steven

von Falk B. (falk)


Lesenswert?

Nein, kompakter wird das nix.

von Bernd S. (bernds1)


Lesenswert?

Ich weiß nicht, warum du das mit temp machst.

Bits rücksetzen:
1
PORTA &= ~0x03;

Bits setzen:
1
PORTA |= 0x03;

Wenn du z.B. die ersten beiden Bits toggeln willst, kannst du das mit
1
PORTA ^= 0x03;
tun.

von Steven (. (ovular) Benutzerseite


Lesenswert?

>Ich weiß nicht, warum du das mit temp machst.

Weil ich 2 Ports möglichst schnell hintereinander ändern will.
1
void LEDBar_setLEDs(uint8_t temp){
2
  uint8_t savePortB = 0;
3
  uint8_t savePortA = 0;
4
  
5
  if(temp & (1<<5)) temp ^= 0x1F; //Invertiert die ersten 5 Bits,
6
  savePortB = 0x03 & (temp>>3);  //bereitet PortB vor
7
  savePortA = (0x03 & temp);     //bereitet PortA vor
8
 
9
  PORTB = (PORTB&~LEDBar_Mask_B)|savePortB;  //Port dann gezielt setzen
10
  PORTA = (PORTB&~LEDBar_Mask_B)|savePortA;
11
  DDRA |= LEDBar_Mask_A;        //Ports einschalten
12
  DDRB |= LEDBar_Mask_B;
13
}

vielleicht nehme ich den Rest auch noch hoch in die Saves

also
1
  savePortB = (PORTB&~LEDBar_Mask_B)|0x03 & (temp>>3);  //bereitet PortB vor
2
  savePortA = (PORTB&~LEDBar_Mask_B)|(0x03 & temp);     //bereitet PortA vor
3
  PORTB = savePortB;  //Port dann gezielt setzen
4
  PORTA = savePortA;
5
}

von Karl H. (kbuchegg)


Lesenswert?

Steven () schrieb:

> Weil ich 2 Ports möglichst schnell hintereinander ändern will.

definiere 'möglichstr schnell' und auch warum das bei einer LED-Matrix 
so wichtig ist.

Ob die LED jetzt nach 1 Millionstel Sekunde brennen oder nach 2 kann 
kein Mensch feststellen. Und nachdem die LED umgeschaltet wurden brennen 
sie dann sowieso wieder 1 Millisekunde oder länger ehe dann der nächste 
Wechsel in entweder 1 oder 2 Millionstel Sekunden erfolgt.

Du optimierst etwas, wo es kaum was zu optimieren gibt.

von Karl H. (kbuchegg)


Lesenswert?

void LEDBar_setLEDs(uint8_t temp){

....

  DDRA |= LEDBar_Mask_A;        //Ports einschalten
  DDRB |= LEDBar_Mask_B;
}

und du beschwerst dich über die Laufzeit vom Port setzen?
Spar doch einfach mal die Zeit ein, die du problemlos einsparen kannst!

Welchen Sinn soll das haben, wenn du die Pins wieder und immer wieder 
auf Ausgang stellst? Die vergessen das nicht. Einmal, beim Programmstart 
einstellen, reicht doch dicke aus.

von Steven (. (ovular) Benutzerseite


Lesenswert?

Hallo Karl Heinz,

auch wenn es sehr wohl etwas unsinnig ist, hat mich das im Allgemeinen 
mal interessiert, weil ich schon ein paar mal darauf gestoßen bin.
Diese Optimierung also, um es mal zu wissen, Du kannst es ja als Übung 
betrachten.

Mir ist es eben aufgefallen, dass ohne diese Optimierung bei hoher 
Aktuallisierungsfrequenz ein paar LEDs schwach leuchten. Und deswegen 
dieser Schritt. Ich wollte auch nicht einfach die Frequenz ändern 
sondern dem Grund auf die Schliche kommen.

Folgendes ist gerade mein Projekt. Ein LED-Bargraph mit 10LEDs mit 6 
Pins steuern, das ganze mit dem Attiny24. Nebenbei will ich SPI und I2C 
freihalten.

//LEDs sind so verschaltet:
// PA0 PA1 PA2 PB0 PB1 PA0 PA1 PA2 PB0 PB1
//  A   A   A   A   A   K   K   K   K   K
//  0   1   2   3   4   5   6   7   8   9  LED_s
//  K   K   K   K   K   A   A   A   A   A
// PB2 PB2 PB2 PB2 PB2 PB2 PB2 PB2 PB2 PB2
//
// Einzelne LED ansteuern:
// LED PB2 PB1 PB0 PA2 PA1 PA0
//  0   0   0   0   0   0   1
//  1   0   0   0   0   1   0
//  2   0   0   0   1   0   0
//  3   0   0   1   0   0   0
//  4   0   1   0   0   0   0
//  5   1   1   1   1   1   0
//  6   1   1   1   1   0   1
//  7   1   1   1   0   1   1
//  8   1   1   0   1   1   1
//  9   1   0   1   1   1   1

Die DDR-Sache ist noch ein Überbleibsel von der letzten Version, dort 
hatte ich die Ports auf Tristate solange sich was geändert hat. Hindert 
das möglichst zeitnahe Ändern der Ports aber nicht. Kann also raus...

...

Gruß Steven

von Karl H. (kbuchegg)


Lesenswert?

Steven () schrieb:

> auch wenn es sehr wohl etwas unsinnig ist, hat mich das im Allgemeinen
> mal interessiert, weil ich schon ein paar mal darauf gestoßen bin.
> Diese Optimierung also, um es mal zu wissen, Du kannst es ja als Übung
> betrachten.


Na, ja.
Was willst du da

PORTB = (PORTB&~LEDBar_Mask_B)|savePortB;

groß optimieren?
Ohne mir jetzt den Assembler Output angesehen zu haben, erwarte ich, 
dass der Compiler das übersetzt zu (LEDBar_Mask_B ist eine Konstante?)

   in   register, PORTB
   andi register, Maskenbyte
   or   register, register in dem savePortB hergerichtet wurde
   out  PORTB, register


kannst dir ja mal ansehen, wieviele Taktzyklen das sind und was das bei 
deiner Taktfrequenz in Zeit ausmacht.


> Mir ist es eben aufgefallen, dass ohne diese Optimierung bei hoher
> Aktuallisierungsfrequenz ein paar LEDs schwach leuchten.

Ja. Das ist aber was anderes.
Das nennt sich Ghosting und kommt daher, dass du eben nicht beide Ports 
exakt gleichzeitig umschaltest / umschalten kannst.
Du musst
  erst die LED auf jeden Fall abschalten
  dann die neuen Einstellungen machen
  und zum Abschluss dann die Einstellung, die die LED wieder
  zum Leuchten bringt

Das Auge ist da sehr empfindlich. Wir sehen es auch, wenn LED nur ganz 
kurz, ein paar µs lang brennen.
Der entscheidende Punkt ist, dass du die LED als erstes auf jeden Fall 
abschalten musst. Dann manipulierst du den einen Port und wenn du dann 
den 2.te Port manipulierst, erst dann dürfen die LED (mit der 
veränderten Konfiguration) erneut zu leuchten beginnen.
Schaltest du nicht ab, dann hast du kurzzeitig einen Zwischenstand an 
den LED, so dass andere als die gewollten Leuchten. Und das siehst du.

von Steven (. (ovular) Benutzerseite


Lesenswert?

>Was willst du da
>PORTB = (PORTB&~LEDBar_Mask_B)|savePortB;
>groß optimieren?

Nichts, das funktioniert wunderbar. Ich dachte es gibt ein Trick in C 
den ich noch nicht kenne.

>Ghosting
Ja, hinzu kommt das Problem, dass ich das nicht einfach ausschalten 
kann. Wenn ich alle 6Pins auf logisch Low lege, sind 5LEDs an.
Wenn ich alle 6Pins auf logisch High lege, sind 5LEDs an.

Ohje, da seh ich selbst den Wald vor lauter Bäume nicht mehr.
Natürlich ist alles aus, wenn alles logisch Low ist.

Trotzallem: Schon alleine wegen
-erst auschchalten (Bits löschen)
-dann bits setzen
trat das "Ghosting" auf.


Deswegen belasse ich jetzt vorerst das funktionierende:
PORTA = savePortB;
PORTB = savePortB;

Und für zukünftige Projekte weis ich jetz, dass ich mehrere Bits auf 
einem PORT parallel verändern kann ohne die anderen zu beeinflussen.
1
#define PINS 0x03 // irgendeine Maske
2
PORTx = PORTx&~PINS | value&PINS

von Karl H. (kbuchegg)


Lesenswert?

Steven () schrieb:
>>Was willst du da
>>PORTB = (PORTB&~LEDBar_Mask_B)|savePortB;
>>groß optimieren?
>
> Nichts, das funktioniert wunderbar. Ich dachte es gibt ein Trick in C
> den ich noch nicht kenne.
>
>>Ghosting
> Ja, hinzu kommt das Problem, dass ich das nicht einfach ausschalten
> kann. Wenn ich alle 6Pins auf logisch Low lege, sind 5LEDs an.
> Wenn ich alle 6Pins auf logisch High lege, sind 5LEDs an.

Tja

// PA0 PA1 PA2 PB0 PB1 PA0 PA1 PA2 PB0 PB1
//  A   A   A   A   A   K   K   K   K   K
//  0   1   2   3   4   5   6   7   8   9  LED_s
//  K   K   K   K   K   A   A   A   A   A
// PB2 PB2 PB2 PB2 PB2 PB2 PB2 PB2 PB2 PB2

scheisse verschaltet.

was hängt extern zwischen Portpin und LED?

von Steven (. (ovular) Benutzerseite


Lesenswert?

Dann mach mir mit 6Pins oder weniger einen besseren Vorschlag.

An PA0..2 und PB0..1 hängt jeweils ein 330R Widerstand davor. Sonst 
nichts. PB2 ist einfach eine Leitung an 5Kathoden und 5Anoden.

von Steven (. (ovular) Benutzerseite


Lesenswert?

Hier werden beide Ports kurz nacheinander aktuallisiert, so passt mir 
das.
1
void LEDBar_setLEDs(uint8_t temp){
2
  //LEDBar_off();
3
  uint8_t savePortB = 0;
4
  uint8_t savePortA = 0;
5
  //es könenn wegen der Kathode/Anode-Umschaltung nur 5LEDs gleichzeitig leuchten.
6
  //das 6. Bit (1<<5) wechselt zu den anderen 5 LEDs
7
  if(temp & (1<<5)) temp ^= 0x1F; //Invertiert die ersten 5 Bits,
8
  bitteNichtUnterbrechen_start;
9
  savePortA = (PORTA&~LEDBar_Mask_A)|(LEDBar_Mask_A & temp);
10
  savePortB = (PORTB&~LEDBar_Mask_B)|(LEDBar_Mask_B & (temp>>LEDBar_Number_LEDPins_on_PortA));  //bereitet den PortB vor
11
  PORTB = savePortB;  //Ports dann schnell hintereinander "verändern"
12
  PORTA = savePortA;  
13
  bitteNichtUnterbrechen_ende;
14
}
15
16
17
##########################################################################
18
ASSEMBLER CODE:
19
20
21
void LEDBar_setLEDs(uint8_t temp){
22
  //LEDBar_off();
23
  uint8_t savePortB = 0;
24
  uint8_t savePortA = 0;
25
  //es k�nenn wegen der Kathode/Anode-Umschaltung nur 5LEDs gleichzeitig leuchten.
26
  //das 6. Bit (1<<5) wechselt zu den anderen 5 LEDs
27
  if(temp & (1<<5)) temp ^= 0x1F; //Invertiert die ersten 5 Bits,
28
0000002A  SBRS R24,5    Skip if bit in register set 
29
0000002B  RJMP PC+0x0003    Relative jump 
30
--- No source file -------------------------------------------------------------
31
0000002C  LDI R25,0x1F    Load immediate 
32
0000002D  EOR R24,R25    Exclusive OR 
33
0000002E  IN R25,0x3F    In from I/O location 
34
0000002F  CLI     Global Interrupt Disable 
35
00000030  IN R18,0x1B    In from I/O location 
36
00000031  MOV R19,R24    Copy register 
37
00000032  ANDI R19,0x07    Logical AND with immediate 
38
00000033  ANDI R18,0xF8    Logical AND with immediate 
39
00000034  OR R18,R19    Logical OR 
40
00000035  IN R19,0x18    In from I/O location 
41
00000036  LSR R24    Logical shift right 
42
00000037  LSR R24    Logical shift right 
43
00000038  LSR R24    Logical shift right 
44
00000039  ANDI R24,0x07    Logical AND with immediate 
45
0000003A  ANDI R19,0xF8    Logical AND with immediate 
46
0000003B  OR R24,R19    Logical OR 
47
  PORTB = savePortB;  //Ports dann schnell hintereinander "ver�ndern"
48
0000003C  OUT 0x18,R24    Out to I/O location 
49
  PORTA = savePortA;  
50
0000003D  OUT 0x1B,R18    Out to I/O location 
51
  bitteNichtUnterbrechen_ende;
52
0000003E  OUT 0x3F,R25    Out to I/O location 
53
0000003F  RET     Subroutine return

von Karl H. (kbuchegg)


Lesenswert?

Steven () schrieb:
> Dann mach mir mit 6Pins oder weniger einen besseren Vorschlag.
>
> An PA0..2 und PB0..1 hängt jeweils ein 330R Widerstand davor. Sonst
> nichts. PB2 ist einfach eine Leitung an 5Kathoden und 5Anoden.

Mutig.

Aber:

Wenn du einen Pin auf Eingang schaltest, kann er weder Strom senken noch 
Strom liefern. Das könnte ein Ansatzpunkt sein. Selbst wenn ein Portpin 
dann schon auf 1 ist, schaltet das lediglich den pullup hinzu und der 
reicht nicht, damit eine LED leuchten kann. (hoffe ich mal)

Jetzt muss man 'nur noch' eine Eiertanzsequenz finden

// PA0 PA1 PA2 PB0 PB1 PA0 PA1 PA2 PB0 PB1
//  A   A   A   A   A   K   K   K   K   K
//  0   1   2   3   4   5   6   7   8   9  LED_s
//  K   K   K   K   K   A   A   A   A   A
// PB2 PB2 PB2 PB2 PB2 PB2 PB2 PB2 PB2 PB2


erst mal PB2 auf Eingang.
Hmm. das sollte eigentlich schon ausreichen, dass keine einzige LED mehr 
leuchten kann.
danach die neue Belegung für PA0:2 PB0:2 ausgeben
und PB2 wieder auf Ausgang


probier das mal. Könnte klappen.
Dein Glück ist, dass jede einzelne LED von PB2 abhängig ist.

von Steven (. (ovular) Benutzerseite


Lesenswert?

>Mutig
Ich wollte Platz sparen.

>Wenn du einen Pin auf Eingang schaltest,
>kann er weder Strom senken noch Strom liefern.
Ja, ich hatte sogar das PUD-Flag gesetzt um die Pullups auszuschalten.

Zu dem Zeitpunkt sah der Code noch etwa so aus:
1
void LEDBar_setLEDs_old(uint8_t temp){
2
  DDRA &= ~LEDBar_Mask_A; //Auf Eingang schalten
3
  DDRB &= ~LEDBar_Mask_B;
4
  
5
  if(temp & (1<<5)) temp ^= 0x1F; //Invertiert die ersten 5 Bits,
6
  PORTA &= ~LEDBar_Mask_A;
7
  PORTB &= ~LEDBar_Mask_B;
8
  PORTA |= LEDBar_Mask_A & temp;
9
  PORTB |= (LEDBar_Mask_B & (temp>>LEDBar_Number_LEDPins_on_PortA);
10
  
11
  DDRA |= LEDBar_Mask_A;        //Ports einschalten
12
  DDRB |= LEDBar_Mask_B;
13
}

Aber trotzdem die Ghostings auch trotz Ports als Eingang mit gesetztem 
PUD-Flag!
Woher das kommt würde mich auch interessieren...

P.S. daher übrigends auch diese DDRA und B Befehle, die ich in einem 
vorherigen Post vergessen hatte rauszunehmen.

Im Moment lasse ich die Ports einfach auf Ausgang. Solange die Ports 
sich unmittelbar nacheinander ändern sieht man bei 1MHz internal Clock 
keine Ghostings.

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.