( I2C_bitbang)
( 3rd June 2016)

CR ." I2C_bitbang"

( Uses module 'GPIO' from Tank)

( simulate I2C controller in software - both clock and data lines)
( Can use any pair where there is a pull-up resistor active -
  normally GPIO pin 2 as data and 3 as clock, but pins 5 and 6 also OK as shown here - B+)
( N.B.  clock pin set as output; data pin set to 0 - for zero output keep pin as output,
        & for 1, change to input state - pull up resistor raises level to 1 as input for reading,
        with pull-up so slave can assert value)
( - data line toggles between output for zero and input for 1 - can also then read input from slave,
    which may drag the line down i.e. when writing '0' or sending ACK)

( choose between Single shot or Continuous ADC conversions - default: continuous)
0 VARIABLE qSingShot  ( 0-continuous; 1-single shot)
HEX 42 VARIABLE modecode DECIMAL  ( code for Config Register for continuous)

( short delay using only Forth words:)
( 4500) 500 VARIABLE smt
: sm_delay  0 DO I I * DROP LOOP ;  ( n ...) 
: dt smt @ sm_delay ;

( 2)   5 CONSTANT SDA    ( 3)   6 CONSTANT SCLK
: GPIOWrite  ( level,pin no...)
  GPWrData DROP DROP ;

: SDA_high  0 SDA   GPWrMode dt ;   ( input - pullup R gives '1')
: SDA_low   1 SDA   GPWrMode dt ;   ( output - level '0')

: SCLK_high 0 SCLK   GPWrMode dt ;   ( input - pullup R gives '1')
: SCLK_low  1 SCLK   GPWrMode dt ;   ( output - level '0')

( set both lines to zero - data then clock)
1 SDA   GPWrMode     0 SDA   GPWrData   ( always zero when pin is output)
1 SCLK  GPWrMode     0 SCLK  GPWrData

: SDA_hi/lo ( 0/1...) 0= IF SDA_low ELSE SDA_high ENDIF ;

: start
  SDA_high     ( '1')       SCLK_high  ( '1')  
  SDA_low      ( '0')       SCLK_low ; ( '0')

: stop       
  SCLK_low       SDA_low  
  SCLK_high      SDA_high ;

HEX
: char_tx ( n...) ( convert 8-bit char to bit stream)
  SDA_low ( CR)
  8 0 DO DUP 80 I >> AND 0= 0= SDA_hi/lo    ( bit in char - 0/1)
             SCLK_high                      ( data ready for clock high for Tx bit)
             SCLK_low SDA_low  
      LOOP DROP
  ( look for ACK - data=0)
  SDA_high        ( value determined by slave)
  SCLK_high 
  ( read 0 or 1 - leave on stack)
  SDA ( gRdData) GPRdData
  SCLK_low SDA_low ;

: char_rx ( ...n)  ( convert bit stream to 8-bit char)
  SCLK_low SDA_high   0 ( CR)
  0 7 DO DUP
       SCLK_high
       ( wait until clock line goes high; low if clock-stretching from slave)
       BEGIN  SCLK GPRdData dt UNTIL
       ( carry on reading data)
       SDA  GPRdData    ( DUP .) ( read 0 or 1 - leave on stack)
       I << OR OR
       SCLK_low
    -1 +LOOP ;

: send_nak ( ...)
   SDA_low   1 SDA  GPIOWrite     ( '1' from master)
   SCLK_high
   SCLK_low
   SDA_low   0 SDA  GPIOWrite ;

: send_ack ( ...)   SDA_low SCLK_high  SCLK_low ; ( send '0' - i.e. SDA_low state)

( Data for ADS1115)
HEX
48 CONSTANT ADCaddr   ( bus address of ADC chip)
0 VARIABLE channel    ( channels 0 to 3)

( data structures)
( Define BYTE arrays:)
: byte_array 4/ 1+ 4* <BUILDS ALLOT DOES> + ;
4 byte_array ConfigData
4 byte_array ConvertData

HEX
( Fill BYTE arrays:)
: fill_struct_1
  ( 00000001)     01 0 ConfigData C!
  ( 0/11000010/1) modecode @ channel @ 4 << OR  1 ConfigData C! ( single/continuous conversion)
  ( 10000011)     83 2 ConfigData C!     ( 128 sps - default)
;

: fill_struct_3
  ( 10010000)  90 0 ConvertData C!
  ( 00000000)   0 1 ConvertData C!   ( now points to Conversion reg)
  ( 10010001)  91 2 ConvertData C! ;

: ?AckRx ( 0/1...) 1 = IF CR ." Acknowledge not received from slave"  KEY DROP QUIT ENDIF ;

DECIMAL
: request_ADC
  start
  ADCaddr 1 << char_tx ?AckRx
  3 0 DO  I ConfigData C@ char_tx ?AckRx LOOP
  stop ;                

( not needed for continuous conversion mode:)
: wait_bit15
  start
  BEGIN
   SDA_low ADCaddr 1 <<  1+  char_tx DROP
   SDA_high  char_rx send_ack 128 AND
   ?Esc OR 
  UNTIL
 stop ;

: read_ADC
 start
 2 0 DO  I ConvertData C@ char_tx ?AckRx LOOP   ( select conversion reg - high byte first)
 ( read 2 x 8-bit values from ADC)
 start ADCaddr 1 << 1+ char_tx ?AckRx char_rx send_ack char_rx send_nak
 SWAP 8 << +                        ( 16-bit value)
 16 << 16 >>>                       ( 32-bit nos - including -ve nos)
 stop ;

: rd_single        ( .....n)  ( read single-shot conversion)
   request_ADC
   wait_bit15 
   read_ADC ;

( n...)   ( display n readings from ADC)
: ADC_loop 0 DO
       qSingShot @ 0= IF  read_ADC  ELSE  rd_single  ENDIF
          . LOOP ;

DECIMAL
( fill structures for channel no.:)        
fill_struct_1 fill_struct_3 

request_ADC   ( read and create initial setup - can be changed using words below)

( To change parameters when active:)
HEX
: sel_channel  ( 0 to 3....)      ( setup for continuous reading)
  channel ! modecode @  channel @ 4 << OR 1 ConfigData C! request_ADC ;
DECIMAL 
: SPS  ( n...) 5 <<  3 +  2 ConfigData C! request_ADC ;
: 8sps   0 SPS ;  : 16sps  1 SPS ;  : 32sps  2 SPS ;  : 64sps  3 SPS ;
: 128sps 4 SPS ;  : 250sps 5 SPS ;  : 465sps 6 SPS ;  : 860sps 7 SPS ;

( single shot/ continuous choice:)
: singleshot 1 qSingShot ! [ HEX ] C3 modecode ! request_ADC ;
: continuous 0 qSingShot ! [ HEX ] 42 modecode ! request_ADC ;

DECIMAL

