Forum: Mikrocontroller und Digitale Elektronik Atmega Dynamische Portzuweisung in Schleife


von Hannes J. (hannes_j)


Lesenswert?

Hi!

ich bastle gerade etwas mit einem Atmega2560 in c++ herum. Ich möchte 
gern in einer for-Schleife die Ports beschreiben.
Also mit
1
i=0    PORTA |= (....);
2
i=1    PORTC |= (....);
3
i=2    PORTD |= (....);
4
...

Der Übersichtlichkeit halber habe ich die entsprechenden Ports definiert 
als
1
#define CC0  PORTA
2
#define CC1  PORTC
3
#define CC2  PORTD
4
...
und hatte auf eine Möglichkeit gehofft, in der Schleife jetzt dynamisch 
a la CCi zuzugreifen. Aber die Möglichkeit gibt es ja in c++ nicht. Bin 
aber überfordert ob man einen Pointer auf PORTA setzen kann? Was sagt 
die Schwarmintelligenz?

von Oliver S. (oliverso)


Lesenswert?

Ja, man kann. Schließlich verbirgt sich hinter den PORTn-Defines nichts 
anderes als ein Pointer. Schau dir die Definition im Header an. Das 
nutzt dir aber letztendlich nicht viel, weil die Port-Register nicht 
direkt hintereinander im Adressraum angeordnet sind.

Am saubersten ist da ein switch oder ein if/else if, oder aber ein Array 
mit den Port-Pointern mit Zugriff über den Index.

Oliver

: Bearbeitet durch User
von Hannes J. (hannes_j)


Lesenswert?

Hmm, ich hatte das _SFR_IO8(0X02) schon gesehen aber kann damit nicht 
viel anfangen. Eine switch-Anweisung wird da halt übel ausladend bei 7 
Ports. Hätte das echt gern als Schleife gemacht.

Edit: Ah also das _SFR_IO8(0X02) in ein Array legen und dann auf die 
Adresse zugreifen geht? Dann werd ich damit mal rumprobieren.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

> Eine switch-Anweisung wird da halt übel ausladend bei 7 Ports.

Ja, aber alle anderen Methoden sind unterm Strich auch nicht eleganter.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Hannes J. schrieb:
> ich hatte das _SFR_IO8(0X02) schon gesehen aber kann damit nicht viel
> anfangen.
Das ist auch nur ein Makro. Und das Drama löst sich in der sfr_defs.h 
(ein Stück weit) auf:
1
    #define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
Jetzt musst du nur noch das Makro _MMIO_BYTE() auflösen und den 
__SFR_OFFSET suchen, und du kannst dann daraus die Adresse berechnen.

Oder im Netz nach diesen Makros suchen. Diese Frage hatten schon einige 
vor dir:
https://www.avrfreaks.net/forum/how-does-macro-mmioword-actually-work
Und zum Schluss findest du heraus, dass das "nur" eine RAM-Adresse ist, 
die du sogar im Datenblatt wiederfindest...  ;-)

Stefanus F. schrieb:
> Ja, aber alle anderen Methoden sind unterm Strich auch nicht eleganter.
Man könnte mit dem Index über eine Pointertabelle fahren...

: Bearbeitet durch Moderator
von Hannes J. (hannes_j)


Lesenswert?

Lothar M. schrieb:
> Das ist auch nur ein Makro. Und das Drama löst sich in der sfr_defs.h
> (ein Stück weit) auf:
>
1
>     #define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
2
>
> Jetzt musst du nur noch das Makro _MMIO_BYTE() auflösen und den
> __SFR_OFFSET suchen, und du kannst dann daraus die Adresse berechnen.
>
> Oder im Netz nach diesen Makros suchen. Diese Frage hatten schon einige
> vor dir:
> https://www.avrfreaks.net/forum/how-does-macro-mmioword-actually-work
> Und zum Schluss findest du heraus, dass das "nur" eine RAM-Adresse ist,
> die du sogar im Datenblatt wiederfindest...  ;-)
> [...]
> Man könnte mit dem Index über eine Pointertabelle fahren...


Ah danke für die erhellende Antwort! Damit kann ich mich doch mal 
außeinandersetzen. :)

von Peter D. (peda)


Lesenswert?

Hannes J. schrieb:
> Eine switch-Anweisung wird da halt übel ausladend bei 7
> Ports. Hätte das echt gern als Schleife gemacht.

Switch kann man auch in einer Schleife aufrufen.
Du kannst Ports über deren Adresse ansprechen, nur wird das viel 
aufwändiger als ein Switch, da die Adressen 16bittig sind.
Auch sind die Ports nicht hintereinander angeordnet, d.h. Du must Dir 
die Adresse erst aus einem Array der Portadressen holen.
Bei nur 7 Case ist ein Switch daher schneller und weniger Code.

von Hannes J. (hannes_j)


Lesenswert?

Peter D. schrieb:
>
> Switch kann man auch in einer Schleife aufrufen.

Schon klar, mit Schleife brauche ich aber keine switch mehr und mit 
switch keine Schleife.

> Du kannst Ports über deren Adresse ansprechen, nur wird das viel
> aufwändiger als ein Switch, da die Adressen 16bittig sind.
> Auch sind die Ports nicht hintereinander angeordnet, d.h. Du must Dir
> die Adresse erst aus einem Array der Portadressen holen.
> Bei nur 7 Case ist ein Switch daher schneller und weniger Code.

Dazu ein paar Fragen..
Genau das ist was ich machen will, warum meinst du sollte das mit 
Schleife aufwändiger werden als switch? Wenn ich die Adressen in ein 
Array packe kann ich das doch in der Schleife durchgehen, 
dereferenzieren und ich hab was ich will? Ob 8 oder 16 Bit macht doch 
keinen Unterschied, außer dass ich uint16_t* schreibe anstatt uint8_t*? 
Eine switch kommt mir hier fehl am Platze vor, weil im Prinzip in jedem 
Port die gleiche Operation erfolgt, nur mit unterschiedlichen Daten. Ich 
hätte also 14x den gleichen Befehl anstatt 2x.

Was mich jetzt noch verwirrt: Die Adressen der meisten Ports haben 8 
Bit. Erst ab Port H sind es mehr. Port H hat bspw. die Adresse 0x102. Am 
Ende der Macro-Kette werden die aber alle gecasted auf (*(volatile 
uint8_t *)(mem_addr)). Da wird doch die führende 1 abgeschnitten? Warum 
dann überhaupt 0x102?

von Oliver S. (oliverso)


Lesenswert?

Die Größe eines Pointers in C ist unabhängig davon, auf was der zeigt.

Oliver

von Hannes J. (hannes_j)


Lesenswert?

Ja ist mir auch gerade aufgefallen. Vor 12 klappt das mit dem denken 
noch nicht so ;-)

Sollte doch dann so funktionieren?:
1
volatile uint8_t * _ptrList[7];
2
// Fill pointer list
3
_ptrList[0] = &PORTA;
4
_ptrList[1] = &PORTB;
5
_ptrList[2] = &PORTD;
6
_ptrList[3] = &PORTE;
7
_ptrList[4] = &PORTF;
8
_ptrList[5] = &PORTH;
9
_ptrList[6] = &PORTJ;
10
11
// Assign row
12
for(uint8_t i=0; i<7; i++)
13
{
14
  *_ptrList[i] = prop.getRow(i);
15
  [...]
16
}

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Du hast Dir die Makros immer noch nicht angesehen.

Oliver

von Axel S. (a-za-z0-9)


Lesenswert?

Hannes J. schrieb:
> Wenn ich die Adressen in ein
> Array packe kann ich das doch in der Schleife durchgehen

Genauso würde ich das machen.
1
volatile uint8_t* ports[] =
2
{
3
  &PORTA,
4
  &PORTC,
5
  &PORTD,
6
  &PORTE,
7
  /* ... */
8
}
9
10
for (uint8_t i=0; i<(sizeof(ports)/sizeof(ports[0]); i++)
11
{
12
  *(ports[i]) |= /* ... */;
13
}

Wie man drauf kommt? Man liest die Dokumentation:

https://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass

von Hannes J. (hannes_j)


Lesenswert?

Oliver S. schrieb:
> Du hast Dir die Makros immer noch nicht angesehen.
>
> Oliver

Um ehrlich zu sein, doch..

Axel S. schrieb:
> Genauso würde ich das machen.
>
>
1
> volatile uint8_t* ports[] =
2
> {
3
>   &PORTA,
4
>   &PORTC,
5
>   &PORTD,
6
>   &PORTE,
7
>   /* ... */
8
> }
9
> 
10
> for (uint8_t i=0; i<(sizeof(ports)/sizeof(ports[0]); i++)
11
> {
12
>   *(ports[i]) |= /* ... */;
13
> }
14
>
>
> Wie man drauf kommt? Man liest die Dokumentation:
>
> https://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass

Thx!

von Eric B. (beric)


Lesenswert?

Hannes J. schrieb:
> ich bastle gerade etwas [...] in c++ herum.
1
volatile uint8_t *portList[] = { PORTA, PORTB, PORTC, ... };
2
3
int i = 0;
4
for(const uint8_t * &port: portList) {
5
  port = prop.getRow(i++);
6
}

: Bearbeitet durch User
von Purzel H. (hacky)


Lesenswert?

Was so nebenbei nicht geht ...

 PORTA |= (....);

Bedeutet Read - modify - write

Und das geht so nicht. Denn wenn man das Port A lesen moechte muss man 
PINA lesen.

Also eher : PORTA = PINA | (..)

von Peter D. (peda)


Lesenswert?

Name H. schrieb:
> Bedeutet Read - modify - write
>
> Und das geht so nicht.

Na klar geht das. Der AVR hat sogar extra Befehle dafür (SBI, CBI).

von Oliver S. (oliverso)


Lesenswert?

Name H. schrieb:
> Was so nebenbei nicht geht ...
>
>  PORTA |= (....);
>
> Bedeutet Read - modify - write
>
> Und das geht so nicht. Denn wenn man das Port A lesen moechte muss man
> PINA lesen.

Das stimmt zwar, mit
1
PORTA |= (....);
möchte man aber nicht die Eingänge lesen, sondern einzelne Ausgänge 
setzen, ohne die schon gesetzten zu löschen.

Oliver

: Bearbeitet durch User
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.