218 lines
7.7 KiB
Forth
218 lines
7.7 KiB
Forth
\ ############################################################################
|
|
\ # i2c.fth - This program reads and writes EEPROMs using the I2C protocol.
|
|
\ # This program is based on Mike Green's basic_i2c_driver, which is found in
|
|
\ # the Parallax Object Exchange (OBEX).
|
|
\ #
|
|
\ # The first parameter for all routines is the pin number of the clock. It
|
|
\ # is assumed that the data pin number is one greater than the clock pin
|
|
\ # number.
|
|
\ #
|
|
\ # Copyright (c) 2012 Dave Hein
|
|
\ # MIT Licensed
|
|
\ ############################################################################
|
|
|
|
: i2c_dira_sda_high dup dira@ or dira! ;
|
|
: i2c_outa_sda_high dup outa@ or outa! ;
|
|
: i2c_dira_scl_high over dira@ or dira! ;
|
|
: i2c_outa_scl_high over outa@ or outa! ;
|
|
: i2c_dira_sda_low dup invert dira@ and dira! ;
|
|
: i2c_outa_sda_low dup invert outa@ and outa! ;
|
|
: i2c_dira_scl_low over invert dira@ and dira! ;
|
|
: i2c_outa_scl_low over invert outa@ and outa! ;
|
|
|
|
\ This routine should be called before calling any of the other ones to ensure
|
|
\ that the EEPROM is in a known ready state.
|
|
: i2c_init ( scl ... )
|
|
1 swap lshift dup 2* \ sda := scl + 1
|
|
i2c_outa_scl_high \ outa[scl] := 1
|
|
i2c_dira_scl_high \ dira[scl] := 1
|
|
i2c_dira_sda_low \ dira[sda] := 0
|
|
9 0 do \ repeat 9
|
|
i2c_outa_scl_low \ outa[scl] := 0
|
|
i2c_outa_scl_high \ outa[scl[ := 1
|
|
dup ina@ and if leave then \ if ina[sda] quit
|
|
loop
|
|
2drop
|
|
;
|
|
|
|
\ This routine sends a start bit
|
|
: i2c_start ( scl ... )
|
|
1 swap lshift dup 2* \ sda := scl + 1
|
|
i2c_outa_scl_high \ outa[scl]~~
|
|
i2c_dira_scl_high \ dira[scl]~~
|
|
i2c_outa_sda_high \ outa[sda]~~
|
|
i2c_dira_sda_high \ dira[sda]~~
|
|
i2c_outa_sda_low \ outa[sda]~
|
|
i2c_outa_scl_low \ outa[scl]~
|
|
2drop
|
|
;
|
|
|
|
\ This routine sends a stop bit
|
|
: i2c_stop ( scl ... )
|
|
1 swap lshift dup 2* \ sda := scl + 1
|
|
i2c_outa_scl_high \ outa[scl]~~
|
|
i2c_outa_sda_high \ outa[sda]~~
|
|
i2c_dira_scl_low \ dira[scl]~
|
|
i2c_dira_sda_low \ dira[sda]~
|
|
2drop
|
|
;
|
|
|
|
\ This routine sends one byte and returns the ACK bit
|
|
: i2c_write ( scl data ... ackbit )
|
|
23 lshift swap \ data <<= 23
|
|
1 swap lshift dup 2* \ sda := scl + 1
|
|
8 0 do \ repeat 8
|
|
rot 2* dup 0<
|
|
if
|
|
rot rot
|
|
i2c_outa_sda_high \ outa[sda] := 1
|
|
else
|
|
rot rot
|
|
i2c_outa_sda_low \ outa[sda] := 0
|
|
then
|
|
i2c_outa_scl_high \ outa[scl]~~
|
|
i2c_outa_scl_low \ dira[scl]~
|
|
loop
|
|
i2c_dira_sda_low \ dira[sda]~
|
|
i2c_outa_scl_high \ outa[scl]~~
|
|
dup ina@ and >r \ ackbit := ina[sda]
|
|
i2c_outa_scl_low \ outa[scl]~
|
|
i2c_outa_sda_low \ outa[sda]~
|
|
i2c_dira_sda_high \ dira[sda]~~
|
|
2drop drop
|
|
r> \ return ackbit
|
|
;
|
|
|
|
\ This routine reads one byte from the EEPROM
|
|
: i2c_read ( scl ackbit ... data )
|
|
>r \ save ackbit
|
|
0 swap \ data := 0
|
|
1 swap lshift dup 2* \ sda := scl + 1
|
|
i2c_dira_sda_low \ dira[sda]~
|
|
8 0 do \ repeat 8
|
|
i2c_outa_scl_high \ outa[scl]~~
|
|
rot 2* \ data <<= 1
|
|
over ina@ and if 1 or then \ if ina[sda] data |= 1
|
|
rot rot
|
|
i2c_outa_scl_low \ outa[scl]~
|
|
loop
|
|
r> if
|
|
i2c_outa_sda_high \ outa[sda]~~
|
|
else
|
|
i2c_outa_sda_low \ outa[sda]~
|
|
then
|
|
i2c_dira_sda_high \ dira[sda]~~
|
|
i2c_outa_scl_high \ outa[scl]~~
|
|
i2c_outa_scl_low \ outa[scl]~
|
|
i2c_outa_sda_low \ outa[sda]~
|
|
2drop \ return data
|
|
;
|
|
|
|
\ This routine reads up to one page of data from the EEPROM
|
|
: i2c_readpage ( scl devsel addrreg dataptr count ... ackbit )
|
|
>r >r \ Move count and dataptr to the return stack
|
|
dup 15 rshift 14 and rot or \ Assemble the devsel byte
|
|
dup >r rot dup dup >r \ Copy devsel and scl to the return stack
|
|
i2c_start \ Send a start bit
|
|
swap \ Arrange the scl and devsel on the stack
|
|
i2c_write drop \ Send the devsel byte
|
|
dup 8 rshift 255 and r@ swap \ Extract the second address byte
|
|
i2c_write drop \ Send the second address byte
|
|
255 and r@ swap \ Extract the third address byte
|
|
i2c_write drop \ Send it
|
|
r@ \ Get the scl from the return stack
|
|
i2c_start \ Send a start bit
|
|
r> r> 1 or over >r \ Get the scl and devsel byte and set the LSB
|
|
i2c_write drop \ Send the devsel byte
|
|
r> r> r> 1 ?do \ Get scl, dataptr and count and start do loop
|
|
over 0 i2c_read over c! 1+ \ Read a byte from the EEPROM and save it
|
|
loop
|
|
over 1 i2c_read swap c! \ Read the last byte from the EEPROM
|
|
i2c_stop \ Send a stop bit
|
|
0 \ Return the ack bit
|
|
;
|
|
|
|
variable i2c_var
|
|
|
|
\ This routine reads a byte from the specified address
|
|
: i2c_readbyte ( scl devsel addrreg )
|
|
i2c_var 1 i2c_readpage drop i2c_var c@ ;
|
|
|
|
\ This routine reads a word from the specified address
|
|
: i2c_readword ( scl devsel addrreg )
|
|
0 i2c_var ! i2c_var 2 i2c_readpage drop i2c_var @ ;
|
|
|
|
\ This routine reads a long from the specified address
|
|
: i2c_readlong ( scl devsel addrreg )
|
|
i2c_var 4 i2c_readpage drop i2c_var @ ;
|
|
|
|
\ This routine writes up to one page of data to the EEPROM
|
|
: i2c_writepage ( scl devsel addrreg dataptr count ... ackbit )
|
|
>r >r \ ( scl devsel addrreg ) r( count dataptr )
|
|
dup 15 rshift 14 and rot or \ ( scl addrreg devsel )
|
|
rot dup >r \ ( addrreg devsel scl ) r( count dataptr scl )
|
|
i2c_start \ ( addrreg devsel ) r( count dataptr scl )
|
|
r@ swap \ ( addrreg slc devsel ) r( count dataptr scl )
|
|
i2c_write drop \ ( addrreg ) r( count dataptr scl )
|
|
dup 8 rshift 255 and r@ swap
|
|
i2c_write drop
|
|
255 and r@ swap
|
|
i2c_write drop
|
|
r> r> r> 0 ?do
|
|
2dup c@ i2c_write drop 1+
|
|
loop
|
|
drop
|
|
i2c_stop
|
|
0
|
|
;
|
|
|
|
\ This routine writes a byte to the specified address
|
|
: i2c_writebyte ( scl devsel addrreg data )
|
|
i2c_var ! i2c_var 1 i2c_writepage drop ;
|
|
|
|
\ This routine writes a word to the specified address
|
|
: i2c_writeword ( scl devsel addrreg data )
|
|
i2c_var ! i2c_var 2 i2c_writepage drop ;
|
|
|
|
\ This routine writes a long to the specified address
|
|
: i2c_writelong ( scl devsel addrreg data )
|
|
i2c_var ! i2c_var 4 i2c_writepage drop ;
|
|
|
|
\ This routine returns a zero if the EEPROM is ready after a write
|
|
\ Otherwise it returns a non-zero value
|
|
: i2c_writewait ( scl devsel addrreg )
|
|
15 rshift 14 and or
|
|
over i2c_start
|
|
over >r i2c_write
|
|
r> i2c_stop
|
|
;
|
|
|
|
\ This word will be run at startup
|
|
: startup
|
|
1 30 lshift dup outa! dira!
|
|
pfthversion type cr
|
|
;
|
|
|
|
\ Set up the cog config struct
|
|
: setupconfig
|
|
496 cog@ \ Get config struct address from PAR
|
|
dup 16 + @ \ Get the address of the return stack
|
|
4 + over 12 + ! \ Add four and set initial address of return stack
|
|
['] interpret >body \ Get the address of the Forth interpreter
|
|
over 16 + @ ! \ Write it to the return stack
|
|
['] startup >body \ Get the address of the startup word
|
|
swap ! \ Write it to the intial value of the program counter
|
|
;
|
|
|
|
\ Save the hub RAM to EEPROM
|
|
: eesave
|
|
setupconfig
|
|
28 i2c_init
|
|
512 0
|
|
do
|
|
28 160 i 64 * dup 64 i2c_writepage drop
|
|
begin 28 160 0 i2c_writewait 0= until
|
|
i 7 and 7 = if [char] . emit then
|
|
loop
|
|
;
|