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
Nein, kompakter wird das nix.
Ich weiß nicht, warum du das mit temp machst.
Bits rücksetzen:
Bits setzen:
Wenn du z.B. die ersten beiden Bits toggeln willst, kannst du das mit
tun.
>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 | }
|
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.
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.
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
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.
>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
|
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?
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.
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
|
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.
>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.
|