This post will explain how to modify Peter Fleury's I2c Master Interface library to work with the XMEGA. Modifications are necessary due to the fact that unlike other AVRs, XMEGA I/O port registers have addresses outside the I/O space and thus must be mapped to "virtual ports" (which have registers in the I/O space) for this library to function.
1. Download the I2C Master Interface library from Peter Fleury's website (here) or here and unzip.
2. The only file which needs to modified is i2cmaster.S:
Original (Lines 41-56):
;***** Adapt these SCA and SCL port and pin definition to your target !!
;
#define SDA4 // SDA Port D, Pin 4
#define SCL5 // SCL Port D, Pin 5
#define SDA_PORT PORTD // SDA Port D
#define SCL_PORT PORTD // SCL Port D
;******
;-- map the IO register back into the IO address space
#define SDA_DDR (_SFR_IO_ADDR(SDA_PORT) - 1)
#define SCL_DDR (_SFR_IO_ADDR(SCL_PORT) - 1)
#define SDA_OUT _SFR_IO_ADDR(SDA_PORT)
#define SCL_OUT _SFR_IO_ADDR(SCL_PORT)
#define SDA_IN (_SFR_IO_ADDR(SDA_PORT) - 2)
#define SCL_IN (_SFR_IO_ADDR(SCL_PORT) - 2)
With Modifications (Lines 41-56):
;***** Adapt these SCA and SCL port and pin definition to your target !!
;
#define SDA 0
#define SCL 1
#define SDA_PORT VPORT0_OUT
#define SCL_PORT VPORT0_OUT
;******
;-- map the IO register back into the IO address space
#define SDA_DDR (_SFR_IO_ADDR(SDA_PORT) - 1)
#define SCL_DDR (_SFR_IO_ADDR(SCL_PORT) - 1)
#define SDA_OUT _SFR_IO_ADDR(SDA_PORT)
#define SCL_OUT _SFR_IO_ADDR(SCL_PORT)
#define SDA_IN (_SFR_IO_ADDR(SDA_PORT) + 1)
#define SCL_IN (_SFR_IO_ADDR(SCL_PORT) + 1)
3. The next step is to map the appropriate port (the one containing the pins which you will use as the I2C interface) to the virtual port as defined in the modified code above (Virtual Port 0 has been chosen).
For example, the following will make PORTA PIN0 SDA and PORTA PIN1 SCL:
PORTCFG.VPCTRLA = PORTCFG_VP0MAP_PORTA_gc;
(This line of configuration code must be placed in your C source file and should be executed before initializing and using the I2C interface). Of course, the port and pin mappings can be changed to suit your particular needs.
Note: It is possible to have SDA and SCL on different ports by using two virtual ports. Also note that register VPCTRLA configures Virtual Port 0 and 1 whereas VPCTRLB configures Virtual Port 2 and 3.
4. Next we must modify the i2c_delay_T2 assembly function (also in i2cmaster.S) to give the appropriate delay:
Original (Lines 66-83):
;*************************************************************************
; delay half period
; For I2C in normal mode (100kHz), use T/2 > 5us
; For I2C in fast mode (400kHz), use T/2 > 1.3us
;*************************************************************************
.stabs "",100,0,0,i2c_delay_T2
.stabs "i2cmaster.S",100,0,0,i2c_delay_T2
.func i2c_delay_T2 ; delay 5.0 microsec with 4 Mhz crystal
i2c_delay_T2: ; 4 cycles
rjmp 1f ; 2 "
1: rjmp 2f ; 2 "
2: rjmp 3f ; 2 "
3: rjmp 4f ; 2 "
4: rjmp 5f ; 2 "
5: rjmp 6f ; 2 "
6: nop ; 1 "
ret ; 3 "
.endfunc ; total 20 cyles = 5.0 microsec with 4 Mhz crystal
With Modifications (Lines 66-85):
The following modification assumes a 2MHz clock and that the I2C interface is operating in Normal Mode. Use R17 (thanks Joerg!).Note: Use the AVR Delay Loop Generator program to generate appropriate ASM delay functions for other combinations of clock frequency and I2C modes.
;*************************************************************************
; delay half period
; For I2C in normal mode (100kHz), use T/2 > 5us
; For I2C in fast mode (400kHz), use T/2 > 1.3us
;*************************************************************************
.stabs "",100,0,0,i2c_delay_T2
.stabs "i2cmaster.S",100,0,0,i2c_delay_T2
.func i2c_delay_T2 ; delay 5.0 microsec with 2 Mhz crystal
i2c_delay_T2: ; 4 cycles
; =============================
; delay loop generator
; 3 cycles:
; -----------------------------
; delaying 3 cycles:
ldi R17, 01
WGLOOP0: dec R17
brne WGLOOP0
; =============================
ret ; 3 cycles
.endfunc ; total 10 cyles = 5.0 microsec with 2 Mhz clock
5. Finally, follow the documentation provided by Peter Fleury (here or in the archive downloaded in Step 1) to correctly initialize and use the I2C interface.