TACHYON
[~
cache #EEBLK BL + ERASE
FORGET W5500.fth
pub W5500.fth PRINT" WIZNET W5500 driver 141122.1200 " ;
--- some default IP settings that are only loaded in on a new pcb and if storage locations in upper eeprom is blank
&192.168.16.1 == myGW
&192.168.16.150 == myIP
&255.255.255.0 == mySN
{ *** WIZnet W5500 driver ***
Implements the SPI interface to the WIZnet W5500 including the primitives to talk to the chip
CHANGELOG:
141122 Reformatted .SOCKET listing - more compact
141118 Incorporate SPI16 operation plus auto chip select etc.
Removed VC@ and VC! and updated LREAD and LWRITE for faster operation
140818 Removed the old masking LSEND with a simplified version for the W5500
130817 Moved W5500 specific methods back from EASYNET
NAMES: $6908...715F for 2,135 bytes (+1,336)
CODE: $0924...5199 for 18,549 bytes (+2,199)
NOTE: Refresh webpage for latest version
}
( HARDWARE DEFINITIONS )
IFNDEF &WNCS
#P12 |< == &WNDO '' MISO from WIZNET
#P13 |< == &WNCS '' WIZNET CS
#P14 |< == &WNCK
#P15 |< == &WNDI '' MOSI to WIZNET
}
\ Compile time constant for pin configuration
&WNCS >| #24 SHL &WNDO >| 16 SHL + &WNDI >| 8 SHL + &WNCK >| + == wizpins ( &ce.miso.mosi.sck )
IFNDEF WRESET \ what to do if the user has not specified the reset and pwrdn
pub WRESET ( on/off -- ) DROP ; \ assume it's not connected
}
IFNDEF WPWRDN \ what to do if the user has not specified the reset and pwrdn
pub WPWRDN ( on/off ) DROP ;
}
BYTE wctrl --- b7..5 = socket ; b4..b3 = tx/rx/skt/com section ; b2 = r/w ; b1..b0 = 0
--- skt.2 skt.1 skt.0 seg.1 - seg.0 rw 0 0
pri WR_WCTRL wctrl C@ 4 OR SPIWRB DROP ;
pri WCTRL wctrl C@ SPIWRB DROP ; --- Access the Wiznet using current skt+section+r/w
pri WR 4 wctrl SET ;
pri RD 4 wctrl CLR ;
--- BLOCK ADDRESSING ---
\ 7 6 5 4 3 2 1 0
\ --BLOCK-- -SET- RW -LEN-
pub @RX $18
pub _WS wctrl C@ $18 ANDN OR wctrl C! ;
pub @TX $10 _WS ;
pub @COMMON wctrl C~ ;
--- WRITE METHODS ---
pub LC! ( byte wizadr -- ) ( 34.6us )
SPIWR16 DROP --- write address
WR_WCTRL
SPIWRB DROP
&WNCS OUTSET
;
pub LW! ( word wizadr -- )
SPIWR16 DROP --- write address
WR_WCTRL
SPIWR16 DROP
&WNCS OUTSET
;
pub L! ( long wizadr -- )
SPIWR16 DROP --- write address
WR_WCTRL
SPIWR SPIWR SPIWR SPIWR DROP --- write 4 bytes (long)
&WNCS OUTSET
;
--- READ METHODS ---
pub LC@ ( wizadr -- data )
SPIWR16 DROP --- send addr
RD WCTRL
0 SPIRD --- read back data
&WNCS OUTSET
;
pub LW@ ( wizadr -- data )
SPIWR16 DROP --- send addr
RD WCTRL --- send count
0 SPIRD SPIRD --- read back data
&WNCS OUTSET
;
pub L@ ( wizadr -- data )
SPIWR16 DROP --- send addr
RD WCTRL --- send count
0 SPIRD SPIRD SPIRD SPIRD --- read back data
&WNCS OUTSET
;
--- BLOCK TRANSFER METHODS ---
--- 140703 - Allow block read/write to access virtual memory if > $8000
LONG vread,vwrite --- maintain a read/write pointer for any virtual memory writes
--- Read a block of memory from the WIZnet
--- if dst is > $8000 then use virtual memory
pub LREAD ( src dst cnt -- )
ROT SPIWR16 DROP ( dst cnt) --- src address (WIZnet)
RD WCTRL
OVER $7FFF >
IF 2DUP + vwrite ! ADO 0 SPIRD I XC! LOOP
ELSE ADO 0 SPIRD I C! LOOP
THEN
&WNCS OUTSET
;
--- Write a block to the WIZnet - 3.2ms/512 bytes = = 6.25us/byte = 160kB/sec
pub LWRITE ( src dst cnt -- )
SWAP
SPIWR16 DROP --- Send dst address
WR_WCTRL --- Control block
( src cnt ) ADO I C@ SPIWRB DROP LOOP --- now just send the bytes sequentially
&WNCS OUTSET
;
( ----------------------------------- REGISTER LEVEL INTERFACE -------------------------------- )
--- EEPROM CONFIG BACKUP ---
--- Use the top part of the 64K EEPROM to save IP settings
--- 141219 Moved from $FF00 to $FFC0 because EEWORDS using top 32K of EEPROM
$FFC0 ORG
4 DS @gateway
4 DS @sip
4 DS @subnet
4 DS @mac
4 DS @wcold
#32 DS @ports
\ common registers
\ Access common registers
pub wMODE ( mask -- ) 0 @COMMON LC! ;
pub GATEWAY ( addr -- ) DUP @gateway E! 1 @COMMON L! ;
pub SUBNET ( mask -- ) DUP @subnet E! 5 @COMMON L! ;
pub MAC ( high low -- ) DUP @mac E! $0B @COMMON L! 9 LW! ;
pub SIP ( long -- ) DUP @sip E! $0F @COMMON L! ;
pri INTS@ ( -- ints ) $15 @COMMON LC@ ;
pri INTMASK ( mask -- ) $16 @COMMON LC! ;
pri @RTR ( -- adr ) $19 @COMMON ;
pri @RCR ( -- adr ) $1B @COMMON ;
pri UIP ( ip -- ) $28 @COMMON L! ;
pri UPORT ( port -- ) $2C @COMMON LW! ;
( SOCKET INTERRUPTS )
4 |< == &SENDOK
3 |< == &TIMEOUT
2 |< == &RECV
1 |< == &DISCON
0 |< == &CON
{
( SOCKET STATUS CODES )
$00 == SOCK_CLOSED
$13 == SOCK_INIT
$14 == SOCK_LISTEN
$17 == SOCK_ESTABLISHED
$1C == SOCK_CLOSE_WAIT
$22 == SOCK_UDP
$32 == SOCK_IPRAW
$42 == SOCK_MACRAW
$5F == SOCK_PPOE
( SOCKET TRANSIENT STATUS CODES )
$15 == SOCK_SYNSENT
$16 == SOCK_SYNRECV
$18 == SOCK_FIN_WAIT
$1A == SOCK_CLOSING
$1B == SOCK_TIME_WAIT
$1D == SOCK_LAST_ACK
$11 == SOCK_ARP
$21 == SOCK_ARP1
$31 == SOCK_ARP2
}
2 BYTES socket --- current socket plus foreground socket
\ Set the socket number ( calculates address and sets as a SOCKET constant )
pub SKT
pub SOCKET ( socket -- ) DUP socket C! 5 SHL wctrl C@ $1C AND OR wctrl C! ;
pub SKT@ socket C@ ;
--- modify wctrl to address a socket register
pub @SOCKET wctrl DUP C@ $E4 AND 8 OR SWAP C! ;
\ Socket registers ( -- addr )
pri sMODE 0 @SOCKET ; --- mode
pri sCMD! 1 @SOCKET LC! ; --- command
pri sINTS 2 @SOCKET ; --- interrupts
pri sSTAT 3 @SOCKET ; --- status
pri sPORT 4 @SOCKET ; --- 2 byte src port
pri sDHAR 6 @SOCKET ; --- 6 byte dest hardware address
pri sDIP $0C @SOCKET ; --- 4 byte dest IP address
pri sDPORT $10 @SOCKET ; --- 2 byte dest port
pri sSSIZE $12 @SOCKET ; --- 2 byte dest max seg size
pri sPRO $14 @SOCKET ; --- protocol in IP raw mode
pri sRXMEM $1E @SOCKET ;
pri sTXMEM $1F @SOCKET ;
pri TXFREE@ $20 @SOCKET LW@ ;
pri TXREAD $22 @SOCKET ;
pri TXWRITE $24 @SOCKET ;
pri RXSIZE@ $26 @SOCKET LW@ ;
pri RXREAD $28 @SOCKET ;
pri RXWRITE $2A @SOCKET ;
pri KEEPTMR $2D @SOCKET ; --- keep alive timer
\ Protocol modes
pri CLOSED 0 sMODE LC! ;
pub TCP 1 sMODE LC! ;
pub UDP 2 sMODE LC! ;
pri IPRAW 3 sMODE LC! ;
pri MACRAW 4 sMODE LC! ;
pri PPPoE 5 sMODE LC! ;
( SOCKET COMMANDS - use currently select socket )
pub sOPEN 1 sCMD! ;
pub sLISTEN 2 sCMD! ;
pub sCONNECT 4 sCMD! ;
pub sDISCON 8 sCMD! ;
pub sCLOSE $10 sCMD! ;
pub sSEND $20 sCMD! ;
pub sSENDMAC $21 sCMD! ;
pub sSENDKEEP $22 sCMD! ;
pub sRECV $40 sCMD! ;
pri sCLOSED? sSTAT LC@ 0= ;
pri sINACTIVE? sSTAT LC@ $17 < ;
pri sESTAB? sSTAT LC@ $17 = ;
pri sCLOSING? sSTAT LC@ $18 $1B WITHIN ; --- closing or wait closing
pri sCONNECTED? sINTS LC@ &CON AND DUP IF &CON sINTS LC! THEN ; --- Test and reset the connection interrupt;
pri sDISCON? sINTS LC@ &DISCON AND DUP IF -1 sINTS LC! THEN ;
pub PORT! ( srcport -- ) --- save port config and set port
DUP SKT@ 2* 2* @ports + E!
pub SetPORT
sPORT LW!
;
( Set the factory defaults on very first run - generate a random MAC in the 01.FF.xx.xx.xx.xx range )
pub WCOLD
CR PRINT" Setting default IP configuration "
myGW @gateway E!
myIP @sip E!
mySN @subnet E!
RND @mac E! \ random MAC at first run, Need to have a unique ID
8 0 DO 0 I 2* 2* @ports + E! LOOP
$A55A @wcold E!
;
\ Init the SPI for the WIZnet chip
pub !WIZIO
!PCB
wizpins SPIPINS \ Use SPI
&WNCK OUTCLR \ Clock is an output - leave low
&WNDI OUTCLR \ MOSI is an output
ON WRESET OFF WPWRDN OFF WRESET
;
$02FF == oui2
pub !WIZIP
@wcold E@ $A55A <> IF WCOLD THEN
@gateway E@ GATEWAY \ Assign a default gateway
@subnet E@ SUBNET
@sip E@ SIP
oui2 @mac E@ MAC
8 0 DO I 2* 2* @ports + E@ I SOCKET SetPORT LOOP
;
pri !TXBUFS 8 0 DO I SOCKET $800 0 DO 0 I @TX L! 0 I @RX L! 2 +LOOP LOOP ;
pub !WIZ !WIZIO !TXBUFS !WIZIP ;
{ Since the W5500??? read and write index registers are unreadable until a connection has been established
or cannot be written incrementally until a send then these are buffered for when they become readable
}
8 WORDS txwr PRIVATE --- tx write buffers for 8 sockets
pub @txwr ( -- addr ) txwr SKT@ 2* + ;
pub !TXWR txwr BL $FF FILL ; --- invalidate all txwr pointers (W5500 workaround)
LONG txtime,txsize
BYTE autosend --- Flag to control whether LANSEND sends when it receives a LF or not
$0A autosend C! --- preset to autosend
TIMER sendtimer PRIVATE
pri WAITSEND
txsize @ 0EXIT --- don't bother if it's empty
#5000 sendtimer TIMEOUT --- give it some time to send what it has
BEGIN
sINTS LC@ &SENDOK AND --- until sent
TXREAD LW@ TXWRITE LW@ = AND
sendtimer TIMEOUT? OR --- or until timeout
UNTIL
&SENDOK sINTS LC! --- reset the sent interrupt flag
;
\ Request WIZnet to send off current transmit data in buffer
pub ?SENDPOLL
txtime @ CNT@ - ABS CLKFREQ 7 SHR > \ 1/128 sec timeout since last
0EXIT
pub ?SEND \ check to see if anything still needs to be sent
txsize @ 0EXIT \ Exit if buffer empty
pub LANSEND
&SENDOK sINTS LC! \ Clear send interrupt
@txwr W@ TXWRITE LW! sSEND \ update TXWRITE register and command WIZnet to SEND
WAITSEND
txsize ~ \ reset txsize (buffer empty)
;
--- send out a character through the WIZnet - either block mode or autosend
pub LANEMIT ( ch -- )
\\\ #100 sendtimer TIMEOUT \ retrigger timeout even in "block" mode
\\\ @txwr @ -1 = IF TXWRITE LW@ @txwr ! THEN \ force an update (assumed valid) if the index is "invalid"
@txwr W@
OVER SWAP ( ch ch index ) @TX LC! \ write character to buffer
@txwr W++ CNT@ txtime !
txsize ++
$0A = autosend C@ AND \ SEND if this is an CRLF end of line and autosend is active
txsize @ $3F0 > OR \ AUTOSEND if buffer size is large enough already
IF LANSEND THEN
;
pub LANKEY ( -- ch )
RXSIZE@
IF
RXREAD LW@ DUP @RX LC@ --- read a character from the receive buffer
SWAP 1+ RXREAD LW! sRECV --- update read index
ELSE
keypoll W@ ?DUP IF CALL THEN --- implement a keypoll for when LANKEY is doing nothing
0 --- return with a null character
THEN
;
( Redirect console output to the LAN )
pub LAN
' LANEMIT uemit W!
' LANKEY ukey W!
@txwr @ -1 = IF TXWRITE LW@ @txwr ! THEN \ force an update (assumed valid) if the index is "invalid"
;
( diagnostic to the terminal to reflect what is begin typed to the LAN )
pub LANCONEMIT
DUP (EMIT) LANEMIT
;
IFDEF KEY?
( allow input from both the serial console and the LAN socket )
pub LANCONKEY ( -- ch )
LANKEY KEY? AND OR
;
}
IFNDEF KEY?
( allow input from both the serial console and the LAN socket )
pub LANCONKEY ( -- ch )
LANKEY KEY OR
;
}
--- use both LAN and CON for output
pub LANCON
CON CR LAN
' LANCONEMIT uemit W!
;
--- set WIZnet chip as source for any DUMP type operations
pub WIZ ' LC@ mc@ W! ' LW@ mw@ W! ' L@ m@ W! ;
$800 == WBUFSZ
pri LSEND DUP @txwr W+! TXWRITE LW@ SWAP @TX LWRITE LANSEND ;
--- Read from WIZnet buffer into file until buffer is exhausted
--- Used by FTP STOR
pri LREADSKT ( dst --- )
vwrite !
BEGIN
RXSIZE@ ?DUP
WHILE ( cnt )
RXREAD LW@ ( cnt index ) --- fetch receive read index for this socket
SWAP
vwrite @ SWAP ( wizptr filedst cnt )
DUP >R
@RX LREAD vwrite ++ --- read WIZnet buffer directly into file and update vwrite+1
R> RXREAD LW@ + RXREAD LW! --- update read index in advance
sRECV --- signal that buffer up to READ index has been read
#10 ms
REPEAT
;
$40 BYTES sktbuf --- holding buffer for socket registers
pri @SKTBUF sktbuf + ;
pri .IPX DUP >B $30A .NUM 8 SHR ;
pri .IP ( off -- )
DECIMAL 3 SPACES
pub .IP1
@SKTBUF U@
.IPX "." EMIT .IPX "." EMIT .IPX "." EMIT .IPX DROP
;
pri .@SKTBUF @SKTBUF
pri .PTR C@++ 8 SHL SWAP C@ + ( $7FF AND ) $2410 .NUM "." EMIT ;
IFNDEF SWAPB
pri SWAPB ( word -- word2 \ Swap the bytes in a 16-bit word )
DUP 8 SHR SWAP >B 8 SHL OR
;
}
IFNDEF .TIME
pub .TIME ." 00:00:00" ;
}
{
SKT HH:MM:SS MODE PORT DEST TXRD TXWR RXRD RXWR RXSZ IR STATUS IP ADDR
#1 00:00:00 TCP 21 52775 967F.967F. . . . 00 17 ESTABLISHED 192.168.016.002.
}
pub .SKTHD CR BOLD PRINT" SKT HH:MM:SS MODE PORT DEST TXRD TXWR RXRD RXWR RXSZ IR STATUS IP ADDR" PLAIN ;
pub .SOCKET ( n -- )
.SKTHD
pub .SKT
DUP SOCKET 0 sktbuf $30 @SOCKET LREAD --- read in registers
0 @SKTBUF C@ IF CR "#" EMIT . 2 SPACES .TIME ELSE DROP EXIT THEN
0 @SKTBUF C@ SPACE
4 * " CLSDTCP UDP IPRWMACRPPP !06!!07!" + 4 CTYPE SPACE
4 @SKTBUF W@ SWAPB $250A .NUM SPACE --- PORT
16 @SKTBUF W@ SWAPB $250A .NUM SPACE --- DPORT
$22 .@SKTBUF $24 .@SKTBUF --- TXRD TXWR
$28 .@SKTBUF $2A .@SKTBUF --- RXRD RXWR
$26 .@SKTBUF SPACE --- RX SIZE
2 @SKTBUF C@ .BYTE SPACE --- INT REG
3 @SKTBUF C@ DUP .BYTE --- STATUS REG
\ pri .SSTAT ( byte -- ) --- STATUS DESCRIPTION
SWITCH SPACE
0 CASE PRINT" closed " BREAK
$13 CASE PRINT" INIT " BREAK
$14 CASE PRINT" LISTEN " BREAK
$16 CASE PRINT" SYNRECV " $0C .IP BREAK
$17 CASE PRINT" ESTABLISHED " $0C .IP BREAK
$18 CASE PRINT" FIN WAIT " $0C .IP BREAK
$1C CASE PRINT" closed wait " $0C .IP BREAK
$22 CASE PRINT" UDP OPEN " BREAK
$32 CASE PRINT" IPRAW OPEN " BREAK
;
pub ifconfig
0 sktbuf $40 @COMMON LREAD \ read in common registers
BOLD
CR PRINT" ************ NETWORK STATUS ************ " PLAIN
CR PRINT" LINK " 1 $2E @SKTBUF SET? IF PRINT" *UP*" ELSE PRINT" DOWN" THEN
CR PRINT" HARDWARE: " PCB$ PRINT$ PRINT" using WIZnet W5500 V" $39 @SKTBUF C@ .
CR PRINT" SRC IP " $0F .IP
CR PRINT" MASK " 5 .IP
CR PRINT" GATEWAY" 1 .IP
CR PRINT" MAC " 9 6 ADO I @SKTBUF C@ .BYTE I 14 <> IF PRINT" ." THEN LOOP
pub .SOCKETS
.SKTHD 8 0 DO I .SKT LOOP CR
;
\ SOCKETS - W5500 has 8 sockets - define 4 of them
0 == NETMAN --- network management
1 == FTP
2 == FTPDAT
3 == TELNET
4 == HTTP --- uses sockets 4..7
" NETFTPDATTELWEBWEBWEBWEB" 0 STRING skt$
{ NOTE: Use WIZ DUMP or variation thereof instead of WDUMP
0 40 @COMMON WIZ DUMP
0 40 1 SKT @TX DUMP
}
]~
END