| CODE |
.include "2313def.inc" ;Pull-up resistors are required on SDA and SCL lines. See the I2C spec. for details. ;The subroutine, I2CInit, needs to be called during initialization, preferable after the I/O ;ports are initialized. I2CInit will set up the I2C I/O pins and initialize the bus. ;The EEPROM code needs to be customized to deal with the I/O pins being used and the ;processor clock speed. All constants and registers that need to be modified are found ;in the I2C Memory Driver section. Check and if necessary, change usage of I2CPORT, I2CDDR, ;and I2CPIN in the I2C routines if necessary. Also change the values of bSDA and bSCL to ;agree with the I/O bits used for SDA and SCL respectively. ;Delays to accommodate various bus speed and processor clock rates are controlled by ;the constant I2CDelayConstant. ;The device address constant (ADDR24LC64 in this case) needs to match that of the ;EEPROM being used. The 24LC64 and 24LC256 use address A0 when their address pins are tied low. ;There are two main subroutines of interest. ;WriteI2Mem writes the byte contained in register "I2WData" into the EEPROM location ;pointed to by ZH,ZL. ;ReadI2Mem reads the byte at the EEPROM location pointed to by ZH,ZL and returns the byte in ;register "temp". ;**************Start I2C Memory Driver Constants and Registers ;I2C routines written by sellin (Sean Ellis) ;temp and I2WDtata must be high registers .def DCounter = r2; Used for counting loops. Can be used for other things in other routines .def temp = r16; Return value from functions. Can be used for other things in other rotuines .def I2WData = r17; Write data for EEPROM reoutines ; I2C Device addressing .equ ADDR24LC64 = 0xA0; 'LC64, 'LC128, and 'LC256 address byte (1010 000R) .equ I2CREAD = 0x01; read/write bit = read .equ I2CWRITE = 0x00; read/write bit = write .equ I2CDelayConstant=$04; Constant for delay loop- change as function of uC clock frequency ; IO Port Bits .equ bSDA = 6;* SET THIS The bit on the I/O Port used for SDA .equ bSCL = 5;* SET THIS The bit on the I/O Port used for SCL .equ mSDA = (1<<bSDA);Calculate binary value corresponding to I/O port bit for masking .equ mSCL = (1<<bSCL);Calculate binary value corresponding to I/O port bit for masking .equ I2CPORT = PORTD;*Set to correspond to I/O Port used for I2C .equ I2CDDR = DDRD;*Set to correspond to I/O Port used for I2C .equ I2CPIN = PIND;*Set to correspond to I/O Port used for I2C ;**************End I2C Memory Driver Constants and Registers .equ clock = 4000000;* Set for uC clock frequency .equ baudrate = 9600 ;choose a baudrate .equ baudconstant = (clock/(16*baudrate))-1 ;D0 USED FOR UART RECEIVE ;D1 USED FOR UART TRANSMIT ;D2 ;D3 ;D4 ;D5 SCA via firmware ;D6 SDA via firmware ;D7 The AT90S2313 does not have a D7 I/O port. ;UART baud rate calculation .cseg .ORG $0000 ;Reset rjmp start start: ldi r16,RAMEND ;Init Stack Pointer out spl,r16 ldi temp,$FF ;All I/O inputs with pasive pullups out PORTB,temp out PORTD,temp ldi temp,$00 out DDRB,temp out DDRD,temp ldi temp,baudconstant out ubrr,temp ;Load baud rate for UART. sbi ucr,rxen ;Enable WAUT receive sbi ucr,txen ;Enable UART Transmit rcall I2CInit ; Initialize I2C bus rjmp main recvchar: ;Receive a byte from the terminal into temp sbis usr,rxc ;Wait for byte to be received rjmp recvchar in temp,udr ;Read byte ret emitchar:;Send character contained in temp sbis usr,udre ;wait until the register is cleared. rjmp emitchar out udr,temp ;send the character. ret ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ;----------------------------------------------------------------- ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ; START I2C ROUTINES ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ;----------------------------------------------------------------- ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx I2CDelay: push temp ldi temp,I2CDelayConstant delaymore: dec temp brne delaymore pop temp ret ;----------------------------------------------------------------- ; I2CInit ; Initialize I2C interface, ports, etc. I2CInit: ; Set up port assignments ldi temp,mSCL ; SCL only output by default out I2CDDR,temp out I2CPORT,temp ; All outputs high, input pullups disabled cbi I2CPORT,bSDA; Data line always drives low if driven cbi I2CDDR,bSDA; SDA as input to start with - floats high cbi I2CPORT,bSCL; Set SCL low to try and avoid... sbi I2CDDR,bSCL; ...false start transitions rcall I2CDelay rjmp I2CStop ; Stop any erroneous transfer ;----------------------------------------------------------------- ; I2CStart ; Send I2C start condition ; Assumes SCL and SDA are high to start with. ; Leaves SCL and SDA low I2CStart: rcall I2CDelay sbi I2CDDR,bSDA; Drive data line low (start bit) rcall I2CDelay cbi I2CPORT,bSCL; Clock line low (ready to start) ret ;----------------------------------------------------------------- ; I2CStop ; Send I2C stop condition ; Assumes SCL is low to start with, SDA may be in either state ; Leaves SCL and SDA high I2CStop: sbi I2CDDR,bSDA; SDA driven low rcall I2CDelay sbi I2CPORT,bSCL; Clock line high (ready to stop) rcall I2CDelay cbi I2CDDR,bSDA; Data line floats high (stop bit) rcall I2CDelay ret ;----------------------------------------------------------------- ReadI2Mem:; Reads external memory at Z to temp rcall I2CStart ldi I2WData,(ADDR24LC64|I2CWRITE) rcall I2CSendAddress brne ReadI2MemError; No ack! mov I2WData,ZH rcall I2CSendByte mov I2WData,ZL rcall I2CSendByte rcall I2CStop rcall I2CDelay rcall I2CStart ldi I2WData,(ADDR24LC64|I2CREAD) rcall I2CSendAddress brne ReadI2MemError; No ack! ldi I2WData,1; Don't send ack rcall I2CReadByte ret ReadI2MemError: rcall I2CStop clr DCounter ReadI2MemErrorDelay: dec DCounter brne ReadI2MemErrorDelay rjmp ReadI2Mem; and try again ;----------------------------------------------------------------- WriteI2Mem:; Writes I2WData to external memory at Z mov temp,I2WData; save value rcall I2CStart ldi I2WData,(ADDR24LC64|I2CWRITE) rcall I2CSendAddress brne WriteI2MemError; No ack! mov I2WData,ZH rcall I2CSendByte mov I2WData,ZL rcall I2CSendByte mov I2WData,temp rcall I2CSendByte rcall I2CStop ret WriteI2MemError: rcall I2CStop clr DCounter WriteI2MemErrorDelay: dec DCounter brne WriteI2MemErrorDelay mov I2WData,temp ; restore data value rjmp WriteI2Mem; and try again ;----------------------------------------------------------------- ; I2CReadByte Read a byte from the I2C bus and acknowledge ; If I2WData is zero, acknowledge is sent, otherwise not. ; Byte is returned in temp ; Leaves SCL low, I2C bus not driven. I2CReadByte: ldi temp,8 mov DCounter,temp clr temp cbi I2CDDR,bSDA; release bus (floats high) I2CReadBits: sbi I2CPORT,bSCL; clock high rcall I2CDelay add temp,temp sbic I2CPIN,bSDA; if SDA clear, skip increment inc temp cbi I2CPORT,bSCL; clock low rcall I2CDelay dec DCounter brne I2CReadBits tst I2WData ; Send an ACK...? brne I2CReadBitsNack sbi I2CDDR,bSDA; yes... drive data bus low I2CReadBitsNack: sbi I2CPORT,bSCL; clock high rcall I2CDelay cbi I2CPORT,bSCL; clock low rcall I2CDelay cbi I2CDDR,bSDA; release bus (floats high) ret ;----------------------------------------------------------------------------------------------------------------- ; I2CSendByte ; Sends 8 bits of data from I2WData and listens for an ACK at the ; end. Returns Z if ack received, else NZ. ; Assumes a start condition has already been sent, and SDA and SCL ; are low Leaves SCL low, SDA not driven. Uses DCounter and I2WData. I2CSendAddress: I2CSendByte: push temp ldi temp,8 mov DCounter,temp pop temp I2CSendBits: add I2WData,I2WData brcc I2CSendBits0 cbi I2CDDR,bSDA; send 1 (floats high) rjmp I2CSendBits1 I2CSendBits0: sbi I2CDDR,bSDA; send 0 (drives low) I2CSendBits1: rcall I2CDelay sbi I2CPORT,bSCL; clock high rcall I2CDelay cbi I2CPORT,bSCL; clock low dec DCounter brne I2CSendBits cbi I2CDDR,bSDA; stop driving data bus rcall I2CDelay sbi I2CPORT,bSCL; clock high rcall I2CDelay in I2WData,I2CPIN; sample data bus cbi I2CPORT,bSCL; clock low rcall I2CDelay andi I2WData,mSDA; check ack bit (z set if OK) ret ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ;----------------------------------------------------------------- ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ; END I2C ROUTINES ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ;----------------------------------------------------------------- ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ;DEMONSTRATION PROGRAM ;Writes carriage return, linefeed, and asterisk to terminal, ;Then echos characters back to terminal as it writes each character to the ;EEPROM. When a dollar sign is encountered, it is treated as an end of file ;indicator and stored in memory, then the contents of the memory is sent to the ;terminal until the dollar sign is reached, at which time the program startrs over. ;If the first character received from the terminal at the start of the program is ;a dollar sign, the contents of the EEPROM will be dumped up to the first dollar sign ;stored in EEPROM. Main: ldi temp,$0A ;Send return, linefeed, asterisk rcall emitchar ldi temp,$0D rcall emitchar ldi temp,$2A rcall emitchar clr ZH ;Clear Z register (EEPROM pointer) clr ZL MainLoop: rcall recvchar ;Get char from terminal cpi temp,$24 ;dump if its a dollar sign $ breq dumpsome push temp ;Save char on return stack mov I2WData,temp ;Write char to EEPROM rcall WriteI2Mem adiw ZL,1 ;Increment ZH,ZL for chips that support adiw instruction pop temp ;Retrieve char rcall emitchar ;Echo char to the terminal rjmp MainLoop ;Go back and do it again dumpsome: cpi ZH,0 ;If ZH,ZL = 00 00, then dont' write dollar sign brne zwrite cpi ZL,0 breq nowriteds ;Don't write a dollar sign if this is EEPROM location 00 00 zwrite: mov I2WData,temp ;Write the dollar sign $ in temp that brought us here rcall WriteI2Mem adiw ZL,1 ;Increment ZH,ZL for chips that support adiw instruction nowriteds: clr ZH ;Dump EEPROM contents until dollar sigsn is encountered clr ZL dumpsomemore: rcall ReadI2Mem adiw ZL,1 ;Increment ZH,ZL for chips that support adiw instruction rcall emitchar cpi temp,$24 breq Main rjmp dumpsomemore .exit |