Source file: /~heha/basteln/PC/FunkUsb/FunkUsb.zip/FunkUsb/ve/flash_rw.cpp

/*****************************************\
 * 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
Wrong umlauts? - Assume file is ANSI (CP1252) encoded