/**
* @file	at45db.c
* @brief AT45DB Module
*
* Support Atmel AT45DB Flash Memory
*
* @author      Rustem Kalimullin
* @par E-mail:
*              hellos@mail.ru
* @par Copyright:
*              (c) Kurt, 2005
*/
 
#include "common.h"
#include "at45db.h"
#include "spi.h"
 
// Define DF_AUTODETECT_FEATURES to enable autodetect features
//#define DF_AUTODETECT_FEATURES
 
//! defines for all opcodes
enum {
	BLOCK_ERASE                      = 0x50, /**< erase 512 pages */
	MAIN_MEMORY_PAGE_READ            = 0x52, /**< main memory page read */
	MM_PAGE_TO_B1_XFER               = 0x53, /**< main memory page to buffer 1 transfer */
	BUFFER_1_READ                    = 0x54, /**< Buffer 1 read */
	MM_PAGE_TO_B2_XFER               = 0x55, /**< main memory page to buffer 2 transfer */
	BUFFER_2_READ                    = 0x56, /**< buffer 2 read */
	STATUS_REGISTER_READ             = 0x57, /**< status register */
	AUTO_PAGE_REWRITE_THROUGH_B1     = 0x58, /**< auto page rewrite through buffer 1 */
	AUTO_PAGE_REWRITE_THROUGH_B2     = 0x59, /**< auto page rewrite through buffer 2 */
	MM_PAGE_TO_B1_COMP               = 0x60, /**< main memory page compare to buffer 1 */
	MM_PAGE_TO_B2_COMP               = 0x61, /**< main memory page compare to buffer 2 */
	ARRAY_READ                       = 0x68, /**< start continuous array read */
	PAGE_ERASE                       = 0x81, /**< erase a 264 byte page */
	MM_PAGE_PROG_THROUGH_B1          = 0x82, /**< main memory page program through buffer 1 */
	B1_TO_MM_PAGE_PROG_WITH_ERASE    = 0x83, /**< buffer 1 to main memory page program with built-in erase */
	BUFFER_1_WRITE                   = 0x84, /**< Buffer 1 write */
	MM_PAGE_PROG_THROUGH_B2          = 0x85, /**< main memory page program through buffer 2 */
	B2_TO_MM_PAGE_PROG_WITH_ERASE    = 0x86, /**< buffer 2 to main memory page program with built-in erase */
	BUFFER_2_WRITE                   = 0x87, /**< buffer 2 write */
	B1_TO_MM_PAGE_PROG_WITHOUT_ERASE = 0x88, /**< buffer 1 to main memory page program without built-in erase */
	B2_TO_MM_PAGE_PROG_WITHOUT_ERASE = 0x89  /**< buffer 2 to main memory page program without built-in erase */
};
 
//! Error codes
enum { DF_NO_ERROR=0, DF_WAIT_FAIL, DF_NOT_MATCH, DF_UNKNOWN_DEVICE };
 
//! Last error
int df_last_error = DF_UNKNOWN_DEVICE;
 
//! Current position
__no_init unsigned long df_cur_pos;
 
////////////////////////////////////////////////////////////////////////////////
#if defined(DF_AUTODETECT_FEATURES)
 
//! Chip info structure
//! Supported chips
const struct stru_df_info {
	const char    *pszName;   //!< Name of chip
	unsigned int  pages;      //!< Number of pages
	unsigned int  page_size;  //!< Page size in bytes
	unsigned char page_bit;   //!< Page address len in bits
	unsigned char id_density; //!< Density ID mask
} df_table [] = {
	{ "AT45DB011B", 512,  264,  9,  (0x3<<2) }, // 1M
	{ "AT45DB021B", 1024, 264,  9,  (0x5<<2) }, // 2M
	{ "AT45DB041B", 2048, 264,  9,  (0x7<<2) }, // 4M
	{ "AT45DB081B", 4096, 264,  9,  (0x9<<2) }, // 8M
	{ "AT45DB161B", 4096, 528,  10, (0xB<<2) }, // 16M
	{ "AT45DB321B", 8192, 528,  10, (0xD<<2) }, // 32M
	{ "AT45DB642B", 8192, 1056, 11, (0xF<<2) }, // 64M
};
 
const char    *df_name      = "UNKNOWN";
unsigned char df_page_bits  = 9;
unsigned int  df_num_pages  = 4096;
unsigned int  df_page_size  = 264;
unsigned int  df_id_density = (0x9<<2);
unsigned long df_density    = 4096L*264L;
 
#else //////////////////////////////////////////////////////////////////////////
 
#define df_name       ("AT45DB081B")
#define df_num_pages  (4096)
#define df_page_size  (264)
#define df_page_bits  (9)
#define df_id_density (0x9<<2)
#define df_density    (4096L*264L)
 
#endif /////////////////////////////////////////////////////////////////////////
 
//---------------------------------------------------------
int at45db_getLastError( void )
{
	int err = df_last_error;
	df_last_error = DF_NO_ERROR;
	return err;
}
 
//---------------------------------------------------------
const char* at45db_getErrorString( int iErr )
{
	switch(iErr)
	{
	case DF_NO_ERROR:       return "No error";
	case DF_WAIT_FAIL:      return "Wait failed";
	case DF_NOT_MATCH:      return "Cfg failed";
	case DF_UNKNOWN_DEVICE: return "Unknown device";
	default:                return "Unknown error";
	}
}
 
//---------------------------------------------------------
void at45db_init_op( void )
{
	DATAFLASH_ENABLE();
	__no_operation();
}
 
//---------------------------------------------------------
void at45db_end_op( void )
{
	DATAFLASH_DISABLE();
	__no_operation();
}
 
//---------------------------------------------------------
void at45db_delay( void )
{
	unsigned char i=0x00;
	do { __no_operation(); } while (--i);
}
 
//---------------------------------------------------------
void at45db_reset(void)
{
	at45db_init_op();
	at45db_delay();
	DF_RESET_HI();
	at45db_delay();
	DF_RESET_LO();
	at45db_delay();
	at45db_end_op();
}
 
//---------------------------------------------------------
//! wait until DataFlash busy
void at45db_wait(void)
{
	unsigned int i;
	at45db_init_op();
	i =0x1FFF;
	do {
		spi_write(STATUS_REGISTER_READ);
		if( (spi_read() & (1<<7) ) != 0 ) break;
	} while( --i );
	df_last_error = (i != 0) ? DF_NO_ERROR : DF_WAIT_FAIL;
	at45db_end_op();
}
 
//---------------------------------------------------------
unsigned int _read_density_id(void)
{
	unsigned int id;
	at45db_init_op();
	spi_write(STATUS_REGISTER_READ);
	id = ( spi_read() & (0xF<<2) );
	at45db_end_op();
	return id;
}
 
//---------------------------------------------------------
int at45db_init(void)
{
	at45db_detect_error();
#if defined(DF_AUTODETECT_FEATURES)
	if ( df_last_error == DF_WAIT_FAIL ) return df_last_error;
 
	const struct stru_df_info *found = 0;
	df_id_density = _read_density_id();
	for( int i=0; i<(sizeof(df_table)/sizeof(df_table[0])); i++ ) {
		if( df_id_density == df_table[i].id_density ) {
			found = &df_table[i];
			break;
		}
	}
 
	if( !found ) {
		found = &df_table[0];
		df_last_error  = DF_UNKNOWN_DEVICE;
	}
 
	df_num_pages = found->pages;
	df_page_size = found->page_size;
	df_page_bits = found->page_bit;
	df_name      = found->pszName;
	df_density   = (long)df_num_pages * df_page_size;
#endif
	return df_last_error;
}
 
//---------------------------------------------------------
int at45db_detect_error(void)
{
	at45db_reset();
	at45db_wait();
	if( df_last_error == DF_NO_ERROR ) {
		if( _read_density_id() != df_id_density ) df_last_error = DF_NOT_MATCH;
	}
	return df_last_error;
}
 
//---------------------------------------------------------
void at45db_setpos(unsigned long pos) { df_cur_pos = pos; }
unsigned long at45db_getpos(void) { return df_cur_pos; }
 
//---------------------------------------------------------
const char*   at45db_getName(void)       { return df_name;      }
unsigned long at45db_getDensity ( void ) { return df_density;   }
unsigned int  at45db_getPageSize( void ) { return df_page_size; }
unsigned int  at45db_getNumPages( void ) { return df_num_pages; }
 
//---------------------------------------------------------
void at45db_start_read( void )
{	
	ldiv_t pos = ldiv(df_cur_pos, df_page_size);
	unsigned int cur_page = pos.quot, cur_ofs  = pos.rem;
 
	at45db_init_op();
	
	spi_write(ARRAY_READ);                                                       // Continuous Array Read op-code
	spi_write((unsigned char)(cur_page >> (16 - df_page_bits)));                 // upper part of page address
	spi_write((unsigned char)((cur_page << (df_page_bits - 8))+ (cur_ofs>>8)));  // lower part of page address and MSB of int.page adr.
	spi_write((unsigned char)(cur_ofs));                                         // LSB byte of internal page address
	spi_write(0x00);                                                             // perform 4 dummy writes
	spi_write(0x00);                                                             // in order to initiate DataFlash
	spi_write(0x00);                                                             // address pointers
	spi_write(0x00);
}
 
//---------------------------------------------------------
void at45db_cont_read(unsigned char *dst, unsigned int size)
{
	while( size-- ) {
		df_cur_pos++;
		*dst++ = spi_read();
	}
}
 
//---------------------------------------------------------
unsigned char at45db_cont_readbyte(void)
{
  df_cur_pos++;
  return spi_read();
}
 
//---------------------------------------------------------
void at45db_end_read( void )
{
	at45db_end_op();
}
 
//---------------------------------------------------------
unsigned int at45db_cont_verify(unsigned int size)
{
	unsigned int crc = 0xFFFF;
	do {
		df_cur_pos++;
		crc ^= spi_read();
		unsigned char k = 8;
		do {
			if (crc & 1)  { crc >>=1;  crc ^=0xA001; }
			else  { crc >>=1; }
		} while( --k );
	} while( --size );
	return crc;
}
 
//---------------------------------------------------------
void at45db_read( unsigned char *dst, unsigned int size )
{
	at45db_start_read();
	at45db_cont_read(dst, size);
	at45db_end_op();
}
 
//---------------------------------------------------------
void df_page_func( unsigned char cmd, unsigned int page )
{
	at45db_init_op();
	spi_write(cmd);
	spi_write((unsigned char)(page >> (16 - df_page_bits)));  // upper part of page address
	spi_write((unsigned char)(page << (df_page_bits - 8)));   // lower part of page address
	spi_write(0x00);                                          // don't cares
	at45db_end_op();
	at45db_wait();
}
 
//---------------------------------------------------------
void at45db_write( const unsigned char *src, unsigned int size )
{	
	while(size) {
	
		ldiv_t pos = ldiv( df_cur_pos, df_page_size );
		unsigned int cur_page = pos.quot, cur_ofs  = pos.rem;
		
		df_page_func(MM_PAGE_TO_B1_XFER, cur_page);
 
		at45db_init_op();
		spi_write(BUFFER_1_WRITE);                 // buffer 1 write op-code
		spi_write(0x00);                           // don't cares
		spi_write((unsigned char)(cur_ofs>>8));    // upper part of internal buffer address
		spi_write((unsigned char)(cur_ofs));       // lower part of internal buffer address
		do {
			spi_write(*src++);
			df_cur_pos++;
		} while( (--size) && (++cur_ofs < df_page_size) );
		at45db_end_op();
 
		df_page_func( B1_TO_MM_PAGE_PROG_WITH_ERASE, cur_page );
		df_page_func( AUTO_PAGE_REWRITE_THROUGH_B1,  cur_page );
	}
}
 
//---------------------------------------------------------
void at45db_start_write( void )
{
	ldiv_t pos = ldiv( df_cur_pos, df_page_size );
	unsigned int cur_page = pos.quot, cur_ofs  = pos.rem;
	at45db_end_op();
	df_page_func(MM_PAGE_TO_B1_XFER, cur_page );
	at45db_init_op();
	spi_write(BUFFER_1_WRITE);                 // buffer 1 write op-code
	spi_write(0x00);                           // don't cares
	spi_write((unsigned char)(cur_ofs>>8));    // upper part of internal buffer address
	spi_write((unsigned char)(cur_ofs));       // lower part of internal buffer address
 
}
 
//--------------------------------------------------------
void at45db_cont_write( unsigned char data )
{
	ldiv_t pos = ldiv( df_cur_pos, df_page_size );
	unsigned int cur_page = pos.quot, cur_ofs  = pos.rem;
	spi_write( data );
	df_cur_pos++;	
	if( cur_ofs == (df_page_size-1) ) {
		// internal buffer full. flush to nvram
		at45db_end_op();
		df_page_func( B1_TO_MM_PAGE_PROG_WITH_ERASE, cur_page );
		at45db_start_write();
	}
}
 
//--------------------------------------------------------
void at45db_end_write( void )
{
	at45db_end_op();
	ldiv_t pos = ldiv( df_cur_pos, df_page_size );
	unsigned int cur_page = pos.quot, cur_ofs  = pos.rem;
	if( cur_ofs ) {
		df_page_func( B1_TO_MM_PAGE_PROG_WITH_ERASE, cur_page );
	}
}
 
//---------------------------------------------------------
int at45db_erase(void)
{
	unsigned int i, err = 0;
	at45db_init();
	for( i=0; i<df_num_pages; i++ ) {
		df_page_func(PAGE_ERASE, i);
		if( df_last_error != DF_NO_ERROR ) err++;
	}
	return err;
}
 
sources/at45db.txt · Последние изменения: 2021/10/09 14:22 kurt