Atom Pro Hardware I2C support - both basic and C

Yah I think I have a ping (SRF08) working using the hardware I2C support on the Atom Pro 28 :smiley: . I have versions of it written in C as well as in Basic (with lots of assembly code). It took me longer then I thought it would, but I ran into a few road blocks, like my first C version written for the wrong H8 chip. The Pro 28 uses the 3694 not the 3664 and yes minor details like this do matter :blush: ! Also ran into problems in my conversion of it to basic and assembly as all of the register definitions built into the basic compiler appear to be for the 3664. (I had to disassembly the generated code to see that it was pointing to the wrong places). Also I found that sometimes just setting or clearing a bit in the I2C hardware registers worked and other times not, so my code currently reads the byte in, sets the bit and writes it back out…

I am not sure if anyone else will be interested in using the hardware support, but will post it just in case…

First the current basic test program with the I2C Support…

[code]’==================================================================================================
'Hardware I2C Test program
’ Atom Pro I/O Pin definitions.

SDA con P6
SCL con P7

TRUE con 1
FALSE con 0
;Variable declaration

lTimerWOverflowCnt var long ; used in WTimer overflow… Will keep a 32 bit overflow so we have a 48 bit timer
g_wI2CCountDown var word ; used in our I2c routines as a timeout to make sure we don’t hang
wRange var word ; used for ping

;=============================- Interrupt definitions =======================================
;Interrupt init

ONASMINTERRUPT TIMERWINT, Handle_TIMERW

;===================================== I2C =================================================
; Define helper stuff for hardware I2C support
gabI2C_Buffer var byte(10) ; don’t have anywhere now that requires more than maybe 2 or 3 bytes…

;===================================== SRF08 =================================================
;
; SRF helper functions
SRF08a Con 0xE0 ’ 1st Sonar I2C Address
SRFCmdReg Con 0 ’ Sonar Command register
SRFLightReg Con 1 ’ Sonar Light sensor register
SRFRangeReg Con 2 ’ Sonar 1st Range register

;============================== Start of CODE ================================================
; Initialization code
sound 9,[50\3960]

; Initiialize Timer
WTIMERTICSPERMS con 2 ; we have 16 clocks per ms and we are incrementing every 8 so divide again by 2
TCRW = 0x30 ;clears TCNT and sets the timer to inc clock cycle / 8
TMRW = 0x80 ;starts the timer counting
g_wI2CCountDown = 0;
lTimerWOverflowCnt = 0;

enable TIMERWINT_OVF

;================================ Main Loop =================================================
main_loop
; gosub SRFPing[SRF08a], wRange;
gosub Hardware_SRFPing[SRF08a], wRange

serout s_out, i2400, "Ping:", dec wRange, 13, 10]			

pause 500

goto main_loop

;-----------------------------------------------------------------------------------
; Handle TimerW interrupt
BEGINASMSUB
HANDLE_TIMERW
push.l er1 ; save away register we will use

bclr #7,@TSRW:8					; clear the overflow bit in the Timer status word
mov.l @LTIMERWOVERFLOWCNT:16,er1	; We will increment the word that is the highword for a clock timer
inc.l #1,er1
mov.l er1, @LTIMERWOVERFLOWCNT:16
; add some support for a I2C Countdown timer
mov.w	@G_WI2CCOUNTDOWN:16, r1		; get the current value
beq		_HTW_NODEC					; if zero we do nothing
dec.w	#1,r1						; Otherwise decrement...
mov.w	r1,@G_WI2CCOUNTDOWN:16

_HTW_NODEC:
pop.l er1 ; restore our registers
rte ; and return

ENDASMSUB

;============================================= Bit Bang SRF08 Helper functions ==================================================

srfLightVal Var Byte ’ Light sensor
srfAdr var Byte
srfRange Var Word ’ 16 bit variable for Range
SRFPing[srfAdr]
I2COut SDA, SCL, SRFPFail, srfAdr, SRFCmdReg, [80] ; do a ping in inches…
srfRange = 0
pause 67

; lets try to wait until the Ping completes
i2cin SDA, SCL, srfAdr, SRFRangeReg, [srfRange.HighByte, srfRange.LowByte]
    
; lets try to wait until the Ping completes
;i2cin SDA, SCL, srfAdr, SRFRangeReg, [srfRange.HighByte, srfRange.LowByte]
return srfRange

SRFPFail
return 0xffff

SRFLight[srfAdr]
i2cin SDA, SCL, srfAdr, SRFLightReg, [SRFLightVal]
return srfLightVal

;============================================================================================
; I2C - Functions
;============================================================================================
; First an ASM helper function used by both The read and write functions to start off the communications.
; Assume that caller function has saved away all registers and
; the variable BADDR has the device address:

BEGINASMSUB

I2C_ICCR1 con 0xF748
I2C_ICCR2 con 0xF749
I2C_ICMR con 0xF74A
I2C_ICIER con 0xF74B
I2C_ICSR con 0xF74C
SAR con 0xF74D
I2C_ICDRT con 0xF74E
I2C_ICDRR con 0xF74F

;I2C_ICCR1
I2C_ICE con 7
I2C_RCVD con 6
I2C_MST con 5
I2C_TRS con 4
;CKS3 CKS2 CKS1 CKS0
;I2C_ICCR2
I2C_BBSY con 7
I2C_BBSY_MASK con 0x80
I2C_SCP con 6
I2C_SDAO con 5
I2C_SDAOP con 4
I2C_SCLO con 3
I2C_IICRST con 1

;I2C_ICMR
I2C_MLS con 7
I2C_WAIT con 6
I2C_WAIT_MASK con 0x40
I2C_BCWP con 4
; BC2 BC1 BC0

;I2C_ICIER
I2C_TIE con 7
I2C_TEIE con 6
I2C_RIE con 5
I2C_NAKIE con 4
I2C_STIE con 3
I2C_ACKE con 2
I2C_ACKBR con 1
I2C_ACKBR_MASK con 0x02
I2C_ACKBT con 0

;I2C_ICSR
I2C_TDRE con 7
I2C_TDRE_MASK con 0x80
I2C_TEND con 6
I2C_TEND_MASK con 0x40
I2C_RDRF con 5
I2C_RDRF_MASK con 0x20
I2C_NACKF con 4
I2C_I2CSTOP con 3
I2C_I2CSTOP_MASK con 0x08
I2C_AL_OVE CON 2
I2C_AAS con 1
I2C_ADZ con 0

I2C_START_ADDR:
mov.w #0,e0 ; Use e0 for the return value, init to zero

mov.b		@I2C_ICCR1:16, r2l
bset.b		#I2C_ICE,r2l			;IIC2.I2C_ICCR1.BIT.ICE = 1;		// First set some address make sure ICE is not set.
mov.b		r2l, @I2C_ICCR1:16

; Ok CLR I2C_ICMR, SET WAIT, BC= 0
mov.b		@I2C_ICMR, r2l
bclr.b		#I2C_MLS,  r2l		;IIC2.I2C_ICMR.BIT.MLS = 0;		// Should be zero for I2c Format...
mov.b		r2l, @I2C_ICMR:16

mov.b		@I2C_ICMR, r2l
bset.b		#I2C_WAIT, r2l		;IIC2.I2C_ICMR.BIT.WAIT = 1;
mov.b		r2l, @I2C_ICMR:16

;IIC2.I2C_ICMR.BIT.BC = 0;		// I2c format 9 bits...
mov.b		@I2C_ICMR, r2l
and.b		#0xf8,r2l				; zero out BC
mov.b		r2l, @I2C_ICMR:16

mov.b		@I2C_ICIER:16,r2l			;IIC2.I2C_ICIER.BIT.ACKE = 1; 
bset.b		#I2C_ACKE,r2l 
mov.b		r2l,@I2C_ICIER:16 

mov.b		@I2C_ICCR1:16, r2l
or.b		#0x0f,r2l		;IIC2.I2C_ICCR1.BIT.CKS = 0xf;
mov.b		r2l, @I2C_ICCR1:16

mov.w	#100,r1
mov.w	r1,@G_WI2CCOUNTDOWN:16		;	g_wI2CCountDown = 100

; while (IIC2.I2C_ICCR2.BIT.BBSY != 0)
; {
; if (g_wI2CCountDown == 0)
; {
; iReturn = I2C_ERROR_TIMEOUT_BBSY;
; goto Exit;
; }
; }
_I2C_SA_BBSY:
mov.b @I2C_ICCR2:16,r2l
and.b #I2C_BBSY_MASK,r2l
beq _I2C_SA_20

mov.w	@G_WI2CCOUNTDOWN,r2
bne		_I2C_SA_BBSY:8
mov.w	#0xffff,e0
bra		_I2C_SA_EXIT:16

_I2C_SA_20:

; Now lets Set our self into the Master Write mode
mov.b	@I2C_ICCR1:16, r2l
bset.b	#I2C_MST, r2l
bset.b	#I2C_TRS, r2l
mov.b	r2l, @I2C_ICCR1:6

; Issue a start condition
; IIC2.I2C_ICCR2.BIT.BBSY = 1;
; IIC2.I2C_ICCR2.BIT.SCP = 0;
; Must be set at same time or start condition may not happen correctly...
mov.b	@I2C_ICCR2:16,r2l
bset.b	#I2C_BBSY,r2l
bclr.b	#I2C_SCP,r2l
mov.b	r2l,@I2C_ICCR2:16		;	IIC2.I2C_ICCR2.BYTE = (IIC2.I2C_ICCR2.BYTE | 0x80) & 0xbf;  // set BBSY and clear SCP.


;wait until IRIC bit goes high
;g_wI2CCountDown = 100;
mov.w	#100, r1
mov.w	r1,@G_WI2CCOUNTDOWN:16		;	g_wI2CCountDown = 100;

;while (IIC2.I2C_ICSR.BIT.TDRE == 0)
;{
;	if (g_wI2CCountDown == 0)
;	{
;		iReturn = I2C_ERROR_TIMEOUT_IRIC1;
;		goto Exit;
;	}
;}

_I2C_SA_TDRE:
mov.b @I2C_ICSR:16,r2l
and #I2C_TDRE_MASK,r2l
bne _I2C_SA_22

mov.w	@G_WI2CCOUNTDOWN,r2
bne		_I2C_SA_TDRE:8
mov.w	#0xfffe,e0
bra	_I2C_SA_EXIT:16

_I2C_SA_22:

; Now output the address into the data register.
mov.b	@BADDR:16, r0l				; get from variable... - caller sets this up
mov.b	r0l, @I2C_ICDRT:16		;IIC2.I2C_ICDRT = bAddr;

; Wait until the byte transfer is complete - With timeouts
mov.w	#100, r1
mov.w	r1,@G_WI2CCOUNTDOWN:16		;	g_wI2CCountDown = 100;

;while (IIC2.I2C_ICSR.BIT.TEND == 0)
;{
;	if (g_wI2CCountDown == 0)
;	{
;		iReturn = I2C_ERROR_TIMEOUT_IRIC2;
;		goto Exit;
;	}
;}

_I2C_SA_TEND:
mov.b @I2C_ICSR:16,r2l
and #I2C_TEND_MASK,r2l
bne _I2C_SA_24
mov.w @G_WI2CCOUNTDOWN,r2
bne _I2C_SA_TEND:8
mov.w #0xfffd,e0
bra _I2C_SA_EXIT:8
_I2C_SA_24:

; See if we got an ack
;if (IIC2.I2C_ICIER.BIT.ACKBR)
;	iReturn = I2C_ERROR_NOACK;
mov.b	@I2C_ICIER:16,r2l
and.b	#I2C_ACKBR_MASK,r2l
beq		_I2C_SA_EXIT:8
mov.w	#0xfffc,e0

; and return

_I2C_SA_EXIT:
rts ;return iReturn;
ENDASMSUB

;=============================================================================================
; Master Write
;=============================================================================================
;gabI2C_Buffer
bAddr var byte
cbout var byte
iReturn var sword
I2C_MasterWriteBuffer[bAddr, cbout]

; now transisition to assembly language
mov.b	@BADDR:16, r0l				; get from variable... 
jsr		I2C_START_ADDR:16	; call our startup function which sets up I2c

mov.w	e0, e0			; 
bne		_I2C_MWB_CLEANUP:16

mov.b	@CBOUT:16, r4l		; Ok load CB into r4l
mov.l   #GABI2C_BUFFER, er3	; setup R3 to have the pointer to our output buffer

;	while (cb > 1)
;	{

_I2C_MWB_LOOP:
cmp.b #1,r4l ; check cb
ble _I2C_MWB_CBLE1
dec.b r4l ;cb–
; Now output the next byte from our buffer.
mov.b @er3+,r2l ; IIC2.I2C_ICDRT = *pbBuff++;
mov.b r2l, @I2C_ICDRT:16

; now wait for TDRE with a timeout
mov.w #100, r1
mov.w r1,@G_WI2CCOUNTDOWN:16 ; g_wI2CCountDown = 100;

_I2C_MWB_TDRE:
mov.b @I2C_ICSR:16,r2l
and.b #I2C_TDRE_MASK:8,r2l ;check TDRE
bne _I2C_MWB_LOOP:8 ; check to see if we have any more to output

mov.w	@G_WI2CCOUNTDOWN,r2
bne		_I2C_MWB_TDRE:8
mov.w	#0xfffb,e0
bra	_I2C_MWB_EXIT:16

_I2C_MWB_CBLE1:
; fOR the last byte we wait for transfer end, not just transfer data register empty…
; make sure we were not passed in 0…
mov.b r4l,r4l
beq _I2C_MWB_CLEANUP:8
; output the last byte and check tend…
mov.b @er3,r2l ; IIC2.I2C_ICDRT = *pbBuff;
mov.b r2l, @I2C_ICDRT:16

; now wait for TEND with a timeout
mov.w	#100, r1
mov.w	r1,@G_WI2CCOUNTDOWN:16		;	g_wI2CCountDown = 100;

_I2C_MWB_TEND:
mov.b @I2C_ICSR:16,r2l
and #I2C_TEND_MASK,r2l ;check TEND(0x40)
bne _I2C_MWB_CLEANUP:8 ; check to see if we have any more to output

mov.w	@G_WI2CCOUNTDOWN,r2
bne		_I2C_MWB_TEND:8
mov.w	#0xfffa,e0
bra	_I2C_MWB_EXIT:16

_I2C_MWB_CLEANUP:
mov.b @I2C_ICSR:16,r2l
bclr.b #I2C_TEND,r2l ;IIC2.I2C_ICSR.BIT.TEND = 0;
bclr.b #I2C_I2CSTOP,r2l ;IIC2.I2C_ICSR.BIT.STOP = 0;
mov.b r2l, @I2C_ICSR:16
;IIC2.I2C_ICCR2.BYTE &= 0x3f; // cleart BBSY and clear SCP.
mov.b @I2C_ICCR2:16,r2l
and.b #0x3f,r2l
mov.b r2l,@I2C_ICCR2:16

; now wait until stop happens...		
mov.w	#100, r1
mov.w	r1,@G_WI2CCOUNTDOWN:16		;	g_wI2CCountDown = 100;

_I2C_MWB_STOP:
mov.b @I2C_ICSR:16,r2l
and #I2C_I2CSTOP_MASK,r2l ; check TEND
bne _I2C_MWB_CLEANUP2:8 ; Got to our next cleanup function

mov.w	@G_WI2CCOUNTDOWN,r2
bne		_I2C_MWB_STOP:8
mov.w	e0,e0						; see if we already have an error code
bne		_I2C_MWB_EXIT:16			; yes don't overwrite
mov.w	#0xfff9,e0
bra	_I2C_MWB_EXIT:16

_I2C_MWB_CLEANUP2:
mov.b @I2C_ICCR1:16, r2l
bset.b #I2C_MST,r2l ;IIC2.I2C_ICCR1.BIT.MST = 1;
bclr.b #I2C_TRS,r2l ;IIC2.I2C_ICCR1.BIT.TRS = 0; // clear the transmit status
mov.b r2l, @I2C_ICCR1:16

mov.b	@I2C_ICSR:16,r2l
bclr.b	#I2C_TDRE, r2l				;IC2.I2C_ICSR.BIT.TDRE = 0;
mov.b	r2l, @I2C_ICSR:16

_I2C_MWB_EXIT:
mov.w e0,@IRETURN:16

; transisition back to basic...		
return iReturn;

;=============================================================================================
; Master Read
;=============================================================================================
cbin var byte
I2C_MasterReadBuffer[bAddr, cbin]
; now transisition to assembly language
mov.b @BADDR:16, r0l ; pass the hardware address
jsr I2C_START_ADDR ; call our startup function which sets up I2c
mov.w e0, e0 ;
bne _I2C_MRB_CLEANUP:16

; Select Receive mode
mov.b	@I2C_ICSR:16,r2l
bclr.b	#I2C_TEND,r2l				;	IIC2.I2C_ICSR.BIT.TEND = 0;
mov.b	r2l, @I2C_ICSR:16

mov.b	@I2C_ICCR1:16, r2l	
bclr.b	#I2C_TRS,r2l				;	IIC2.I2C_ICCR1.BIT.TRS = 0;
mov.b	r2l, @I2C_ICCR1:16

mov.b	@I2C_ICSR:16,r2l
bclr.b	#I2C_TDRE, r2l				;	IC2.I2C_ICSR.BIT.TDRE = 0;
mov.b	r2l, @I2C_ICSR:16

mov.b	@I2C_ICIER:16, r2l
bclr.b	#I2C_ACKBT,	r2l				;	IIC2.I2C_ICIER.BIT.ACKBT = 0;
mov.b	r2l, @I2C_ICIER:16 
	
; Start receiving.  First read is a dummy.
mov.b	@I2C_ICDRR:16,r0l	;	bRecv = IIC2.I2C_ICDRR;
	
mov.l   #GABI2C_BUFFER, er3	; setup R3 to have the pointer to our output buffer

mov.b	@CBIN:16, r4l		; Ok load CB into r4l

_I2C_MRB_LOOP:
mov r4l,r4l
beq _I2C_MRB_ENDLOOP:8 ; no more bytes
dec.b r4l ;cb–
bne _I2C_MRB_NOTLASTBYTE:8
mov.b @I2C_ICCR1:16, r2l
bset.b #I2C_RCVD,r2l ; IIC2.I2C_ICCR1.BIT.TRS = 0;
mov.b r2l, @I2C_ICCR1:16

; Wait until a byte is available - With timeouts

_I2C_MRB_NOTLASTBYTE
mov.w #500, r1
mov.w r1,@G_WI2CCOUNTDOWN:16 ; g_wI2CCountDown = 100;
_I2C_MRB_RDRF1:
mov.b @I2C_ICSR:16,r2l
and #I2C_RDRF_MASK,r2l
bne _I2C_MRB_HAVE_RDRF1:8
mov.w @G_WI2CCOUNTDOWN,r2
bne _I2C_MRB_RDRF1:8
mov.w #0xfffb,e0
bra _I2C_MRB_EXIT:16

_I2C_MRB_HAVE_RDRF1:
mov.b @I2C_ICDRR:16,r0l
mov.b r0l,@er3
inc.l #1,er3

bra		_I2C_MRB_LOOP:8		; go see if we have more bytes to get

_I2C_MRB_ENDLOOP:
; Now acknowledge data for the last reception
mov.b @I2C_ICIER:16, r2l
bset.b #I2C_ACKBT, r2l ; IIC2.I2C_ICIER.BIT.ACKBT = 1;
mov.b r2l, @I2C_ICIER:16

; Clear IRIC to end the wait insertion
mov.b	@I2C_ICDRR:16,r0l		;	bRecv = IIC2.I2C_ICDRR;

; Wait until a byte is available - With timeouts
mov.w	#500, r1
mov.w	r1,@G_WI2CCOUNTDOWN:16		;	g_wI2CCountDown = 100;

_I2C_MRB_RDRF2:
mov.b @I2C_ICSR:16,r2l
and #I2C_RDRF_MASK,r2l
bne _I2C_MRB_HAVE_RDRF2:8
mov.w @G_WI2CCOUNTDOWN,r2
bne _I2C_MRB_RDRF2:8
mov.w #0xfffa,e0
bra _I2C_MRB_EXIT:16

_I2C_MRB_HAVE_RDRF2:

_I2C_MRB_CLEANUP:
mov.b @I2C_ICSR:16,r2l
bclr.b #I2C_TEND, r2l
bclr.b #I2C_I2CSTOP, r2l ; IIC2.I2C_ICSR.BIT.STOP = 0;
mov.b r2l, @I2C_ICSR:16

mov.b	@I2C_ICCR2:16, r0l
and.b	#0x3f,r0l
mov.b	r0l, @I2C_ICCR2:16		;	IIC2.I2C_ICCR2.BYTE &= 0x3f;  // cleart BBSY and clear SCP.	// outputs a stop 

; now wait until stop happens...		
mov.w	#100, r1
mov.w	r1,@G_WI2CCOUNTDOWN:16		;	g_wI2CCountDown = 100;

_I2C_MRB_STOP:
mov.b @I2C_ICSR:16,r2l
and #I2C_I2CSTOP_MASK,r2l ; check STOP
bne _I2C_MRB_CLEANUP2:8 ; Got to our next cleanup function

mov.w	@G_WI2CCOUNTDOWN,r2
bne		_I2C_MRB_STOP:8
mov.w	e0,e0						; see if we already have an error code
bne		_I2C_MRB_EXIT:16			; yes don't overwrite
mov.w	#0xfff9,e0
bra	_I2C_MRB_EXIT:16

_I2C_MRB_CLEANUP2:
mov.b @I2C_ICDRR:16,r0l ; bRecv = IIC2.I2C_ICDRR;

mov.b	@I2C_ICCR1:16, r2l	
bclr.b	#I2C_RCVD,r2l			;IIC2.I2C_ICCR1.BIT.RCVD = 0;
bclr.b	#I2C_MST,r2l			;IIC2.I2C_ICCR1.BIT.MST = 0;
mov.b	r2l, @I2C_ICCR1:16

_I2C_MRB_EXIT:
mov.w e0,@IRETURN:16 ; setup or return value.

; transisition back to basic...		
return iReturn;

;=============================================================================================
; Write Register
;=============================================================================================
bReg var byte
bVal var byte
I2C_WriteRegister[bAddr, bReg, bVal]

; serout s_out, i2400, ā€œI2CWriteRegister:ā€, hex bAddr," ",hex bReg, " ", hex bVal, 13, 10]

gabI2C_Buffer(0) = bReg;
gabI2C_Buffer(1) = bVal;

gosub I2C_MasterWriteBuffer[bAddr, 2], iReturn;
return iReturn

;=============================================================================================
; Read Register
;=============================================================================================

I2C_ReadRegister[bAddr, bReg]
gabI2C_Buffer(0) = bReg;

gosub I2C_MasterWriteBuffer[bAddr, 1], iReturn

if iReturn = 0 then
	gosub I2C_MasterReadBuffer[bAddr, 1], iReturn
endif

if iReturn = 0 then
	return gabI2C_Buffer(0)
else
	return 0xffff
endif

;=============================================================================================
; read Multiple Registers
;=============================================================================================
cb var byte
I2C_ReadMultiplRegisters[bAddr, bReg, cb]
gabI2C_Buffer(0) = bReg;

gosub I2C_MasterWriteBuffer[bAddr, 1], iReturn

if iReturn = 0 then
	gosub I2C_MasterReadBuffer[bAddr|1, cb], iReturn
endif

return iReturn;

;==============================================================================================
; Test Hardware SRF ping
;==============================================================================================
iError var sword
iPing var sword
foo var byte
Hardware_SRFPing[srfAdr]
iPing = 0 ; default to zero
gosub I2C_WriteRegister[srfAdr, 0, 80], iError
mov.b @I2C_ICCR1:16, r0l
mov.b r0l,@FOO:16
; serout s_out, i2400, ā€œI2CWrite called:ā€, hex iError," ",hex foo, 13, 10]
if iError = 0 then
pause 100
gosub I2C_ReadMultiplRegisters[srfAdr, 2, 2], iError
; If our read succedded then build the value
if iError = 0 then
iPing.highbyte = gabI2C_Buffer(0)
iPing.lowbyte = gabI2C_Buffer(1)
endif
; serout s_out, i2400, ā€œI2CRead called:ā€, hex iError, " ", dec iPing, 13, 10]
endif

	return iPing

[/code]

Note, it would be great if I could simply pass in a list of variables into the I2C Read and Write functions like you can with the bit bang versions, but I don’t know how, so it uses a fixed buffer. Maybe some day we will be able to add extensions to the language like is done for Hardware Serial (hint to AcidTech)… But this works for now.

Kurt

Now for the C version. Note: the source code here relies on a header file that defines structures for the IO registers. The file was downloaded from the Renesis web site.

This is just simple test code. As I did not have any bit bang serial output functions for the H8 on hand, I simply used the LEDS on the Bot Board 2 to give me status. For a long time some of the code lit up different LEDS to give me an idea where I was hanging up. Later once the pings were working I used it to give me distances. Blink one light to show 10s and another light to show 1’s. The code is not great, but it does show you things like, how to setup and use TimerW, IO ports and some I2C support in Master mode.

First the test file: (helloworld.cpp)

[code]#include <signal.h>
//#include ā€œregdefs.hā€

extern ā€œCā€ {
#include ā€œ3694s.hā€
};
#include ā€œi2c.hā€

volatile unsigned long g_lWTimerOverflowCount;

/*

  • ms_sleep() - delay for specified number of milliseconds
    */
    void ms_sleep(unsigned int ms)
    {
    // Ok lets play with the actual timer. First lets try the long long integer…
    unsigned long ulCountEnd;
    unsigned long ulTicEnd;

    // calculate the end time based on there are 2 tics per uS so there would be 2*1024 tics per ms
    // We will set up two different values for a 48 bit clock which is in .5uS increments.
    ulTicEnd = ((unsigned long int)ms * 2048) + TW.TCNT; // convert mS to clock tics to wait

    ulCountEnd = g_lWTimerOverflowCount + (ulTicEnd >> 16);
    ulTicEnd &= 0xffff;

    for (;:wink:
    {
    if ((ulCountEnd < g_lWTimerOverflowCount) ||
    ((ulCountEnd == g_lWTimerOverflowCount) && ((unsigned int)ulTicEnd <= TW.TCNT)))
    break;
    }
    }

/*

  • millisecond counter interrupt vector
    */
    extern ā€œCā€ void TIMERW();

#pragma interrupt(TIMERW)

void TIMERW() // for TimerW 16mhz/8 = 2cl/uS. So 210241024/65536) -> called 32 times per second.
{
g_lWTimerOverflowCount++;

if (g_wI2CCountDown)
{
	g_wI2CCountDown--;
	IO.PDR8.BIT.B6 = 0x1;		// start off in some known state
}
else
	IO.PDR8.BIT.B6 = 0x0;		// start off in some known state

	
TW.TSRW.BIT.OVF = 0;

}

//
// initialize timer 0 to generate an interrupt every millisecond.
//
void init_timer(void)
{

g_lWTimerOverflowCount = 0;				// initialize the count	
g_wI2CCountDown = 0;
TW.TCRW.BIT.CKS = 0x3; 		// set clock to use system clock/8 as increment.  so about 1/2 Us per increment overflow every or 128uS
TW.TSRW.BIT.OVF = 0;		// make sure the overflow is cleared
TW.TMRW.BIT.CTS = 1;		// starts the timer counting 
TW.TIERW.BIT.OVIE = 1;		// enable interrupt on overflow

#if TIMERA
TA.TMA.BYTE=4; //increments on clock/256
IRR1.BIT.IRRTA = 0; // Make sure the interrupt happened flag is clear
IENR1.BIT.IENTA = 1; // ENABLE TIMERAINT
#endif
}

int main(void)
{
int iError;
init_timer();
unsigned short iVal;
unsigned char abRegs[2];
TW.TIOR1.BIT.IOD = 0; // Make sure P84 is an IO pin…
IO.PCR8 = 0x70; // make sure it is write
IO.PDR8.BIT.B6 = 0x1; // start off in some known state
IO.PDR8.BIT.B5 = 0x1;
IO.PDR8.BIT.B4 = 0x1;

IO.PMR5.BYTE |= 0xc0;
IO.PCR5 = 0;	

while (1) {
    ms_sleep(512);    /* wait 0.5 seconds */
	IO.PDR8.BIT.B4 = 0x0;    /* toggle LED */
	ms_sleep(512);
	IO.PDR8.BIT.B4 = 0x1;    /* toggle LED */
	// Now lets try to do a ping with our I2c code
	iError = I2C_WriteRegister(0xe0, 0, 80);
	switch (iError)
	{
		case I2C_ERROR_TIMEOUT_BBSY:
	   		IO.PDR8.BIT.B5 = 0x0;    /* toggle LED */
	   		IO.PDR8.BIT.B6 = 0x1;    /* toggle LED */
			break;
		case I2C_ERROR_TIMEOUT_IRIC1:
	   		IO.PDR8.BIT.B5 = 0x1;    /* toggle LED */
	   		IO.PDR8.BIT.B6 = 0x0;    /* toggle LED */
			break;
		case I2C_ERROR_TIMEOUT_IRIC2:
	   		IO.PDR8.BIT.B5 = 0x0;    /* toggle LED */
	   		IO.PDR8.BIT.B6 = 0x0;    /* toggle LED */
			break;
	}
	if (iError == 0)
	{
		ms_sleep(100);
		//I2C_ReadRegister(0xe0, 2, &bVal);
		iError = I2C_ReadMultiplRegisters(0xe0, 2, 2, abRegs);
		if (iError == 0)
		{
			iVal = (((unsigned short)abRegs[0])<< 8) + abRegs[1];
			while (iVal >= 10)
			{
				IO.PDR8.BIT.B6 = 0x0;    /* toggle LED */
				ms_sleep(150);
				IO.PDR8.BIT.B6 = 0x1;    /* toggle LED */
				ms_sleep(150);
				iVal -= 10;
			}
			
			while (iVal-- > 0)
			{
				IO.PDR8.BIT.B5 = 0x0;    /* toggle LED */
				ms_sleep(150);
				IO.PDR8.BIT.B5 = 0x1;    /* toggle LED */
				ms_sleep(150);
			}
 			
		}
		
	}
}

}

[/code]
Then the simple i2c.h file:

[code]//-------------------------------------------------------------------------------------
// I2c.H
//-------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------
// Error Definition
//-------------------------------------------------------------------------------------
#define I2C_ERROR_OK 0x0000
#define I2C_ERROR_NOACK 0x0001

#define I2C_ERROR_TIMEOUT_BBSY 0x8001
#define I2C_ERROR_TIMEOUT_IRIC1 0x8002
#define I2C_ERROR_TIMEOUT_IRIC2 0x8003
#define I2C_ERROR_TIMEOUT_WRITE 0x8004
#define I2C_ERROR_TIMEOUT_READ 0x8005
//-------------------------------------------------------------------------------------
// external variables…
//-------------------------------------------------------------------------------------
#define SETI2CTIMEOUTVALUE(x) g_wI2CCountDown = x
#define I2CTIMEOUT (g_wI2CCountDown == 0)
extern volatile unsigned int g_wI2CCountDown;
//-------------------------------------------------------------------------------------
// function definitions
//-------------------------------------------------------------------------------------

extern int I2C_StartAddr(unsigned char bAddr);
extern int I2C_MasterWriteBuffer(unsigned char bAddr, unsigned char *pbBuff, int cb);
extern int I2C_MasterReadBuffer(unsigned char bAddr, unsigned char *pbBuff, int cb);

// Now define functions to read and write registers…
extern int I2C_WriteRegister(unsigned char bAddr, unsigned char bReg, unsigned char bVal);
extern int I2C_ReadRegister(unsigned char bAddr, unsigned char bReg, unsigned char *pbVal);
extern int I2C_ReadMultiplRegisters(unsigned char bAddr, unsigned char bReg, unsigned char cb, unsigned char *pb);
[/code]
And finally the I2C support code I2C.Cpp:

[code]/* ------------------------------------------------------------------------------------------ /
/
------------------------------------------------------------------------------------------ /
/
I2C helper function ---------------------------------------------------------------------- /
/
------------------------------------------------------------------------------------------ /
/
------------------------------------------------------------------------------------------ */
extern ā€œCā€ {
#include ā€œ3694s.hā€
};

#include ā€œi2c.hā€
extern volatile short int ms_count;
volatile unsigned int g_wI2CCountDown;
/*

  • millisecond counter interrupt vector
    */

//=============================================================================================
// Master Read
//=============================================================================================
int I2C_StartAddr(unsigned char bAddr)
{
int iReturn = 0;
IIC2.ICCR1.BIT.ICE = 1; // First set some address make sure ICE is not set.
IIC2.ICMR.BIT.MLS = 0; // Should be zero for I2c Format…
IIC2.ICMR.BIT.WAIT = 1;
IIC2.ICMR.BIT.BC = 0; // I2c format 9 bits…
IIC2.ICIER.BIT.ACKE = 1; // enable acks
IIC2.ICCR1.BIT.CKS = 0xf; // set to initial state.

g_wI2CCountDown = 100;
while (IIC2.ICCR2.BIT.BBSY != 0)
{
	if (g_wI2CCountDown == 0)
	{
		iReturn = I2C_ERROR_TIMEOUT_BBSY;
		goto Exit;
	}
}
// Now lets Set our self into the Master Write mode
IIC2.ICCR1.BIT.MST = 1;
IIC2.ICCR1.BIT.TRS = 1;

// Issue a start condition
//IIC2.ICCR1.BIT.BBSY = 1;
//IIC2.ICCR1.BIT.SCP = 0;
// Must be set at same time or start condition may not happen correctly...
IIC2.ICCR2.BYTE = (IIC2.ICCR2.BYTE | 0x80) & 0xbf;  // set BBSY and clear SCP.


// wait until IRIC bit goes high
g_wI2CCountDown = 100;
while (IIC2.ICSR.BIT.TDRE == 0)
{
	if (g_wI2CCountDown == 0)
	{
		iReturn = I2C_ERROR_TIMEOUT_IRIC1;
		goto Exit;
	}
}

// Now output the address into the data register.
IIC2.ICDRT = bAddr;

// Wait until the byte transfer is complete - Again add timeouts
g_wI2CCountDown = 100;
while (IIC2.ICSR.BIT.TEND == 0)
{
	if (g_wI2CCountDown == 0)
	{
		iReturn = I2C_ERROR_TIMEOUT_IRIC2;
		goto Exit;
	}
}
	
// See if we got an ack
if (IIC2.ICIER.BIT.ACKBR)
	iReturn = I2C_ERROR_NOACK;

Exit:
return iReturn;
}

//=============================================================================================
// Master Write
//=============================================================================================
int I2C_MasterWriteBuffer(unsigned char bAddr, unsigned char *pbBuff, int cb)
{
int iReturn;

//See if we can start up communications
iReturn = I2C_StartAddr(bAddr);

if (iReturn == I2C_ERROR_OK)
{
	while (cb > 1)
	{
		// Now output the next byte from our buffer.
		cb--;	// decrement
		IIC2.ICDRT = *pbBuff++;

		// Wait until the byte transfer is complete - Again add timeouts
		g_wI2CCountDown = 100;
		while (!IIC2.ICSR.BIT.TDRE)
		{
			if (g_wI2CCountDown == 0)
			{
				iReturn = I2C_ERROR_TIMEOUT_WRITE;
				goto Exit;
			}
		}				
	}
	
	// fOR the last byte we wait for transfer end, not just transfer data register empty...
	// make sure we were not passed in 0...
	if (cb)
	{
		IIC2.ICDRT = *pbBuff;
	
		// See if we got an ack
		while (!IIC2.ICSR.BIT.TEND)
		{
			if (g_wI2CCountDown == 0)
			{
				iReturn = I2C_ERROR_TIMEOUT_WRITE;
				goto Exit;
			}
		}				
	}
}			

IIC2.ICSR.BIT.TEND = 0;
IIC2.ICSR.BIT.STOP = 0;
IIC2.ICCR2.BYTE &= 0x3f;  // cleart BBSY and clear SCP.

// wait until stop happens...		
while (!IIC2.ICSR.BIT.STOP)
{
	if (g_wI2CCountDown == 0)
	{
		iReturn = I2C_ERROR_TIMEOUT_WRITE;
		goto Exit;
	}
}				

IIC2.ICCR1.BIT.MST = 1;
IIC2.ICCR1.BIT.TRS = 0;	// clear the transmit status
IIC2.ICSR.BIT.TDRE = 0;

Exit:
return iReturn;
}

//=============================================================================================
// Master Read
//=============================================================================================
int I2C_MasterReadBuffer(unsigned char bAddr, unsigned char *pbBuff, int cb)
{
int iReturn;
unsigned char bRecv;

//See if we can start up communications
iReturn = I2C_StartAddr(bAddr|0x01);		// tell them we are trying to do a read

if (iReturn == I2C_ERROR_OK)
{
	// Select Receive mode
	IIC2.ICSR.BIT.TEND = 0;
	IIC2.ICCR1.BIT.TRS = 0;
	IIC2.ICSR.BIT.TDRE = 0;
	IIC2.ICIER.BIT.ACKBT = 0;
	
	
	// Start receiving.  First read is a dummy.
	bRecv = IIC2.ICDRR;
	
		
	while (cb-- > 0)
	{
		// if this is our last byte let the system know before reading it...
		if (cb == 0)
			IIC2.ICCR1.BIT.RCVD = 1;
		
		// wait for the next byte to come available.
		while (!IIC2.ICSR.BIT.RDRF)
			;		//  BUGBUG - again should have timeouts...

		// Now put the next byte into our buffer.
		*pbBuff++ = IIC2.ICDRR;

	}
	
	// Now acknowledge data for the last reception
	IIC2.ICIER.BIT.ACKBT = 1;
	
	// Clear IRIC to end the wait insertion
	bRecv = IIC2.ICDRR;
	while (!IIC2.ICSR.BIT.RDRF)
		;		//  BUGBUG - again should have timeouts...
	
}

IIC2.ICSR.BIT.STOP = 0;
IIC2.ICCR2.BYTE &= 0x3f;  // cleart BBSY and clear SCP.	// outputs a stop 

// wait until stop happens...		
while (!IIC2.ICSR.BIT.STOP)
	;
 
bRecv = IIC2.ICDRR;

IIC2.ICCR1.BIT.RCVD = 0;
IIC2.ICCR1.BIT.MST = 0;
	
return iReturn;

}

//=============================================================================================
// Write Register
//=============================================================================================

int I2C_WriteRegister(unsigned char bAddr, unsigned char bReg, unsigned char bVal)
{
unsigned char abBuff[2];
abBuff[0] = bReg;
abBuff[1] = bVal;

return I2C_MasterWriteBuffer(bAddr, abBuff, 2);

}

//=============================================================================================
// Read Register
//=============================================================================================

int I2C_ReadRegister(unsigned char bAddr, unsigned char bReg, unsigned char *pbVal)
{

int iReturn;
	
iReturn = I2C_MasterWriteBuffer(bAddr, &bReg, 1);
if (iReturn == I2C_ERROR_OK)
	iReturn = I2C_MasterReadBuffer(bAddr, pbVal, 1);
return iReturn;

}

//=============================================================================================
// read Multiple Registers
//=============================================================================================
int I2C_ReadMultiplRegisters(unsigned char bAddr, unsigned char bReg, unsigned char cb, unsigned char *pb)
{
int iReturn;

iReturn = I2C_MasterWriteBuffer(bAddr, &bReg, 1);
if (iReturn == I2C_ERROR_OK)
	iReturn = I2C_MasterReadBuffer(bAddr, pb, cb);
return iReturn;

}
[/code]

Note, you also need to update the interrupt vectors file (vects.c) that is generated when you create a C project, to point to the timerW interrupt handler. You should update the appropriate line in the file to look something like:

	asm("jmp @_TIMERW"); //_TIMERW:

That is all for now :smiley:

Kurt

Good work on the I2C. I don’t think I’ll be working with the Renesas too soon or the Atom Pro, but I can certainly empathize with the task of getting I2C working.

I’ve got I2C implemented on Loki to read the ultrasonic sensor. It might still need work, as it can stop working occasionally. I don’t know why yet.

I noticed the WHILE loops in your code. I hate to see all that blocking! I implemented the I2C code in a state machine, and it gets called off the background task. No blocking!

Alan KM6VV

Thanks, as I said in a different thread, I previously implemented them on both Atmel AVR chips as well as on the Robonova. So I knew some of the things to look out for.

I have had this also in the past. Usually it is one of the devices pulling either SCL or SDA low and not releasing it. For example it might be waiting for an Ack from the master. The biggest culprit I had was an I2C LCD. When this happens, hitting the reset on the CPU usually does not clear it. You end up needing to power cycle. At times I have been tempted to power the I2C off of an IO line so I receive some form of timeout, I would simply turn the IO line off and then back on to reset the devices.

Yep, I have done this in the past as well. May extend this on to do the same, probably using the hardware interrupt, which I can have trigger for 5 different states (Transmit buffer empty, Transmit completed, Recieve buffer full, Stop, Arbitrition lost). This is more or less all of the conditions of the while loops. When I also integrate this into my Rover that I will have at least two SRF08s, I will also break up the ping codes, such that I can potentially issue the ping for both sensors concurently. Of course you need to be careful not to have them aiming in overlapping locations or they will probably get confussed!

Kurt

Yes, that’s it. I’m seeing the same thing. I haven’t implemented any timeouts, but it looks like I’ll have to. Probably not that hard to add another timer!

Two SRF08s! I should think about that. I could definitely use two on my 'bots. But can you inter-mingle their accesses? Easy enough to just toggle back and forth between devices, but run them both concurrently? That’s got to be complicated!

Alan KM6VV

I think it is doable, but only when their angles are far enough apart. For example if I have them both pointed at opposit sides of the robot, then there should not be any overlap. If you run them both pointing out at lets say 45 degree angles, then you might start getting overlap. If so you can experiment with changing the gain and see if that reduces it.

Kurt