\ ********************************************************************* 
\ I2C interface management for FlashForth                             * 
\    Filename:      i2c-new.txt                                       * 
\    Date:          04.11.2020                                        * 
\    Updated:       04.11.2020                                        * 
\    File Version:  1.0                                               * 
\    MCU:           ARDUINO all models                                * 
\    GNU General Public License                                       * 
\    FF Version:    5.0                                               * 
\    Peter J.  2014-10-27                                             * 
\    modified Marc PETREMANN 04 nov 2020                              * 
\ ********************************************************************* 
 
\ standardisation suggered by Matthias Trute 
\ source: https://theforth.net/package/i2c 
\ i2c.restart ( -- ) send the restart condition ------------------- OK 
\ i2c.start ( -- ) send start condition --------------------------- OK 
\ i2c.stop ( -- ) send stop condition ----------------------------- OK 
\ i2c.rx ( -- c ) receive 1 byte, send ACK 
\ i2c.rxn ( -- c ) receive 1 byte, send NACK 
\ i2c.tx ( c -- ) send 1 byte ------------------------------------- OK 
\ i2c.wait ( -- ) wait for the bus -------------------------------- OK 
 
\ The following two words are not essential but are useful 
\ for tools and checks. 
 
\ i2c.ping? ( addr -- f ) detect the presence of -------------------- OK 
\    a device on the bus, f is true if a device at addr responds 
\ i2c.status ( -- n ) get i2c status in a system specific way 
 
\ Other sources: 
\ i2c amForth: https://theforth.net/package/i2c/current-view/i2c.frt 
\   http://amforth.sourceforge.net/TG/recipes/I2C-Slave.html 
 
\ *** essential code from i2c-base-avr-v2.txt *********** 
-i2c-new 
marker -i2c-new 
 
\ reg: is an alias for CONSTANT 
\ use to define registers - for more readability 
: reg: ( comp: n ---  | exec: --- n) 
    create 
        , 
    does> 
        @ 
  ; 
\ alias for CONSTANT, use to define bits 
: bit:  ( c --- ) 
    reg: 
  ; 
 
flash 
\ i2c Two-Wire-Interface Registers 
184 reg: TWBR   \ TWI Bit Rate register 
185 reg: TWSR   \ TWI Status Register 
\ 186 reg: TWAR	\ TWI (Slave) Address register - unused 
187 reg: TWDR   \ TWI Data register 
188 reg: TWCR   \ TWI Control Register 
 
\ Bits in the Address register TWAR 
%11111110 bit: TWAR_TWA     \ (Slave) Address register Bits 
%00000001 bit: TWAR_TWGCE   \ General Call Recognition Enable Bit 
 
\ Bits in the Control Register TWCR 
%10000000 bit: TWCR_TWINT   \ Interrupt Flag 
%01000000 bit: TWCR_TWEA    \ Enable Acknowledge Bit 
%00100000 bit: TWCR_TWSTA   \ Start Condition Bit 
%00010000 bit: TWCR_TWSTO   \ Stop Condition Bit 
%00001000 bit: TWCR_TWWC    \ Write Collition Flag 
%00000100 bit: TWCR_TWEN    \ Enable Bit 
%00000001 bit: TWCR_TWIE    \ Interrupt Enable 
 
\ bits in the Status Register TWI 
%11111000 bit: TWSR_TWS     \ TWI Status 
%00000011 bit: TWSR_TWPS    \ TWI Prescaler 
ram 
 
\ Set clock frequency to 100kHz 
: i2c.init ( -- ) 
    TWSR_TWPS TWSR mclr         \ prescale value = 1 
    [ Fcy 100 / 16 - 2/ ] literal TWBR c! 
    %00000011 TWCR mset 
  ; 
 
\ Wait for operation to complete 
: i2c.wait ( -- )  
    \ When TWI operations are done, the hardware sets 
    \ the TWINT interrupt flag, which we will poll. 
    begin 
        TWCR c@ TWCR_TWINT and 
    until 
  ; 
 
\ Send start condition 
: i2c.start ( -- )  
    [ TWCR_TWINT TWCR_TWEN or TWCR_TWSTA or ] literal TWCR c! 
    i2c.wait 
  ; 
 
\ Send repeated start condition 
: i2c.rsen ( -- )  
    i2c.start     \ AVR doesn't distinguish 
  ; 
 
: i2c.restart 
    i2c.start     \ AVR doesn't distinguish 
  ; 
 
\ Send stop condition 
: i2c.stop ( -- )  
    [ TWCR_TWINT TWCR_TWEN or TWCR_TWSTO or ] literal TWCR c! 
  ; 
 
\ Write one byte to bus, returning 0 if ACK was received, -1 otherwise. 
: i2c.c! ( c -- f ) 
    i2c.wait \ Must have TWINT high to write data 
    TWDR c! 
    [ TWCR_TWINT TWCR_TWEN or ] literal TWCR c! 
    i2c.wait 
    \ Test for arrival of an ACK depending on what was sent. 
    TWSR c@ $f8 and $18 xor 0= if   0 exit  then \ SLA+W 
    TWSR c@ $f8 and $28 xor 0= if   0 exit  then \ data byte 
    TWSR c@ $f8 and $40 xor 0= if   0 exit  then \ SLA+R 
    -1  \ Something other than an ACK resulted 
; 
 
\ Write one byte to bus 
: i2c.tx ( c ---) 
   i2c.c! drop ; 
 
\ Read one byte and ack for another. 
: i2c.c@.ack ( -- c ) 
    [ TWCR_TWINT TWCR_TWEN or TWCR_TWEA or ] literal TWCR c! 
    i2c.wait 
    TWDR c@ 
  ; 
 
\ Read one last byte. 
: i2c.c@.nack ( -- c ) 
    [ TWCR_TWINT TWCR_TWEN or ] literal TWCR c! 
    i2c.wait 
    TWDR c@ 
 ; 
 
\ Address slave for writing, leaving true if slave ready. 
: i2c.addr.write ( 7-bit-addr -- f ) 
    1 lshift 1 invert and \ Build full byte with write-bit as 0 
    i2c.start i2c.c! 
    if      false 
    else    true    then 
  ; 
 
\ Address slave for reading, leaving true if slave ready. 
: i2c.addr.read ( 7-bit-addr -- f ) 
    1 lshift 1 or \ Build full byte with read-bit as 1 
    i2c.start i2c.c! 
    if      false 
    else    true    then 
  ; 
 
\ Detect presence of device, leaving true if slave responded. 
\ If the slave ACKs the read request, fetch one byte only. 
: i2c.ping? ( 7-bit-addr -- f ) 
    1 lshift 1 or     \ Build full byte with read-bit as 1 
    i2c.start i2c.c! 0= 
    if      i2c.c@.nack drop true 
    else    false 
    then 
  ; 
 
 
\ use i2c.detect for test and find peripherials connected 
\ to i2c bus 
 
-i2c-detect 
marker -i2c-detect 
 
: device.detect ( n ---) 
    i2c.ping? \ does device respond? 
    if      dup 2 u.r 
    else    ." -- " 
    then 
  ; 
 
 
\ not all bitpatterns are valid 7bit i2c addresses 
: i2c.7bitaddr? ( a --) 
    $07 $78 within 
    if      dup device.detect 
    else    ."    " 
    then 
  ; 
 
\ display header line 
: disp.0line ( ---) 
    cr 
    ."      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" 
  ; 
 
: start.line? ( n ---) 
    $0f and 0= 
    if      cr dup 2 u.r ." : " 
    then 
  ; 
 
: i2c.detect   ( -- ) 
    i2c.init 
    base @ hex 
    disp.0line  \ header line 
    0 $80 
    for 
        dup start.line? 
        dup i2c.7bitaddr? 
        1+ 
    next 
    drop 
    cr base ! 
    i2c.stop 
  ;