c: gcc/avr gleiche/ungleiche varianten of string declarartion

(Firma: @home) #5927532
Lesenswert?

hi,
variante 1 und variante 2 sollten eigentlich gleichen code erzeugen ... 
tun sie aber nicht bzgl. ram allocation, bei variante 2 liegt die string 
copy im ram, wie kann ich das vermeiden?

mt
1
variante 1:
2
PROGMEM const char unit0[] = "uSv/h";
3
PROGMEM const char unit1[] = "uR/h";
4
PROGMEM const char unit2[] = "mR/h";
5
PROGMEM PGM_P const unitTable[] = {unit0, unit1, unit2};
6

7
variante 2:
8
enum {UNIT_uSv, UNIT_uR, UNIT_mR};
9
PROGMEM PGM_P const unitTable[] = {
10
  [UNIT_uSv] = "uSv/h",
11
  [UNIT_uR] = "uR/h",
12
  [UNIT_mR] = "mR/h",
13
};
Persönliche Seite #5927608
Lesenswert?

Variante 2 ist mit progmem nicht darstellbar.

PSTR ist an der Stelle syntaktisch nicht möglich.

Was geht:

Variante A
1
#include <avr/pgmspace.h>
2

3
const char unitTable[][6] PROGMEM =
4
{
5
  "uSv/h", "uR/h", "mR/h"
6
};
Allerdings ist das dann kein Array von const char*.  Für ein Array von 
const char* braucht man Strings in progmem wie in deiner Variante 1 oder 
man verwendet __flash mit Compound Literals:

Variante B
1
#define FSTR(X) (const __flash char[]) { X }
2

3
const __flash char* const __flash unitTable[] =
4
{
5
  FSTR ("uSv/h"), FSTR ("uR/h"), FSTR ("mR/h")
6
};
progmem ist ein Attribut und bezieht sich immer auf das definierte 
Objekt.  __flash ist ein Qualifier (wie z.B. const oder volatile) und 
kann sich auch auf ein Pointer-Target beziehen.  Mit __flash geht 
natürlich auch das Analogon von Variante A:

Variante C
1
const __flash char unitTable[][6] =
2
{
3
  "uSv/h", "uR/h", "mR/h"
4
};

Nachteil der Varianten A und C ist, dass man die Maximallänge der 
beteiligten Strings kennen muss, und dass man Verschnitt hat, wenn viele 
Strings unterschiedlicher Längen auftreten.  Außerdem muss mit der 
Stringlänge multipliziert werden, was teuer ist, wenn der µC keine MUL 
Instruktion kennt.

Bei Variante B hat man hingegen Verschnitt durch die zusätzlich zu 
speichernden String-Adressen, und man hat eine Indirektion mehr.  Dafür 
braucht es nur eine einfache Multiplikation mit 2:
1
char get_first_B (const __flash char* const __flash strings[], uint8_t index)
2
{
3
    return strings[index][0];
4
}
5

6
char get_first_C (const __flash char strings[][6], uint8_t index)
7
{
8
    return strings[index][0];
9
}
compiliert zu:
1
get_first_B:
2
  mov r30,r22
3
  ldi r31,0
4
  lsl r30
5
  rol r31
6
  add r30,r24
7
  adc r31,r25
8
  lpm r0,Z+
9
  lpm r31,Z
10
  mov r30,r0
11
  lpm r24,Z
12
  ret
13

14
get_first_C:
15
  ldi r18,lo8(6)
16
  mul r22,r18
17
  add r24,r0
18
  adc r25,r1
19
  clr __zero_reg__
20
  movw r30,r24
21
  lpm r24,Z
22
  ret

Vorteil von __flash ist, dass man die hässlichen pgm_read_xxx nicht mehr 
braucht und dass es einfacher ist, an Situationen anzupassen, in denen 
__flash nicht verfügbar oder ungünstig ist:
1
#if !defined(__FLASH) || defined (__AVR_PM_BASE_ADDRESS__)
2
#define __flash // empty
3
#endif
Nachteil ist, dass es nicht in C++ verfügbar ist (kann man wahlweise 
auch als Nachteil von C++ ansehen ;-))

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