#include <avr/io.h>
#include "myFlash.h"
#include <string.h> // memset
#include <avr/eeprom.h>
void fattime::out2(char*s, byte v) {
*--s=v%10+'0';
*--s=v/10+'0';
}
fattime::operator char*() const{
word t=(int)this<0?pgm_read_word(&time):time;
string[15]=0;
out2(string+15,t<<1&0x3E); // Sekunde
out2(string+13,t>>5&0x3F); // Minute
out2(string+11,t>>11); // Stunde
string[8]='T';
word d=(int)this<0?pgm_read_word(&date):date;
out2(string+8,d&0x1F); // Tag
out2(string+6,d>>5&0x0F); // Monat
out2(string+4,(d>>9)-30); // Jahr
out2(string+2,20);
return string;
}
byte fattime::in2(const char*s) {
byte r=(*s++-'0')*10;
return r+*s++-'0';
}
void fattime::operator() (const char*s) {
date=in2(s+2)+30<<9 | in2(s+4)<<5 | in2(s+6);
time=in2(s+9)<<11 | in2(s+11)<<5 | in2(s+13)>>1;
}
char fattime::string[16];
void myFlash::sec_t::empty() {
memset(data,0xFF,sizeof data);
}
byte myFlash::eer(byte a) {
EEARL=a;
EECR|=1;
return EEDR;
}
void myFlash::eew(byte a, byte b) {
#if 0
eeprom_write_byte((byte*)a,b);
#else
EECR=0;
EEARL=a;
EECR|=1;
a=EEDR;
if (a!=b) {
// if (a==0xFF) EECR|=0x20; // Write Only
// if (b==0xFF) EECR|=0x10; // Erase Only
EEDR=b;
EECR|=4;
EECR|=2;
while (EECR&2);
}
#endif
}
/* Um Flash-Speicher zu beschreiben benötigt jegliches AVR-Anwendungsprogramm,
welches auf einem Controller mit Urlader-Unterstützung läuft, die Hilfe des Urladers!
Denn der "spm"-Befehl kann/darf nur im Urlader-Bereich ausgeführt werden.
Gibt es keinen Urlader, muss "spm" im Flash ganz hinten angeordnet werden.
Aber „kein Urlader“ ist für einen ATmega32U4 oder einen Arduino wenig sinnvoll.
Daraus folgt, dass für ein Anwendungsprogramm Flash-Datenspeicher
weder ganz vorn [Interruptvektoren] noch ganz hinten ["spm"-Befehl]
liegen kann, sondern immer dazwischen.
Hier ist es der Urlader <ubaboot>. Da <ubaboot> auf Kürze optimiert ist,
gibt es keinen Platz für standardisierte Eintrittspunkte.
Stattdessen wird <do_spm> und <do_spm_rwwsre> verwendet, die Adresse hart kodiert.
Da ein Rücksprung aus <do_spm> in das Anwendungsprogramm bei "erase" und "write" crasht,
muss auf dem Stack ein Rücksprung zu <do_spm_rwwsre> zusammengebastelt werden,
um dazu im Urlader-Bereich zu verweilen.
Die Adresse von <do_spm> wird im makefile als Linker-Symbol übergeben,
<do_spm_rwwsre> ist stets 1 Instruktion davor.
Alle diese Macken entfallen bei Controllern ohne Urlader-Unterstützung,
etwa alle ATtiny und bspw. ATmega48. Aber die haben nur wenig Flash.
Das Anwendungsprogramm kann sich selbst überschreiben, das muss es selbst prüfen;
der Urlader bleibt durch ein Sperrbit vor Überschreiben geschützt.
*/
// Nur Aufruf aus Assembler! Nicht inlinen!
// do_spm mit anschließendem Rücksetzen des RWW-Sperrbits, alles im Urlader
// Interrupts müssen gesperrt sein, Watchdog wird (in <ubaboot>) beruhigt.
// PE: R19 = Operation für SPMCR (3 = löschen, 5 = schreiben)
// VR: R0, R18
// Fürs Füllen (R19 = 1) kann man dospm aufrufen.
extern "C" void dospm2() __attribute__((naked));
void dospm2() {
asm(
" ldi r18,lo8(pm(do_spm-2)) \n" // Zeiger auf RWW-Enable-Funktion
" push r18 \n"
" ldi r18,hi8(pm(do_spm-2)) \n"
" push r18 \n"
"dospm: jmp do_spm \n");
}
void myFlash::flashwrite(byte sec, const void*b) {
asm(
" ldi r19,3 \n" // page erase
" rcall dospm2 \n"
" ldi r19,1 \n" // page fill
" ldi r18,64 \n"
"1: ld r0,X+ \n"
" ld r1,X+ \n"
" rcall dospm \n"
" adiw ZL,2 \n"
" dec r18 \n"
" brne 1b \n"
" sbiw ZL,2 \n"
" ldi r19,5 \n" // page write
" rcall dospm2 \n"
" clr r1 \n"
::"z"(sec<<SEC_SH),"x"(b):"r18","r19");
}
// b = Adresse vorheriger FAT- oder Verzeichniseintrag
void myFlash::reserve(byte b, byte needsec) {
for (byte c=FSTART; needsec; c++) if (eer(c)==0xFF) {
eew(b,c); // Freien Platz im Flash finden und reservieren
b=c;
--needsec;
}
eew(b,0xFE); // „Letztes Glied der Kette“ eintragen
}
byte myFlash::lastWrittenPage;
byte myFlash::createdDirEnt;
// Die Länge muss hier bekannt sein!
bool myFlash::Create(const sec_t*s) {
if (s->dirent.size>getFreeSpace()+(1<<SEC_SH)) return false; // Kein Platz im Dateisystem
byte a;
for (a=1;a<FSTART;a++) if (eer(a)==0xFF) goto found1; // Freien Platz im Wurzelverzeichnis suchen
return false;
found1:
byte needsec=(word)s->dirent.size+(2<<SEC_SH)-1 >> SEC_SH; // mindestens 1 Sektor für den Verzeichniseintrag
reserve(a,needsec);
createdDirEnt=a;
byte x=eer(a);
flashwrite(x,s->data);
lastWrittenPage=x;
return true;
}
bool myFlash::writeNextPage(const sec_t*s) {
byte x=lastWrittenPage;
if (x<FSTART || x>=FEND1) return false;
x=eer(x); // Nächster Datensektor (bereits reserviert)
if (x<FSTART || x>=FEND1) return false;
flashwrite(x,s->data);
lastWrittenPage=x;
return true;
}
bool myFlash::DeleteObject(byte handle) {
for(;;){
byte next=eer(handle);
eew(handle,0xFF); // Freispeicher daraus machen
if (next>=FEND1) return true; // Ende der Kette erreicht
if (next<FSTART) return false; // Fehler!
handle=next;
}
}
Vorgefundene Kodierung: UTF-8 | 0
|