/*****************************************\
* 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 <avr/pgmspace.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 unsigned char byte;
#define IS_DIRTY (GPIOR0&0x40)
#define SET_DIRTY GPIOR0|=0x40
#define CLEAR_DIRTY GPIOR0&=~0x40
static byte buffer[SPM_PAGESIZE] __attribute__ ((section (".noinit")));
// Referenzierter Flash-Sektor
static byte FlashPage __attribute__ ((section (".noinit")));
static byte*FlashAddr(byte flashpage) {return (byte*)(flashpage*SPM_PAGESIZE);}
// 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(byte flashpage, const void*buf) {
eeprom_busy_wait();
short*flash = (short*)FlashAddr(flashpage);
boot_page_erase(flash);
byte cnt = SPM_PAGESIZE/2;
const short*src=(const short*)buf;
do boot_page_fill(flash++,*src++); while(--cnt);
flash-=SPM_PAGESIZE/2;
boot_page_write(flash);
}
void flash_read(void *dst, const void*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) {
const byte *s = reinterpret_cast<const byte*>(src);
byte *d = reinterpret_cast<byte*>(dst);
const byte *p = FlashAddr(FlashPage);
if (len) do{
size_t offs = s-p;
*d++ = offs < SPM_PAGESIZE ? buffer[offs] : pgm_read_byte(s);
s++;
}while(--len);
}else memcpy_P(dst,src,len);
}
static void pgm_write_byte(byte*d,byte b) {
byte dpage = (size_t)d/SPM_PAGESIZE;
if (!IS_DIRTY || FlashPage!=dpage) { // nicht im Puffer?
if (pgm_read_byte(d) == b) return; // Byte bleibt gleich?
if (IS_DIRTY && memcmp_P(buffer,FlashAddr(FlashPage),SPM_PAGESIZE))
extern_flash_flush(); // Puffer ausschreiben
FlashPage = dpage; // neu laden
memcpy_P(buffer,FlashAddr(dpage),SPM_PAGESIZE);
SET_DIRTY;
}
buffer[(size_t)d&SPM_PAGESIZE-1]=b; // Datenbyte immer in den Puffer!
}
void flash_write(void*dst, const void*src, size_t len) {
// Auch hier wieder einzelbyteweise arbeiten.
const byte *s = reinterpret_cast<const byte*>(src);
byte *d = reinterpret_cast<byte*>(dst);
if (len) do pgm_write_byte(d++,*s++);
while(--len);
}
void flash_flush() {
if (!IS_DIRTY) return; // ist nicht in Verwendung
program_page(FlashPage,buffer);
CLEAR_DIRTY; // ist jetzt frei, FlashPtr ungültig
}
void* eeprom_update(void*dst, const void*src, size_t len) {
const byte *s = reinterpret_cast<const byte*>(src);
if (!eeprom_is_ready() || !len) return NULL;
EEAR = (size_t)dst;
do{
EECR|=0x01; // lesen
byte 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;
}
| Detected encoding: ANSI (CP1252) | 4
|
|
|