blob: d6399874d25f4d4a897341817dc7d2754c3d200d [file] [log] [blame]
\ Bitbanging I2C driver for use with FTDI TTL-232RG serial dongles
\ modified according to the "Mod: INA219 Current Sensing" section of
\ https://sites.google.com/a/nod-labs.com/development/proto2-ftdi-debug-cable
\ CBUS0 is connected to SDA, CBUS1 to SCL
\
\ This assumes that serial-ih is already set to the result of open-com,
\ as with open-serial in wicedforth/serial-tools.fth, and also requires
\ ft-bit-change therefrom.
\ SCL low is $20, SCL high is $00, SDA low is $10, SDA high is data&$20 = 0
: scl-sda! ( mask -- ) $33 ft-bit-change ;
: scl-sda@ ( -- mask ) serial-ih ft-getbits ;
: sda@ ( -- 1|0 ) scl-sda@ 1 and ;
: scl@ ( -- 2|0 ) scl-sda@ 2 and ;
\ $10 puts SCL in output mode, $20 puts SDA in output mode
\ $01 is the SCL output state bit, $02 is the SDA output state bit
\ Since I2C is an open drain bus with pullups, we never have to
\ set the output state bits explicitly to 1; to let a line go
\ high we put it in input mode so the pullup will drive it.
: +scl+sda ( -- ) $00 scl-sda! ; \ Both SCL and SDA inputs
: +scl-sda ( -- ) $10 scl-sda! ; \ SCL input, SDA output low
: -scl+sda ( -- ) $20 scl-sda! ; \ SCL output low, SDA input
: -scl-sda ( -- ) $30 scl-sda! ; \ Both SCL and SDA output low
\ Send enough clocks with data high to clear any hung devices
: select-i2c +scl+sda d# 16 0 do -scl+sda +scl+sda loop ;
\ Each primitive below assumes that SCL is high on entry
\ The bit primitives first drive SCL low and SDA to the
\ desired state, then drive SCL high, leaving it high in
\ preparation for the next bit. Each primitive except
\ i2c-start performs a falling edge and a rising edge of SCL.
: i2c-start ( -- ) +scl-sda ; \ On entry: SDA and SCL are high (bus idle)
: i2c-repeated-start ( -- ) -scl+sda +scl+sda +scl-sda ;
: i2c-stop ( -- ) -scl-sda +scl-sda +scl+sda ;
: i2c-bit! ( flag -- ) if -scl+sda +scl+sda else -scl-sda +scl-sda then ;
: i2c-bit@ ( -- n ) -scl+sda +scl+sda sda@ ;
: i2c-byte! ( n -- )
8 0 do dup $80 and i2c-bit! 2* loop drop
i2c-bit@ if i2c-stop true abort" No ACK" then
;
: i2c-byte@ ( nack? -- n )
0 ( nack? n )
8 0 do 2* i2c-bit@ or loop ( nack? n )
swap i2c-bit! ( n )
;
0 value devadr \ 7-bit form
: set-i2c-slave ( n -- ) to devadr ;
: i2c-begin ( 1|0 -- ) i2c-start devadr 2* or i2c-byte! ;
: read-i2c ( adr len -- )
dup 0= if 2drop exit then ( adr len )
1 i2c-begin
\ Read len-1 bytes and ACK afterwards
1- 0 ?do false i2c-byte@ over c! 1+ loop ( adr' )
\ Read the last byte and NACK afterwards
true i2c-byte@ swap c!
i2c-stop
;
: write-i2c ( adr len -- )
0 i2c-begin
bounds ?do i c@ i2c-byte! loop
i2c-stop
;
: i2c-b@ ( -- b ) 1 i2c-begin true i2c-byte@ i2c-stop ;
: i2c-b! ( b -- ) 0 i2c-begin i2c-byte! i2c-stop ;
: i2c-be-w@ ( -- w )
1 i2c-begin false i2c-byte@ true i2c-byte@ i2c-stop ( b.hi b.lo )
swap bwjoin
;
: i2c-be-w! ( w -- ) wbsplit 0 i2c-begin i2c-byte! i2c-byte! i2c-stop ;