; Copyright (c) 2008 Charlie DeTar ; ; Permission is hereby granted, free of charge, to any person ; obtaining a copy of this software and associated documentation ; files (the "Software"), to deal in the Software without ; restriction, including without limitation the rights to use, ; copy, modify, merge, publish, distribute, sublicense, and/or sell ; copies of the Software, and to permit persons to whom the ; Software is furnished to do so, subject to the following ; conditions: ; ; The above copyright notice and this permission notice shall be ; included in all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ; OTHER DEALINGS IN THE SOFTWARE. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; AT45DB memory chip code - designed for AT45DB081D, but should work ; with any other 2-buffer AT45DB chips. ; Code to write, read, and erase flash memory. ; ; NOTE: This code assumes the flash is configured for 256-bit ("power of two") ; page sizes. This is a one-time-only configuration that can be done by ; running the provided macro "AT45DB_permanently_set_page_256", then power ; cycling. This cannot be undone. ; ; Sector protection and lockdown functionality is omitted. ; ; REQUIREMENTS: ; ; 1. Define the following registers externally: ; spi_byte ; mem_3, mem_2, mem_1 (memory pointers) ; ; 2. Define the following SPI interface macros:; ; ; AT45DB_cs_hi ; Bring level of chip select pin high ; AT45DB_cs_lo ; Bring level of chip select pin low ; AT45DB_write_spi ; Write a byte over the SPI interface ; AT45DB_read_spi ; Read a byte from the SPI interface ; ; example: ; .macro AT45DB_cs_hi ; sbi PORTB, PB2 ; .endmacro ; .macro AT45DB_cs_lo ; cbi PORTB, PB2 ; .endmacro ; ; ; See "MEGA88_code.asm" for an example implementation ; ; of "hw_read_spi" and "hw_write_spi" ; .macro AT45DB_read_spi ; rcall hw_read_spi ; .endmacro ; .macro AT45DB_write_spi ; rcall hw_write_spi ; .endmacro ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Op-codes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .equ MAIN_MEMORY_PAGE_READ = 0xD2; .equ CONTINUOUS_ARRAY_READ = 0x0b; .equ CONTINUOUS_ARRAY_READ_LOW_FREQ = 0x03; .equ BUFFER_1_READ = 0xD4; .equ BUFFER_1_READ_LOW_FREQ = 0xD1; .equ BUFFER_2_READ = 0xD6; .equ BUFFER_2_READ_LOW_FREQ = 0xD3; .equ BUFFER_1_WRITE = 0x84; .equ BUFFER_2_WRITE = 0x87; .equ BUFFER_1_TO_PAGE_WITH_ERASE = 0x83; .equ BUFFER_2_TO_PAGE_WITH_ERASE = 0x86; .equ BUFFER_1_TO_PAGE_WITHOUT_ERASE = 0x88; .equ BUFFER_2_TO_PAGE_WITHOUT_ERASE = 0x89; .equ PAGE_ERASE = 0x81; .equ BLOCK_ERASE = 0x50; .equ SECTOR_ERASE = 0x7C; .equ CHIP_ERASE_1 = 0xC7; .equ CHIP_ERASE_2 = 0x94; .equ CHIP_ERASE_3 = 0x80; .equ CHIP_ERASE_4 = 0x9A; .equ PAGE_PROGRAM_THROUGH_BUFFER_1 = 0x82; .equ PAGE_PROGRAM_THROUGH_BUFFER_2 = 0x85; .equ PAGE_TO_BUFFER_1_TRANSFER = 0x53; .equ PAGE_TO_BUFFER_2_TRANSFER = 0x55; .equ PAGE_TO_BUFFER_1_COMPARE = 0x60; .equ PAGE_TO_BUFFER_2_COMPARE = 0x61; .equ AUTO_PAGE_BUFFER_1_REWRITE = 0x58; .equ AUTO_PAGE_BUFFER_2_REWRITE = 0x59; .equ STATUS_REGISTER_READ = 0xD7; .equ POWER_OF_2_a = 0x3D; .equ POWER_OF_2_b = 0x2A; .equ POWER_OF_2_c = 0x80; .equ POWER_OF_2_d = 0xA6; .equ DEEP_POWER_DOWN = 0xB9 .equ RESUME_FROM_DEEP_POWER_DOWN = 0xAB ; ; status register bit positions ; .macro AT45DB_skip_if_ready sbrs spi_byte, 7 .endmacro ; ; convenience ; .macro AT45DB_cs_active AT45DB_cs_lo .endmacro .macro AT45DB_cs_inactive AT45DB_cs_hi .endmacro .macro AT45DB_write_spi_i ldi spi_byte, @0 AT45DB_write_spi .endmacro .macro AT45DB_write_spi_r mov spi_byte, @0 AT45DB_write_spi .endmacro .macro AT45DB_permanently_set_page_256 AT45DB_cs_active AT45DB_write_spi_i POWER_OF_2_a AT45DB_write_spi_i POWER_OF_2_b AT45DB_write_spi_i POWER_OF_2_c AT45DB_write_spi_i POWER_OF_2_d AT45DB_cs_inactive .endmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; memory routines ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .macro AT45DB_open_continuous_read AT45DB_cs_active AT45DB_write_spi_i CONTINUOUS_ARRAY_READ AT45DB_write_spi_r @0 AT45DB_write_spi_r @1 AT45DB_write_spi_r @2 AT45DB_write_spi ; don't care byte .endmacro .macro AT45DB_close_continuous_read AT45DB_cs_inactive .endmacro .macro AT45DB_open_buffer_read AT45DB_cs_active AT45DB_write_spi_i @0 ; opcode AT45DB_write_spi ; don't care AT45DB_write_spi ; don't care AT45DB_write_spi_r @1 ; buffer position AT45DB_write_spi ; don't care .endmacro .macro AT45DB_open_buffer_1_read AT45DB_open_buffer_read BUFFER_1_READ, @0 .endmacro .macro AT45DB_open_buffer_2_read AT45DB_open_buffer_read BUFFER_2_READ, @0 .endmacro .macro AT45DB_close_buffer_read AT45DB_cs_inactive .endmacro .macro AT45DB_open_buffer_write AT45DB_cs_active AT45DB_write_spi_i @0 ; opcode AT45DB_write_spi ; don't care AT45DB_write_spi ; don't care AT45DB_write_spi_r @1 ; address .endmacro .macro AT45DB_open_buffer_1_write AT45DB_open_buffer_write BUFFER_1_WRITE, @0 .endmacro .macro AT45DB_open_buffer_2_write AT45DB_open_buffer_write BUFFER_2_WRITE, @0 .endmacro .macro AT45DB_close_buffer_write AT45DB_cs_inactive .endmacro .macro AT45DB_buffer_to_page AT45DB_cs_active AT45DB_write_spi_i @0 ; opcode AT45DB_write_spi_r @1 ; page address AT45DB_write_spi_r @2 ; " AT45DB_write_spi ; 8 don't care bits AT45DB_cs_inactive .endmacro .macro AT45DB_buffer_1_to_page AT45DB_buffer_to_page BUFFER_1_TO_PAGE_WITH_ERASE, @0, @1 .endmacro .macro AT45DB_buffer_2_to_page AT45DB_buffer_to_page BUFFER_2_TO_PAGE_WITH_ERASE, @0, @1 .endmacro .macro AT45DB_page_to_buffer AT45DB_cs_active AT45DB_write_spi_i @0 ; opcode AT45DB_write_spi_r @1 ; page address AT45DB_write_spi_r @2 ; " AT45DB_write_spi ; 8 don't care bits AT45DB_cs_inactive .endmacro .macro AT45DB_page_to_buffer_1 AT45DB_page_to_buffer PAGE_TO_BUFFER_1_TRANSFER, @0, @1 .endmacro .macro AT45DB_page_to_buffer_2 AT45DB_page_to_buffer PAGE_TO_BUFFER_2_TRANSFER, @0, @1 .endmacro .macro AT45DB_open_write_thru_buf AT45DB_cs_active AT45DB_write_spi_i @0 ; opcode AT45DB_write_spi_r @1 ; page address AT45DB_write_spi_r @2 ; " AT45DB_write_spi_r @3 ; .endmacro .macro AT45DB_open_write_thru_buf_1 AT45DB_open_write_thru_buf PAGE_PROGRAM_THROUGH_BUFFER_1, @0, @1, @2 .endmacro .macro AT45DB_open_write_thru_buf_2 AT45DB_open_write_thru_buf PAGE_PROGRAM_THROUGH_BUFFER_2, @0, @1, @2 .endmacro .macro AT45DB_close_page_write_thru_buf AT45DB_cs_inactive .endmacro .macro AT45DB_read_status_register AT45DB_cs_active AT45DB_write_spi_i STATUS_REGISTER_READ AT45DB_read_spi AT45DB_cs_inactive .endmacro .macro AT45DB_deep_power_down AT45DB_cs_active AT45DB_write_spi_i DEEP_POWER_DOWN AT45DB_cs_inactive rcall delay_1us rcall delay_1us rcall delay_1us ; 3us delay .endmacro .macro AT45DB_resume_from_power_down AT45DB_cs_active AT45DB_write_spi_i RESUME_FROM_DEEP_POWER_DOWN AT45DB_cs_inactive rcall delay_26us rcall delay_5us rcall delay_5us ; 35us delay .endmacro AT45DB_wait_for_ready: AT45DB_read_status_register AT45DB_skip_if_ready rjmp AT45DB_wait_for_ready ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; rewrite: write small bits of data to specific addresses. ; Loads page into buffer then rewrites to that buffer. ; Does not cross page/buffer edges. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; depends on the existence of mem_3, mem_2, mem_1 AT45DB_open_rewrite: rcall AT45DB_wait_for_ready AT45DB_page_to_buffer_1 mem_3, mem_2 rcall AT45DB_wait_for_ready AT45DB_open_buffer_1_write mem_1 ret ; call AT45DB_write_spi to write data AT45DB_rewrite: AT45DB_write_spi inc mem_1 brne AT45DB_write_return ; end of buffer reached? ; reached end of buffer; grab next rcall AT45DB_close_rewrite ldi temp, 1 add mem_2, temp adc mem_3, ZERO rcall AT45DB_open_rewrite AT45DB_write_return: ret AT45DB_close_rewrite: AT45DB_close_buffer_write AT45DB_buffer_1_to_page mem_3, mem_2 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; continuous write: write to memory continously, switching ; buffers automatically. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; depends on the existence of the following registers: ; mem_3, mem_2, mem_1, buffer, ZERO AT45DB_open_continuous_write: cp buffer, ZERO brne AT45DB_resume_continuous_write_open2 AT45DB_open_buffer_1_write mem_1 ret AT45DB_resume_continuous_write_open2: AT45DB_open_buffer_2_write mem_1 ret AT45DB_cont_write: AT45DB_write_spi inc mem_1 breq AT45DB_cont_write_switch ; switch buffers when this one fills ret AT45DB_cont_write_switch: AT45DB_close_buffer_write rcall AT45DB_wait_for_ready tst buffer breq AT45DB_cont_write_open2 AT45DB_cont_write_open1: ; close out buffer 2 AT45DB_buffer_2_to_page mem_3, mem_2 ; open buffer 1 AT45DB_open_buffer_1_write ZERO rjmp AT45DB_cont_write_update AT45DB_cont_write_open2: ; close out buffer 1 AT45DB_buffer_1_to_page mem_3, mem_2 ; open buffer 2 AT45DB_open_buffer_2_write ZERO nop ; even timing for rjmp + br false (3) vs. fall-through + br true (2) AT45DB_cont_write_update: ; reset byte counter mov mem_1, ZERO ; increment buffers - add 256 (add 1 to byte 2) ldi temp, 1 add mem_2, temp adc mem_3, ZERO com buffer ; switch buffer marker ret AT45DB_pause_continuous_write: AT45DB_close_buffer_write ret AT45DB_close_continuous_write: AT45DB_close_buffer_write ; check if our buffer has new data. cp mem_1, ZERO breq AT45DB_close_continuous_write_done rcall AT45DB_wait_for_ready cp buffer, ZERO brne AT45DB_close_continuous_write2 ; close out buffer 1 AT45DB_buffer_1_to_page mem_3, mem_2 rjmp AT45DB_close_continuous_write_done AT45DB_close_continuous_write2: ; close out buffer 2 AT45DB_buffer_2_to_page mem_3, mem_2 AT45DB_close_continuous_write_done: ret