( 1-Wire.fth ) { first part is base code from Peter Jakacki with minor adaptions second part is romsearch extensions by MJB Markus Baer } 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 1W.RESET ( -- ack ) DQ@ OUTCLR 1 ms DQ@ INPUTS #60 us DQ@ IN 0= #400 us ; pub 1W.BITWR ( 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 1W.BITRD ( 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 1W.BITWR 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 1W.BITRD #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 1W.RESET DROP SKIPROM ; \ reset, skip ROM for single sensor only and prepare for next command pub RDROMH 1W.RESET 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 and 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 ; : SHOWTEMPS BEGIN SHOWTEMP 2 seconds KEY 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 *********************** } { To check the ROM of a single device: 1WRESET DROP READROM } ]~ END \ here starts the code by MJB Markus Baer ( 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 ) IF SET ELSE CLR THEN \ 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 ; } BYTE 1W.devcnt LONG 1W.devaddr pri 1W.NEWSEARCH ( -- \ clear variables used in search ) 1W.lastdisc C~ 1W.doneflag C~ 1 1W.rombit C! \ 1..64 1W.romid #8 ERASE 1W.devcnt C~ \ set device count to 0 ; pri 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? ) 1 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 ) 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 ; \ calc the CRC given start and length of string/bytes. If string includes the CRC the result is 0 for OK pub CRC8 ( str cnt -- crc ) 0 ROT ROT ADO I C@ SWAP 8 FOR 2DUP XOR 1 AND ROT 2/ ROT 2/ ROT IF $8C XOR THEN NEXT NIP LOOP ; \ Demonstrates how to use SEARCHROM to find all attached devices and store them in table 1W.1Wdevs pri 1W.PRINTromid ( addr -- ) \ print the ROM-ID at address 8 ADO I C@ .BYTE SPACE LOOP ; pri 1W.PRINTromidT ( addr -- ) \ print the ROM-ID at address in Tachyon table format 8 ADO ." $" I C@ .BYTE ." | " LOOP \ ADO ( from cnt -- ) ; TABLE 1W.1Wdevs #40 #8 * ALLOT \ prepare for 40 devices here, change as appropriate pub 1W.SCAN ( -- ) \ scan for any devices on 1-wire bus and store them in table 1W.1Wdevs CR 1 1W.NEWSEARCH 1W.1Wdevs 1W.devaddr ! \ begin at start of table CR BEGIN DUP .DEC ":" EMIT 2 SPACES 1+ 1W.SEARCHROM 1W.romid 1W.PRINTromid 5 SPACES 1W.romid 1W.PRINTromidT CR \ print it 1W.romid 8 CRC8 IF ." CRC mismatch above" CR THEN \ ' 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 ! \ increment the address to store by 8 bytes 0= UNTIL CR ; { test it on 2 devices \ $3F00 INPUTS \ disable pins near my 1Wire for protection LAP 1W.SCAN LAP .LAP 28 09 6C CD 04 00 00 82 28 F7 E6 BF 04 00 00 3A Time: 56.01ms ok for discovering 2 DS18B20 sensors 1W.1Wdevs $20 DUMP 1W.1Wdevs $20 ADO I C@ .BYTE SPACE LOOP 1W.1Wdevs $16 ERASE } { 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 ; \ print the discovered 1Wire-IDs 1W..1Wdevs \ print the table of 1W.1Wdevs discovered by 1w.scan in Table format for inclusion in Tachyon code 1W..1WdevsT } \ 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 ; pub 1W.SKIP ( -- ) 1W.RESET DROP SKIPROM ; \ reset and send Skip ROM command \ IFNDEF SMALL \ DS18B20 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 -- ) CR BEGIN 1 OVER ADO \ from 1 to n OVER I 1W.SHOWTEMPM SPACE LOOP CR 2 seconds \ KEY? AND \ AND required, since KEY? ( -- key flag ) KEY UNTIL 2DROP ; } \ end IFNDEF SMALL { the generated table ready to paste into Tachyon again 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 temperature for my 2 sensors \ next line works directly after romsearch from found IDs \ 1W.1Wdevs 2 1W.SHOWTEMPSM 1W.1Wdevs 2 1W.SHOWTEMPSM +023.00'C +023.18'C +023.00'C +023.18'C +023.31'C +023.18'C +024.00'C +023.18'C +024.50'C +023.18'C +025.00'C +023.18'C +025.37'C +023.18'C +025.81'C +023.18'C +026.12'C +023.18'C +026.18'C +023.25'C +026.25'C +023.75'C +026.06'C +024.18'C +025.87'C +024.62'C +025.75'C +025.06'C +025.62'C +025.31'C ok 1W.1Wdevs 1 1W.SHOWTEMPSM : bb 1W.SCAN 1W.1Wdevs SWAP 1W.SHOWTEMPSM ; : bbb 1 bb ; \ Temp measurement GUI pub TEMPGUI ( nSensors -- ) VCLS 1+ BEGIN HOME DUP 1 DO ERLINE I .DEC SPACE 1W.1Wdevs I 1W.SHOWTEMPM CR LOOP \ KEY? AND \ AND required, since KEY? ( -- key flag ) KEY UNTIL 2DROP ; #22 TEMPGUI pub TEMPGUI2 ( nSensors -- ) VCLS 1+ BEGIN HOME --- for all sensors start the temperature conversion, this requires power to the sensors, parasitic will not work. DUP 1 DO 1W.1Wdevs I 1- 8 * + \ calculate start of ROM code to address 1W.MATCH CONVERTT \ start temp measurement LOOP #750 ms --- now get the measured temperature value from each sensor DUP 1 DO ERLINE I .DEC SPACE 1W.1Wdevs I 1- 8 * + 1W.MATCH TEMP@ .TEMP CR LOOP \ KEY? AND \ AND required, since KEY? ( -- key flag ) KEY UNTIL 2DROP ; \ #22 TEMPGUI2 BEGIN "-" ECHO KEY .S UNTIL } {HELP new 1-Wire scan on pins with pull up resistor and report } pub 1W.DISCOVER ." 1-Wire pin scan" CR 0 #30 ADO I PINLOAD? "U" = IF I DQ! 1W.SCAN THEN ; 1W.DISCOVER ]~ END