Forum: Compiler & IDEs [avr-gcc] PORT Definition in einer Liste speichern


von Markus B. (markus_b77)


Lesenswert?

Hi, ich stehe gerade vor einem Problem. Ich möchte eine Liste mit Ports 
und Pins anlegen. Eigentlich ein zweidimensionales Array. Nur mal als 
Beispiel zur Veranschaulichung
1
liste = {{PORTB, 1},
2
         {PORTB, 2},
3
         {PORTD, 1},
4
         {usw}
5
        };

Ich möchte diese Liste durchlaufen und jede Zeile an eine Funktion 
übergeben, die dann mit PORTB Pin 1 etwas tut.

Nur wie? Ich hab schon alles mögliche probiert. Ich weiß, dass ich PORTB 
so nicht direkt angeben kann, weil das ein Makro ist und ja versuchen 
würde, den Inhalt des PORT Registers zu lesen. Mit dem Adressoperator 
hat man auch keinen Erfolg.

Wie kann ich das Problem lösen?

von Klaus W. (mfgkw)


Lesenswert?

PORTB ist zwar ein Makro, aber eines in der Art:
1
#define   PORTB   *(uint8_t*)20

(die 20 ist jetzt nur ein Beispiel; tatsächlich muß man sich dafür die 
tatsächliche Adresse denken, an der PORTB in den Speicher eingeblendet 
wird - je nach konkretem AVR.)

PORTB expandiert also zu einer Referenz auf eine Speicherstelle, also 
wie eine Variable, die an der Stelle 20 im Speicher liegt.

Das wiederum heißt, daß man davon auch die Adresse nehmen kann.
Du musst aber bereits in deinem Feld Adressen eintragen, also
etwa so:
1
typedef struct
2
{
3
   uint8_t   *p_port;
4
   uint8_t    pin;
5
} port_pin_paar_t;
6
7
port_pin_paar_t liste[] = { { &PORTB, 1 },
8
                            { &PORTB, 2 },
9
                            { &PORTD, 1 },
10
                            {usw}
11
                          };
12
13
// Zugriff:
14
f1( liste[0] );
15
f1( &liste[0] );
16
...
17
18
void f1( port_pin_paar_t port_pin_paar )
19
{
20
    // Bit setzen:
21
    *(port_pin_paar.p_port) |= (1<<port_pin_paar.pin);
22
}
23
24
void f2( const port_pin_paar_t *p_port_pin_paar )
25
{
26
    // Bit setzen:
27
    *(p_port_pin_paar->p_port) |= (1<<p_port_pin_paar->pin);
28
}

von Markus B. (markus_b77)


Lesenswert?

Danke für die Erklärung. Wenn ich dein Beispiel jedoch übernehme bekomme 
ich folgenden Fehler

main.cpp:19: error: invalid conversion from 'volatile uint8_t*' to 
'uint8_t*'

Wenn ich in der Struct folgendes ändere funktioniert es.
1
typedef struct
2
{
3
   volatile uint8_t   *p_port;
4
   uint8_t    pin;
5
} port_pin_paar_t;

Ist das richtig so?

von Klaus W. (mfgkw)


Lesenswert?

ja

Das volatile hatte ich schändlicherweise unterschlagen.

von Klaus W. (mfgkw)


Lesenswert?

Musst du jetzt noch in offenen Wunden bohren? :-)

von Markus B. (markus_b77)


Lesenswert?

:)

Danke

von Stefan E. (sternst)


Lesenswert?

Klaus Wachtler schrieb:
> Musst du jetzt noch in offenen Wunden bohren? :-)

Mein Gott bist du schnell. Der Beitrag war doch gerade mal 10 Sekunden 
da. ;-)

von Klaus W. (mfgkw)


Lesenswert?

Stefan Ernst schrieb:
> bist du schnell

Wir sind hier bei C, und nicht bei C#, Java oder BASIC.

von Markus B. (markus_b77)


Lesenswert?

Eins noch
1
// Zugriff:
2
f1( liste[0] );
3
f1( &liste[0] );
sollte wohl
1
// Zugriff:
2
f1( liste[0] );
3
f2( &liste[0] );
heißen

von Klaus W. (mfgkw)


Lesenswert?

ja, das war wiederum zu schnell :-(

von Markus B. (markus_b77)


Lesenswert?

Fehler passieren. Am besten du korrigierst deinen Beitrag, falls später 
mal jemand darüber stolpert und nicht den ganzen Beitrag ließt

von Klaus W. (mfgkw)


Lesenswert?

Im Originaltext kann ich nichts mehr korrigieren, sobald eine Antwort 
dahinter steht.

Wer nicht bis zum Ende liest, hat dann Pech.

von Markus B. (markus_b77)


Lesenswert?

Eine Frage noch. Die Liste müsste ins Flash, weil die doch etwas länger 
wird. Wie lege ich das an? Einfach ein PROGMEM anhängen geht nich. Und 
wie greife ich darauf zu?

von Stefan E. (sternst)


Lesenswert?

Markus B. schrieb:
> Eine Frage noch. Die Liste müsste ins Flash, weil die doch etwas länger
> wird. Wie lege ich das an? Einfach ein PROGMEM anhängen geht nich. Und
> wie greife ich darauf zu?

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29

von Markus B. (markus_b77)


Lesenswert?

Da war ich schon. Bekomme aber wie gesagt eine Fehlermeldung

> main.cpp:32: warning: only initialized variables can be placed into
> program memory area
1
const testpin_t liste[] PROGMEM =

von Markus B. (markus_b77)


Lesenswert?

Hier mal der komplette Code
1
typedef struct
2
{
3
   volatile uint8_t   *p_portT;
4
      uint8_t    pinT;
5
   volatile uint8_t   *p_portL;
6
      uint8_t    pinL;
7
   volatile uint8_t   *p_portR;
8
      uint8_t    pinR;
9
   volatile uint8_t   *p_portC;
10
      uint8_t    pinC;
11
} testpin_t;
12
13
const testpin_t liste[] PROGMEM = 
14
{
15
  { &PORTE, 0, &PORTE, 1, 0, 0, &PORTD, 2}
16
}

von fluch (Gast)


Lesenswert?

Tu mal die [] da weg.

von fluch (Gast)


Lesenswert?

(und eine Ladung geschweifte Klammern)

von Markus B. (markus_b77)


Lesenswert?

fluch schrieb:
> Tu mal die [] da weg.
Dann kommen nur noch Fehler

fluch schrieb:
> (und eine Ladung geschweifte Klammern)

???

von fluch (Gast)


Lesenswert?

1
const testpin_t liste PROGMEM = { &PORTE, 0, &PORTE, 1, 0, 0, &PORTD, 2};

von Markus B. (markus_b77)


Lesenswert?

Sorry, ich dachte aufgrund des restlichen Threads wäre klar, dass es 
mehrere Einträge gibt.
1
const testpin_t liste[] PROGMEM = 
2
{
3
  { &PORTE, 0, &PORTE, 1, 0, 0, &PORTD, 2},
4
  { &PORTE, 0, 0, 0, 0, 0, &PORTD, 2},
5
  { &PORTE, 0, 0, 0, &PORTE, 1, &PORTD, 2},
6
  { &PORTE, 0, &PORTE, 1, &PORTE, 1, &PORTD, 2},
7
  { &PORTE, 0, &PORTE, 1, &PORTE, 1, &PORTD, 2}
8
}

von fluch (Gast)


Lesenswert?

Ah ok. Dann schreib mal die Anzahl in die eckigen Klammern rein, wenn 
das nicht hilft muss Karl-Heinz oder ein anderer Spezi ran...

von Markus B. (markus_b77)


Lesenswert?

Nein, nach wie vor die Fehlermeldung

> main.cpp:32: warning: only initialized variables can be placed into
> program memory area

von Fabian G. (kjion) Benutzerseite


Lesenswert?

Markus B. schrieb:
> Nein, nach wie vor die Fehlermeldung
>
>> main.cpp:32: warning: only initialized variables can be placed into
>> program memory area

Du compilierst das als C++?
Die Meldung ist ein Fehler in avr-g++.

Siehe Beitrag "avr-gcc, C++ und PROGMEM"

Workaround:
1
extern const testpin_t liste[] PROGMEM;
2
const testpin_t liste[] = 
3
{
4
  { &PORTE, 0, &PORTE, 1, 0, 0, &PORTD, 2},
5
  { &PORTE, 0, 0, 0, 0, 0, &PORTD, 2},
6
  { &PORTE, 0, 0, 0, &PORTE, 1, &PORTD, 2},
7
  { &PORTE, 0, &PORTE, 1, &PORTE, 1, &PORTD, 2},
8
  { &PORTE, 0, &PORTE, 1, &PORTE, 1, &PORTD, 2}
9
};

Auch wenn es komisch aussieht funktioniert es so.

Grüße Fabian

von Markus B. (markus_b77)


Lesenswert?

Tatsache. Danke. Hab gar nicht daran gedacht, dass ich mit g++ 
compiliere.

Jetzt fehlt noch das lesen einer Struct aus dem Array. Das fehlt leider 
noch im Wiki

von Karl H. (kbuchegg)


Lesenswert?

Markus B. schrieb:
> Tatsache. Danke. Hab gar nicht daran gedacht, dass ich mit g++
> compiliere.
>
> Jetzt fehlt noch das lesen einer Struct aus dem Array. Das fehlt leider
> noch im Wiki

Einfach in einer Schleife sizeof(testpin_t) Bytes aus dem Flash lesen 
und auf einer im SRAM bereitgestellten testpin_t Variable ablegen. Dazu 
muss man ein wenig casten.

Man kann dazu zb die Funktion memcpy_P() sinnvoll benutzen.

von Klaus W. (mfgkw)


Lesenswert?

Markus B. schrieb:
> Jetzt fehlt noch das lesen einer Struct aus dem Array. Das fehlt leider
> noch im Wiki

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29

von Klaus W. (mfgkw)


Lesenswert?

Ich habe mir erlaubt, die obige Diskussion ins Tutorial zu übernehmen:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Speichern_und_.C3.9Cbergeben_von_Port_und_Pinnummer

(mit volatile natürlich)

von Markus B. (markus_b77)


Lesenswert?

Zur Berechnung von DDR und PIN gibt es ein kleines Makro. Die Lösung im 
Wiki funktioniert beim Mega128 PortF nicht
1
#define DDR(x) (*(&x - 1))
2
#if defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__)
3
    #define PIN(x) ( &PORTF==&(x) ? _SFR_IO8(0x00) : (*(&x - 2)) )
4
#else
5
    #define PIN(x) (*(&x - 2))
6
#endif

von Markus B. (markus_b77)


Lesenswert?

@Klaus: ich meine den Teil

> TODO: Beispiele für structs und pointer aus Flash auf struct im Flash
> (menues, state-machines etc.).

von Klaus W. (mfgkw)


Lesenswert?

oh, das ist ja blöd.

Danke!

von Klaus W. (mfgkw)


Lesenswert?

Markus B. schrieb:
> @Klaus: ich meine den Teil
>
>> TODO: Beispiele für structs und pointer aus Flash auf struct im Flash
>> (menues, state-machines etc.).

Daran habe ich aber gar nichts geändert?
Aus meinem Teil von eben 
(http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Speichern_und_.C3.9Cbergeben_von_Port_und_Pinnummer) 
habe ich den DDR-Teil wieder entfernt.

von Klaus W. (mfgkw)


Lesenswert?

Klaus Wachtler schrieb:
> Markus B. schrieb:
>> Jetzt fehlt noch das lesen einer Struct aus dem Array. Das fehlt leider
>> noch im Wiki
>
> 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29

Sorry für die schroff kurze Antwort; als du nachgesehen hattest, gab es 
die Stelle im Tutorial tatsächlich noch nicht.
(Gut daß ich nicht deutlicher gelästert hatte über mangelndes Bemühen 
beim Suchen, es lag mir ja auf der Zunge :-)

Das hat Karl-Heinz da gerade schnell reingebaut (Danke!).

Wehe, es beschwert sich noch mal jemand über schlechten Service hier!

von Markus B. (markus_b77)


Lesenswert?

Klaus Wachtler schrieb:
> Das hat Karl-Heinz da gerade schnell reingebaut (Danke!).

Ach da unten. Ich hatte den neuen Eintrag wohl gesehen, den Teil aber 
übersehen. Hab noch keinen Kaffee :)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Klaus Wachtler schrieb:
> Ich habe mir erlaubt, die obige Diskussion ins Tutorial zu übernehmen:
> 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Speichern_und_.C3.9Cbergeben_von_Port_und_Pinnummer

Inzwischen ist das Tutorial wohl so unübersichtlich, daß ein und 
dasselbe x-mal erklärt wird:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#IO-Register_als_Parameter_und_Variablen
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Speichern_und_.C3.9Cbergeben_von_Port_und_Pinnummer

Und warum extra erklärt wird, wie man das in Flash legt, ist doch total 
unnötig. Oder soll für jeden in C möglichen Tyedef erklört werden, wie 
man den ins Flash legt? Steht doch alles schon da:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29

von Klaus W. (mfgkw)


Lesenswert?

Sag das doch gleich; ist wieder weg.

von Klaus W. (mfgkw)


Lesenswert?

Und wovon kommt's?
Weil die Leute dauernd fragen, ohne vorher danach zu suchen und das 
Tutorial zu lesen :-)

von Markus B. (markus_b77)


Lesenswert?

Johann L. schrieb:
> Inzwischen ist das Tutorial wohl so unübersichtlich, daß ein und
> dasselbe x-mal erklärt wird:

Unübersichtlich? Ja. Ein und das selbe? Nein, finde ich nicht. Es gibt 
halt Menschen wie mich, die die nötigen Informationen aus dem Wust in 
dem Tutorial nicht finden und zusammenfügen können, weil die Erfahrung 
fehlt.

von Markus B. (markus_b77)


Lesenswert?

Ich habe jetzt noch ein anderes Problem, das für mich keinen Sinn 
ergibt. Eventuell hat es was damit zu tun, dass ich mit dem g++ 
compiliere?

Hier erstmal der wichtige Code
1
typedef struct
2
{
3
   volatile uint8_t   *p_portT;
4
      uint8_t    pinT;
5
   volatile uint8_t   *p_portL;
6
      uint8_t    pinL;
7
   volatile uint8_t   *p_portR;
8
      uint8_t    pinR;
9
   volatile uint8_t   *p_portC;
10
      uint8_t    pinC;
11
} testpin_t;
12
13
testpin_t liste[] =
14
{
15
  { &PORTA, 0, &PORTA, 1, 0, 0, &PORTD, 2},
16
  { &PORTB, 0, 0, 0, 0, 0, &PORTD, 2},
17
  { &PORTC, 0, 0, 0, &PORTE, 1, &PORTD, 2},
18
  { &PORTD, 0, &PORTB, 1, &PORTE, 1, &PORTD, 2},
19
  { &PORTE, 0, &PORTC, 1, &PORTE, 1, &PORTD, 2},
20
  { 0, 0, 0, 0, 0, 0, 0, 0}
21
}
22
23
int main(void)
24
{
25
  sei();
26
  uint8_t i = 0;
27
  while(liste[i].p_portT)
28
  {
29
    stream << i << " ";
30
    stream << &liste[i] << " ";
31
    stream << *(liste[i].p_portL) << " ";
32
    stream << sizeof(liste[i]);
33
    i++;
34
    stream << xpcc::endl;
35
  }
36
  while (1)
37
  {
38
  }
39
  return 0;
40
}

&liste[i] gibt mir die Adresse des jeweiligen Struct aus. 
sizeof(liste[i]) ergibt interessanterweise 12 Byte, nicht 8, wie man 
erwarten würde. Braucht ein Struct 4 Byte extra? Oder brauchen die 
volatilen Felder ein extra Byte?

*(liste[i].p_portL) gibt jedoch völligen Blödsinn aus. Überall da, wo in 
der Liste ein &PORTx eingetragen ist, wird eine 0 ausgegeben. Da wo eine 
0 in der Liste steht erhalte ich aber einen Wert. Das ergibt überhaupt 
keinen Sinn und ich komme auch nicht dahinter, wieso.

Jemand eine Idee?

von Klaus W. (mfgkw)


Lesenswert?

Markus B. schrieb:
> ergibt interessanterweise 12 Byte, nicht 8, wie man
> erwarten würde.

Ein Zeiger braucht auf einem bestimmten System immer eine feste Anzahl 
Byte, egal wie groß das ist, worauf er zeigt.
Bei AVR offenbar 2 Byte, auch wenn er auf eine ein Byte große uint8_t 
zeigt.

Vier Zeiger, 4 uint8_t macht zumsammen 12 Byte.

von Klaus W. (mfgkw)


Lesenswert?

Wo nimmst du auf einem AVR streams her, und vor allem: willst du das 
wirklich?

von Klaus W. (mfgkw)


Lesenswert?

Markus B. schrieb:
> Überall da, wo in
> der Liste ein &PORTx eingetragen ist, wird eine 0 ausgegeben.

Da, wo du eine &PORTx eingetragen hast, und mit * darauf zugreifst, 
bekommst du den aktuellen Wert von PORTx.
Auch wenn PORTx eigentlich zur Ausgabe gedacht ist, kannst du aus dem 
Register etwas lesen. Meines Wissens ist es der aktuelle ausgegebenen 
Wert, also 0, solange du nicht vorher mit PORTx = ... etwas anderes 
ausgibst.

von Markus B. (markus_b77)


Lesenswert?

Klaus Wachtler schrieb:
> Wo nimmst du auf einem AVR streams her, und vor allem: willst du das
> wirklich?
http://xpcc.sourceforge.net/

Klaus Wachtler schrieb:
> Ein Zeiger braucht auf einem bestimmten System immer eine feste Anzahl
> Byte, egal wie groß das ist, worauf er zeigt.
> Bei AVR offenbar 2 Byte, auch wenn er auf eine ein Byte große uint8_t
> zeigt.

Ist ja kein uint8_t sondern ein Zeiger auf uint8_t. Das passiert, wenn 
man zu wenig geschlafen hat.

Trotzdem bekomme ich immer den Wert 0, sobald ich einen Port in die 
Liste schreibe. Komme nicht dahinter, warum.

von Klaus W. (mfgkw)


Lesenswert?

Daß du in den anderen Fällen irgendwelche Werte bekommst, liegt daran, 
daß du den Zeiger mit 0 intialisierst, aber trotzdem mit *(uint8_t)0 
dann darauf zugreifst. Je nach AVR (siehe Datenblatt) wird da auch ein 
Register sein, dessen Wert du ausgibst.
Böser Junge!

von Stefan E. (sternst)


Lesenswert?

Markus B. schrieb:
> Überall da, wo in
> der Liste ein &PORTx eingetragen ist, wird eine 0 ausgegeben.

Du hast ja auch nichts in die Port-Register geschrieben, und 0 ist der 
Default-Inhalt.

Markus B. schrieb:
> Da wo eine
> 0 in der Liste steht erhalte ich aber einen Wert.

Ja, den Inhalt von Adresse 0.

von Markus B. (markus_b77)


Lesenswert?

Klaus Wachtler schrieb:
> Da, wo du eine &PORTx eingetragen hast, und mit * darauf zugreifst,
> bekommst du den aktuellen Wert von PORTx.
> Auch wenn PORTx eigentlich zur Ausgabe gedacht ist, kannst du aus dem
> Register etwas lesen. Meines Wissens ist es der aktuelle ausgegebenen
> Wert, also 0, solange du nicht vorher mit PORTx = ... etwas anderes
> ausgibst.

Man, ich hab heute Watte im Schädel. Danke

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.