Re: Atmel Data Flash
2005-10-29 by raimond712002
The file below is my dataflash library. It is a professional
implementation, but I can make it open source here because you
can't know the real application (which is very big).
Some comments: SPI is mode 3, I can't remember why, but I can
remember this was a 'must' at that time. GCC please.
Check the SPI pins, they are used for programming the mega32 too.
Check the 3.3V power for the dataflash chip.
Good luck!
/*====================================================================
====================
AT45DB041 DataFlash Library
Chip: ATMEGA64
Freq: 4.9152 MHz
======================================================================
==================*/
#include <string.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include "type.h"
#include "rtos.h"
#include "dataflash.h"
/* NVDS EEPROM MEMORY - 16bit locations
NVDS Memory = High endurance eeprom memory, soft implemented, 16bit.
One high endurance 16bit location is maintained by an eeprom array
organized
as follow:
- first byte is the direction byte (0xFF=ahead, 0x00=back).
- FACTOR-1 bytes as moving flags.
- FACTOR words for storage.
So, for one 16bit high endurance eeprom location, it is consumed
FACTOR*3
eeprom bytes! FACTOR is the multiplication endurance factor for the
internal
eeprom. So, if the internal eeprom has an 100,000 writes endurance,
a FACTOR = 100 will make the 16bit value have 10,000,000 writes
endurance!
(but with the expense of 300 bytes of eeprom for only one 16bit
value!).
The eeprom array need not be initialized. Anyway, first reading the
value
after reset with ReadNVDS() function should be compared against
permissible
range (if any).
*/
#define FACTOR 16
/* Search the last flag != 0xFF in eeprom (16 flags zone)
Return the address of the high byte of the value */
static char search_last(char *eetab)
{
char i;
for (i = FACTOR-1; i; i--) {
if (eeprom_read_byte(eetab+i) != 0xff) break;
}
return i;
}
static void write_ahead(char *eetab, char index, uint val)
{
eeprom_write_byte(eetab+FACTOR+index*2+2, (char)(val>>8));
eeprom_write_byte(eetab+FACTOR+index*2+3, (char)val);
eeprom_write_byte(eetab+index+1, 0x00);
}
static void write_back(char *eetab, char index, uint val)
{
eeprom_write_byte(eetab+FACTOR+index*2-2, (char)(val>>8));
eeprom_write_byte(eetab+FACTOR+index*2-1, (char)val);
eeprom_write_byte(eetab+index, 0xff);
}
void WriteNVDS(char *eetab, uint val)
{
char i;
i = search_last(eetab);
if (eeprom_read_byte(eetab) == 0xff) { /* DIR = ahead */
if (i != FACTOR-1) { /*
It's not last */
write_ahead(eetab, i, val);
} else {
/* Last pointer */
write_back(eetab, i, val);
eeprom_write_byte(eetab, 0x00); /* DIR ->
back */
}
} else {
/* DIR = back */
if (i) {
/* It's not first */
write_back(eetab, i, val);
} else {
/* First pointer */
write_ahead(eetab, i, val);
eeprom_write_byte(eetab, 0xff); /* DIR ->
ahead */
}
}
}
uint ReadNVDS(char *eetab)
{
char i;
i = search_last(eetab) * 2;
return ((uint)eeprom_read_byte(eetab+FACTOR+i)<<8) +
eeprom_read_byte(eetab+FACTOR+i+1);
}
/* SPI port */
/* Enables spi port in master mode.
Direction for bits are set in main() */
void spi_init()
{
SPCR = 0x5C; /* MODE 3, Master, fastest rate, no
intr. */
SPSR = 1; /* X2 clk */
}
/* Shifts one byte on the spi port. */
void wrspi(char c)
{
SPDR = c;
while (!(SPSR & (1<<SPIF)));
}
char rdspi()
{
SPDR = 0;
while (!(SPSR & (1<<SPIF)));
return SPDR;
}
/* DATA FLASH
Reading and writing to DataFlash device is implemented in a circular
fashion.
There are two pointers, one for reading and one for writing. The
DataFlash
device contains 2048 pages, each page containing two records. So,
there are
4096 records, 132 bytes each.
Because the writing is done in a circular fashion, the wearing is
assured
(every page must be updated once per 10000 writes in that sector).
Because
the (larger) sector has 512 pages, a page is updated once every 512
writes,
so this is OK.
Pointers for in and out are maintained in a high endurance eeprom
area,
because they must be updated every record write/read from DataFlash.
High endurance eeprom is achieved in software, every high endurance
16bit
location consuming an array in eeprom. See high endurance eeprom
section.
*/
uint DFPin, DFPout; /* pointers in
dataflash */
char EETin[48] eeprom; /* memorized in high
endurance eeprom */
char EETout[48] eeprom;
void DFPin_read()
{
DFPin = ReadNVDS((char*)EETin);
if (DFPin >= 4096) DFPin = 0;
}
void DFPout_read()
{
DFPout = ReadNVDS((char*)EETout);
if (DFPout >= 4096) DFPout = 0;
}
void DFPin_write()
{
WriteNVDS((char*)EETin, DFPin);
}
void DFPout_write()
{
WriteNVDS((char*)EETout, DFPout++);
}
/* Inits the DataFlash device.
Must be called only once, after reset. */
void df_init()
{
spi_init();
DFPin_read();
DFPout_read();
}
#define SS0 clr(PORTB,0) /* CS low */
#define SS1 set(PORTB,0) /* CS high */
void df_ready()
{
char stat;
for (;;) {
SS0;
wrspi(0x57); /* opcode */
stat = rdspi(); /* read status byte */
SS1;
if (stat & 0x80) break; /* ready */
os_pause();
}
}
/* Writes a block of data (132bytes = 1/2page) to the DataFlash
device.
The write is controlled by the DFPin pointer. Before calling,
check if there is space left in dataflash!
src: source array.
Usage: if (!df_full()) df_write(src); */
void df_write(char *src)
{
uchar i, BFA;
BFA = (DFPin & 1) ? 132 : 0; /* buffer address */
/* Main memory to buffer1 transfer:
\ [0x53] [0000.PPPP] [PPPP.PPPx] [xxxx.xxxx] / */
df_ready();
SS0;
wrspi(0x53); /*
opcode */
wrspi(DFPin>>8); /*
address */
wrspi(DFPin);
wrspi(0);
SS1;
/* Write src to buffer1:
\ [0x84] [0000.xxxx] [xxxx.xxxB] [BBBB.BBBB] ...data... /
*/
df_ready();
SS0;
wrspi(0x84); /*
opcode */
wrspi(0);
/* address */
wrspi(0);
wrspi(BFA);
for (i = 0; i < 132; i++) {
wrspi(src[i]); /*
data */
}
SS1;
/* Buffer program with builtin erase:
\ [0x83] [0000.PPPP] [PPPP.PPPx] [xxxx.xxxx] / */
df_ready();
SS0;
wrspi(0x83); /*
opcode */
wrspi(DFPin>>8); /*
address */
wrspi(DFPin);
wrspi(0);
SS1;
DFPin = (DFPin + 1) & 4095; /* Update in pointer
*/
DFPin_write();
}
/* Reads a block of data from the DataFlash device. The read is
controlled
by the DFPout pointer. 132 bytes are read. Before calling, check
if there
are still records in dataflash!
dst: the address of the buffer where to put the bytes read.
Usage: if (!df_empty()) df_read(dst); */
void df_read(char *dst)
{
uchar i, BFA;
BFA = (DFPout & 1) ? 132 : 0; /* buffer address */
/* Main memory to buffer1 transfer:
\ [0x53] [0000.PPPP] [PPPP.PPPx] [xxxx.xxxx] / */
df_ready();
SS0;
wrspi( 0x53 ); /*
opcode */
wrspi( DFPout >> 8 ); /* address */
wrspi( DFPout );
wrspi( 0 );
SS1;
/* Read buffer1 to dst:
\ [0x54] [0000.xxxx] [xxxx.xxxB] [BBBB.BBBB]
[x] ...data... / */
df_ready();
SS0;
wrspi( 0x54 ); /*
opcode */
wrspi( 0 );
/* address */
wrspi( 0 );
wrspi( BFA );
wrspi( 0 ); /*
don't care */
for (i = 0; i < 132; i++) {
dst[ i ] = rdspi(); /* data */
}
SS1;
DFPout = (DFPout + 1) & 4095; /* Update out pointer
*/
DFPout_write();
}
/* Get the number of records stored in DataFlash */
uint df_records()
{
return (DFPin - DFPout + ((DFPin < DFPout) ? 4096 : 0));
}
/* Get the EMPTY flag.
DataFlash is empty, we have nothing to read. */
char df_empty()
{
return (df_records() == 0);
}
/* Get the FULL flag.
DataFlash is full, we cannot write anymore. */
char df_full()
{
return (df_records() == 4095);
}