Struct welches im Flash liegt an Funktion übergeben

OP #4012122
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
Gast #4012344
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
#4012372
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
}
#4012426
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.
#4012634
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.
Gast #4012701
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

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