Forum: Compiler & IDEs Aufruf mit/ohne Flash. Zwei identische Funktionen nötig?


von Pupsomat (Gast)


Lesenswert?

Kann man in avr-gcc aus diesen beiden Funktionen eine machen?
1
void foo(const char *data) {
2
  // ...
3
}
4
5
void bar(const __flash char *data) {
6
  // ...
7
}

Inhaltlich machen die das gleiche, werden halt nur einmal mit einem im 
Speicher befindlichen String und einmal mit einem aus dem Flash 
aufgerufen... Es erscheint mir unsinnig, zwei Funktionen dafür bereit 
halten zu müssen.

von Markus (Gast)


Lesenswert?

Hi,

den Zugriff über den Pointer kannst Du wegkapseln. Dazu brauchst Du 
einen Indicator, der Dir sagt, welchen Zugriff die Kapselung ausführen 
soll. Den Indicator gibst Du von dort rein, wo Du weißt, ob Ram oder 
Flash.

Grüße, Markus

von Stefan E. (sternst)


Lesenswert?

Pupsomat schrieb:
> Kann man in avr-gcc aus diesen beiden Funktionen eine machen?

Ja:
1
void foo_bar (const __memx char *data) {
2
  // ...
3
}

von Pupsomat (Gast)


Lesenswert?

Ok, das funktioniert. Vergrößert aber das Programm um 200 Bytes...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Pupsomat schrieb:
> Ok, das funktioniert. Vergrößert aber das Programm um 200 Bytes...

__memx verwendet 24-Bit Zeuger anstatt der sonst üblichen 16 Bits. 
Zudem muß bei jedem Zugriff zur Laufzeit getestet werden, ob dieser 
vom Flash oder aus dem RAM lesen soll.

Evtl. ist man dann händisch besser dran:
 
1
int get_int (const int *p, bool from_flash)
2
{
3
    if (from_flash)
4
        return *(const __flash int*) p;
5
    else
6
        return *p;
7
}

Das testet zwar auch zur Laufzeit, aber vielleicht wird der Code so 
kleiner.  Falls die Funktion nicht allzu groß ist und der Aufrufer weiß, 
von wo er lesen will, kann es lohnen zu inlinen:
 
1
static inline int
2
get_int (const int *p, bool from_flash)
3
{
4
    if (from_flash)
5
        return *(const __flash int*) p;
6
    else
7
        return *p;
8
}
9
10
extern int a, b, val_b;
11
extern const __flash int val_a;
12
13
void set_ab (void)
14
{
15
    a = get_int (&val_a, true);
16
    b = get_int (&val_b, false);
17
}

gibt:
 
1
set_ab:
2
  ldi r30,lo8(val_a)
3
  ldi r31,hi8(val_a)
4
  lpm r24,Z+
5
  lpm r25,Z+
6
  sts a+1,r25
7
  sts a,r24
8
  lds r24,val_b
9
  lds r25,val_b+1
10
  sts b+1,r25
11
  sts b,r24
12
  ret

Versucht man das gleiche mit __memx:
 
1
static inline int
2
get_int (const __memx int *p)
3
{
4
    return *p;
5
}
6
7
void set_ab (void)
8
{
9
    a = get_int (&val_a);
10
    b = get_int (&val_b);
11
}

dann ist der Code größer; ohne die Bibliotheksfunktion __xload2:
 
1
set_ab:
2
  ldi r24,lo8(val_a)
3
  ldi r25,hi8(val_a)
4
  ldi r26,0
5
  movw r30,r24
6
  mov r21,r26
7
  call __xload_2
8
  sts a+1,r23
9
  sts a,r22
10
  ldi r24,lo8(val_b)
11
  ldi r25,hi8(val_b)
12
  ldi r26,lo8(-128)
13
  movw r30,r24
14
  mov r21,r26
15
  call __xload_2
16
  sts b+1,r23
17
  sts b,r22
18
  ret

Die Möglichkeiten für avr-gcc das zu optimieren sind beschränkt.  Grund 
sind Situationen wie diese:
 
1
#include <avr/pgmspace.h>
2
3
extern void foo_P (const __memx char *);
4
5
void hallo (void)
6
{
7
    foo_P (PSTR ("Hallo"));
8
}

Der Zeiger, den PSTR zurückliefert, liegt formal im Generic Address 
Space (RAM), enthält aber eine Flash-Adresse.  Für dieses Beispiel 
erzeugt der Compiler Code wie erwartet, d.h. übergibt an foo_P einen 
24-Bit Zeiger aufs Flash.

Dummerweise ist das, was PSTR macht, nicht auf Embedded-C abbildbar.

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.