/*****************************************\
* Flash-Speicher mit wahlfreiem Zugriff *
\*****************************************/
/* Flash-Adresse ungetestet für > 64 KByte, SPM_PAGESIZE ohne Einschränkungen
* Kode für Application Section, nicht für evtl. Bootloader Section geeignet.
* ATtiny: Haben keine Bootloader-Sektion! Dafür drei hübsche GPIORs.
* Die SELFPRGEN-Fuse (EFUSE Bit 0) muss gebrannt (=0) sein!
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/boot.h>
#include <string.h> // NULL
#include "flash_rw.h"
/* Der Zugriff erfolgt solange über einen Puffer,
* bis ein anderer Sektor gelesen werden muss.
* Somit kann man kleine Datenportionen schreiben,
* ohne den Flash zu stark zu „verbrauchen“.
* Man darf aber kein memcpy_P auf den infrage
* kommenden Flash-Bereich benutzen!
* Sondern flash_read() verwenden.
* Oder man stellt durch vorheriges extern_flash_flush() sicher,
* dass der Flash ausgeschrieben ist.
*/
typedef uint8_t uchar;
typedef uint16_t ushort;
#define IS_DIRTY (GPIOR0&0x40)
#define SET_DIRTY GPIOR0|=0x40
#define CLEAR_DIRTY GPIOR0&=~0x40
static uchar buffer[SPM_PAGESIZE] __attribute__ ((section (".noinit")));
// Referenzierter Flash-Sektor
static size_t FlashPtr __attribute__ ((section (".noinit")));
// Diese Version ist gegenüber boot.h gekürzt um:
// - Kein CLI, der Prozessor steht sowieso
// - Kein Warten, der Prozessor steht sowieso
// Müsste 7,4 ms dauern: 1x löschen, 1x schreiben
static void program_page(ushort*flash, const ushort*buf) {
uchar cnt=SPM_PAGESIZE/2;
eeprom_busy_wait();
boot_page_erase(flash);
do boot_page_fill(flash++, *buf++); while(--cnt);
flash-=SPM_PAGESIZE/2;
boot_page_write(flash);
}
void flash_read(void *dst, PGM_VOID_P src, size_t len) {
// Anstatt umständlich die (3 möglichen) Bereiche zu finden,
// wird hier einzelbyteweise die Fallunterscheidung gemacht.
// Das kostet nicht sonderlich viel auf einem AVR.
if (IS_DIRTY) {
uchar *s = (uchar*)src;
uchar *d = dst;
if (len) do{
size_t offs = (size_t)s-FlashPtr;
*d++ = offs < SPM_PAGESIZE ? buffer[offs] : __LPM(s);
s++;
}while(--len);
}else memcpy_P(dst,src,len);
}
void flash_write(PGM_VOID_P dst, const void *src, size_t len) {
// Auch hier wieder einzelbyteweise arbeiten.
const uchar *s = src;
size_t d = (size_t)dst;
if (len) do{
size_t dpage = d&~(SPM_PAGESIZE-1);
if (!IS_DIRTY || dpage!=FlashPtr) { // nicht im Puffer?
if (__LPM((char*)d) == *s) goto skip; // Byte bleibt gleich?
if (IS_DIRTY && memcmp_P(buffer,(prog_uchar*)FlashPtr,SPM_PAGESIZE))
extern_flash_flush(); // Puffer ausschreiben
FlashPtr = dpage; // neu laden
memcpy_P(buffer,(prog_uchar*)dpage,SPM_PAGESIZE);
SET_DIRTY;
}
buffer[d-dpage]=*s; // Datenbyte immer in den Puffer!
skip:
s++;
d++;
}while(--len);
}
void flash_flush(void) {
if (!IS_DIRTY) return; // ist nicht in Verwendung
program_page((ushort*)FlashPtr,(ushort*)buffer);
CLEAR_DIRTY; // ist jetzt frei, FlashPtr ungültig
}
void* eeprom_update(void*dst, const void*src, size_t len) {
const uchar *s=src;
if (!eeprom_is_ready() || !len) return NULL;
EEAR = (ushort)dst;
do{
EECR|=0x01; // lesen
uchar b=EEDR;
if (b!=*s) {
EEDR=*s;
cli();
EECR|=0x04;
EECR|=0x02; // schreiben
sei();
return (void*)s; // raus mit RAM-Adresse
}
s++;
EEAR++;
}while (--len);
return NULL;
}
Vorgefundene Kodierung: UTF-8 | 0
|