Mit svar på et spørgsmål i Facebook-gruppen Danske Arduino Entusiaster omkring Arduino ROM/Flash, PROGMEM og system-inklude-filer.
Spørgsmål
Hej er der en der ved hvor jeg kan hente dett lib. <avr/pgmspace.h> jeg skal bruge denne funktion PROGMEM
så jeg kan gemme et billede i Arduino uden SD kort
det kan være der er en der kender en anden måde at gøre det på.
Svar
pgmspace.h er en inklude-fil som er en del af c-biblioteket til AVR-arkitekturen (avr-libc). C-bibliotekets inklude-filer vil normalt ligge i kompilerens “system include”-sti (se GCC options -I og -isystem). Dermed kan den inkluderes blot med “#include <avr/pgmspace.h>”. Se evt. også Arduino-referencen på https://www.arduino.cc/en/Reference/PROGMEM.
Bemærk at PROGMEM ikke er en funktion, men en storage modifier (lager-modifikator) som fortæller kompileren at den kan placere en en given variabel i ikke-skrivbar lager (ROM/Flash). Der skal efterfølgende anvendes specielle funktioner til at læse data fra en sådan variabel (se referencen).
Arduino-frameworket har dog lavet en nem måde at placere konstant-strenge i Flash på (normalt lagres de i SRAM!), nemlig funktionen F() som kan anvendes direkte i f.eks. printf/write/print (Serial.print(F(“Waiting for connection”));)
Hvis du vil inspicere indholdet af pgmspace.h, kan du finde filen i Arduino IDE’ets installations-mappe under hardware/tools/avr/avr/include/avr/pgmspace.h. Det er ikke en man kan/skal redigere manuelt i, da den er tæt koblet med den binære kode i selve biblioteket.
Se svaret på Facebook.
Den videre færd med F()
Da jeg ikke kunne finde en uddybende forklaring på F()-funktionen (som egentlig er en makro) i Arduino-dokumentationen (brugen nævnes meget kort i PROGMEM , Memory og Print), gravede jeg efterfølgende lidt rundt for at lære mere. I de sparsomme Arduino-eksempler er den anvendt udelukkende med konstante strenge, hvilket også viser sig at være et krav (eller i hvert fald noget der kan castes til const char *).
Makroen er defineret af Arduino-frameworket i filen hardware/arduino/avr/cores/arduino/WString.h (referencerne er ifht. min lokale installation af Arduino 1.6.9, pt. er nyeste 1.6.13) således:
#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
Altså parametren til F() bruges som parameter til PSTR() (progmem string, er mit bud på navn) som er en makro defineret i pgmspace.h fra avr-libc.
Dens funktion er at caste parametrens type til konstant streng-pointer med PROGMEM modifier;
#define PSTR(s) ((const PROGMEM char *)(s))
Skal vi se på hvad PROGMEM rent faktisk er, så finder vi endnu et sæt makroer der ender med at blive udviddet til kompiler-attributten __progmem__, igen definieret i pgmspace.h (hardware/tools/avr/avr/include/avr/pgmspace.h):
#define PROGMEM __ATTR_PROGMEM__
#define __ATTR_PROGMEM__ __attribute__((__progmem__))
__progmem__ attributten er en instruks til kompileren (GCC) og linkeren om ved programmering/flashing af programmet at placere disse data i en sektion af hukommelsen der hedder “.progmem“. Se evt. mere om dette i GCC-kompilerens dokumentation. For hver AVR-chip kompileren understøtter er der eksakte definitioner af hvilke hukommelsesadresser .progmem ligger på for netop denne chip.
Dvs. når man i sin kode skriver F(“test”) får man i virkeligheden:
(reinterpret_cast<const __FlashStringHelper *>(((const __attribute__((__progmem__)) char *)(“test”)))
Altså en konstant streng der lagres i AVR-processorens progmem-sektion, og som returværdi får en pointer til en konstant instans af en klasse kaldet “__FlashStringHelper“. Denne klasse må være lavet sådan at den anvender de korrekte mekanismer til at læse fra progmem-området (måske mere om dette i en senere artikel). Arduinos funktion-bibliotek (Serial.print() mm.) er lavet således at de direkte kan tage en parameter af denne type som erstatning for en konstant-streng (og det er netop her Arduino-frameworket viser sin værdi ved at abstrahere sådanne kompleksiteter væk fra programmøren).