; ; audiomega3-sampler ; ; Audio sampler ; .include "m88def.inc" ;.device ATmega88 ; ; dataflash definitions ; ; read .equ CONTINUOUS_ARRAY_READ = 0xE8 .equ MAIN_MEMORY_PAGE_READ = 0xD2 .equ BUFFER_READ = 0xD4 .equ STATUS_REGISTER_READ = 0xD7 .equ FLASH_BUSY = 7 ; write .equ BUFFER_WRITE = 0x84 .equ BUFFER_TO_MAIN_MEMORY_PAGE_PROGRAM_WITH_ERASE = 0x83 .equ BUFFER_TO_MAIN_MEMORY_PAGE = 0x88 .equ PAGE_ERASE = 0x81 .equ BLOCK_ERASE = 0x50 .equ MAIN_MEMORY_PAGE_PROGRAM_THROUGH_BUFFER = 0x82 ; additional .equ MAIN_MEMORY_PAGE_TO_BUFFER_TRANSFER = 0x53 .equ MAIN_MEMORY_PAGE_TO_BUFFER_COMPARE = 0x60 .equ AUTO_PAGE_REWRITE_THROUGH_BUFFER = 0x58 ; properties .equ PAGE_COUNT = 512 .equ BUFFER_COUNT = 264 ; ; definitions ; .equ tx = PD1; transmit pin .equ tx_port = PORTD .equ tx_ddr = DDRD .equ led1 = PC5 .equ led1_port = PORTC .equ led1_ddr = DDRC .equ led2 = PD5 .equ led2_port = PORTD .equ led2_ddr = DDRD .equ but1 = PD6 .equ but1_port = PORTD; .equ but1_ddr = DDRD; .equ but1_pin = PIND; .equ but2 = PD7 .equ but2_port = PORTD; .equ but2_ddr = DDRD; .equ but2_pin = PIND; .equ cs = PB2 .equ cs_port = PORTB .equ cs_ddr = DDRB ; ; registers ; .def bit_count = R16; bit counter .def temp = R17; temporary storage .def temp1 = R18; temporary storage .def temp2 = R19; .def txbyte = R20; data byte .def spi_byte = R21; .def remember_spi_byte = R26 .def page_count_low = R22 .def page_count_high = R23 .def buffer_count_low = R24 .def buffer_count_high = R25 ; ; macros ; ; 16-bit math .macro inc_16 ldi temp, 1 ldi temp1, 0 add @0, temp adc @1, temp1 .endmacro .macro add_16 ldi temp, low(@2) ldi temp1, high(@2) add @0, temp adc @1, temp1 .endmacro .macro cpi_16 cpi @0, low(@2) ldi temp, high(@2) cpc @1, temp .endmacro ; timing .macro delay_i ldi temp, @0 delay_loop: dec temp brne delay_loop .endmacro ; memory functions .macro write_flash_page_address ; bits: xxxxxx11 11111110 00000000 ; ^res ^^page pos^^buff pos^ mov temp1, page_count_low mov temp2, page_count_high lsl temp1 rol temp2 ; rotate through carry to shift bits 1 to the left mov spi_byte, temp2 ; N.B. high first, then low ; careful... write_spi poisons "temp" rcall write_spi mov spi_byte, temp1 rcall write_spi ldi spi_byte, 0 rcall write_spi .endmacro .macro write_dont_care_bytes ldi spi_byte, 0 rcall write_spi rcall write_spi rcall write_spi rcall write_spi .endmacro .macro write_opcode ldi spi_byte, @0 rcall write_spi .endmacro .macro enable_flash cbi cs_port, cs .endmacro .macro disable_flash sbi cs_port, cs .endmacro .macro reset_page_count ldi page_count_low, 0 ldi page_count_high, 0 .endmacro .macro reset_buffer_count ldi buffer_count_low, 0 ldi buffer_count_high, 0 .endmacro ; feedback & debugging .macro write_led_on cbi led2_port, led2 .endmacro .macro write_led_off sbi led2_port, led2 .endmacro .macro read_led_on cbi led1_port, led1 .endmacro .macro read_led_off sbi led1_port, led1 .endmacro .macro debug_spi mov txbyte, page_count_low rcall putchar mov txbyte, page_count_high rcall putchar mov txbyte, buffer_count_low rcall putchar mov txbyte, buffer_count_high rcall putchar mov txbyte, spi_byte rcall putchar .endmacro ; ; code segment ; .cseg .org 0 rjmp reset ; ; bit delay ; serial bit delay ; ;.equ b = 13 ; 9600 baud (clock /8) ;.equ b = 130 ; 9600 baud (clock /1) .equ b = 8 ; 115200 baud (clock /1) bit_delay: ldi temp, b bitloop: dec temp brne bitloop ret ; ; putchar ; assumes no line driver (doesn't invert bits) ; .equ sb = 1; number of stop bits putchar: ldi bit_count, 9+sb; 1+8+sb com txbyte; invert everything sec; set start bit putchar0: brcc putchar1; if carry set sbi tx_port, tx; send a '0' rjmp putchar2; else putchar1: cbi tx_port, tx ; send a '1' nop ; even out timing putchar2: rcall bit_delay; one bit delay rcall bit_delay lsr txbyte; get next bit dec bit_count; if not all bits sent brne putchar0; send next bit ret; ; ; char_delay ; delay between characters ; char_delay: ldi temp, 255 char_delay_loop: ldi temp1, 10 char_delay_loop1: dec temp1 brne char_delay_loop1 dec temp brne char_delay_loop ret ; ; Write data to SPI ; write_spi: mov remember_spi_byte, spi_byte rcall rw_spi mov spi_byte, remember_spi_byte ret read_spi: rcall rw_spi ret ; ; Software SPI ; .equ miso = PB4 .equ mosi = PB3 .equ sck = PB5 .equ nss = PB2 .equ spi_port = PORTB .macro ss_active cbi spi_port, nss .endmacro .macro ss_inactive sbi spi_port, nss .endmacro .macro sck_hi sbi spi_port, sck .endmacro .macro sck_lo cbi spi_port, sck .endmacro .macro mosi_hi sbi spi_port, mosi .endmacro .macro mosi_lo cbi spi_port, mosi .endmacro .macro addi subi @0, -@1 ;subtract the negative of an immediate value .endm .macro set_delay ;set up the time delay amount, from 1 to 7 subi @0, (@1 << 5) ;NOTE: THIS shift affects INC macro (below)! .endm .macro inc_delay ;bump the delay counter subi @0, -(1 << 5) ;shift value here must be same as above! .endm init_spi: ss_inactive ;set latch bit hi (inactive) sbi ddrb,nss ;make it an output ; sck_lo ;set clk line lo sbi ddrb,sck ;make it an output ; mosi_lo ;set data-out lo sbi ddrb,mosi ;make it an output ; cbi ddrb,miso ;not really required, it powers up clr'd! ret ena_spi: sck_lo ;(should already be there...) mosi_lo ss_active ret disa_spi: ss_inactive ret rw_spi: ;ldi temp,16 ;init loop counter to 16 bits ldi temp,8 ;use THIS line instead if 8-bit desired ; spi_loop: lsl spi_byte ;move 0 into D0, all other bits UP one slot, ;rol spi_hi ; and C ends up being first bit to be sent. ;If 8-bit desired, also comment out the preceding ROL SPI_HI statement ; brcc lo_mosi mosi_hi rjmp mosi_done ;this branch creates setup time on MOSI lo_mosi: mosi_lo nop ;also create setup time on MOSI mosi_done: ; sck_hi ; ;must now time the hi pulse - not much else we can do here but waste time ; set_delay temp,4 ;(4 * 3) cycle delay; range is from 1 to 7! time_hi: inc_delay temp ;inc upper nibble until it rolls over; then, brcs time_hi ; C gets CLEARED, & temp has original value ; sck_lo ;drop clock line low ; ;must now delay before reading in SPI data on MISO ; set_delay temp,4 time_lo: inc_delay temp brcs time_lo ; sbic pinb,miso ;after delay, read in SPI bit & put into D0 inc spi_byte ;we FORCED D0=0, so use INC to set D0. ; dec temp brne spi_loop ret wait_until_flash_ready: enable_flash write_opcode STATUS_REGISTER_READ wait_until_flash_ready_loop: rcall read_spi sbrs spi_byte, FLASH_BUSY rjmp wait_until_flash_ready_loop disable_flash ret ; ; Put flash pointer to 0 ; write_test: reset_page_count write_pages_loop: reset_buffer_count ;rcall wait_until_flash_ready write_led_on enable_flash write_opcode MAIN_MEMORY_PAGE_PROGRAM_THROUGH_BUFFER write_flash_page_address ; ; write 4 zeros, followed by the page address, ; followed by the low byte of the page address ; filling the rest of the page. ; ldi spi_byte, 1 rcall write_spi ; 1 rcall write_spi ; 2 rcall write_spi ; 3 rcall write_spi ; 4 mov spi_byte, page_count_low rcall write_spi ; 5 mov spi_byte, page_count_high rcall write_spi ; 6 add_16 buffer_count_low, buffer_count_high, 6 write_buffer_loop: ; fill rest of page mov spi_byte, page_count_low rcall write_spi debug_spi inc_16 buffer_count_low, buffer_count_high cpi_16 buffer_count_low, buffer_count_high, BUFFER_COUNT brlo write_buffer_loop disable_flash inc_16 page_count_low, page_count_high cpi_16 page_count_low, page_count_high, PAGE_COUNT brlo write_next_page brsh return write_next_page: rjmp write_pages_loop return: write_led_off ret main_memory_page_read_test: read_led_on reset_page_count main_memory_page_read_loop: enable_flash write_opcode MAIN_MEMORY_PAGE_READ write_flash_page_address write_dont_care_bytes reset_buffer_count mmpr_buffer_loop: rcall read_spi debug_spi ; print! inc_16 buffer_count_low, buffer_count_high cpi_16 buffer_count_low, buffer_count_high, BUFFER_COUNT brlo mmpr_buffer_loop disable_flash ; reset page count if we are over PAGE_COUNT inc_16 page_count_low, page_count_high cpi_16 page_count_low, page_count_high, PAGE_COUNT brlo mmpr_next_page reset_page_count mmpr_next_page: sbis but2_pin, but2 rjmp main_memory_page_read_loop disable_flash read_led_off ret continuous_read_test: read_led_on reset_page_count reset_buffer_count enable_flash write_opcode CONTINUOUS_ARRAY_READ write_flash_page_address write_dont_care_bytes continuous_read_loop: rcall read_spi debug_spi sbis but2_pin, but2 rjmp continuous_read_loop disable_flash read_led_off ret test_flash_page_address: reset_page_count ; bits: xxxxxx11 11111110 00000000 ; ^res ^^page pos^^buff pos^ test_flash_page_address_loop: ; Use buffer count as temp var (putchar poisons "temp") mov buffer_count_low, page_count_low mov buffer_count_high, page_count_high lsl buffer_count_low ; shift left rol buffer_count_high ; mov txbyte, buffer_count_high ; N.B. high first rcall putchar mov txbyte, buffer_count_low ; then low rcall putchar inc_16 page_count_low, page_count_high cpi_16 page_count_low, page_count_high, PAGE_COUNT brlo test_flash_page_address_loop ret test_inc_16: reset_page_count test_inc_16_loop: mov txbyte, page_count_low rcall putchar mov txbyte, page_count_high rcall putchar inc_16 page_count_low, page_count_high cpi_16 page_count_low, page_count_high, PAGE_COUNT brlo test_inc_16_loop ret ; ; main program ; reset: ; ; set clock divider to /1 ; ldi temp, (1 << CLKPCE) ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0) sts CLKPR, temp sts CLKPR, temp1 ; ; set stack pointer to top of RAM ; ldi temp, high(RAMEND) out SPH, temp ldi temp, low(RAMEND) out SPL, temp ; ; init pins ; sbi tx_ddr, tx sbi tx_port, tx sbi led1_ddr, led1 sbi led1_port, led1 sbi led2_ddr, led2 sbi led2_port, led2 cbi but1_ddr, but1 sbi but1_port, but1 cbi but2_ddr, but2 sbi but2_port, but2 ; SPI rcall init_spi ; ; main loop ; main_loop: sbis but1_pin, but1 rcall write_test sbis but2_pin, but2 rcall continuous_read_test rjmp main_loop