/** * @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; }