;*************************************************************************** ; ; File Name :'DFlash.asm" ; Title :SPI interface to Serial DataFlash ; Date :2003.06.01. ; Version :1.0.0 ; Support telephone :+36-70-333-4034, old: +36-30-9541-658 VFX ; Support fax : ; Support Email :info@vfx.hu ; Target MCU :ATmega8 ; ;*************************************************************************** ; D E S C R I P T I O N ; ; SPI interface to AT45DBxxx Serial DataFlash ; ; Functions to access the Atmel AT45Dxxx dataflash series ; Supports 1Mbit - 128Mbit ; ;*************************************************************************** ; M O D I F I C A T I O N H I S T O R Y ; ; ; rev. date who why ; ---- ---------- --- ------------------------------------ ; 0.01 2003.06.01 VFX Creation ; ;*************************************************************************** ;Hardware ;*************************************************************************** ;* ;* SYSCLK: f=16.000 MHz (T= 62.5 ns) ;* ;*************************************************************************** ; ; CS - Chip Select ; SCK - Serial Clock ; SI - Serial Input ; SO - Serial Output ; WP - Hardware Page Write Protect Pin ; RESET - Chip Reset ; RDY/BUSY - Ready/Busy ; ; To provide optimal flexibility, the memory array of the AT45DB081B is ;divided into three levels of granularity comprising of sectors, blocks, ;and pages. All program operations to the DataFlash occur on a page-by-page ;basis; however, the optional erase operations can be performed at the block ;or page level. ; ; ;The device operation is controlled by instructions from the host processor. ;The list of instructions and their associated opcodes are contained in ;Tables 1 through 4. A valid instruction starts with the falling edge of CS ;followed by the appropriate 8-bit opcode and the desired buffer or main memory ;address location. While the CS pin is low, tog-gling the SCK pin controls the ;loading of the opcode and the desired buffer or main memory address location ;through the SI (serial input) pin. All instructions, addresses and data are ;transferred with the most significant bit (MSB) first. ;Buffer addressing is referenced in the datasheet using the terminology ;BFA8 - BFA0 to denote the nine address bits required to designate a byte address ;within a buffer. Main memory addressing is referenced using the terminology ;PA11 - PA0 and BA8 - BA0 where PA11 - PA0 denotes the 12 address bits required ;to designate a page address and BA8 - BA0 denotes the nine address bits ;required to designate a byte address within the page. ; ; ;Status Register Format ;Bit 7 6 5 4 3 2 1 0 ; RDY/BUSY COMP 1 0 0 1 X X 45DB161 ;*************************************************************************** ;* Const Def ; ;Look-up table for these sizes -> 512k, 1M, 2M, 4M, 8M, 16M, 32M, 64M ;flash unsigned char DF_pagebits[] ={ 9, 9, 9, 9, 9, 10, 10, 11}; //index of internal page address bits ;Dataflash opcodes .equ MainMemPageRead = 0x52 ;Main Memory Page Read Inactive Clk Pol Low or High .equ FlashPageRead = 0x52 ;Main memory page read .equ FlashToBuf1Transfer= 0x53 ;Main memory page to buffer 1 transfer .equ Buf1Read = 0x54 ;Buffer 1 read .equ FlashToBuf2Transfer= 0x55 ;Main memory page to buffer 2 transfer .equ Buf2Read = 0x56 ;Buffer 2 read .equ StatusReg = 0x57 ;Status register .equ StatusRegMode3 = 0xD7 ;Status register read SPI Mode 0 or 3 .equ AutoPageReWrBuf1 = 0x58 ;Auto page rewrite through buffer 1 .equ AutoPageReWrBuf2 = 0x59 ;Auto page rewrite through buffer 2 .equ FlashToBuf1Compare = 0x60 ;Main memory page to buffer 1 compare .equ FlashToBuf2Compare = 0x61 ;Main memory page to buffer 2 compare .equ ContArrayRead = 0x68 ;Continuous Array Read (Note : Only A/B-parts supported) .equ FlashProgBuf1 = 0x82 ;Main memory page program through buffer 1 .equ Buf1ToFlashWE = 0x83 ;Buffer 1 to main memory page program with built-in erase .equ Buf1Write = 0x84 ;Buffer 1 write .equ FlashProgBuf2 = 0x85 ;Main memory page program through buffer 2 .equ Buf2ToFlashWE = 0x86 ;Buffer 2 to main memory page program with built-in erase .equ Buf2Write = 0x87 ;Buffer 2 write .equ Buf1ToFlash = 0x88 ;Buffer 1 to main memory page program without built-in erase .equ Buf2ToFlash = 0x89 ;Buffer 2 to main memory page program without built-in erase .equ MainMemPageReadSPI = 0xD2 ;Main Memory Page Read SPI Mode 0 or 3 .equ ContArrayReadSPI = 0xE8 ;Continuous Array Read SPI Mode 0 or 3 ;Table 2. Program and Erase Commands .equ Buffer1Write = 0x84 ;Buffer 1 Write .equ Buffer2Write = 0x87 ;Buffer 2 Write .equ Buffer1toMem = 0x88 ;Buffer 1 to Main Memory Page Program without Built-in Erase .equ Buffer2toMem = 0x89 ;Buffer 2 to Main Memory Page Program without Built-in Erase .equ DFPageErase = 0x81 ;Page Erase .equ DFBlockErase = 0x50 ;Block Erase .equ MemPageProgBuff1 = 0x82 ;Main Memory Page Program through Buffer 1 .equ MemPageProgBuff2 = 0x85 ;Main Memory Page Program through Buffer 2 ;Table 3. Additional Commands .equ MemPagetoBuff1 = 0x53 ;Main Memory Page to Buffer 1 Transfer .equ MemPagetoBuff2 = 0x55 ;Main Memory Page to Buffer 2 Transfer .equ MemPagetoBuff1Cmp = 0x60 ;Main Memory Page to Buffer 1 Compare .equ MemPagetoBuff2Cmp = 0x61 ;Main Memory Page to Buffer 2 Compare .equ PageRewriteBuff1 = 0x58 ;Auto Page Rewrite through Buffer 1 .equ PageRewriteBuff2 = 0x59 ;Auto Page Rewrite through Buffer 2 ;************************************************************************** ;* Hardware Def. ; ; RDY/BUSY - Ready/Busy .equ DFRDY_PORT = PORTD .equ DFRDY_DIR = DDRD .equ DFRDY_PIN = PIND .equ DFRDY = 7 .equ DFRES_PORT = PORTD .equ DFRES_DIR = DDRD .equ DFRES = 5 .equ DFCS_PORT = PORTC .equ DFCS_DIR = DDRC .equ DFCs = 2 .equ MOSI_DIR = DDRB .equ MOSI_PORT = PORTB .equ MOSI = 3 .equ MISO_DIR = DDRB .equ MISO_PORT = PORTB .equ MISO_PIN = PINB .equ MISO = 4 .equ SCLK_DIR = DDRB .equ SCLK_PORT = PORTB .equ SCLK = 5 .equ DFSS_DIR = DDRB .equ DFSS_PORT = PORTB .equ DFSS = 2 ;*************************************************************************** ;**** VARIABLES .DSEG DFSize: .byte 1 DFPageSize: .byte 1 DFPageBits: .byte 1 .equ DFBuffer = AppData ;DFBuffer: .byte 128 ;Data buffer ; !!! use AppData buffer from user.asm --> save RAM Space !!! ; !!! Self update not supported in application !!! ;*************************************************************************** .ESEG ;*************************************************************************** ;**** CODE SEG ;*************************************************************************** .CSEG ;p,x,y,z,0 ;page bits = p ;page number = x * 256 ;page size = y * 256 (264) ;type = z ; AT45DBxxx: .db 9, 2,1,0b00001100 ;AT45DB011 1Mbit .db 9, 4,1,0b00010100 ;AT45DB021 2Mbit .db 9, 8,1,0b00011100 ;AT45DB041 4Mbit .db 9,16,1,0b00100100 ;AT45DB081 8Mbit .db 10,16,2,0b00101100 ;AT45DB161 16Mbit .db 10,32,2,0b00110100 ;AT45DB321 32Mbit .db 11,32,4,0b00111000 ;AT45DB642 64Mbit .db 11,64,4,0b00010000 ;AT45DB1282 128Mbit .dw 0,0 ;**************************************************************************** ;*** S P I R U T I N S ;*** ;**************************************************************************** ;* SPI_init ;* ;* Initialize our port pins for use as SPI master. ;* ;*************************************************************************** ; SPI_init: sbi DFCS_PORT,DFCS ;DFlash CE pin is output for ATmega sbi DFCS_DIR,DFCS ;CS=1 sbi DFRES_PORT,DFRES sbi DFRES_DIR,DFRES ;DFlash Reset = H cbi DFRDY_DIR,DFRDY sbi DFRDY_PORT,DFRDY ;DFlash R/B pullup input in R16,PORTB andi R16,0b11000011 ori R16,0b00101100 out PORTB,R16 ;SS, MOSI, SCK output; MISO input in R16,DDRB andi R16,0b11000011 ori R16,0b00101100 out DDRB,R16 ldi R16,0b01011100 out SPCR,R16 ;[7] - SPIE: SPI Interrupt Enable ;[6] - SPE: SPI Enable ;[5] - DORD: Data Order ;[4] - MSTR: Master/Slave Select ;[3] - CPOL: Clock Polarity ;[2] - CPHA: Clock Phase ;[1:0] - SPR1,SPR0: SPI Clock Rate Select ; SPI2X SPR1 SPR0 SCK Frequency ; 0 0 0 fosc/4 ; 0 0 1 fosc/16 ; 0 1 0 fosc/64 ; 0 1 1 fosc/128 ; 1 0 0 fosc/2 ; 1 0 1 fosc/8 ; 1 1 0 fosc/32 ; 1 1 1 fosc/64 ldi R16,0b00000001 out SPSR,R16 ;[7] - SPIF: SPI Interrupt Flag ;[6] - WCOL: Write COLlision flag ;[5:1] - Res: Reserved Bits ;[0] - SPI2X: Double SPI Speed Bit in R16,SPSR in R16,SPDR ;Clear SPIF & WCOL bits cbi DFRES_PORT,DFRES ;DFlash Reset ;DFlash Reset>10us ldi ZL,low(SYSCLK/100000) ldi ZH,High(SYSCLK/100000) W10us1: sbiw ZL,1 brne W10us1 sbi DFRES_PORT,DFRES ;DFlash Reset = 1 ldi R16,50 ;Reset Recoveri time = 1 us W10us2: dec R16 brne W10us2 rcall DF_ReadStatus cbr R16,0b11000011 ldi ZL,low(AT45DBxxx*2) ;ATmega128 -> set PAMPZ!! ldi ZH,high(AT45DBxxx*2) SearchDF: lpm R17,Z+ sts DFPageBits,R17 lpm R18,Z+ sts DFSize,R18 lpm R18,Z+ sts DFPageSize,R18 lpm R2,Z+ cp R16,R2 breq DFHit tst R17 brne SearchDF DFHit: ret ;***************************************************************************** ;*DF_SPI_RW ;* ;* Read and writes one byte from/to SPI master ;* ;* In: R16 - Byte to be written to SPI data register ;* ;* Out: R16 - Byte read from SPI data register ;* ;****************************************************************************** ; DF_SPI_RW: out SPDR,R16 DF_SPI_RW0: sbis SPSR,spif rjmp DF_SPI_RW0 ;wait for transfer complete, poll SPIF-flag in R16,SPDR ret ;***************************************************************************** ;*Read_DF_status ;* ;* Status info concerning the Dataflash is busy or not. ;* Status info concerning compare between buffer and flash page ;* Status info concerning size of actual device ;* ;* In: - ;* ;* Out: R16 - status byte. Consult Dataflash datasheet for further decoding info ;* ;****************************************************************************** ; DF_ReadStatus: sbi DFCS_PORT,DFCS ;DF CS inactive nop nop cbi DFCS_PORT,DFCS ;DF CS Active! ;to reset dataflash command decoder ldi R16,StatusRegMode3 rcall DF_SPI_RW ;send status register read op-code clr R16 rcall DF_SPI_RW ;dummy write to get result ret ;***************************************************************************** ;* WaitToDF ;* ;* Wait for DataFlash to Ready ;* ;* In: - ;* ;* Out: - ;* ;****************************************************************************** ; WaitToDF: rcall DF_ReadStatus cbr R16,0x7F ;Csak a Busy Flag marad meg breq WaitToDF ;monitor the status register, wait until busy-flag is high sbi DFCS_PORT,DFCS ;DF CS inactive nop cbi DFCS_PORT,DFCS ;DF CS Active! , reset dataflash command decoder ret ;***************************************************************************** ;Page_To_Buffer ; ; Transfers a page from flash to dataflash SRAM buffer ; ; In: R0 = BufferNo -> R0 = 0 usage Buffer 1 ; = non zero usage Buffer 2 ; R19:R18 = PageAdr -> Address of page to be transferred to buffer ; ; Out: - ;***************************************************************************** ; DF_PageToBuffer: rcall WaitToDF ldi R16,FlashToBuf1Transfer ;transfer to buffer 1 op-code tst R0 breq DF_PageToBuff01 ldi R16,FlashToBuf2Transfer ;transfer to buffer 2 op-code DF_PageToBuff01: rcall DF_SPI_RW ;send op-code lds R16,DFPageBits subi R16,8 DF_PageToBuff02: lsl R18 rol R19 dec R16 brne DF_PageToBuff02 mov R16,R19 rcall DF_SPI_RW ;upper part of page address mov R16,R18 rcall DF_SPI_RW ;lower part of page address clr R16 rcall DF_SPI_RW sbi DFCS_PORT,DFCS ;DF CS inactive ret ;***************************************************************************** ;DF_BufferReadByte ; ; Reads one byte from one of the dataflash internal SRAM buffers ; ; In: R0 = BufferNo -> R0 = 0 usage Buffer 1 ; = non zero usage Buffer 2 ; R19:R18 = IntPageAdr -> Internal page address ; ; Out: R16 - One read byte (any value) ; ;***************************************************************************** ; DF_BufferReadByte: rcall WaitToDF ldi R16,Buf1Read ;read byte from buffer 1 tst R0 breq BufferReadByte01 ldi R16,Buf2Read ;read byte from buffer 2 BufferReadByte01: rcall DF_SPI_RW ;send op-code clr R16 rcall DF_SPI_RW ;don't cares mov R16,R19 rcall DF_SPI_RW ;upper part of page address mov R16,R18 rcall DF_SPI_RW ;lower part of page address clr R16 rcall DF_SPI_RW ;additional don't cares clr R16 rcall DF_SPI_RW ;read byte sbi DFCS_PORT,DFCS ;DF CS inactive ret ;***************************************************************************** ;DF_BufferReadStr ; ; Reads one or more bytes from one of the dataflash internal SRAM buffers, ; and puts read bytes into buffer pointed to by X ; In: R0: BufferNo -> R0 = 0 usage Buffer 1 ; = non zero usage Buffer 2 ; R19:R18 -> Internal page address ; R21:R20 -> Number of bytes to be read ; X -> address of buffer to be used for read bytes ; ; Out: - ; ;***************************************************************************** ; DF_BufferReadStr: rcall WaitToDF ldi R16,Buf1Read ;read byte from buffer 1 tst R0 breq DF_BufferReadStr01 ldi R16,Buf2Read ;read byte from buffer 2 DF_BufferReadStr01: rcall DF_SPI_RW ;send op-code clr R16 rcall DF_SPI_RW ;don't cares mov R16,R19 rcall DF_SPI_RW ;upper part of page address mov R16,R18 rcall DF_SPI_RW ;lower part of page address clr R16 rcall DF_SPI_RW ;additional don't cares push YL push YH movw YL,R20 ;Internal page address + Number of bytes to be read <= Page Size!!! DF_BufferReadStr02: clr R16 rcall DF_SPI_RW st X+,R16 ;Store DF data byte sbiw YL,1 brne DF_BufferReadStr02 pop YH pop YL sbi DFCS_PORT,DFCS ;DF CS inactive ret ;***************************************************************************** ; DF_BufferWriteEnable ; ; Enables continous write functionality to one of the dataflash buffers ; NOTE : User must ensure that CS goes high to terminate this mode ; before accessing other dataflash functionalities ; ; In: R0: BufferNo -> R0 = 0 usage Buffer 1 ; = non zero usage Buffer 2 ; R19:R18 -> Internal page address to start writing from ; ; Out: None ; ;***************************************************************************** ; DF_BufferWriteEnable: rcall WaitToDF ldi R16,Buf1Write ;buffer 1 write op-code tst R0 breq DF_BufferWriteEnable01 ldi R16,Buf2Write ;buffer 2 write op-code DF_BufferWriteEnable01: rcall DF_SPI_RW ;send op-code clr R16 rcall DF_SPI_RW ;don't cares mov R16,R19 rcall DF_SPI_RW ;upper part of page address mov R16,R18 rcall DF_SPI_RW ;lower part of page address ret ;***************************************************************************** ; DF_BufferWriteByte: ; ; Writes one byte to one of the dataflash internal SRAM buffers ; ; In: R0: BufferNo -> R0 = 0 usage Buffer 1 ; = non zero usage Buffer 2 ; R19:R18 -> Internal page address to write byte to ; R16 -> Data byte to be written ; ; Out: None ; ;***************************************************************************** ; DF_BufferWriteByte: push R16 rcall WaitToDF ldi R16,Buf1Write ;buffer 1 write op-code tst R0 breq DF_BufferWriteByte01 ldi R16,Buf2Write ;buffer 2 write op-code DF_BufferWriteByte01: rcall DF_SPI_RW ;send op-code clr R16 rcall DF_SPI_RW ;don't cares mov R16,R19 rcall DF_SPI_RW ;upper part of page address mov R16,R18 rcall DF_SPI_RW ;lower part of page address pop R16 rcall DF_SPI_RW ;write data byte DF_EndWrite: sbi DFCS_PORT,DFCS ;DF CS inactive ret ;***************************************************************************** ; DF_BufferWriteStr ; ; Copies one or more bytes to one of the dataflash internal SRAM buffers ; from AVR SRAM buffer pointed to by X ; ; In: R0: BufferNo -> R0 = 0 usage Buffer 1 ; = non zero usage Buffer 2 ; R19:R18 -> Internal page address ; R21:R20 -> Number of bytes to be read ; X -> address of buffer to be used for write bytes ; ; Out: None ; ;***************************************************************************** ; DF_BufferWriteStr: rcall WaitToDF ldi R16,Buf1Write ;write byte to buffer 1 tst R0 breq DF_BufferWriteStr01 ldi R16,Buf2Write ;write byte to buffer 2 DF_BufferWriteStr01: rcall DF_SPI_RW ;send op-code clr R16 rcall DF_SPI_RW ;don't cares mov R16,R19 rcall DF_SPI_RW ;upper part of page address mov R16,R18 rcall DF_SPI_RW ;lower part of page address push YL push YH movw YL,R20 ;Internal page address + Number of bytes to be read <= Page Size!!! DF_BufferWriteStr02: ld R16,X+ ;Store DF data byte rcall DF_SPI_RW sbiw YL,1 brne DF_BufferWriteStr02 pop YH pop YL ret ;***************************************************************************** ; DF_BufferToPage ; DF_BufferToPageWErase ; ; Transfers a page from dataflash SRAM buffer to flash ; ; In: R0 = BufferNo -> R0 = 0 usage Buffer 1 ; = non zero usage Buffer 2 ; R19:R18 = PageAdr -> Address of flash page to be programmed ; ; Out: None ; ;***************************************************************************** ; DF_BufferToPage: rcall WaitToDF ldi R16,Buffer1toMem ;buffer 1 to flash without erase tst R0 breq DF_BufferToPage01 ldi R16,Buffer1toMem rjmp DF_BufferToPage01 DF_BufferToPageWErase: rcall WaitToDF ldi R16,Buf1ToFlashWE ;buffer 1 to flash with erase op-code tst R0 breq DF_BufferToPage01 ldi R16,Buf2ToFlashWE ;buffer 2 to flash with erase op-code DF_BufferToPage01: rcall DF_SPI_RW ;send op-code lds R16,DFPageBits subi R16,8 DF_BufferToPage02: lsl R18 rol R19 dec R16 brne DF_BufferToPage02 mov R16,R19 rcall DF_SPI_RW ;upper part of page address mov R16,R18 rcall DF_SPI_RW ;lower part of page address clr R16 rcall DF_SPI_RW nop sbi DFCS_PORT,DFCS ;DF CS inactive ret ;***************************************************************************** ; DF_PageErase ; ; Erase Flash Page ; ; In: R19:R18 = PageAdr -> Address of flash page to be erased ; ; Out: None ; ;***************************************************************************** ; DF_PageErase: rcall WaitToDF ldi R16,DFPageErase rcall DF_SPI_RW ;send op-code lds R16,DFPageBits subi R16,8 DF_PageErase01: lsl R18 rol R19 dec R16 brne DF_PageErase01 mov R16,R19 rcall DF_SPI_RW ;upper part of page address mov R16,R18 rcall DF_SPI_RW ;lower part of page address clr R16 rcall DF_SPI_RW ;dumpy part of address!!! nop sbi DFCS_PORT,DFCS ;DF CS inactive ret ;***************************************************************************** ; DF_CheckErasedPage ; ; Test Erased Flash Page ; ; In: R19:R18 = PageAdr -> Address of flash page to be tested ; R0 = used buffer 0 = buffer0 other = buffer1 ; ; Out: c=0 no error ; c=1 Bad Page! Dont use!! ; ;***************************************************************************** ; DF_CheckErasedPage: rcall WaitToDF push R0 rcall DF_PageToBuffer clr XL ldi R16,8 lds XH,DFPageSize mul R16,XH add XL,R0 adc XH,R1 ;X full lenght of page in byte pop R0 DF_Check00: push R0 push XL push XH sbiw XL,1 movw R18,XL rcall DF_BufferReadByte pop XH pop XL pop R0 cpi R16,0xFF brne DF_BadPage sbiw XL,1 brne DF_Check00 clc ret ;ok page cleared well DF_BadPage: sec ret ;Bad Page!