Forum: Mikrocontroller und Digitale Elektronik Wie Ports am Besten zusammenfassen?


von Unlucky2012 (Gast)


Lesenswert?

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,

von M. M. (blackcow)


Lesenswert?

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

von holger (Gast)


Lesenswert?

>Ä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;)

von spess53 (Gast)


Lesenswert?

Hi

Welcher Controller?

MfG spess

von Unlucky2012 (Gast)


Lesenswert?

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:
1
.include "m88def.inc"
2
3
.equ LED_PORT1  = PORTD
4
.equ LED_DDR1   = DDRD
5
.equ LED_PORT2  = PORTC
6
.equ LED_DDR2   =  DDRC
7
8
.equ LED1 = PD0
9
.equ LED2 = PD1
10
.equ LED3 = PD2
11
.equ LED4 = PD3
12
.equ LED5 = PD4
13
.equ LED6 = PD5
14
.equ LED7 = PD6
15
.equ LED8 = PD7
16
.equ LED9 = PC0
17
18
19
.equ LED_LAYER1 = PC1
20
.equ LED_LAYER2 = PC2
21
.equ LED_LAYER3 = PC3
22
23
.equ LED_LAYER_DDR = PORTC
24
25
.def temp1 = r16
26
27
main:
28
29
    ldi temp1, 1 << LED1 | 1 << LED2 | 1 << LED3 | 1 << LED4 | \
30
        1 << LED5 | 1 << LED6 | 1 << LED7 | 1 << LED8
31
    out LED_DDR1, temp1
32
    
33
    in temp1, LED_DDR2    
34
    ori temp1, 1 << LED9
35
    out LED_DDR2, temp1

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,

von Achim M. (minifloat)


Lesenswert?

Machs halt so:
1
...
2
.equ LED9_PORT = PORTD
3
.equ LED9      = PD1
4
...
Und die Befehle dazu eben "zu Fuß" einzeln. Als Routinen zum An- und 
Ausknipsen natürlich.

mfg mf

von Unlucky2012 (Gast)


Lesenswert?

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 :(.

von holger (Gast)


Lesenswert?

>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.

von Unlucky2012 (Gast)


Lesenswert?

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ß.

von Achim M. (minifloat)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Unlucky2012 (Gast)


Lesenswert?

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.

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.