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

/*****************************************\
 * 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;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded