Forum: Compiler & IDEs Struct welches im Flash liegt an Funktion übergeben


von Tobias B. (horschtx)


Lesenswert?

Hallo Hallo,

ich hänge mal wieder an Pointern rum..
Habe da noch so meine schwierigkeiten.

Es gibt aber bestimmt wieder jemand der Helfen kann :)

Ich möchte gerne Daten die als Struct im Flash abgelegt sind an eine 
Funktion übergeben. Je nachdem welche Daten ich verwenden möchte.
Das ganze soll auf einem XMega mit dem AVR Studio laufen.

So habe ich es mal jetzt Probiert.

Das ist das Struct und die Daten.
1
typedef struct
2
{
3
  uint8_t B2[4];
4
  uint8_t B1[4];
5
  uint8_t B0[4];
6
  uint8_t A2[4];
7
  uint8_t A1[4];
8
}Filter;
9
10
11
12
const Filter _50_Hz_TP PROGMEM =  
13
{{0x00,0x00,0x00,0x0B},
14
                   {0x00,0x00,0x00,0x16},
15
                   {0x00,0x00,0x00,0x0B},
16
                   {0xFF,0x00,0x97,0x7A},
17
                   {0x01,0xFF,0x68,0x5A}};

Der Funktionsprototyp:
1
void write_dsp_hp(Filter filter_array);

Die Funktion selber:
1
void write_dsp_hp(Filter filter_array)
2
{
3
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1B2_2, Crossover_BYTE, filter_array.B2);
4
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1B1_2, Crossover_BYTE, filter_array.B1);
5
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1B0_2, Crossover_BYTE, filter_array.B0);
6
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1A2_2, Crossover_BYTE, filter_array.A2);
7
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1A1_2, Crossover_BYTE, filter_array.A1);}

so ruf ich die Funktion auf
1
write_dsp_hp(_50_Hz_HP);

Ich bekomme es so kompiliert, aber die Daten was ich bekomme sind 
falsch.

Wenn ich das filter_array.x direkt mit _50_Hz_TP.x ersetze funktioniert 
es.

Jetzt bin ich halt gerade an einem Punkt wo ich nicht mehr weiterkomme 
und die Hilfe was mich weiterbringt konnte ich nicht finden.

Danke schön fürs Helfen

Gruß Tobias

: Verschoben durch User
von *pointer (Gast)


Lesenswert?

Wenn du auf einen Pointer zugreifen willst, musst du auch einen Pointer 
übergeben.

von Tobias B. (horschtx)


Lesenswert?

mhh... hab jetzt schon viel probiert, bekomme es aber nicht hin..
vielleicht kann mir ja jemand unter die Arme greifen.

Gruß Tobias

von Stefan F. (Gast)


Lesenswert?

Du hast die ganze Struktur als Übergabeparameter definiert. Beim 
Funktionsaufruf werden daher die ganzen Bytes, aus denen die Struktur 
physikalisch besteht, auf den Stack kopiert.

Aber: Dieser Kopiervorgang funktioniert nur, wenn denn die Quelldaten im 
RAM liegen. Tun sie aber nicht.

Deswegen, und auch aus Performancegründen solltest du einen Pointer 
übergeben. Des weiteren musst du den Programmspeicher mit 
pgm_read_byte() lesen.
1
   void write_dsp_hp(Filter *filter_array) {
2
     uint8_t temp[4];
3
     temp[0]=pgm_read_byte(&(filter_array.B2[0]));
4
     temp[1]=pgm_read_byte(&(filter_array.B2[1]));
5
     temp[2]=pgm_read_byte(&(filter_array.B2[2]));
6
     temp[3]=pgm_read_byte(&(filter_array.B2[3]));
7
     Sigma_block_write(...,  temp);
8
     temp[0]=pgm_read_byte(&(filter_array.B1[0]));
9
     temp[1]=pgm_read_byte(&(filter_array.B1[1]));
10
     temp[2]=pgm_read_byte(&(filter_array.B1[2]));
11
     temp[3]=pgm_read_byte(&(filter_array.B1[3]));
12
     Sigma_block_write(...,  temp);
13
     temp[0]=pgm_read_byte(&(filter_array.B0[0]));
14
     temp[1]=pgm_read_byte(&(filter_array.B0[1]));
15
     temp[2]=pgm_read_byte(&(filter_array.B0[2]));
16
     temp[3]=pgm_read_byte(&(filter_array.B0[3]));
17
     Sigma_block_write(...,  temp);
18
     temp[0]=pgm_read_byte(&(filter_array.A2[0]));
19
     temp[1]=pgm_read_byte(&(filter_array.A2[1]));
20
     temp[2]=pgm_read_byte(&(filter_array.A2[2]));
21
     temp[3]=pgm_read_byte(&(filter_array.A2[3]));
22
     Sigma_block_write(...,  temp);
23
     temp[0]=pgm_read_byte(&(filter_array.A1[0]));
24
     temp[1]=pgm_read_byte(&(filter_array.A1[1]));
25
     temp[2]=pgm_read_byte(&(filter_array.A1[2]));
26
     temp[3]=pgm_read_byte(&(filter_array.A1[3]));
27
     Sigma_block_write(...,  temp);
28
   }
29
30
   int main() {
31
     write_dsp_hp(&_50_Hz_HP);
32
   }


Der Punkt ist, dass die normalen Speicherzugriffe von C sich immer auf 
das RAM beziehen. Der Programmspeicher wird jedoch über einen anderen 
Bus angesprochen, mit anderen Maschinen-Instruktionen, die C nicht 
beherrscht. Die avr C library stellt daher in avr/pgmspace.h spezielle 
Funktionen zum Lesen des Programmspeichers bereit, die in Assembler 
implementiert wurden. Leider können diese Funktionen nur einfache 
Datentype (byte, integer, etc) lesen. Deswegen musst du den 
Programspeicher "zu fuß" byteweise auslesen und daraus ein neues 
temporäres Array im RAM basteln.

Wenn du denn Quelltext von Sigma_block_write() ändern kann, würde ich 
das dort machen. Eventuell kannst du dann den Kopiervorgang in das 
temporäre Array weg optimieren.

Lesestoff dazu: http://www.nongnu.org/avr-libc/user-manual/pgmspace.html

von Stefan F. (Gast)


Lesenswert?

Mir ist gerade noch eingefallen, dass man die vier Bytes auch am Stück 
mit memcp_P() kopieren kann.

von (prx) A. K. (prx)


Lesenswert?

In neueren Versionen von GCC (z.B. 4.7.2) geht das deutlich entspannter:
1
const __flash struct S { int a, b, c, d; } var; // => ROM
2
3
extern void f(struct S param);
4
extern void g(const struct S __flash *param);
5
6
void h(void)
7
{
8
        f(var); // wird vom ROM zum Stack kopiert
9
        g(&var); // g() greift direkt auf ROM zu
10
}

: Bearbeitet durch User
von Bshit (Gast)


Lesenswert?

Stefan Us schrieb:
> Der Punkt ist, dass die normalen Speicherzugriffe von C sich immer auf
> das RAM beziehen.

Was n das n für Quatsch? C kennt kein RAM oder ROM.

Du spielst hier auf die Harvard Architektur an, oder?

von (prx) A. K. (prx)


Lesenswert?

Bshit schrieb:
> Was n das n für Quatsch? C kennt kein RAM oder ROM.

C ursprünglich nicht, AVRs jedoch schon und die Implementierung von GCC 
auf AVRs mittlerweile auch. Daher kein Quatsch.

> Du spielst hier auf die Harvard Architektur an, oder?

Ja.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Siehe auch hier, insbesondere die universellen Pointer per __memx
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Flash_mit_flash_und_Embedded-C

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

> Du spielst hier auf die Harvard Architektur an, oder?
Ja, ich auch

von Karl H. (kbuchegg)


Lesenswert?

Stefan Us schrieb:
> Mir ist gerade noch eingefallen, dass man die vier Bytes auch am Stück
> mit memcp_P() kopieren kann.

Man könnte auch die komplette Struktur mittels memcpy_P in einem Rutsch 
vom Flash ins SRAM kopieren. Nur fürs Protokoll, das würde dann 
beispielsweise so aussehen
1
const Filter _50_Hz_TP PROGMEM =  
2
{ {0x00,0x00,0x00,0x0B},
3
  {0x00,0x00,0x00,0x16},
4
  {0x00,0x00,0x00,0x0B},
5
  {0xFF,0x00,0x97,0x7A},
6
  {0x01,0xFF,0x68,0x5A}
7
};
8
9
void write_dsp_hp(Filter* filterFlash)
10
{
11
  Filter filterSRAM;
12
13
  memcpy_P( &filterSRAM, filterFlash, sizeof(FilterSRAM) );
14
15
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1B2_2, Crossover_BYTE, filterSRAM.B2);
16
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1B1_2, Crossover_BYTE, filterSRAM.B1);
17
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1B0_2, Crossover_BYTE, filterSRAM.B0);
18
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1A2_2, Crossover_BYTE, filterSRAM.A2);
19
  Sigma_block_write( DEVICE_ADDR_IC_1, Crossover_HP_1A1_2, Crossover_BYTE, filterSRAM.A1);
20
}
21
22
...
23
  write_dsp_hp(_50_Hz_HP);
24
...

Aber egal wie man es dreht und wendet, es muss einen Kopiervorgang vom 
Flash ins SRAM geben. Die von A.K angesprochene Variante mit dem 'neuen' 
__flash Schlüsselwort ist die deutlich eleganteste Lösung dafür. Zum 
einen macht der Compiler dann den Kopiervorgang, wenn auf die 
tatsächlichen Werte zugegriffen wird und zum anderen kann der Compiler 
auch die Weitergabe von Pointer an Funktionen besser überwachen, da 
__flash anders ins Sprachkonzept von C eingebunden ist als die alte 
PROGMEM Lösung, die die Hauptverantwortung für die korrekte Verwendung 
im Code ausschliesslich an den Programmierer abgetreten hatte. Letzters 
ging dann schon auch mal schief.

: Bearbeitet durch User
von Le X. (lex_91)


Lesenswert?

Karl Heinz schrieb:
> Man könnte auch die komplette Struktur mittels memcpy_P

Man könnte auch die PROGMEM Krücke in Würde sterben lassen und 
konsequent nur noch __flash verwenden. (Von legacy Projekten mal 
abgesehen).
Und Anfänger auch zur Benutzung dieses auffordern, anstatt ihnen 
Workarounds für den Workaround anzubieten.

von Karl H. (kbuchegg)


Lesenswert?

le x. schrieb:

> Und Anfänger auch zur Benutzung dieses auffordern, anstatt ihnen
> Workarounds für den Workaround anzubieten.

Mit welchem Teil meines Postings, das die von A.K. angesprochene und von 
dir propagierte Lösung ebenfalls als die deutlichst bessere Variante 
hervorhebt, bist du nicht einverstanden?

Eventuell benutzt er ja noch (so wie ich) eine gcc Version, die __flash 
noch nicht kann. Spätestens dann muss er wissen was da dahinter steckt, 
so wie es nicht schadet, wenn er auch weiss, dass __flash vom Compiler 
eine andere Behandlung erfordert und es daher zb nicht egal ist, wenn er 
es (so wie manchmal bei einem volatile) einfach wegcastet, bzw. das 
__flash nicht ohne einen Laufzeit-Penalty daher kommt und worin dieser 
besteht.

: Bearbeitet durch User
von Bshit (Gast)


Lesenswert?

Karl Heinz schrieb:
> dann muss er wissen was da dahinter steckt,
> so wie es nicht schadet, wenn er auch weiss, dass

... die Havard Architektur getrennte Adressräume für Code und Daten hat. 
Und gleiche Adressen auf unterschiedliche Speicherzellen zeigen (Code / 
Daten). Und dass Variablen ersteinmal im Datenbereich liegen. Und der µC 
auf (konstante) Variblen im Codebereich nicht einfach so zugreifen kann. 
Und ...

http://www.netzmafia.de/skripten/mikrocomputer/harvard.gif
http://de.wikipedia.org/wiki/Harvard-Architektur

von Tobias B. (horschtx)


Lesenswert?

So, jetzt funktioniert es.

Danke für die Hilfe und die Denkanstöße. Das hat mir weitergeholfen.

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.