[avr-gcc] PORT Definition in einer Liste speichern

OP #2312836
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?
#2312839
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
}
OP #2312847
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?
OP #2313170
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
}
OP #2313190
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
}
Persönliche Seite #2313240
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
#2313441
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.
OP #2313578
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
#2313584
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!
Persönliche Seite #2313794
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
OP #2313992
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?
#2314029
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.
OP #2314032
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.
OP #2314036
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren