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 ;-))