Hi,
mir stellt sich gerade die Frage wie man Ports am Besten zusammenfassen
kann. Ich benötige z.B. für einen 3x3x3 LED Cube 9 Pins, die
"gleichwertig" sind, d.h. jeder schaltet jeweils eine LED in der
aktuellen Ebene.
Wie würde ich nun die Ports "herausziehen", so wie es im Tutorial hier
(https://www.mikrocontroller.net/articles/AVR-Tutorial:_LCD#Portnamen_aus_dem_Code_herausziehen)
beschrieben wird.
Aktuell schaut mein Code in etwa so aus:
1
.equ LED_PORT1 = PORTA
2
.equ LED_DDR1 = DDRA
3
.equ LED_PORT2 = PORTB
4
.equ LED_DDR2 = DDRB
So ganz gefällt mir das aber noch nicht, weil ich die beiden Ports nicht
gleich behandeln kann. Während ich bei Port A z.B. das Datenregister auf
"ff" schalten kann, hängen bei Port B ja unter Umständen noch andere
Dinge mit dran, welche ich beim Setzen des Datenregisters aber nicht
"blind" überschreiben kann/möchte.
Wenn ich aber LED_PORT2 erst einlesen muss, um dann einzelne Bits zu
manipulieren, um dann anschließend schreiben zu können, vergehen ja
automatisch mehr Takte als im Fall von A, wodurch eine Asymmetrie
entsteht. Diese auszugleichen würde "nops" bei Port A erfordern, welche
ich im Vorfeld so ja gar nicht bestimmen kann. Die Idee das jedes
manuell machen zu müssen gefällt mir nicht.
Ähnlich verhält es sich bei der Ausgabe von Werten: Während ich bei Port
A "blind" schreiben kann, muss ich Port B erst einlesen, einzelne Bits
manipulieren, und wieder den ganzen Port ausgeben.
Wie handhabt man das am Besten? Bzw. wie macht ihr das?
Mit freundlichen Grüßen,
ich bin selbst Anfänger was MCs betrifft, aber es bleibt wohl nichts
anderes übrig als Bitmanipulation, wenn keine Pins verschwendet werden
sollen.
und wenn die Befehle gleich lang sein sollen, würde ich einfach bei
beiden Bitmanipulation machen
>Ähnlich verhält es sich bei der Ausgabe von Werten: Während ich bei Port>A "blind" schreiben kann, muss ich Port B erst einlesen, einzelne Bits>manipulieren, und wieder den ganzen Port ausgeben.
Nö, einzelne Bits kannst du zumindest bei PORTB über cbi, sbi setzen.
Bei PORTF sieht das dann wieder anders aus;)
spess53 schrieb:> Welcher Controller?
Das wollte ich eigentlich ganz unabhängig vom Controller machen. Zur
Zeit benutze ich einen ATmega88, davor war ein ATmega8515 in Benutzung,
welcher sich aber z.B. nicht über "PINx" togglen lässt.
Ich hab ganz einfach folgendes Problem: Ich möchte meine "Konstanten"
unabhängig vom Port benutzen, d.h. unabhängig davon wo LED1 letztendlich
angeschlossen ist, möchte ich diese umschalten können ohne wissen zu
müssen an welchem PORT diese letztendlich hängt. Ist dies irgendwie
möglich?
Mein Code sieht jetzt (ungefähr) so aus:
Aktuell habe ich halt das Problem, dass ich wissen muss, dass LED9 an
Port LED_PORT2 (=PORTC) hängt.
Damit kann ich mir das "Herausziehen" des Portnamens aber auch sparen.
Oder mache ich hier irgendwas systematisch falsch?
Mit freundlichen Grüßen,
Mini Float schrieb:> Und die Befehle dazu eben "zu Fuß" einzeln.
Dann brauch ich aber 9 Takte, um LEDs zu schalten, welches sich in zwei
Takten lösen lassen würde.
Wird aber wohl nichts anderes übrig bleiben :(.
>Ich hab ganz einfach folgendes Problem: Ich möchte meine "Konstanten">unabhängig vom Port benutzen, d.h. unabhängig davon wo LED1 letztendlich>angeschlossen ist, möchte ich diese umschalten können ohne wissen zu>müssen an welchem PORT diese letztendlich hängt. Ist dies irgendwie>möglich?
Nein, du musst immer wissen an welchem Port sie angeschlossen ist.
Was soll so eine blödsinnige Anforderung?
Wenn du deine Leds an einem beliebigen Port frei konfigurierbar
anschliessen möchtest dann musst du dir Macros basteln. Du kommst
aber nicht drum herum festzulegen an welchem Port und welcher Pin.
DDR musst du ja auch entsprechend setzen. Bei einigen Ports gehts dann
eben über cbi, sbi, bei anderen Ports nicht. Und schneller wird es
auch nicht weil 8 Bits über cbi, sbi zu bearbeiten langsamer ist als
8 Bits an einem Port auf einmal zu setzen.
holger schrieb:> dann musst du dir Macros basteln.
Wie würde ein solches Makro denn aussehen? Gibt es z.B. eine Möglichkeit
sich die Angabe des DDRD zu "sparen". Das ist ja in gewissermaßen
redundant, wenn ich bereits den Port weiß.
Unlucky2012 schrieb:> Das ist ja in gewissermaßen> redundant, wenn ich bereits den Port weiß
Schau mal ins Datenblatt rein: die Register zum Steuern eines Ports
liegen immer in einem festen Schema. Vom DDR kommt man relativ mit einer
Addition immer zum zugehörigen PIN- oder PORT-Register.
mfg mf
Unlucky2012 schrieb:> Wenn ich aber LED_PORT2 erst einlesen muss, um dann einzelne Bits zu> manipulieren, um dann anschließend schreiben zu können, vergehen ja> automatisch mehr Takte als im Fall von A, wodurch eine Asymmetrie> entsteht. Diese auszugleichen würde "nops" bei Port A erfordern, welche> ich im Vorfeld so ja gar nicht bestimmen kann. Die Idee das jedes> manuell machen zu müssen gefällt mir nicht.
Gehts dir mehr ums Prinzip oder um den konkreten Fall?
Denn im konkreten Fall ist das ein deutlicher Hinweis darauf, dass deine
Cube-Ansteuerung fehlerhaft ist. Ein Multiplexing, welches in Schleifen
mit Wartezeiten läuft (und nur dann ist der Ausgleich mittels NOP
eventuell notwendig), ist grundlegend falsch.
Multiplexing macht man mit einem Timer und einer ISR, die die
Refreshzeit steuert. Und dann spielt es auch keine Rolle mehr, ob du auf
den einen Port schneller und auf den anderen etwas langsamer zugreifen
kannst oder nicht.
> Wie handhabt man das am Besten? Bzw. wie macht ihr das?
Genauso wie du auch:
Programme auf dieser Ebene sind immer hardwarespezifisch. Je nachdem wie
und wo die Hardware angeschlossen ist, kommen andere Techniken zum
Einsatz. Abgesehen von ganz wenigen Ausnahmen, ist es dabei nie
erforderlich CPU-Takte abzuzählen bzw. auf den einzelnen Takt
anzugleichen. Timingsachen werden mittels Timer erledigt.
> Aktuell habe ich halt das Problem, dass ich wissen muss,> dass LED9 an Port LED_PORT2 (=PORTC) hängt.>> Damit kann ich mir das "Herausziehen" des Portnamens aber auch> sparen.
Dieses Argument sticht nicht.
Denn dadurch, dass du den Dingen einen 'Namen' gibst, ermöglichst du
eine 'Identifizierung' im Code.
Ein
sbi ERROR_LED_PORT, ERROR_LED
erzählt einem Lesenden sehr viel mehr als ein schnödes
sbi PORTC, PC0
Im ersten Fall brauch ich nicht wirklich einen Kommentar um zu wissen,
dass es um die Error LED geht. Im zweiten Fall schon. Und jeder
Kommentar, der wegfallen kann, weil mir der Code selber das Wissenswerte
erzählt, ist ein guter Kommentar. Dementsprechend ist jede Codeänderung,
die zum Wegfallen von Kommentaren führt eine gute Änderung.
Zum anderen hab ich den Vorteil, dass ich bei Hardware-Änderungen das
Wissen um den konkreten Anschluss der Error LED an einer Stelle
konzentriert habe. Ich brauch dann nur an einer Stelle die Konfiguration
verändern und hab damit den hardwareseitigen Umzug in 0 komma Nix
fehlerfrei erledigt. Ist die Bezeichnung nicht heruasgezogen, muss ich
quer durchs ganze Programm und alle Portzugriffe ändern.
Voraussetzung ist natürlich, dass ich das Setzen bzw. Löschen der LED so
universell wie möglich mache. Was im diesem speziellen Fall aber kein
Problem darstellt.
Mini Float schrieb:> Schau mal ins Datenblatt rein: die Register zum Steuern eines Ports> liegen immer in einem festen Schema. Vom DDR kommt man relativ mit einer> Addition immer zum zugehörigen PIN- oder PORT-Register.
Ja, nur ist das nicht unbedingt besonders übertragbar - selbst zwischen
verschiedenen AVRs.
Karl Heinz Buchegger schrieb:> Gehts dir mehr ums Prinzip oder um den konkreten Fall?
Sowohl als auch ;).
Karl Heinz Buchegger schrieb:> Multiplexing macht man mit einem Timer und einer ISR, die die> Refreshzeit steuert. Und dann spielt es auch keine Rolle mehr, ob du auf> den einen Port schneller und auf den anderen etwas langsamer zugreifen> kannst oder nicht.
Dann verstehe ich wohl etwas falsch. Mal angenommen mein Timer erzeugt
jede Millisekunde einen Interrupt. Es macht ja jetzt einen Unterschied,
ob ich innerhalb der ISR LEDs direkt ein und ausschalten kann (indem ich
blind schreibe, oder ob manche LEDs eine Extrabehandlung in Form von
Abfrage und Bitmanipulation brauchen. Klar kann man mit geeignete Werten
für den Timer verhindern, dass es einen sichtbaren Effekt hat, aber eine
perfekte Lösung sieht halt anders aus und zwar so, dass jede LED egal an
welchem Port gleichwertig ist. Ich werde wohl tatsächlich dazu übergehen
das Ganze so zu definieren:
1
.equ LED1_PORT = PORTD
2
.equ LED1_DDR = DDRD
3
.equ LED1 = PD0
Dann steuere ich jede LED einzeln an. Das braucht halt ein paar Takte
mehr, als alle gleichzeitig zu schalten, aber scheinbar gibt es keine
bessere Möglichkeit.
Das einzige was mich noch extrem stört ist halt diese Redundanz zwischen
PORT und DDR. Da wird es wohl tatsächlich auf ein Makro hinauslaufen.
Wenn ich die entsprechende Include Datei gerade richtig interpretiere,
dann ist das DDRx Register immer um eins "kleiner" als das PORTx
Register. Das PINx Register ist um zwei "kleiner".
Bin ich hier zu perfektionistisch, oder wie seht ihr das? Redundanz im
Quellcode ist ja eigentlich immer ein Zeichen dafür, dass man etwas
vereinfachen kann. Und diese Redundanz zwischen DDRx, PORTx und PINx
dürfte ja auch die C-Programmierer unter euch betreffen.
Karl Heinz Buchegger schrieb:> Dieses Argument sticht nicht.> Denn dadurch, dass du den Dingen einen 'Namen' gibst, ermöglichst du> eine 'Identifizierung' im Code.
Ja, nur wenn ich wissen muss, dass LED9 an LED_PORT2 angeschlossen ist,
dann ist mein Code halt ganz schön unflexibel, weil eine einfache
Änderung der Belegung im Header unter Umständen nicht funktioniert.
Immerhin ist der Zusammenhang zwischen LED9 und PORT2 dann wirklich hart
einkodiert. Das gefällt mir einfach nicht.