mikrocontroller.net

Forum: Compiler & IDEs Registernamen/Variablennamen erstellen


Autor: Michael S. (michi88)
Datum:

Bewertung
0 lesenswert
nicht 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.
void init_move_out(volatile char *port) {

  char* test = "DDR";
  char* append = port[4]; 
  volatile char* data_direction = strcat(test,append);

  data_direction = (1 << 0)

}

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

Autor: tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PORTD ist aber kein String

Autor: Michael S. (michi88)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
char* test = "DDR";
char* append = port[4]; 
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

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Michael S. (michi88)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auszug aus der iom8.h:
/* Port D */
#define PIND  _SFR_IO8(0x10)
#define DDRD  _SFR_IO8(0x11)
#define PORTD  _SFR_IO8(0x12)

/* Port C */
#define PINC  _SFR_IO8(0x13)
#define DDRC  _SFR_IO8(0x14)
#define PORTC  _SFR_IO8(0x15)

/* Port B */
#define PINB  _SFR_IO8(0x16)
#define DDRB  _SFR_IO8(0x17)
#define PORTB  _SFR_IO8(0x18)

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

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

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Heißt das, dass ich jetzt abfragen einbauen muss?

Nein.
if (*port == PORTD)
   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.

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist Dir klar was
#define PORTC  _SFR_IO8(0x15)

bedeutet?

Autor: Michael S. (michi88)
Datum:

Bewertung
0 lesenswert
nicht 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:
void init_move_out(volatile char *port) {
  volatile char *DDR;

  if (*port == PORTB)
    DDR = &DDRB;

  if (*port == PORTC)
    DDR = &DDRC;

  if (*port == PORTD)
    DDR = &DDRD;


  // 5 ausgänge am µC
  *DDR  = (1 << 0)  // Serialdata OUT
      | (1 << 1)  // Serialdata CLOCK
      | (1 << 2)  // RESET
      | (1 << 3)  // Latch CLOCK
      | (1 << 4);  // Output ENABLE

  *port &= ~(1 << RST);  // Reset
  *port |= (1 << RST);  

  *port |= (1 << LAT);
  *port &= ~(1 << LAT);  

  *port &= ~(1 << EN); // Enable

}

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.

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das sollte eigentlich garnicht kompilieren, denke ich.

Autor: Michael S. (michi88)
Datum:

Bewertung
0 lesenswert
nicht 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 ;)

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
SFR = Special Function Register

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.htm...

Oliver

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.:
#define IN(p)  PIN##p

IN(A) /* wird zu PINA */

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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*
#ifndef PORTS_H
#define PORTS_H

#include <avr/io.h>

#define LED_PORT PORTB
#define LED_PIN  PINB
#define LED_DDR  DDRB
#define LED_PAD  PB5

#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
#include "ports.h"

int main (void)
{
    // LED als Ausgang
    LED_DDR |= 1 << LED_PAD;

    // Main Loop
    while (1)
    {
        // LED an
        LED_PORT |= 1 << LED_PAD;

        // LED aus
        LED_PORT &= ~(1 << LED_PAD);
    }
}

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

Autor: Simon K. (simon) Benutzerseite
Datum:

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

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

Autor: Jörg G. (joergderxte)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn die Ports zur Compilezeit feststehen, ist gjlayde's Lösung 
natürlich die beste.
 Falls das nicht so ist, geht auch sowas:
enum ioPorts = {IO_A, IO_B, IO_D};

int16_t foo(enum ioPorts port)
{
  switch (port) 
  {
  case IO_A:
    DDRA = //...
    PORTA = 
    break;
  case IO_B:
    DDRB = //...
    PORTB = 
    break;
  //...
  }
  //...
}
/* ... main ... */
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
void foo( volatile unsigned char * Port )
{
  volatile unsigned char * Ddr = Port - 1;

  *Ddr = 0xFF;    // alles auf Ausgang
  *Port = 0xFF;   // und alle Ausgnänge auf High
}

int main()
{
  foo( &PORTB );
}


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.

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gegenfrage:
void init_move_out(volatile char *port) {

  char* test = "DDR";
  char* append = port[4]; 
  volatile char* data_direction = strcat(test,append);

  data_direction = (1 << 0)
}

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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
#define LED_PORT   PORTA
#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.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
#include <stdint.h>
#include <avr/io.h>

typedef struct
{
    volatile uint8_t * port;   
    volatile uint8_t * pin;   
    volatile uint8_t * ddr;   
    uint8_t pad;
} port_t;

port_t port_led = 
{
    .port = &PORTG,
    .pin  = &PING,
    .ddr  = &DDRG,
    .pad  = PG5
};

void foo (port_t * port)
{
    port->ddr |= (1 << port->pad);
}

void bar (void)
{
    foo (&port_led);
}

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

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

Johann

Autor: Johann L. (gjlayde) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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
   if (IS_SET (PORT_EINER))
   {
        MAKE_OUT (PORT_ZEHNER);
        SET (PORT_ZEHNER);
   }
   else
   {
        MAKE_IN (PORT_BEEPA);
        CLR (PORT_BEEPA);
   }

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.
#ifndef _PORTS_H_
#define _PORTS_H_

#include <avr/io.h>
#include <avr-port-macros.h>
#include <avr-port-enum.h>

#if defined (__AVR_ATtiny2313__)
enum
{
  PORT_ZEHNER = PORTD_2, // INT0
  PORT_EINER  = PORTD_3, // INT1
  
  PORT_BEEPA = PORTB_2,
  PORT_BEEPB = PORTD_5,

  [...]  
  
  PORT_NIL
};
#endif // ATtiny2313

#if defined (__AVR_ATtiny24__)
enum
{
  PORT_ZEHNER = PORTA_1, // PC1  
  PORT_EINER  = PORTA_0, // PC0
  
  PORT_BEEPA = PORTA_6, // A3, MOSI
  PORT_BEEPB = PORTA_5, // MISO

  // [...]
  
  PORT_NIL
};
#endif // ATtiny24

/*****************************************************************/

#endif /* _PORTS_H_ */

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.