PROGMEM?

Antworten
Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1079
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

PROGMEM?

Beitrag von fliegermichl »

Hallo,

wenn ich in C bzw. der Arduino IDE

Code: Alles auswählen

static const uint8_t PROGMEM A[10] ={0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
definiere, dann werden diese Daten im Flashspeicher abgelegt.
Gibt es da für den FPC eine Entsprechung?

PascalDragon
Beiträge: 535
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: PROGMEM?

Beitrag von PascalDragon »

Es müsste mit Hilfe des section Modifiers gehen (nicht getestet):

Code: Alles auswählen

const
  A: array[0..9] of Byte = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; section '.progmem';
Bitte beachte, dass du zumindest aktuell diese Daten dann manuell holen musst (wie hier beschrieben).
FPC Compiler Entwickler

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1079
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: PROGMEM?

Beitrag von fliegermichl »

PascalDragon hat geschrieben:
Fr 5. Feb 2021, 15:11
Es müsste mit Hilfe des section Modifiers gehen (nicht getestet):

Code: Alles auswählen

const
  A: array[0..9] of Byte = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; section '.progmem';
Danke. Das war's!
PascalDragon hat geschrieben:
Fr 5. Feb 2021, 15:11
Bitte beachte, dass du zumindest aktuell diese Daten dann manuell holen musst (wie hier beschrieben).
Das ist klar. Aber auf dem ATMega328P habe ich 32Kb Programmspeicher und nur 8Kb Ram. Da muß man ein unveränderliches starres array nicht im Ram haben.

Timm Thaler
Beiträge: 1220
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: PROGMEM?

Beitrag von Timm Thaler »

Ich glaub ich muss mal wieder Tutorial überarbeiten. Hier auf die Schnelle:

Einen String im Flash ablegen

Code: Alles auswählen

  Smenu : string[32] = 'Test                            '; section '.progmem';
Die Länge steht Pascal-üblich im ersten Byte (Smenu[0]). Ein String mit 32 Zeichen benötigt also 33 Bytes. Da der AVR Flash Wordweise belegt, werden 34 Bytes verwendet.

Eine LOT im Flash ablegen, hier für einen NTC Sensor

Code: Alles auswählen

  Pntccal : array [0..Cntclen] of int16 = (
    3976, 3932, 3875, 3802, 3711, 3600, 3466, 3311,
    3134, 2937, 2725, 2502, 2275, 2048, 1828, 1619,
    1424, 1246, 1086,  943,  817,  706,  611,  528,
     457,  396,  344,  298,  260,  227,  198,  174,
     152,  134,  118,  105,   93,   82,   73,    0); section '.progmem';
Ein Byte aus dem Flash holen

Code: Alles auswählen

function load_pgm_byte(paddr : pbyte) : uint8; assembler; nostackframe;
// Rein: r24, r25 Pointer
// Raus: r24
asm
  push r31
  push r30

  mov r30, r24
  mov r31, r25
  lpm r24, Z

  pop r30
  pop r31
end[];       
Aufruf des k-ten Wertes mit

Code: Alles auswählen

temp := load_pgm_word(@Pntccal[k]);
Ein Word aus dem Flash holen

Code: Alles auswählen

function load_pgm_word(paddr : pword) : uint16; assembler; nostackframe;
// Rein: r24, r25 Pointer
// Raus: r24, r25 Daten
asm
  push r31
  push r30

  mov r30, r24
  mov r31, r25
  lpm r24, Z+
  lpm r25, Z

  pop r30
  pop r31
end[];        
Dabei ist es egal ob das signed oder unsigned ist. Man kann das über einen Typecast zuweisen.

Code: Alles auswählen

procedure copy_pgm_string(psrc, pto : pchar); assembler; nostackframe;
// Rein: r22, r23, r24, r25 Pointer
label
  loop;
asm
  push r31
  push r30
  push r27
  push r26
  push r16
  push r0

  mov r26, r22
  mov r27, r23
  mov r30, r24
  mov r31, r25
  lpm r0, Z+
  st X+, r0
  mov r16, r0
  loop:
  lpm r0, Z+
  st X+, r0
  dec r16
  brne loop

  pop r0
  pop r16
  pop r26
  pop r27
  pop r30
  pop r31
end[]; 
Einen String aus dem Flash in einen Buffer schreiben. Die Länge steht Pascal-üblich im ersten Byte.

Aufruf mit Flashadresse, Bufferadresse

Code: Alles auswählen

copy_pgm_string(@Smenu, @dispbuf);

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1079
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: PROGMEM?

Beitrag von fliegermichl »

Danke für diese Infos. Das sollte unbedingt in das Tutorial!

Ich habe gelesen, dass man die verwendeten Register nicht selbst pushen und poppen soll, sondern dem Compiler diese in den eckigen Klammern am Ende mitteilen.

Code: Alles auswählen

function pgm_read_byte(addr: PByte): uint8; assembler; nostackframe;
// Rein: r24, r25 Pointer
// Raus: r24
asm
  mov r30, r24
  mov r31, r25
  lpm r24, Z
end['r30','r31'];
Edit: Da bekomme ich eine Warnung "Registerlist is ignored for pure assembler routines" - also doch selbst pushen und poppen.

PascalDragon
Beiträge: 535
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: PROGMEM?

Beitrag von PascalDragon »

fliegermichl hat geschrieben:
Sa 6. Feb 2021, 07:20
Edit: Da bekomme ich eine Warnung "Registerlist is ignored for pure assembler routines" - also doch selbst pushen und poppen.
Das ist nur für inline assembly relevant, da hier der Compiler nicht mit überprüft welche Register beeinflusst werden (vor allem die indirekten Beeinflussungen wie die Verwendung von EDX im Fall von Multiplikation auf x86). Bei assembly Routinen hält sich der Compiler an die ABI und solange du das auch machst, ist alles fein.
FPC Compiler Entwickler

Timm Thaler
Beiträge: 1220
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: PROGMEM?

Beitrag von Timm Thaler »

Ja, wie Pascal Dragon sagt:

Bei Inline-Assembler, also Assembler der in normalen Proceduren steht ist das nötig.

Bei reinen Assembler-Routinen (function ...; assembler;) muss man selbst drauf achten, benutzte Register zu sichern. Ganz wichtig ist das Poppen in der richtigen Reihenfolge, sonst Stack-Overflow. Und ganz wichtig ist auch das Nullen von r0 am Ende - welches von Pascal als Nullregister verwendet wird - wenn man mul verwendet, weil mul r0, r1 beschreibt.

Also die eckigen Klammern bei mir sind Relikte die eigentlich weg können.

nostackframe verhindert, dass ein Stackframe angelegt wird, das spart nochmal einiges an Speicher. Beim Stackframe wird noch Stackpointer, Sreg usw. gespeichert. Das ist bei normalen Funktionsaufrufen unnötig.

Bei Assembler in Interrupt-Routinen ist es aber sehr wichtig, auch Sreg und Stackpointer zu sichern, weil der Interrupt ja an jeder Stelle im Programm zuschlagen kann.

Also man sollte schon Erfahrung mit Assembler haben, aber dann ist das sehr mächtig. Die Assembler-Implementierung in Pascal ist zum Glück deutlich besser als in C oder Ada.

Antworten