Dr. Imre Bartfai says:
[this is my] iButton reader routine, especially for [PIC type] processors having only 2-level stack. The dialect is the Parallax Assembler one. 4MHz clock is assumed. When routine returns with zero, the iBData structure is filled with the appropriate data. The routine seems to be pretty robust. Please note, that the copyright is retained for me, even if the use [of] the routine is free.
;---------------------------------------------------
; This code fragment handles the Dallas iButton(TM)
;---------------------------------------------------
;
;Line must be equated as a bit also: LPort.LBit
LFloat = IOCfg ; line bit as input
LOut = 1 << LBit ^ LFloat ; all bits input but LBit
iButOrg = $
Org iBData
Cnt DS 1 ; counter
TCnt DS 1 ; delay counter
SSave DS 1 ; STATUS save cell
FamCod DS 1 ; Family code \
SerNo DS 6 ; serial number } do not separate!
CRC DS 1 ; CRC value /
Cmd DS 1 ; command/data cell
Acc DS 1 ; virtual accumulator
CSave EQU SSave.0
iBEnd EQU $
Org iButOrg
iButton Call _Init ; reset prom
Or W,#0 ; check for zero
SZ ; skip if good
RetW 1 ; otherwise return
Mov W,#0Fh ; Read ROM command
_SndCmd Mov Cmd,W ; store command
Mov Cnt,#8 ; # of bits
:loop Rr Cmd ; LSB first
_SendC Mov SSave,STATUS ; send Cy
Mov !LPort,#LOut ; turn line to output
ClrB Line ; low pulse
Jmp $+1 ; tlow1
Jmp $+1
Jmp $+1 ; tlow1
Jmp $+1
JNB CSave,:Slot ; if zero, do not float
Mov !LPort,#LFloat
:Slot Mov Acc,#20 ; for 1 usec / instruction!
:loopi Nop ; loopi makes 80 usec
DJNZ Acc,:loopi ; sampling window
JB CSave,:cont ; continue if 1
Mov !LPort,#LFloat
:cont DJNZ Cnt,_SndCmd:loop
Call _GetByt ; receive byte
Mov FamCod,Cmd ; save family code
Mov Cnt,#6 ; length of serial number
Mov FSR,#SerNo ; serial number address
:loop1 Call _GetByt ; Receive SerNo
Mov INDF,Cmd ; received value
Inc FSR
DJNZ Cnt,:loop1
Call _GetByt ; receive CRC
; ----
; From here compare received CRC in Cmd with that to be calculated
; upon FamCod & SerNo
;
CRCChk
Mov FSR,#FamCod ; start:
Mov Cnt,#7 ; length
Clr CRC ; clear initial CRC
:loop2 Mov W,INDF ; fetch the byte
Mov SSave,W ; save bits to be shifted
Mov TCnt,#8 ; set shift=8bits
Mov W,SSave ;; restore result
:loop XOr W,CRC ; calculate CRC
Mov Acc,W ;; last CRC value
Rr Acc ; move it to carry
JNC :Zero ; skip if data=0
XOr CRC,#18h ; update the CRC value
:Zero Rr CRC ; position the new CRC
Rr SSave ; position the next bit
Mov W,SSave ; use the remaining bits
DJNZ TCnt,:loop
Inc FSR ; next pointer
DJNZ Cnt,:loop2
;------ now xchg CRC & Cmd
Mov W,CRC
XOr W,Cmd ; here exchange Cmd with W
XOr Cmd,W
XOr W,Cmd
Mov CRC,W
;------
Clr Wdt
CSE CRC,Cmd ; received==calculated
RetW 2
RetW 0 ; good
;---------------------------------------------------
_Init Mov !LPort,#LOut ; turn port to output
ClrB Line ; master reset
Mov TCnt,#125 ; about 500 usec
:loopi NOp ; loopi makes about 500 usec
DJNZ TCnt,:loopi
Mov !LPort,#LFloat
Mov Acc,#7 ; for 1 usec / instruction!
:looph NOp ; looph makes 30 usec for Tpdh
DJNZ Acc,:looph
Clr Cnt ; clear counter
:pres Mov Acc,#3 ; for 1 usec / instruction!
NOp
:loop NOp
DJNZ Acc,:loop ; 15 usec waiting
JB Line,:eoi ; if high again, end of init
Inc Cnt ; count length of presence pulse
CJAE Cnt,#20,:bad ; too long: shortcut
Clr Wdt
Jmp :pres
:eoi CJB Cnt,#1,:bad
RetW 0 ; iButton found
:bad RetW 1 ; iButton not found
;---------------------------------------------------
_GetByt Mov Cmd,#128 ; hibit as exit condition
:loop Mov !LPort,#LOut
ClrB Line ; low pulse
Jmp $+1 ; short pulse (8 usec)
Jmp $+1
Jmp $+1
Jmp $+1
Mov !LPort,#LFloat
Jmp $+1 ; tRDV = 15 æsec
Jmp $+1
Jmp $+1
Jmp $+1
Jmp $+1
Jmp $+1
Jmp $+1
MovB C,Line ; line state
Rr Cmd ; put received bit
Mov SSave,Status ; save status register
Mov Acc,#14 ; for 1 usec / instruction!
:loopi NOp
DJNZ Acc,:loopi ; 60 usec waiting
JNB CSave,:loop ; hibit not reached yet?
Ret
See