Problema con PROGMEM

Solitamente quando si sceglie di mettere i const char* nella flash ci si chiede: ma li ha messi veramente in flash?
E questa è la domanda che mi sono posto, per avere conferma ho compilato aggiungendo il flag -save-temps e ho notato il seguente problema:
La prima occorrenza di codice che salva in flash non viene salvata, le successive si.
Il codice seguente mostra meglio cosa voglio dire:

// file settings_p.h contiene anche altro
// struct generic parameter
struct Parameter {
    const char *show_name;
    char value;
};

// file settings.c
// Ogni valore di temperatura deve essere espresso in gradi kelvin assoluti
#define ALL_DATA        &temp_abs,\
                        &temp_diff,\
                        &temp_min,\
                        &temp_max,\
                        &defrost_type,\
                        &defrost_end_temp,\
                        &defrost_timeout, \
                        0

// setpoint della temperatura 25°C
char dummy[] PROGMEM = "";
Parameter
dummy_dummy = {
            dummy,
            0
           };

// setpoint della temperatura 25°C
char set[] PROGMEM = "set";
Parameter
temp_abs = {
            set,
            25
           };

// differenziale di intervento temperatura 2°C
char dif[] PROGMEM = "dif";
Parameter
temp_diff = {
             dif,
             2
            };

il char dummy e la struttura seguente la ho inserita di proposito per capire cosa accade. In sostanza dummy non va in flash, ma set e tutte le altre si. Se tolgo dummy e dummy_dummy, set che prima andava in flash non ci va più.

Quindi solo la prima occorrenza di char name PROGMEM = “”; non va in flash.

Quello che segue è il file .s generato durante la compilazione, nota che settings.c appartiene ad una libreria static che sto sviluppando, quindi la compilazione mi crea un file libsettings.a.

.global	dummy
	.type	dummy, @object
	.size	dummy, 1
dummy:
	.string	""                          // commento aggiunto a mano, stesso problema se dummy = "dummy"
.global	dummy_dummy
	.section	.data.dummy_dummy,"aw",@progbits
	.type	dummy_dummy, @object
	.size	dummy_dummy, 3
dummy_dummy:
	.word	dummy
	.byte	0
.global	set
	.section	.progmem.data
	.type	set, @object
	.size	set, 4
set:
	.string	"set"
.global	temp_abs
	.section	.data.temp_abs,"aw",@progbits
	.type	temp_abs, @object
	.size	temp_abs, 3
temp_abs:
	.word	set
	.byte	25
.global	dif
	.section	.progmem.data
	.type	dif, @object
	.size	dif, 4
dif:
	.string	"dif"
.global	temp_diff
	.section	.data.temp_diff,"aw",@progbits
	.type	temp_diff, @object
	.size	temp_diff, 3
temp_diff:
	.word	dif
	.byte	2

Domanda:
Ora volevo capire se il problema dipende dalla avr-libc, da avr-gcc e se è agirabile senza dovere usare un fantoccio come dummy.
Vi è mai capitato questo problema?

Tutto il codice ha l’obbiettivo di realizzare quello che in C++ si chiama map<T, T>, cioè una coppia di chiave valore, che viene
salvata, la key “set” in flash e value in ram, inoltre c’è la gestione automatica della memorizzazione in eeprom.
In sostanza l’uso che se ne farà è questo:

// ritorna un puntatore a char value abbinato alla chiave "key"
const char *value(const char* key);
// si usa cosi

const char *set = value("set");

Inizialmente il codice serviva per mostrare a display “set” o dte o altro e se l’utente preme il pulsante enter il valore di “set” viene mostrato così da permettere la modifica, durante la scrittura di codice è venuto spontaneo pensare alla coppia chiave/valore.

Ciao.

Non va in flash perché non è una costante. Solo le costanti predefinite possono andare in Flash.

const char nomeVar[] PROGMEM = "contenuto_stringa";

Difatti a me il tuo codice non compila dicendomi proprio.

variable 'dummy' must be const in order to be put into read-only section by means of '__attribute__((progmem))'

Grazie leo, per aver tentato di compilare il codice. Io ho compilato con avr-gcc 4.5.1 e le avr-libc 1.7.1 atmel patched è il codice compila. Il motivo della mancata compilazione è legato alla implementazione della versione 1.8 di avr-libc dove a rigor di logica un data che risiede nella memoria flash è non mutabile a runtime per tanto nella nuova implementazione hanno obbligato il programmatore a dichiarare esplicitamente il qualificatore "const".

Per compilare codice vecchio con la nuova versione di avrlibc io ho trovato questo link http://www.tuxgraphics.org/electronics/201207/prog_char.shtml

Dove si vede che c'è la possibilità di impostare avrlibc 1.8 in modo retrocompatibile, con tal fine è sufficiente specificare quanto segue sulla riga di comando di gcc: -Wno-deprecated-declarations -D_PROG_TYPES_COMPAT_

Ma questo credo che il "cost" lo debba comunque usare.

In ogni caso ho già provveduto ad aggiungere const a tutti gli array di string che devono andare in flash. cioè ora ha scritto, const char dummy[] PROGMEM = ""; ma il problema permane.

Ciao.

Io uso l'ultima toolchain Atmel per cui devo per forza usare l'ultima sintassi. Non mi resta che provare a disassemblare il codice.

Io ho compilato questo sketch:

const char nomeVar[] PROGMEM = "contenuto_stringa";

void setup() {
}

void loop() {
    char buffer[20];
    strcpy_P(buffer, (char*)pgm_read_word(&(nomeVar))); //estrae la stringa dalla Flash
}

L’assembly ottenuto è questo:

sketch_may29a.cpp.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:	0c 94 3d 00 	jmp	0x7a	; 0x7a <__ctors_end>
   4:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
   8:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
   c:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  10:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  14:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  18:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  1c:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  20:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  24:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  28:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  2c:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  30:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  34:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  38:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  3c:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  40:	0c 94 7b 00 	jmp	0xf6	; 0xf6 <__vector_16>
  44:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  48:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  4c:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  50:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  54:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  58:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  5c:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  60:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>
  64:	0c 94 4f 00 	jmp	0x9e	; 0x9e <__bad_interrupt>

00000068 <_ZL7nomeVar>:
  68:	63 6f 6e 74 65 6e 75 74 6f 5f 73 74 72 69 6e 67     contenuto_string
  78:	61 00                                               a.

0000007a <__ctors_end>:
  7a:	11 24       	eor	r1, r1
  7c:	1f be       	out	0x3f, r1	; 63
  7e:	cf ef       	ldi	r28, 0xFF	; 255
  80:	d8 e0       	ldi	r29, 0x08	; 8
  82:	de bf       	out	0x3e, r29	; 62
  84:	cd bf       	out	0x3d, r28	; 61

00000086 <__do_clear_bss>:
  86:	21 e0       	ldi	r18, 0x01	; 1
  88:	a0 e0       	ldi	r26, 0x00	; 0
  8a:	b1 e0       	ldi	r27, 0x01	; 1
  8c:	01 c0       	rjmp	.+2      	; 0x90 <.do_clear_bss_start>

0000008e <.do_clear_bss_loop>:
  8e:	1d 92       	st	X+, r1

00000090 <.do_clear_bss_start>:
  90:	a9 30       	cpi	r26, 0x09	; 9
  92:	b2 07       	cpc	r27, r18
  94:	e1 f7       	brne	.-8      	; 0x8e <.do_clear_bss_loop>
  96:	0e 94 6e 00 	call	0xdc	; 0xdc <main>
  9a:	0c 94 07 01 	jmp	0x20e	; 0x20e <_exit>

0000009e <__bad_interrupt>:
  9e:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

000000a2 <setup>:
  a2:	08 95       	ret

000000a4 <loop>:
  a4:	cf 93       	push	r28
  a6:	df 93       	push	r29
  a8:	cd b7       	in	r28, 0x3d	; 61
  aa:	de b7       	in	r29, 0x3e	; 62
  ac:	64 97       	sbiw	r28, 0x14	; 20
  ae:	0f b6       	in	r0, 0x3f	; 63
  b0:	f8 94       	cli
  b2:	de bf       	out	0x3e, r29	; 62
  b4:	0f be       	out	0x3f, r0	; 63
  b6:	cd bf       	out	0x3d, r28	; 61
  b8:	e8 e6       	ldi	r30, 0x68	; 104
  ba:	f0 e0       	ldi	r31, 0x00	; 0
  bc:	85 91       	lpm	r24, Z+
  be:	94 91       	lpm	r25, Z
  c0:	bc 01       	movw	r22, r24
  c2:	ce 01       	movw	r24, r28
  c4:	01 96       	adiw	r24, 0x01	; 1
  c6:	0e 94 00 01 	call	0x200	; 0x200 <strcpy_P>
  ca:	64 96       	adiw	r28, 0x14	; 20
  cc:	0f b6       	in	r0, 0x3f	; 63
  ce:	f8 94       	cli
  d0:	de bf       	out	0x3e, r29	; 62
  d2:	0f be       	out	0x3f, r0	; 63
  d4:	cd bf       	out	0x3d, r28	; 61
  d6:	df 91       	pop	r29
  d8:	cf 91       	pop	r28
  da:	08 95       	ret

000000dc <main>:
  dc:	0e 94 c5 00 	call	0x18a	; 0x18a <init>
  e0:	0e 94 51 00 	call	0xa2	; 0xa2 <setup>
  e4:	c0 e0       	ldi	r28, 0x00	; 0
  e6:	d0 e0       	ldi	r29, 0x00	; 0
  e8:	0e 94 52 00 	call	0xa4	; 0xa4 <loop>
  ec:	20 97       	sbiw	r28, 0x00	; 0
  ee:	e1 f3       	breq	.-8      	; 0xe8 <main+0xc>
  f0:	0e 94 00 00 	call	0	; 0x0 <__vectors>
  f4:	f9 cf       	rjmp	.-14     	; 0xe8 <main+0xc>

000000f6 <__vector_16>:
  f6:	1f 92       	push	r1
  f8:	0f 92       	push	r0
  fa:	0f b6       	in	r0, 0x3f	; 63
  fc:	0f 92       	push	r0
  fe:	11 24       	eor	r1, r1
 100:	2f 93       	push	r18
 102:	3f 93       	push	r19
 104:	8f 93       	push	r24
 106:	9f 93       	push	r25
 108:	af 93       	push	r26
 10a:	bf 93       	push	r27
 10c:	80 91 00 01 	lds	r24, 0x0100
 110:	90 91 01 01 	lds	r25, 0x0101
 114:	a0 91 02 01 	lds	r26, 0x0102
 118:	b0 91 03 01 	lds	r27, 0x0103
 11c:	30 91 08 01 	lds	r19, 0x0108
 120:	23 2f       	mov	r18, r19
 122:	2d 5f       	subi	r18, 0xFD	; 253
 124:	2d 37       	cpi	r18, 0x7D	; 125
 126:	20 f4       	brcc	.+8      	; 0x130 <__vector_16+0x3a>
 128:	01 96       	adiw	r24, 0x01	; 1
 12a:	a1 1d       	adc	r26, r1
 12c:	b1 1d       	adc	r27, r1
 12e:	05 c0       	rjmp	.+10     	; 0x13a <__vector_16+0x44>
 130:	23 2f       	mov	r18, r19
 132:	2a 57       	subi	r18, 0x7A	; 122
 134:	02 96       	adiw	r24, 0x02	; 2
 136:	a1 1d       	adc	r26, r1
 138:	b1 1d       	adc	r27, r1
 13a:	20 93 08 01 	sts	0x0108, r18
 13e:	80 93 00 01 	sts	0x0100, r24
 142:	90 93 01 01 	sts	0x0101, r25
 146:	a0 93 02 01 	sts	0x0102, r26
 14a:	b0 93 03 01 	sts	0x0103, r27
 14e:	80 91 04 01 	lds	r24, 0x0104
 152:	90 91 05 01 	lds	r25, 0x0105
 156:	a0 91 06 01 	lds	r26, 0x0106
 15a:	b0 91 07 01 	lds	r27, 0x0107
 15e:	01 96       	adiw	r24, 0x01	; 1
 160:	a1 1d       	adc	r26, r1
 162:	b1 1d       	adc	r27, r1
 164:	80 93 04 01 	sts	0x0104, r24
 168:	90 93 05 01 	sts	0x0105, r25
 16c:	a0 93 06 01 	sts	0x0106, r26
 170:	b0 93 07 01 	sts	0x0107, r27
 174:	bf 91       	pop	r27
 176:	af 91       	pop	r26
 178:	9f 91       	pop	r25
 17a:	8f 91       	pop	r24
 17c:	3f 91       	pop	r19
 17e:	2f 91       	pop	r18
 180:	0f 90       	pop	r0
 182:	0f be       	out	0x3f, r0	; 63
 184:	0f 90       	pop	r0
 186:	1f 90       	pop	r1
 188:	18 95       	reti

0000018a <init>:
 18a:	78 94       	sei
 18c:	84 b5       	in	r24, 0x24	; 36
 18e:	82 60       	ori	r24, 0x02	; 2
 190:	84 bd       	out	0x24, r24	; 36
 192:	84 b5       	in	r24, 0x24	; 36
 194:	81 60       	ori	r24, 0x01	; 1
 196:	84 bd       	out	0x24, r24	; 36
 198:	85 b5       	in	r24, 0x25	; 37
 19a:	82 60       	ori	r24, 0x02	; 2
 19c:	85 bd       	out	0x25, r24	; 37
 19e:	85 b5       	in	r24, 0x25	; 37
 1a0:	81 60       	ori	r24, 0x01	; 1
 1a2:	85 bd       	out	0x25, r24	; 37
 1a4:	ee e6       	ldi	r30, 0x6E	; 110
 1a6:	f0 e0       	ldi	r31, 0x00	; 0
 1a8:	80 81       	ld	r24, Z
 1aa:	81 60       	ori	r24, 0x01	; 1
 1ac:	80 83       	st	Z, r24
 1ae:	e1 e8       	ldi	r30, 0x81	; 129
 1b0:	f0 e0       	ldi	r31, 0x00	; 0
 1b2:	10 82       	st	Z, r1
 1b4:	80 81       	ld	r24, Z
 1b6:	82 60       	ori	r24, 0x02	; 2
 1b8:	80 83       	st	Z, r24
 1ba:	80 81       	ld	r24, Z
 1bc:	81 60       	ori	r24, 0x01	; 1
 1be:	80 83       	st	Z, r24
 1c0:	e0 e8       	ldi	r30, 0x80	; 128
 1c2:	f0 e0       	ldi	r31, 0x00	; 0
 1c4:	80 81       	ld	r24, Z
 1c6:	81 60       	ori	r24, 0x01	; 1
 1c8:	80 83       	st	Z, r24
 1ca:	e1 eb       	ldi	r30, 0xB1	; 177
 1cc:	f0 e0       	ldi	r31, 0x00	; 0
 1ce:	80 81       	ld	r24, Z
 1d0:	84 60       	ori	r24, 0x04	; 4
 1d2:	80 83       	st	Z, r24
 1d4:	e0 eb       	ldi	r30, 0xB0	; 176
 1d6:	f0 e0       	ldi	r31, 0x00	; 0
 1d8:	80 81       	ld	r24, Z
 1da:	81 60       	ori	r24, 0x01	; 1
 1dc:	80 83       	st	Z, r24
 1de:	ea e7       	ldi	r30, 0x7A	; 122
 1e0:	f0 e0       	ldi	r31, 0x00	; 0
 1e2:	80 81       	ld	r24, Z
 1e4:	84 60       	ori	r24, 0x04	; 4
 1e6:	80 83       	st	Z, r24
 1e8:	80 81       	ld	r24, Z
 1ea:	82 60       	ori	r24, 0x02	; 2
 1ec:	80 83       	st	Z, r24
 1ee:	80 81       	ld	r24, Z
 1f0:	81 60       	ori	r24, 0x01	; 1
 1f2:	80 83       	st	Z, r24
 1f4:	80 81       	ld	r24, Z
 1f6:	80 68       	ori	r24, 0x80	; 128
 1f8:	80 83       	st	Z, r24
 1fa:	10 92 c1 00 	sts	0x00C1, r1
 1fe:	08 95       	ret

00000200 <strcpy_P>:
 200:	fb 01       	movw	r30, r22
 202:	dc 01       	movw	r26, r24
 204:	05 90       	lpm	r0, Z+
 206:	0d 92       	st	X+, r0
 208:	00 20       	and	r0, r0
 20a:	e1 f7       	brne	.-8      	; 0x204 <strcpy_P+0x4>
 20c:	08 95       	ret

0000020e <_exit>:
 20e:	f8 94       	cli

00000210 <__stop_program>:
 210:	ff cf       	rjmp	.-2      	; 0x210 <__stop_program>

Alla riga 80 mi pare proprio che carichi l’indirizzo $80 di dove è alloggiata la stringa in Flash.

Non riesco a capire quando legge dalla flash, dovrebbe usare “lmp”, comunque quello che è importante è il simbolo 00000068 <_ZL7nomeVar> e quello che segue che lascia intendere che la stringa sia proprio scritta in flash.
Tu però usi arduino e quindi C++ e di conseguenza c’è il demangle dei simboli C++ _ZL7, mentre quello che segue e C e quindi in nomi delle variabili sono flat cioè così come compaiono nel sorgente.

00000008 <dummy>:
	...

00000009 <set>:
   9:	73 65       	ori	r23, 0x53	; 83
   b:	74 00       	.word	0x0074	; ????

0000000d <dif>:
   d:	64 69       	ori	r22, 0x94	; 148
   f:	66 00       	.word	0x0066	; ????

Già si vede che dummy non contiene niente in flash.
i ??? dovrebbe essere perchè ho compilato definendo COMPILING_AVR_LIBC che mi permette di scrivere codice generico
senza specificare la MCU. Cosa che mi fa capire che non posso usare quel flag se devo scrivere in flash, quindi sono costretto a compilare una libreria per ogni micro supportato, invece io volevo lasciare la scelta al programmatore.

Dovrei studiare un poco sia l’asm gnu che quello avr, no perchè di tutto quello che leggo capisco lo 0.1%. :blush:

Ciao.

MauroTec:
Non riesco a capire quando legge dalla flash, dovrebbe usare “lmp”,

Difatti se vai a vedere alla routine alla riga $200 trovi appunto l’istruzione lmp. Nel loop viene caricato l’indirizzo della Flash e poi questo viene passato alla funzione strcpy che estrae i dati direttamente dalla memoria non volatile.