;
; i2c.asm: flash memory routines
; over the i2c serial bus
;
; uses the picmicro MSSP module
;
; developed and tested to work with
; atmel 24c128B 128k (16384 * 8) B serial eeprom
;
; 24c128B memory is organized as 256 pages (FADDRH) of 64 bytes (FADDRL)
;
; Note that the B-version of the chip was used in development
; prototype. B-version has 5ms write cycle instead of the regular
; 10ms write cycle.
;
; author: Marko Kanala, raato ]a t[ mulletronic.com
;
; LICENSE
; Creative Commons Attribution-NonCommercial-ShareALike 2.5
;
; You are free:
;
; * to copy, distribute, display, and perform the work
; * to make derivative works
;
; Under the following conditions:
;
; 1. Attribution. You must attribute the work in the manner specified by the
; author or licensor.
; 2. Noncommercial. You may not use this work for commercial purposes.
; 3. Share Alike. If you alter, transform, or build upon this work, you may
; distribute the resulting work only under a license identical to this one.
;
; * For any reuse or distribution, you must make clear to others the license
; terms of this work.
; * Any of these conditions can be waived if you get permission from the
; copyright holder.
;
; Your fair use and other rights are in no way affected by the above.
;
; This is a human-readable summary of the Legal Code.
; See full license at http://creativecommons.org/licenses/by-nc-sa/2.5/legalcode
;
; Copyright: Marko Kanala (raato@mulletronic.com).
;
I2C_DEVWRITE equ b'10100000' ; 0xa0, address pins to gnd
I2C_DEVREAD equ b'10100001' ; 0xa1, address pins to gnd
I2C_FOSC equ 40000000 ; 18f4x2 @ 40mhz
I2C_CLOCK equ 1000000 ; desired clock rate of 1mhz
; baud rate generator value (0x0a)
SSPADD_VALUE equ (((I2C_FOSC/I2C_CLOCK)/4) - 1)
;;; macros for I2C routines
; waits for SSPIF interrupt
I2CWAIT MACRO
btfss PIR1, SSPIF
bra $-2
ENDM
; sends a byte from WREG to i2c bus
I2CSEND MACRO
bcf PIR1, SSPIF
movwf SSPBUF
I2CWAIT
; TODO: should probably handle
; a missed ack here
;btfsc SSPCON2, ACKSTAT
;bra ackfailed
ENDM
; receives a byte from i2c to WREG
I2CRECEIVE MACRO
bcf PIR1, SSPIF
; enable receive mode
bsf SSPCON2, RCEN
I2CWAIT
movf SSPBUF, W
ENDM
; sends ack to i2c bus
I2CACK MACRO
bcf PIR1, SSPIF
; setup for ACK and send
bcf SSPCON2, ACKDT
bsf SSPCON2, ACKEN
I2CWAIT
ENDM
; sends nak to i2c bus
I2CNAK MACRO
bcf PIR1, SSPIF
; setup for nak and send
bsf SSPCON2, ACKDT
bsf SSPCON2, ACKEN
I2CWAIT
ENDM
; sends i2c start
I2CSTART MACRO
bcf PIR1, SSPIF
bsf SSPCON2, SEN
I2CWAIT
ENDM
; sends i2c restart
I2CRESTART MACRO
bcf PIR1, SSPIF
bsf SSPCON2, RSEN
I2CWAIT
ENDM
; sends i2c stop
I2CSTOP MACRO
bcf PIR1, SSPIF
bsf SSPCON2, PEN
I2CWAIT
ENDM
;
; reads pattern data from i2c flash memory
; note: PATTERN_SIZE_FLASH bytes are read.
;
; address is calculated from nextbank / nextpattern
; values.
;
; pattern_data is used as destination address.
;
i2c_flash_read:
lfsr FSR1, pattern_data
movlw 16
mulwf nextbank ; bank * 16
movf PRODL, W
addwf nextpattern, W ; + pattern number
; note: address is clocked in as xxHHHHHH HHLLLLLL
; so bank * 16 + pattern automatically produces the FADDRH
movwf FADDRH
clrf FADDRL
bcf T2CON, TMR2ON ; disable key/led timer on i2c
movlw PATTERN_SIZE_FLASH
call i2c_flash_read_bytes
bsf T2CON, TMR2ON ; enable key/led timer
SETEVENT EVENT_FLASHREADOK
return
;
; writes a full pattern to i2c flash memory
;
; address is calculated from currentbank / currentpattern
; values.
;
; pattern_data is used as a source address.
;
; note that this function uses page writes
; to write a full pattern to the flash memory.
i2c_flash_save:
; note: FSR1 is postincremented automatically
lfsr FSR1, pattern_data
movlw 16
mulwf currentbank ; bank * 16
movf PRODL, W
addwf currentpattern, W ; + pattern number
; note: address is clocked in as xxHHHHHH HHLLLLLL
; so bank * 16 + pattern automatically produces the FADDRH
movwf FADDRH
clrf FADDRL
movlw 64
bcf T2CON, TMR2ON ; disable key/led timer on i2c
call i2c_flash_save_page ; first 64 bytes
movlw b'01000000'
addwf FADDRL, F ; address += 64
call i2c_poll ; poll for write cycle
movlw 64
call i2c_flash_save_page ; bytes 64 - 128
movlw b'01000000'
addwf FADDRL, F ; address += 64
call i2c_poll ; poll for write cycle
movlw PATTERN_SIZE_FLASH-128 ; bytes 128 - 164
call i2c_flash_save_page
call i2c_poll ; poll for write cycle
bsf T2CON, TMR2ON ; enable key/led timer
return
;
; reads data from i2c flash memory
; note: 256 bytes are read.
;
; address is calculated from nextbank / nextpattern
; values.
;
; sysexdata is used as destination address.
;
i2c_flash_read_sysex_256:
lfsr FSR1, sysexdata
movlw 16
mulwf nextbank ; bank * 16
movf PRODL, W
addwf nextpattern, W ; + pattern number
; note: address is clocked in as xxHHHHHH HHLLLLLL
; so bank * 16 + pattern automatically produces the FADDRH
movwf FADDRH
clrf FADDRL
bcf T2CON, TMR2ON ; disable key/led timer on i2c
movlw 0 ; 256 bytes!
call i2c_flash_read_bytes
bsf T2CON, TMR2ON ; enable key/led timer
return
;
; writes 128 bytes of sysex data to i2c flash memory
;
; dest address is calculated from currentbank / currentpattern
; values.
;
; sysexdata is used as a source address.
;
; WREG is the offset the data is written with
; so this function can be used to write the full 256
; bytes of data in two halves
i2c_flash_save_sysex_128:
movwf FADDRL
; note: FSR1 is postincremented automatically
lfsr FSR1, sysexdata
addwf FSR1L
movlw 16
mulwf currentbank ; bank * 16
movf PRODL, W
addwf currentpattern, W ; + pattern number
; note: address is clocked in as xxHHHHHH HHLLLLLL
; so bank * 16 + pattern automatically produces the FADDRH
movwf FADDRH
movlw 64
bcf T2CON, TMR2ON ; disable key/led timer on i2c
call i2c_flash_save_page ; first 64 bytes
call i2c_poll ; poll for write cycle
movlw b'01000000'
addwf FADDRL, F ; address += 64
movlw 64
call i2c_flash_save_page ; bytes 64 - 128
call i2c_poll ; poll for write cycle
bsf T2CON, TMR2ON ; enable key/led timer
return
;
; reads track name and length from the flash memory
;
; data is read for currenttrack
i2c_flash_read_trackmeta:
rlncf currenttrack, W ; w = 2 * currenttrack
addwf WREG ; currenttrack * 4
; track name at the end of page 4
addlw 3
movwf FADDRH
movlw 0xf2 ; name at offset 0x3f2
movwf FADDRL
lfsr FSR1, track_name
movlw 12
bcf T2CON, TMR2ON ; disable key/led timer on i2c
call i2c_flash_read_bytes
; track length at offset 0x3fe
movlw 0xfe
movwf FADDRL
lfsr FSR1, currenttracklen
movlw 2 ; len == 2 bytes
call i2c_flash_read_bytes
bsf T2CON, TMR2ON
return
;
; reads pattern name for track editor
;
; bank & pattern name is decoded from editedmeasuredata
i2c_flash_read_trackpattern_name:
; calculate address for pattern name
movf editedmeasuredata, W
DECODE_BANK_FROM_MEASURE
mullw 16 ; bank * 16
movf editedmeasuredata, W
DECODE_PATTERN_FROM_MEASURE
addwf PRODL, W
movwf FADDRH
clrf FADDRL
lfsr FSR1, pat_name
movlw 12
bcf T2CON, TMR2ON ; disable key/led timer on i2c
call i2c_flash_read_bytes
bsf T2CON, TMR2ON
return
;
; saves track name to the flash memory
;
; address is calculated from currenttrack
;
i2c_flash_save_trackname:
rlncf currenttrack, W ; w = 2 * currenttrack
addwf WREG ; currenttrack * 4
; track name at the end of page 4
addlw 3
movwf FADDRH
movlw 0xf2 ; name at offset 0x3f2
movwf FADDRL
lfsr FSR1, track_name
movlw 12
bcf T2CON, TMR2ON ; disable key/led timer on i2c
call i2c_flash_save_page
call i2c_poll ; poll for write cycle
bsf T2CON, TMR2ON
return
;
; saves current track length to the flash memory
;
; address is calculated from currenttrack
;
i2c_flash_save_tracklength:
rlncf currenttrack, W ; w = 2 * currenttrack
addwf WREG ; currenttrack * 4
addlw 3
movwf FADDRH
movlw 0xfe ; len at offset 0x3fe
movwf FADDRL
lfsr FSR1, currenttracklen
bcf T2CON, TMR2ON ; disable key/led timer on i2c
; 2 bytes
movlw 2
call i2c_flash_save_page
call i2c_poll ; poll for write cycle
bsf T2CON, TMR2ON
return
;
; reads 2 a track measures from the flash memory
;
; the next measure data is set to "nextmeasuredata"
;
; measure position is read from memory location
; nextmeasure
;
; data is set to currentmeasuredata AND
; editedmeasuredata
;
i2c_flash_read_measure:
lfsr FSR1, currentmeasuredata
i2c_flash_read_measure_fsr1:
call i2c_calc_measure_address
; FADDRx now set
bcf T2CON, TMR2ON ; disable key/led timer on i2c
; one byte / measure
movlw 1
call i2c_flash_read_bytes
bsf T2CON, TMR2ON ; enable key/led timer
; when current is read discard edited data
movf POSTDEC1, W ; fsr1 - 1
movff INDF1, editedmeasuredata
return
;
; saves a measure to the flash memory
;
; measure position is read from memory location
; currentmeasure
;
; data is saved from editedmeasuredata
i2c_flash_save_measure:
; i2c_calc_measure_address uses nextmeasure for calculation
; so hoax hoax a bit
movff nextmeasure, i2c_temp
movff nextmeasure + 1, i2c_temp2
movff currentmeasure, nextmeasure
movff currentmeasure + 1, nextmeasure + 1
call i2c_calc_measure_address
movff i2c_temp, nextmeasure
movff i2c_temp2, nextmeasure + 1
lfsr FSR1, editedmeasuredata ; source
movlw 1 ; count
bcf T2CON, TMR2ON ; disable key/led timer on i2c
call i2c_flash_save_page
call i2c_poll ; poll for write cycle
bsf T2CON, TMR2ON
; set current also
movff editedmeasuredata, currentmeasuredata
return
; calculates a measure address in flash memory
; according to values in nextmeasure & nextmeasure+1
; and currenttrack
;
; track data in flash:
;
; offset 0x0a4: first 92 bytes of measure data
; offset 0x1a4: 92 bytes of measure data
; offset 0x2a4: 92 bytes of measure data
; offset 0x3a4: 78 bytes of measure data
; offset 0x3f2: 12 bytes of track name
; offset 0x3fe: 2 bytes of track length
;
; measure boundaries:
;
; < 92 (0x005c), page 1
; < 184 (0x00b8), page 2
; < 276 (0x0114), page 3
; < 354 (0x0162), page 4
i2c_calc_measure_address:
rlncf currenttrack, W ; w = 2 * currenttrack
addwf WREG ; currenttrack * 4
movwf FADDRH
clrf FADDRL
; calculate FADDRx
tstfsz nextmeasure
bra i2c_calc_measure_address_34
; pages 1 or 2
movlw 183
cpfsgt nextmeasure + 1
bra i2c_calc_measure_address_12
; over 183 but less than 256, must be page 3
incf FADDRH
incf FADDRH
movlw 184
subwf nextmeasure + 1, W
addlw 0xa4
movwf FADDRL ; 0x2a4 + measure - 184
return
i2c_calc_measure_address_12:
movlw 91
cpfsgt nextmeasure + 1
bra i2c_calc_measure_address_1
; over 91 but less than 184, must be page 2
incf FADDRH
movlw 92
subwf nextmeasure + 1, W
addlw 0xa4
movwf FADDRL ; 0x1a4 + measure - 92
return
i2c_calc_measure_address_1:
; less than 92, must be page 1
movlw 0xa4
addwf nextmeasure + 1, W
movwf FADDRL ; 0x0a4 + measure
return
; measures >= 0x0100
i2c_calc_measure_address_34:
movlw 0x13
cpfsgt nextmeasure + 1
bra i2c_calc_measure_address_3
; over 276, must be page 4
movlw 3
addwf FADDRH ; += 276
movlw 0xa4 - (276 - 256)
addwf nextmeasure + 1, W
movwf FADDRL
return
i2c_calc_measure_address_3:
; over 255 but less than 276, must be page 3
incf FADDRH
incf FADDRH ; += 184
movlw 0xa4 + 256 - 184
addwf nextmeasure + 1, W
movwf FADDRL
return
; i2c generic -->
;
; writes bytes to i2c flash memory
;
; FADDRH = high address byte in flash
; FADDRL = low address byte in flash
; WREG = byte count (less or equal than 64 bytes)
; FSR1 = source address
;
i2c_flash_save_page:
movwf i2c_counter
; generate start condition
I2CSTART
; send device address to bus
movlw I2C_DEVWRITE
I2CSEND
; send flash address to bus
movf FADDRH, W
I2CSEND
movf FADDRL, W
I2CSEND
i2c_flash_save_loop:
; send byte from FSR1 to bus
movf POSTINC1, W
I2CSEND
decfsz i2c_counter
bra i2c_flash_save_loop
I2CSTOP
; be sure to poll the ACK bit from the device
; or wait 5 ms before calling this function again!
return
;
; reads WREG bytes from FADDR:FADDL and stores
; the results to POSTINC1.
;
i2c_flash_read_bytes:
movwf i2c_counter
; generate start condition
I2CSTART
; send device address to bus
movlw I2C_DEVWRITE
I2CSEND
; send flash address to bus
movf FADDRH, W
I2CSEND
movf FADDRL, W
I2CSEND
I2CRESTART
; send device read address
movlw I2C_DEVREAD
I2CSEND
bra i2c_flash_read_loop_skipack
i2c_flash_read_loop:
; send ack
I2CACK
i2c_flash_read_loop_skipack:
; receive and store byte
I2CRECEIVE
movwf POSTINC1
decfsz i2c_counter
bra i2c_flash_read_loop
; send nak after
I2CNAK
I2CSTOP
return
; Polls the flash device for an ACK bit
; which indicates the internal write cycle
; has finished.
i2c_poll:
I2CRESTART
movlw I2C_DEVWRITE
I2CSEND
btfsc SSPCON2, ACKSTAT ; is ACK low ?
bra i2c_poll
; ACK was low, write cycle has finished
I2CSTOP
return
;
; initializes the i2c bus
;
i2c_init:
; baud rate value
movlw SSPADD_VALUE
movwf SSPADD
; slew rate control disabled for 1mhz mode
bsf SSPSTAT, SMP
; select i2c input levels
bcf SSPSTAT, CKE
; make SDA and SCL as inputs
movlw b'00011000'
iorwf TRISC, F
; serial port enabled, master mode
movlw b'00101000'
movwf SSPCON1
clrf SSPCON2
return
External Labels :
i2c_flash_read_bytes
i2c_flash_save_page
i2c_poll
i2c_calc_measure_address