Forum: Compiler & IDEs Registernamen/Variablennamen erstellen


von Michael S. (michi88)


Lesenswert?

Hallo,

Ich habe vor aus einem char-Array den letzten Buchstaben an einen String 
zu hängen, um zum Beispiel aus "PORTD" "DDRD" abzuleiten.

Das habe ich bis jetzt so versucht.
1
void init_move_out(volatile char *port) {
2
3
  char* test = "DDR";
4
  char* append = port[4]; 
5
  volatile char* data_direction = strcat(test,append);
6
7
  data_direction = (1 << 0)
8
9
}
10
11
Aufruf --> init_move_out(&PORTD);

char* append = port[4];
Hier bin ich mir nicht ganz sicher, ob das übergebene &-Zeichen mitzählt 
oder nicht. Ich vermute mal nicht, deswegen 4 -> fünftes Element (D).

Ich weiß schlussendlich nicht, wie ich data_direction erkläre, dass es 
jetzt kein string mehr ist, sondern ein Registername.

Ich hoffe ihr versteht was mein Problem ist. :S

gruß michi

von tobi (Gast)


Lesenswert?

PORTD ist aber kein String

von Michael S. (michi88)


Lesenswert?

Wie könnte ich daraus einen String machen oder es ggf. als String 
übergeben und dann in einen entsprechenden Typ, der mir noch nicht 
bekannt ist umwandeln?

von Oliver (Gast)


Lesenswert?

>Ich weiß schlussendlich nicht, wie ich data_direction erkläre, dass es
>jetzt kein string mehr ist, sondern ein Registername.

Gar nicht.

DDRx, PORTx, etc, sind Pointer auf Adressen. Wie genau die aussehen, 
kannst du in io.h und den dazugehörigen headersn für deinen AVR ansehen.

Es bleibt dir da nur übrig, diese in Abhängigkeit des übergebenen Namens 
zuzuweisen.

Dazu kommt:
1
char* test = "DDR";
2
char* append = port[4]; 
3
volatile char* data_direction = strcat(test,append);

ist ein klassischer Fehler bei der String-Behandlung. Wie schreibt 
wikipedia so schön:

>strcat can be dangerous because if the string to be appended is too long to fit 
>in the destination buffer, it will overwrite adjacent memory, invoking undefined 
>behavior.

Oliver

von Ahem (Gast)


Lesenswert?

@ Michael S.

Du hast hier ein grundsätzliches Problem mit Kategorien.

Unter einem Port verstehst Du hier einen String "PORTD",
wie Du unter einem Datenrichtungsregister einen String "DDRD" verstehst.

Das sind aber wie schon gesagt einfach nur Strings, Zeichketten, Folgen 
von ASCII-Zeichen im Speicher.

Ein Port wird auch noch als eine Identifikation von elektrischen 
Leitungen verstanden. OK.

Wesentlich ist hier aber auch, das ein Port letzlich als eine Zahl 
verstanden wird. Diese Zahl, sagt man, ist die Identifikation, des 
Ports, seine Adresse, wie eine Hausnummer.

Je nach dem C-Environment, in dem Du arbeitest, wird oft ein Port z.B. 
als variable mit Namen "PORTD" repräsentiert. Variablennamen selbst, 
also die Zeichenkette "PORTD" sind aber, nicht in C-Programmen in 
Ausdrücken so zu verwenden, das ein Rückbezug auf eine Variable erfolgen 
kann.

Sei also ein String char * n = "PORTD", dann gibt es keine (simple) 
Möglichkeit ihn so zu manipulieren, das er als Variablenname verwendet 
werden kann.

Allerdings wird die Variable PORTD eine Adresse haben, die Du auswerten, 
bzw. als Ergebnis anderer Ausdrücke berechnen kannst.

Es käme auch darauf an, was für einen Compiler Du verwendest.

von Michael S. (michi88)


Lesenswert?

Auszug aus der iom8.h:
1
/* Port D */
2
#define PIND  _SFR_IO8(0x10)
3
#define DDRD  _SFR_IO8(0x11)
4
#define PORTD  _SFR_IO8(0x12)
5
6
/* Port C */
7
#define PINC  _SFR_IO8(0x13)
8
#define DDRC  _SFR_IO8(0x14)
9
#define PORTC  _SFR_IO8(0x15)
10
11
/* Port B */
12
#define PINB  _SFR_IO8(0x16)
13
#define DDRB  _SFR_IO8(0x17)
14
#define PORTB  _SFR_IO8(0x18)

Heißt das, dass ich jetzt abfragen einbauen muss? Ala
1
if (*port == PORTD)
2
   DDR = &DDRD;
(evtl. auch mit ner switch-schleife)

ps: verwende avrstudio mit winavr
ps2: wie kann man mit solchen Variablennamen (Registern?) denn rechnen?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Vielleicht erklärst du mal, was du machen willst, also wozu du das 
brauchst.

Willst du indirekt auf ein SFR zugreifen? Oder Identifier zusammenbauen, 
die die Register eines Port bündeln (DDRx, PORTx, PINx)? Oder was?

von Ahem (Gast)


Lesenswert?

>Heißt das, dass ich jetzt abfragen einbauen muss?

Nein.
1
if (*port == PORTD)
2
   DDR = &DDRD;

Ich habe Dir doch auch geschrieben, das ein String nichts unmittelbar 
mit der Identifikation, zu der hier eine numerische Konstante dient, zu 
tun hat

Du hast mit *port ein Zeichen. Was ist das für ein Zeichen?
PORTD ist eine numerische Konstante. Was ist das denn für eine 
Konstante?

&DDRD ist sinnlos, da DDRD ja eine Konstante ist. Sie kann keine Adresse 
haben. Übrigens ist diese Konstante zufällig genau die Adresse.

Und was soll DDR sein?

Schreib doch mal was Du überhaupt tun willst.

von Ahem (Gast)


Lesenswert?

Ist Dir klar was
1
#define PORTC  _SFR_IO8(0x15)

bedeutet?

von Michael S. (michi88)


Lesenswert?

Ahem schrieb:
>Ist Dir klar was
>
>#define PORTC  _SFR_IO8(0x15)
>
>bedeutet?

Nein, leider nicht.
Was bedeutet eigentlich SFR?


Johann L. schrieb:
> Vielleicht erklärst du mal, was du machen willst, also wozu du das
> brauchst.
>
> Willst du indirekt auf ein SFR zugreifen? Oder Identifier zusammenbauen,
> die die Register eines Port bündeln (DDRx, PORTx, PINx)? Oder was?

Eigentlich will ich nur einen Parameter in meiner Funktion einsparen:
1
void init_move_out(volatile char *port) {
2
  volatile char *DDR;
3
4
  if (*port == PORTB)
5
    DDR = &DDRB;
6
7
  if (*port == PORTC)
8
    DDR = &DDRC;
9
10
  if (*port == PORTD)
11
    DDR = &DDRD;
12
13
14
  // 5 ausgänge am µC
15
  *DDR  = (1 << 0)  // Serialdata OUT
16
      | (1 << 1)  // Serialdata CLOCK
17
      | (1 << 2)  // RESET
18
      | (1 << 3)  // Latch CLOCK
19
      | (1 << 4);  // Output ENABLE
20
21
  *port &= ~(1 << RST);  // Reset
22
  *port |= (1 << RST);  
23
24
  *port |= (1 << LAT);
25
  *port &= ~(1 << LAT);  
26
27
  *port &= ~(1 << EN); // Enable
28
29
}

Es handelt sich hier um eine (noch nicht fertige) Funktion, mit der 
verschiedene Portpins (quasie) initialisiert werden, um dann ein 
Schieberegister damit anzusteuern.
Das soll natürlich möglichst dynamisch ausgeführt sein, damit man dann 
später zum Vorteil des Routings auf der Platine, die Pins möglichst 
variabel nutzen kann.

von Ahem (Gast)


Lesenswert?

DDRB ist eine Konstante. Schrieb ich aber oben schon.

Übergib als *port eben _SFR_IO8(0x18), dann geht es.

Schaue Dir mal die Konstanten an, DDRx ist immer eins niedriger als 
PORTx und PINx immer zwei niedriger.

Schau aber nochmal, was _SFR_I08 macht.

von Ahem (Gast)


Lesenswert?

Das sollte eigentlich garnicht kompilieren, denke ich.

von Michael S. (michi88)


Lesenswert?

Doch :S
Und es funktioniert auch noch aufm mega8.

Was bedeutet denn jetzt SFR?
Im Datasheet ist meine Suche erfolglos.

Ich geh jetzt aber ins Bett.
Vielen Dank für die Hilfe.

Bin noch sehr interessiert daran, diese Thematik zu verstehen!
Also bitte nicht mit dem Erklären aufgeben ;)

von Ahem (Gast)


Lesenswert?

SFR = Special Function Register

von Oliver (Gast)


Lesenswert?

SFR ist ein macro, mit etwas Suchen in den headern der avr-libc findest 
du da die Definition.

Zu den Port-Adressen gibt auch einen Beitrag in der avr-libc-FAQ.

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

Oliver

von Sven P. (Gast)


Lesenswert?

Michael S. schrieb:
> [...]

Beschäftige dich mal mit dem C-Präprozessor. Der kennt nämlich so einen 
Token-Paste-Operator, damit geht z.B.:
1
#define IN(p)  PIN##p
2
3
IN(A) /* wird zu PINA */

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Michael S. schrieb:

> Johann L. schrieb:
>> Vielleicht erklärst du mal, was du machen willst, also wozu du das
>> brauchst.
>>
>> Willst du indirekt auf ein SFR zugreifen? Oder Identifier zusammenbauen,
>> die die Register eines Port bündeln (DDRx, PORTx, PINx)? Oder was?
>
> Es handelt sich hier um eine (noch nicht fertige) Funktion, mit der
> verschiedene Portpins (quasie) initialisiert werden, um dann ein
> Schieberegister damit anzusteuern.
> Das soll natürlich möglichst dynamisch ausgeführt sein, damit man dann
> später zum Vorteil des Routings auf der Platine, die Pins möglichst
> variabel nutzen kann.

Dynamisch braucht es wohl nicht zu sein, weil zur Laufzeit des 
Programmes sich diese Ports nicht ändern, denn die Pins sind durch die 
Hardware und das Platinenlayout festgelegt.

Was du wohl haben willst ist, daß in der Software zur Compilezeit an 
einer zentralen Stelle die Port-Zuordningen getroffen werden, so daß 
nicht an X Stellen in der Anwendung die Zuordnungen geändert werden 
müssen.

Deine Port-Funktion ist insofern ungünstig, als daß sie in ineffizientem 
Code resultiert (falls das überhaupt von Belang ist).

Eine Lösung für die Portzuornungen ist ein Header für die Anwendung zu 
schreiben, in dem die Ports zugeordnet werden à la

*ports.h*
1
#ifndef PORTS_H
2
#define PORTS_H
3
4
#include <avr/io.h>
5
6
#define LED_PORT PORTB
7
#define LED_PIN  PINB
8
#define LED_DDR  DDRB
9
#define LED_PAD  PB5
10
11
#endif /* PORTS_H */

Das ist dann zwar nicht eine Stelle, sondern es sind 4, aber es löst 
dein Problem, recht einfach eine Pinbelegung umzurouten.

In der Anwendung schreibt man dann
1
#include "ports.h"
2
3
int main (void)
4
{
5
    // LED als Ausgang
6
    LED_DDR |= 1 << LED_PAD;
7
8
    // Main Loop
9
    while (1)
10
    {
11
        // LED an
12
        LED_PORT |= 1 << LED_PAD;
13
14
        // LED aus
15
        LED_PORT &= ~(1 << LED_PAD);
16
    }
17
}

Bei einer Neuverdrahtuns muss das natürlich neu übersetzt werden, aber 
diese Arbeit überlässt du besser dem Compiler als dem µC zur Laufzeit.

In meinen Anwendungen benutze ich Makros, die es ermöglichen, einen Port 
an einer Stelle in der Anwendung festzulegen. Aber das ist ne 
Makro-Schlacht, die man nicht wirklich sehen will...

Johann

von Simon K. (simon) Benutzerseite


Lesenswert?

Ahem schrieb:
>>Heißt das, dass ich jetzt abfragen einbauen muss?
>
> Nein.
>
1
> if (*port == PORTD)
2
>    DDR = &DDRD;
3
>

Wohl eher
1
if (port == &PORTD)
2
    DDR = &DDRD;

von Jörg G. (joergderxte)


Lesenswert?

Wenn die Ports zur Compilezeit feststehen, ist gjlayde's Lösung 
natürlich die beste.
 Falls das nicht so ist, geht auch sowas:
1
enum ioPorts = {IO_A, IO_B, IO_D};
2
3
int16_t foo(enum ioPorts port)
4
{
5
  switch (port) 
6
  {
7
  case IO_A:
8
    DDRA = //...
9
    PORTA = 
10
    break;
11
  case IO_B:
12
    DDRB = //...
13
    PORTB = 
14
    break;
15
  //...
16
  }
17
  //...
18
}
19
/* ... main ... */
20
foo(IO_B);
Ein 2D-Array aus Pointern ginge wohl auch - AVRs haben aber i.d.R. nicht 
genug ports, damit sich das lohnte ;)
hth, Jörg

von Karl H. (kbuchegg)


Lesenswert?

Ich hab auch schon Programme gesehen, die es ausnutzen, das die 
Adressreihenfolge anscheinend immer
  PINx
  DDRx
  PORTx
ist. Ich weiss aber nicht, ob das auf allen AVR so ist.

Die entsprechenden Funktionen haben dann so ausgesehen
1
void foo( volatile unsigned char * Port )
2
{
3
  volatile unsigned char * Ddr = Port - 1;
4
5
  *Ddr = 0xFF;    // alles auf Ausgang
6
  *Port = 0xFF;   // und alle Ausgnänge auf High
7
}
8
9
int main()
10
{
11
  foo( &PORTB );
12
}


Frage in die Runde: Würdet ihr das Wissen über die Adreslage benutzen?
Für mich gesprochen: Ich bin unsicher, ob das so schlau ist. Auf der 
einen Seite ist man doch Atmels Kompatibilitätsgedanken ausgeliefert, 
auf der anderen Seite wird Atmel das wahrscheinlich nicht mehr ändern.

von Sven P. (Gast)


Lesenswert?

Gegenfrage:
1
void init_move_out(volatile char *port) {
2
3
  char* test = "DDR";
4
  char* append = port[4]; 
5
  volatile char* data_direction = strcat(test,append);
6
7
  data_direction = (1 << 0)
8
}

Mal gesetzt den Fall, dass das funktioniert -- welchen Sinn hat es?! 
Könnte man doch wunderbar mit nem Zeigerarray lösen.


Und zur Adresslage: Jo, würde ich nutzen. Das machst du unter Linux beim 
hardwarenahen Zugriff auf den Parallelport ja genauso, eigentlich noch 
schlimmer.

von Karl H. (kbuchegg)


Lesenswert?

Sven P. schrieb:

> Mal gesetzt den Fall, dass das funktioniert -- welchen Sinn hat es?!
> Könnte man doch wunderbar mit nem Zeigerarray lösen.

Die Idee die der TO hatte ist mir schon klar und ich kann mich durchaus 
in seine Lage versetzen.

Er will in seinem Pgm einfach nur PORTB schreiben und sich nicht um das 
zugehörige DDR Register kümmern müssen, das soll das Programm von 
alleine rausfinden.

Gut. Passiert ist es mir noch nie, aber nach sowas
1
#define LED_PORT   PORTA
2
#define LED_DDR    DDRB

dürfte man wohl eine Weile suchen. Das ist der typische Fehler, den der 
entwickelnde Programmierer selbst nie sieht bis dann ein Anderer 
hinzukommt und in 2 Sekunden mit dem Finger drauf deutet :-)

> Und zur Adresslage: Jo, würde ich nutzen. Das machst du unter Linux beim
> hardwarenahen Zugriff auf den Parallelport ja genauso, eigentlich noch
> schlimmer.

Mal schaun wo mich das hinführen wird.
Wenn ich mich recht erinnere, hab ich diese Adresslagenausnutzung das 
erste mal bei P.Fleury in seiner LCD Lib gesehen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Ich hab auch schon Programme gesehen, die es ausnutzen, das die
> Adressreihenfolge anscheinend immer
>   PINx
>   DDRx
>   PORTx
> ist. Ich weiss aber nicht, ob das auf allen AVR so ist.

Ist es nicht. ZB wenn es einen PORTE, PORTF, PORTG gibt, sind die SFRs 
nicht mehr in der Reihenfolge.

Man könnte aber ne Port-Struktur machen wie
1
#include <stdint.h>
2
#include <avr/io.h>
3
4
typedef struct
5
{
6
    volatile uint8_t * port;   
7
    volatile uint8_t * pin;   
8
    volatile uint8_t * ddr;   
9
    uint8_t pad;
10
} port_t;
11
12
port_t port_led = 
13
{
14
    .port = &PORTG,
15
    .pin  = &PING,
16
    .ddr  = &DDRG,
17
    .pad  = PG5
18
};
19
20
void foo (port_t * port)
21
{
22
    port->ddr |= (1 << port->pad);
23
}
24
25
void bar (void)
26
{
27
    foo (&port_led);
28
}

Aber ob das ist, was man will...?

Es ist sauineffizient und zu Zugriffe nicht atomar, und braucht ewig 
viel Platz.

Johann

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Ich verwende in meinen Projekten ein Enum für die Ports, so wie im 
angehängten Projekt in ports.h.

Verwendet wird das dann über die Makros
1
   if (IS_SET (PORT_EINER))
2
   {
3
        MAKE_OUT (PORT_ZEHNER);
4
        SET (PORT_ZEHNER);
5
   }
6
   else
7
   {
8
        MAKE_IN (PORT_BEEPA);
9
        CLR (PORT_BEEPA);
10
   }

Vorteile
-- Nur eine Stelle für die Portzuordnung
-- Man kann auch Pseudo-Ports verwenden, die etwa im RAM liegen
   und die per SPI an Portexpander ausgegeben werden sollen.
-- Keine Performance-Einbußen (bei Optimierung)

Nachteile
-- Komplexe, undurchschaubare Makros
-- Ohne Optimierung (-O0) ist das nicht nutzbar, weil die
   verwendeten Makros nicht zu einer asm-Instruktion kollabieren.

Der Code in der Anwendung ist ansonsten unabhängig von dem eingesetzten 
µC-Derivat formuliert bzw. der Generierungsvariante "ATtiny24" oder 
"ATtiny2313", zumindest was die Port-Zuordnungen angeht. (Für 
sleep-Modes etc. sind Target-abhängige Aktionen notwendig).

Bei Änderungen des Layouts — auch beim Umrouten eines Ports zwischen 
Port-Expander und Hardware-Port — bleibt die Formulierung auf eine Zeile 
in ports.h beschränkt.
1
#ifndef _PORTS_H_
2
#define _PORTS_H_
3
4
#include <avr/io.h>
5
#include <avr-port-macros.h>
6
#include <avr-port-enum.h>
7
8
#if defined (__AVR_ATtiny2313__)
9
enum
10
{
11
  PORT_ZEHNER = PORTD_2, // INT0
12
  PORT_EINER  = PORTD_3, // INT1
13
  
14
  PORT_BEEPA = PORTB_2,
15
  PORT_BEEPB = PORTD_5,
16
17
  [...]  
18
  
19
  PORT_NIL
20
};
21
#endif // ATtiny2313
22
23
#if defined (__AVR_ATtiny24__)
24
enum
25
{
26
  PORT_ZEHNER = PORTA_1, // PC1  
27
  PORT_EINER  = PORTA_0, // PC0
28
  
29
  PORT_BEEPA = PORTA_6, // A3, MOSI
30
  PORT_BEEPB = PORTA_5, // MISO
31
32
  // [...]
33
  
34
  PORT_NIL
35
};
36
#endif // ATtiny24
37
38
/*****************************************************************/
39
40
#endif /* _PORTS_H_ */

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.