#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;
}
byte myFlash::eew(byte a, byte b) {
#if 0
return eeprom_write_byte((byte*)a,b);
#else
EEARL=a;
EECR=1;
a=EEDR;
if (a==b) return a;
EEDR=b;
if (b==0xFF) EECR|=0x10; // Erase Only
if (!(~a&b)) EECR|=0x20; // Write Only
EECR|=4;
EECR|=2;
while (EECR&2);
return a;
#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; // Der erste Schreibvorgang geht ins Wurzelverzeichnis,
--needsec; // alle weiteren in die FAT.
}
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.sizeW[1]) return false;
if (s->dirent.sizeB[1]>(FEND1-FSTART-1)<<SEC_SH>>8) return false;
byte needsec=1+((s->dirent.sizeW[0]+(1<<SEC_SH)-1)>>SEC_SH);
if (needsec>getFreeSec()) return false; // Kein Platz im Dateisystem
byte a;
for (a=1;a<FSTART;a++) if (eer(a)==0xFF) { // Freien Platz im Wurzelverzeichnis suchen
reserve(a,needsec);
createdDirEnt=a;
byte x=eer(a); // Erster Dateisektor = Metadaten (Dateiname usw.)
flashwrite(x,s->data);
lastWrittenPage=x; // leider muss das als statische Variable geführt werden
return true;
}
return false;
}
bool myFlash::writeNextPage(const sec_t*s) {
byte x=lastWrittenPage;
if (x<FSTART || x>=FEND1) return false; // Notbremse
x=eer(x); // Nächster Datensektor (bereits reserviert)
if (x<FSTART || x>=FEND1) return false; // Notbremse
flashwrite(x,s->data);
lastWrittenPage=x;
return true;
}
bool myFlash::DeleteObject(byte handle) {
for(;;){
byte x=eew(handle,0xFF); // Freispeicher daraus machen
if (x==0xFE) return true; // Ende der Kette erreicht
if (x<FSTART || x>=FEND1) return false; // Fehler!
handle=x;
}
}
| Detected encoding: UTF-8 | 0
|