( 1-Wire.fth ) TACHYON [~ : 1WIRE.fth ." 1-wire interface 121025:0000 " ; { The 1-Wire interface is implemented in bytecode so it does not require a special cog to handle the timing. Testing is being done on DS1820+ and DS2450S+ chips. CHANGELOG: 121025 Fixed bugs in routines Added strong pullup after writing to provide power for parasitic devices Add some of Brian Riley's code Allow for redirection of DQ line } ( I/O DEFINITIONS ) LONG dq pri DQ@ dq @ ; pub DQ! dq ! ; #P14 |< DQ! \ set the default pub 1WRESET ( -- ack ) DQ@ OUTCLR 1 ms DQ@ INPUTS #80 us DQ@ IN 0= #400 us ; pub 1WBITWR ( iomask dat -- iomask dat/2 ) OVER OUTCLR \ pulse low for 1us SHROUT \ output next data bit then shift data right #60 us \ minimum cycle time OVER OUTSET \ Force line high at end of slot (rather than float) ; pub 1WBITRD ( iomask dat -- iomask dat+ ) OVER OUTCLR OVER INPUTS \ pulse line low for 1us then float 8 us SHRINP \ assemble another bit by right shifting #50 us \ MJB OVER OUTSET \ Force line high at end of slot (rather than float) ; \ Transmit 8-bit lsb first pub 1W! ( dat -- ) DQ@ SWAP \ mask and data ( iomask dat ) 8 FOR 1WBITWR NEXT \ write 8 bits lsb first 2DROP \ discard mask and used data ; \ Receive 8-bit data lsb first pub 1W@ ( -- dat ) DQ@ 0 \ initial mask and data ( iomask dat ) 8 FOR 1WBITRD #45 us NEXT NIP #24 SHR \ right justify ( dat ) ; \ Get bytes from bus and print as hex bytes pub .1W ( bytes -- ) FOR 1W@ .BYTE SPACE NEXT ; pub READROM $33 1W! ; \ send read ROM command code pub SKIPROM $CC 1W! ; \ send skip ROM command code pub MATCHROM $55 1W! ; \ command to match an ID to a specific device pub CONVERTT $44 1W! ; \ commands all Temp sensors to start measurement (750 ms) pub READSCR $BE 1W! ; \ starts to: dumps 9 bytes (8 plus CRC) of device scratchpad pub 1WSCMD 1WRESET DROP SKIPROM ; \ reset, skip ROM for single sensor only and prepare for next command pub RDROMH 1WRESET DROP READROM 8 .1W ; \ read and print 8 byte ROM code pub RDSCRH 1WSCMD READSCR 9 .1W ; \ reset, skip ROM, read and print Scratchpad 9 bytes pub TEMP@ ( -- temp ) \ read first 2 bytes from scratchpad and assemble to temperature value on TOS ( binary, low nibble is fraction ) READSCR 1W@ 1W@ B>W \ combine 2 bytes to 1 long on stack ; pub .TEMP ( temp -- ) DUP $0F AND SWAP 4 SHR \ put whole degrees in one long and fraction in another $830A .NUM \ print whole number "." EMIT \ print decimal point #100 * 4 SHR \ turn binary bits to 100ths $020A .NUM \ print decimal ." 'C " ; \ 18B20 diagnostic routines : SHOWTEMP ( reads annd displays a single DS18B20 alone on a 1-W bus ) 1WSCMD CONVERTT #750 ms 1WSCMD TEMP@ DUP CR .TEMP SPACE 4 SHR 0 DO ." *" LOOP ; : SHOWSP ( reads annd displays a single DS18B20 SCRATCHPAD alone on a 1-W bus ) 1WSCMD CONVERTT #750 ms 1WSCMD RDSCRH ; : SHOWTEMPS BEGIN SHOWTEMP 2 seconds KEY? AND UNTIL ; ]~ END { DEMO USAGE SHOWTEMPS +036.81'C ************************************ +036.62'C ************************************ +036.06'C ************************************ +035.68'C *********************************** +034.62'C ********************************** +034.68'C ********************************** +034.25'C ********************************** +033.31'C ********************************* +032.87'C ******************************** +032.50'C ******************************** +032.81'C ******************************** +033.12'C ********************************* +033.25'C ********************************* +033.31'C ********************************* +033.43'C ********************************* +033.50'C ********************************* +033.68'C ********************************* +033.68'C ********************************* +033.62'C ********************************* +032.87'C ******************************** +031.62'C ******************************* +031.00'C ******************************* +028.12'C **************************** +026.62'C ************************** +026.31'C ************************** +025.81'C ************************* +024.43'C ************************ +024.18'C ************************ +022.37'C ********************** +021.68'C ********************* +021.81'C ********************* +022.18'C ********************** +022.50'C ********************** +023.18'C *********************** +023.93'C *********************** } ( 1WireMJB.fth ) TACHYON [~ : 1WIREMJB.fth ." 1-wire interface extensions MJB 140525:0000 " ; \ CREATE SMALL \ define SMALL before a load to specify a SMALL build to limit the sections included IFNDEF SMALL { \ DESCRIPTION \ \ Maxim 1-wire ROM Search algorithm \ straight forward implementation \ per AN937 "Book of iButton Standards", figure 5-3 \ for TACHYON Forth 2014 by Markus Baer aka MJB @ parallax forums, \ inspired by am-forth implementation by Bradford J. Rodriguez. \ USES \ 1WIRE.fth from Tachyon Forth by Peter Jakacki \ and of course the TACHYON System (Kernel 2.3 + EXTEND) - thanks Peter \ \ ** USES from 1-wire.fth **** \ 1W.RESET ( -- ack ) \ 1W.BITWR ( iomask dat -- iomask dat/2 ) \ 1W.BITRD ( iomask dat -- iomask dat+ ) \ 1W! ( c -- ) Write one byte to the 1-wire bus. \ 1W@ ( -- c ) Read one byte from the 1-wire bus. } { if you need it ... : 1W.!N ( xN .. x1 N -- ) FOR 1W! NEXT ; \ send N characters/bytes from stack : 1W.@N ( N -- x1 .. xN ) FOR 1W@ NEXT ; \ fetch N characters/bytes to stack : 1W..ROM RDROMH ; \ print 1Wire single device ROM code } BYTE 1W.lastdisc ( used as byte variable to hold last discrepancy ) BYTE 1W.doneflag BYTE 1W.rombit ( used as byte variable, 1..64 ) BYTE 1W.discmark \ buffer for one ROM-ID 1W.romid ( 8 byte array ) 8 BYTES 1W.romid PRI 1W.!rombit ( f -- \ set the bit (1..32) given by 1W.rombit in buffer 1W.romid to flag value f ) 1W.rombit C@ 1- 8 U/MOD ( -- f bit# byte# ) 1W.romid + ( f bit# addr ) SWAP MASK SWAP ( f bitmask addr ) ROT ( bitmask addr f ) BIT! ; PRI 1W.@rombit ( -- f \ f is 0 or non-0 not necc. 1 \ fetch the bit (1..32) given by 1W.rombit from buffer 1W.romid to flag f ) 1W.rombit C@ 1- 8 U/MOD ( -- bit# byte# ) 1W.romid + C@ ( -- bit# byte ) SWAP MASK ( -- byte bitmask ) AND ; \ helper pub <>0 ( val -- 0or1 \ convert any non 0 to 1 instead of -1 as 0<> does ) IF 1 ELSE 0 THEN ; #P14 |< DQ! \ for my setup MJB, this is the pin my 1-wire bus is attached to 4.7k or 5k pullup required \ single bit operations to read and write 1-wire bus PRI 1W.BIT@ ( -- bit ) DQ@ 0 1W.BITRD NIP <>0 ; \ gives bit as 0 or 1 PRI 1W.BIT! ( bit -- ) DQ@ SWAP 1W.BITWR 2DROP ; \ takes bit as 0 or 1 { for test PRI .rombit ( bitnumber -- ) 1W.rombit C! 1W.@rombit . ; \ print 1W.rombit value PRI .RB ( -- ) 1W.@rombit . ; PRI .romid ( -- ) 1W.romid 8 ADO I C@ . SPACE LOOP ; } PRI 1W.NEWSEARCH ( -- \ clear variables used in search ) 1W.lastdisc C~ 1W.doneflag C~ 1W.rombit C~ 1W.romid #8 ERASE 1W.devcnt C~ \ set device count to 0 ; : 1W.SEARCHROM ( -- f ) ( search for one additional device. Returns 0 if done or 1 if more to come. see 1W.SCAN ) 0 ( default return value ) ( 0 ) 1W.doneflag C@ IF 1W.doneflag C~ \ clear doneflag EXIT ( leaves 0 ) THEN 1W.RESET IF ( presence signal detected? ) 1W.rombit C!~~( yes: set ROM bit index to 1 ) 1W.discmark C~ ( set discrepancy marker to 0 ) ( 0 ) $F0 1W! ( send search command on bus ) ( 0 ) BEGIN 1W.BIT@ 1W.BIT@ 2* + ( 0 BA ) \ 2 bits A & B in bit pos 0 AND 1 DUP $03 = ( 0 BA flag ) IF ( bitA = bitB = 1?) DROP 1W.lastdisc C~ \ clear EXIT ( leaves 0 ) ELSE ( 0 BA ) ?DUP 0= ( 0 BA false | 0 true ) IF ( bitA = bitB = 0?) ( 0 ) 1W.rombit C@ 1W.lastdisc C@ = ( 0 flag ) IF 1 1W.!rombit ( 0 ) ELSE 1W.rombit C@ 1W.lastdisc C@ > ( 0 flag ) IF 0 1W.!rombit 1W.rombit C@ 1W.discmark C! ( 0 ) ELSE 1W.@rombit 0= ( 0 flag ) IF 1W.rombit C@ 1W.discmark C! ( 0 ) THEN THEN ( 0 ) THEN ( 0 ) ELSE ( 0 BA ) $01 AND ( bit A value , bit B is inverse so we don't need it ) 1W.!rombit ( 0 ) THEN THEN ( 0 ) 1W.@rombit <>0 \ gives 0 or 1 as logical values instead of 0 -1 1W.BIT! ( DROP what is this drop for ??? )( send ROM bit to bus ) .S \ should be 0 1W.rombit C@ 1+ DUP 1W.rombit C! ( 0 1W.rombitVal ) \ increment 1W.rombit index $40 > UNTIL \ check > 64 ( 0 ) 1W.discmark C@ DUP 1W.lastdisc C! ( 0 discmark ) 0= IF 1 1W.doneflag C! ( 0 ) ELSE 1+ ( set return value to 1 = true ) THEN ELSE ( no presence signal ) 1W.lastdisc C~ \ set 0 THEN ; \ Demonstrates how to use SEARCHROM to find all attached devices and store them in table 1W.1Wdevs BYTE 1W.devcnt LONG 1W.devaddr TABLE 1W.1Wdevs #8 #8 * ALLOT \ prepare for 8 devices here, change as appropriate { test 1W.1Wdevs $20 DUMP 1W.1Wdevs $16 ERASE 1W.1Wdevs $20 ADO I C@ .BYTE SPACE LOOP } PRI 1W.PRINTromid ( addr -- ) \ print the ROM-ID at address 8 ADO I C@ .BYTE SPACE LOOP CR ; pub 1W.SCAN ( -- ) \ scan for any devices on 1-wire bus and store them in table 1W.1Wdevs 1W.NEWSEARCH 1W.1Wdevs 1W.devaddr ! \ begin at start of table CR BEGIN 1W.SEARCHROM 1W.romid 1W.PRINTromid \ print it \ ' CMOVE ( src dst cnt -- ) Copy bytes from src to dst address for cnt bytes using increment for up or down 1W.romid 1W.devaddr @ 8 CMOVE \ SEARCHROM could be optimized to store directly to table, \ by making 1W.romid a CONST pointing to the table entry and changing it for each run 1W.devaddr DUP @ 8 + SWAP ! 0= UNTIL CR ; { some demo / test code pub 1W..1Wdevs \ print the table of 1W.1Wdevs discovered by 1W.SCAN ( if table is not full ) 1 1W.1Wdevs CR BEGIN \ BEGIN condition WHILE code REPEAT DUP C@ WHILE ( 1 1W.1Wdevs ) \ works only if there is at least one element in table which is 0ed SWAP DUP .BYTE ":" EMIT SPACE 1+ SWAP DUP 1W.PRINTromid 8 + REPEAT DROP ; pub 1W..1WdevsT \ print the table of 1W.1Wdevs discovered by 1w.scan in Table format for inclusion in Tachyon code 1W.1Wdevs CR ." TABLE 1W.my1Wdevs" CR BEGIN \ BEGIN condition WHILE code REPEAT DUP C@ WHILE ( 1 1W.1Wdevs ) SPACE DUP 8 ADO ." $" I C@ .BYTE ." | " LOOP CR \ ADO ( from cnt -- ) 8 + REPEAT DROP ; } { test it on 2 devices NEWCNT 1W.SCAN .LAP } \ 1 Wire Multibus CoMmanD takes the address of a ROM code and performs a matchROM command pub 1W.MCMD ( -- ) 1W.RESET DROP MATCHROM ; \ reset and send Match ROM command pub 1W.MATCH ( addr -- ) \ address sensor with 64-bit rom code given at addr then ready for next command 1W.MCMD 8 ADO I C@ 1W! LOOP ; IFNDEF SMALL \ 18B20 diagnostic routines : 1W.SHOWTEMPM ( tableaddr n -- \ reads and displays a DS18B20 on a multibus. n is the index into the table of devices 1W.1Wdevs ) \ 1W.1Wdevs table of discovered 1W devices with SEARCHROM 1- 8 * + \ calculate start of ROM code to address DUP 1W.MATCH CONVERTT #750 ms \ 1W.MATCH TEMP@ DUP .TEMP 1W.MATCH TEMP@ .TEMP \ SPACE 4 SHR 0 DO ." *" LOOP \ bar graph ; : 1W.SHOWSPM ( tableaddr n -- \ reads and displays a DS18B20 SCRATCHPAD on a multibus n is the index into the table of devices 1W.1Wdevs ) \ 1W.1Wdevs table of discovered 1W devices with SEARCHROM 1- 8 * + \ calculate start of ROM code to address DUP 1W.MCMD CONVERTT #750 ms 1W.MCMD RDSCRH ; \ for sensor# from I = 1 to n from ROM-Code table given show the temp readings cyclically until key pressed : 1W.SHOWTEMPSM ( tableaddr n -- ) BEGIN 1 OVER ADO \ from 1 to n OVER I 1W.SHOWTEMPM CR 2 seconds LOOP KEY? AND \ AND required, since KEY? ( -- key flag ) UNTIL 2DROP ; } \ end IFNDEF SMALL \ 1W.1Wdevs 2 1W.SHOWTEMPSM { TABLE 1W.my1Wdevs $28 | $09 | $6C | $CD | $04 | $00 | $00 | $82 | $28 | $F7 | $E6 | $BF | $04 | $00 | $00 | $3A | 1W.my1Wdevs 2 1W.SHOWTEMPSM \ show the temp for my 2 sensors } ]~ END