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
voidinit_move_out(volatilechar*port){
2
3
char*test="DDR";
4
char*append=port[4];
5
volatilechar*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
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?
>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
volatilechar*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
@ 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.
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?
>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.
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
voidinit_move_out(volatilechar*port){
2
volatilechar*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.
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.
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 ;)
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
intmain(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
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
voidfoo(volatileunsignedchar*Port)
2
{
3
volatileunsignedchar*Ddr=Port-1;
4
5
*Ddr=0xFF;// alles auf Ausgang
6
*Port=0xFF;// und alle Ausgnänge auf High
7
}
8
9
intmain()
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.
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.
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.
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
typedefstruct
5
{
6
volatileuint8_t*port;
7
volatileuint8_t*pin;
8
volatileuint8_t*ddr;
9
uint8_tpad;
10
}port_t;
11
12
port_tport_led=
13
{
14
.port=&PORTG,
15
.pin=&PING,
16
.ddr=&DDRG,
17
.pad=PG5
18
};
19
20
voidfoo(port_t*port)
21
{
22
port->ddr|=(1<<port->pad);
23
}
24
25
voidbar(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
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.