+; I2C (TWI) master interface.
+; This is part of the Z80-CP/M emulator written by Sprite_tm.
+;
+; Copyright (C) 2010 Leo C.
+;
+; This file is part of avrcpm.
+;
+; avrcpm is free software: you can redistribute it and/or modify it
+; under the terms of the GNU General Public License as published by
+; the Free Software Foundation, either version 3 of the License, or
+; (at your option) any later version.
+;
+; avrcpm is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with avrcpm. If not, see <http://www.gnu.org/licenses/>.
+;
+; $Id$
+;
+
+#if I2C
+
+/* General TWI Master status codes */
+#define TWI_START 0x08 /* START has been transmitted */
+#define TWI_REP_START 0x10 /* Repeated START has been transmitted */
+#define TWI_ARB_LOST 0x38 /* Arbitration lost */
+
+/* TWI Master Transmitter status codes */
+#define TWI_MTX_ADR_ACK 0x18 /* SLA+W has been transmitted and ACK received */
+#define TWI_MTX_ADR_NACK 0x20 /* SLA+W has been transmitted and NACK received */
+#define TWI_MTX_DATA_ACK 0x28 /* Data byte has been transmitted and ACK received */
+#define TWI_MTX_DATA_NACK 0x30 /* Data byte has been transmitted and NACK received */
+
+/* TWI Master Receiver status codes */
+#define TWI_MRX_ADR_ACK 0x40 /* SLA+R has been transmitted and ACK received */
+#define TWI_MRX_ADR_NACK 0x48 /* SLA+R has been transmitted and NACK received */
+#define TWI_MRX_DATA_ACK 0x50 /* Data byte has been received and ACK transmitted */
+#define TWI_MRX_DATA_NACK 0x58 /* Data byte has been received and NACK transmitted */
+
+/* TWI Miscellaneous status codes */
+#define TWI_NO_STATE 0xF8 /* No relevant state information available */
+#define TWI_BUS_ERROR 0x00 /* Bus error due to an illegal START or STOP condition */
+
+
+#define I2C_BR ((F_CPU / (2 * I2C_CLOCK)) - 8) /* I2C Bit Rate */
+
+;----------------------------------------------------------------------
+;
+; TWINT: TWI Interrupt Flag
+; TWEA: TWI Enable Acknowledge Bit
+; TWSTA: TWI START Condition Bit
+; TWSTO: TWI STOP Condition Bit
+; TWEN: TWI Enable Bit
+; TWIE: TWI Interrupt Enable
+;
+; (1<<TWEN)|(1<<TWIE)|(1<<TWINT)
+; (1<<TWEN)|(1<<TWIE)|(1<<TWINT)| (1<<TWEA)
+; (1<<TWEN)|(1<<TWIE)|(1<<TWINT)
+;
+; default:
+; (1<<TWEN)| (1<<TWINT)| (1<<TWSTO)
+;
+; Init:
+; (1<<TWEN)
+;
+; start read/write:
+; (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+; (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+; (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+; (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+;
+; wait ready:
+; (1<<TWIE)|(1<<TWSTO)
+;
+;----------------------------------------------------------------------
+
+ .dseg
+
+i2c_var:
+i2ci_idx:
+ .byte 1
+i2c_result:
+ .byte 1
+i2c_bufcnt:
+ .byte 1
+i2c_buf:
+ .byte I2C_BUFSIZE
+
+ .equ oi2ci_idx = 0
+ .equ oi2c_result = 1
+ .equ oi2c_bufcnt = 2
+ .equ oi2c_buf = 3
+
+;------------------------------------------------------------------
+
+ .cseg
+
+
+ INTERRUPT TWIaddr
+
+ push temp
+ in temp,sreg
+ push temp
+ push temp2
+ push zh
+ push zl
+
+ ldiw z,i2c_var
+ ldd temp2,z+oi2ci_idx
+
+ inm8 temp,TWSR
+
+ cpi temp,TWI_START
+ breq i2ci_START
+ cpi temp,TWI_REP_START
+ breq i2ci_REP_START
+ cpi temp,TWI_MTX_ADR_ACK
+ breq i2ci_MTX_ADR_ACK
+ cpi temp,TWI_MTX_DATA_ACK
+ breq i2ci_MTX_DATA_ACK
+ cpi temp,TWI_MRX_ADR_ACK
+ breq i2ci_MRX_ADR_ACK
+ cpi temp,TWI_MRX_DATA_ACK
+ breq i2ci_MRX_DATA_ACK
+ cpi temp,TWI_MRX_DATA_NACK
+ breq i2ci_MRX_DATA_NACK
+ rjmp i2ci_default
+
+i2ci_START:
+i2ci_REP_START:
+ clr temp2 ;reset buffer pointer
+i2ci_MTX_ADR_ACK:
+i2ci_MTX_DATA_ACK:
+ ldd temp,z+oi2c_bufcnt
+ cp temp2,temp
+ brsh i2ci_12
+ add zl,temp2
+ adc zh,_0
+ inc temp2
+ ldd temp,z+oi2c_buf
+ outm8 TWDR,temp
+ ldi temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)
+ rjmp i2ci_end
+
+i2ci_12:
+ std z+oi2c_result,_255
+ rjmp i2ci_default
+
+i2ci_MRX_DATA_ACK:
+ add zl,temp2
+ adc zh,_0
+ inc temp2
+ inm8 temp,TWDR
+ std z+oi2c_buf,temp
+i2ci_MRX_ADR_ACK:
+ lds temp,i2c_bufcnt
+ dec temp
+ cp temp2,temp
+ brsh i2ci_32
+ ldi temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)
+ rjmp i2ci_end
+i2ci_32:
+ ldi temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)
+ rjmp i2ci_end
+
+i2ci_MRX_DATA_NACK:
+ std z+oi2c_result,_255 ;result = 1
+ add zl,temp2
+ adc zh,_0
+ inm8 temp,TWDR
+ std z+oi2c_buf,temp
+; fall thru
+
+i2ci_default:
+ ldi temp,(1<<TWEN)|(1<<TWINT)|(1<<TWSTO)
+
+i2ci_end:
+ outm8 TWCR,temp
+ sts i2ci_idx,temp2
+ pop zl
+ pop zh
+ pop temp2
+ pop temp
+ out sreg,temp
+ pop temp
+ reti
+
+;------------------------------------------------------------------
+
+i2c_init:
+ ldi temp,I2C_BR
+ outm8 TWBR,temp
+ outm8 TWDR,_255 ;
+ ldi temp,(1<<TWEN) ;Enable TWI, disable Interupts.
+ outm8 TWCR,temp
+
+ ldi temp,1
+ sts i2c_result,temp
+ ret
+
+;------------------------------------------------------------------
+
+i2c_waitready:
+ push temp
+i2c_b:
+ inm8 temp,TWCR
+ andi temp,(1<<TWIE)|(1<<TWSTO)
+ brne i2c_b
+ pop temp
+ ret
+
+;------------------------------------------------------------------
+;
+; z: Pointer to the data to write.
+; First byte is slave address
+; temp2: Number of bytes to write including address byte.
+;
+
+i2c_write:
+ rcall i2c_waitready
+
+ push zh
+ push zl
+ push xh
+ push xl
+
+#if 0
+ rcall printstr
+ .db '\r', 'W', 0,0
+ mov temp,temp2
+ rcall printhex
+ rcall printstr
+ .db ": ", 0,0
+ rcall dbg_hexdump_line
+#endif
+
+ ldiw x,i2c_result
+ st x+,_0 ;result = 0
+ st x+,temp2 ;store size
+ ld temp,z+
+ cbr temp,0x01
+i2c_wl:
+ st x+,temp
+ dec temp2
+ breq i2c_wle
+ ld temp,z+
+ rjmp i2c_wl
+i2c_wle:
+ ; Enable TWI, TWI int and initiate start condition
+ ldi temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ outm8 TWCR,temp
+
+ pop xl
+ pop xh
+ pop zl
+ pop zh
+ ret
+
+
+;------------------------------------------------------------------
+;
+; z: Pointer to data buffer.
+; First byte of buffer is slave address
+; temp2: Number of bytes to write including address byte.
+;
+; temp: return (fail = 0, else succsess)
+
+i2c_read:
+ rcall i2c_waitready
+
+ push zh
+ push zl
+ push xh
+ push xl
+ ldiw x,i2c_result
+ st x+,_0 ;result = 0
+ st x+,temp2 ;store size
+ ld temp,z
+ sbr temp,0x01
+ st x,temp
+
+ ; Enable TWI, TWI int and initiate start condition
+ ldi temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ outm8 TWCR,temp
+
+ rcall i2c_waitready
+
+#if 0
+ rcall printstr
+ .db '\r', 'R', 0,0
+ mov temp,temp2
+ rcall printhex
+ rcall printstr
+ .db ": ", 0,0
+#endif
+
+ lds temp,i2c_result
+ tst temp
+ breq i2c_ex ;
+i2c_rl:
+ ld temp,x+
+ st z+,temp
+ dec temp2
+ brne i2c_rl
+
+i2c_ex:
+ lds temp,i2c_result
+ pop xl
+ pop xh
+ pop zl
+ pop zh
+#if 0
+ rcall dbg_hexdump_line
+#endif
+ ret
+
+;------------------------------------------------------------------
+
+ .dseg
+
+vi2c_stat:
+ .byte 1
+vi2c_blen:
+ .byte 1
+vi2c_addr:
+ .byte 2
+
+ .cseg
+
+vi2c_stat_get:
+ lds temp,vi2c_stat
+ ret
+
+vi2c_param_get:
+ ldiw z,vi2c_blen
+ add zl,temp3
+ adc zh,_0
+ ld temp,z
+ ret
+
+vi2c_param_set:
+ ldiw z,vi2c_blen
+ add zl,temp3
+ adc zh,_0
+ st z,temp
+ ret
+
+;------------------------------------------------------------------
+;
+; x: Pointer to the data buffer.
+; First byte is slave address
+; temp3: Number of bytes to write including address byte.
+;
+
+vi2c_read:
+ rcall i2c_waitready
+
+ ldiw z,i2c_var
+ std z+oi2c_result,_0 ;result = 0
+ lds temp3,vi2c_blen
+ std z+oi2c_bufcnt,temp3 ;store size
+ adiw z,oi2c_buf
+ ldsw x,vi2c_addr
+ lcall dram_read_pp
+ sbr temp,0x01
+ st z+,temp
+ dec temp3
+
+ ; Enable TWI, TWI int and initiate start condition
+ ldi temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ outm8 TWCR,temp
+
+ rcall i2c_waitready
+
+ lds temp,i2c_result
+ tst temp
+ breq vi2c_ex ;
+vi2c_rl:
+ ld temp,z+
+ lcall dram_write_pp
+ dec temp3
+ brne vi2c_rl
+
+vi2c_ex:
+ ret
+
+;------------------------------------------------------------------
+;
+; x: Pointer to the data to write.
+; First byte is slave address
+; temp2: Number of bytes to write including address byte.
+;
+
+vi2c_write:
+ rcall i2c_waitready
+ ldiw z,i2c_var
+ std z+oi2c_result,_0 ;result = 0
+ lds temp3,vi2c_blen
+ std z+oi2c_bufcnt,temp3 ;store size
+ adiw z,oi2c_buf
+ ldsw x,vi2c_addr
+ lcall dram_read_pp
+ cbr temp,0x01
+vi2c_wl:
+ st z+,temp
+ dec temp3
+ breq vi2c_wle
+ lcall dram_read_pp
+ rjmp vi2c_wl
+vi2c_wle:
+ ; Enable TWI, TWI int and initiate start condition
+ ldi temp,(1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ outm8 TWCR,temp
+
+ ret
+
+
+vi2c_ctrl:
+ cpi temp,1
+ brne vi2c_c1
+ rcall vi2c_read
+ rjmp vi2c_ce0
+vi2c_c1:
+ cpi temp,2
+ breq vi2c_c2
+ ldi temp,0xff
+ rjmp vi2c_ce
+vi2c_c2:
+ rcall vi2c_write
+vi2c_ce0:
+ lds temp,i2c_result
+ com temp
+vi2c_ce:
+ sts vi2c_stat,temp
+ ret
+
+;------------------------------------------------------------------
+
+
+pcf8574_in:
+ ; make a buffer on stack
+ push _255 ;place holder for input value
+ in zh,sph
+ in zl,spl
+ ldi temp,0x20 ;pcf8574 address (7 bit)
+ add temp,temp3
+ lsl temp
+ push temp ;slave address
+ ldi temp2,2
+ rcall i2c_read
+ pop temp ;remove slave address from stack
+ pop temp ;return input value
+ ret
+
+pcf8574_out:
+ ; make a buffer on stack
+ push temp ;output value
+ in zh,sph
+ in zl,spl
+ ldi temp,0x20 ;pcf8574 address (7 bit)
+ add temp,temp3
+ lsl temp
+ push temp ;slave address
+ ldi temp2,2
+ rcall i2c_write
+ pop temp ;remove buffer from stack
+ pop temp ;
+ ret
+
+
+#endif /* I2C */
+;------------------------------------------------------------------
+; vim:set ts=8 noet nowrap
+