; ; 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
i2c_flash_read_bytes i2c_flash_save_page i2c_poll i2c_calc_measure_address