Given some words for handling the i2c lines:
>SDA ( f -- ) write f to the SDA line SDA> ( -- f ) read the state of the SDA line SCL-0 ( -- ) drive SCL low SCL-1 ( -- ) drive SCL high half ( -- ) half-cycle delay
In the best Forth tradition, one screen is enough:
( i2c JCB 10:09 08/15/10)
: i2c-start \ with SCL high, change SDA from 1 to 0
1 >SDA half SCL-1 half 0 >SDA half SCL-0 ;
: i2c-stop \ with SCL high, change SDA from 0 to 1
0 >SDA half SCL-1 half 1 >SDA half ;
: i2c-rx-bit ( -- b )
1 >SDA half SCL-1 half SDA> SCL-0 ;
: i2c-tx-bit ( f -- )
0<> >SDA half SCL-1 half SCL-0 ;
: i2c-tx ( b -- nak )
8 0 do dup 128 and i2c-tx-bit 2* loop drop i2c-rx-bit ;
: i2c-rx ( nak -- b )
0 8 0 do 2* i2c-rx-bit + loop swap i2c-tx-bit ;
And sample use for some 8-bit device at address 40:
( i2c example usage for 8-bit device JCB 10:09 08/15/10)
: device ( addr -- ) \ common i2c preamble
i2c-start 40 i2c-tx throw i2c-tx throw ;
: device! ( v addr -- ) \ write v to i2c register addr
device i2c-tx throw i2c-stop ;
: device@ ( addr -- v ) \ read i2c register addr
device i2c-start 41 i2c-tx throw 1 i2c-rx i2c-stop ;
This code is used in the WGE100 Ethernet cameras. The source code is in i2c.fs.