Yah I think I have a ping (SRF08) working using the hardware I2C support on the Atom Pro 28 . 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
! 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