One Wire Temperature sensor (DS1820) to PC interface.

Status
Not open for further replies.
Very ingenious, may I suggest you do a multiply by 5 routine (shift left twice and add to original) and call it 3 times as this will keep it in two bytes. I have a Double Dabble routine that will convert it to BCD without the need to do the final multiply by 8. I may have a go at that tomorrow if I get time. The biggest hurdle is RAM.

I had a play with your code and got rid of the sending the ROM serial twice by checking the "conversion complete" flag in the PowerBus code. I had to clear the DataBit but the result was the same length code as the send cr was two location anyway.

BTW, I'm sure you need to subtract 4 (¼°) from your extended result.

Mike.
 
You mentioned that before and the '1820 Data Sheet also mentions subtracting 0.25 but I haven't quite figured out why.

Printing the rom ID once is a much better way to do it. Let me study that to see if there's a way to reclaim a byte (grin).

Your 3 x 5 x Celsius idea is genius but the product will still require 3 bytes, won't it? I'm starting out with a 12 bit number.

Double-dabble? Sounds familiar. Must check google (grin)...

Mike
 
Last edited:
I think we can still save one byte while printing the rom ID only once. Program size is now 165 bytes.

Send the <cr> and <lf> newline character pair during the "OwConvert" pass after the OwPowerBus function instead of at the end of the OwMatch code section and print the rom serial number in the OwWriteBuffer function only during the "OwReadScratch" pass.

Code:
;       bcf     BitMask,3       ; do "convert" (BitMask=00000001)
OwMatch
        [COLOR=Blue]OwReset[/COLOR]                 ; reset all 1-wire devices
        [COLOR=Blue]OwWrite[/COLOR](OwMatchRom)     ; send "match rom" command
        [COLOR=Blue]OwWriteBuffer[/COLOR]           ; send + print rom serial number
        btfsc   BitMask,3       ; conversion complete?
        goto    OwScratch       ; yes, branch, else
        [COLOR=Blue]OwWrite[/COLOR](OwConvert)      ; send "convert" command
        [COLOR=Blue]OwPowerBus[/COLOR]              ; power OW pin during conversion
        [COLOR=Blue]Send232[/COLOR](0x0D)           ; send <cr> char
        [COLOR=Blue]Send232[/COLOR](0x0A)           ; send <lf> char
        bsf     BitMask,3       ; indicate conversion complete
        goto    OwMatch         ; do "match rom" for OwScratch
OwScratch
        [COLOR=Blue]OwWrite[/COLOR](OwReadScratch)  ; send "read scratchpad" command
        [COLOR=Blue]Send232[/COLOR](' ')            ; send space char
;       movlw   9               ; instructions not needed
;       movwf   BitMask         ; BitMask = 9 already from above
RdLoop  [COLOR=Blue]OwReadByte[/COLOR]              ; read + print scratchpad byte
        decfsz  BitMask,F       ; all 9 bytes read + printed?
        goto    RdLoop          ; no, branch, else
        goto    OwSearchNext    ; search and process next device
Code:
OwWriteBuffer   macro
        local   loop
        bcf     FSR,3           ; reset FSR = &RomBuffer (0x10)
loop    movf    INDF,W          ;
        call    Ow.WriteByte    ; send rom serial number byte
        btfsc   BitMask,3       ; OwConvert pass? yes, skip, else
        call    PutHex          ; print rom ID byte as hex
        incf    FSR,F           ;
        btfss   FSR,3           ;
        goto    loop            ;
        endm
 
Last edited:
Forget the Double Dabble stuff, I realized that the fractional part can be done by multiplying by 10 and taking the high nibble.

So, calling this 4 times prints the fraction part,
Code:
FracDigit
	movlw	0x0f
	andwf	TempFrac,F
	clrc
	rlf	TempFrac,W	;*2
	movwf	temp
	rlf	temp,W		;*4
	addwf	TempFrac,F	;*5
	rlf	TempFrac,F	;*10
	swapf	TempFrac,W
	goto	PutNybble

Mike.
 
Last edited:
That routine works extremely well. Bravo!

So, we just need to do an ABS() function, process and print the 3 digit whole number (-55..125), suppress leading zeros (?), print a decimal point, then process and print and the 4 digit fraction. Correct?

Can we do all that with 91 free bytes of memory and our limited RAM resources?
 
I have not seen this sort of byte hording since collage days. Nice work.
 
I have not seen this sort of byte hording since collage days. Nice work.

It's Pommie's fault (grin). He posted this fascinating and clever example of what you can do with only 256 words of program memory and 16 bytes of RAM...
 
Last edited:
Hey, I love to watch this. I may build a few when you get it sorted out.

I have a friend who would like to do some control work but is too tied down with work to get back in to uC's. I would like to drop one of these on his desk and see what happens.

3v0
 
Last edited:
I've quite enjoyed this little exercise in optimization. When I first wrote this I was just pleased to get the code in 256 words and then Mike came along and demonstrated just how lax I had been with optimization. Along the way I've learnt some very interesting techniques, although they are probably more academic than useful.

Anyway, enough of my ramblings, here's a first go at the full decimal part (7 digits ) with absolute and leading zero suppression,
Code:
OwMatch
		OwReset			; reset all 1-wire devices
		OwWrite(OwMatchRom) 	; send "match rom" command
		OwWriteBuffer 		; send + print rom serial number
		btfsc	BitMask,2	; conversion complete?
		goto	OwScratch	; yes, branch, else
		OwWrite(OwConvert) 	; send "convert" command
		OwPowerBus 		; power OW pin during conversion
		bcf	DataBit
		bsf	BitMask,2	; indicate conversion complete
		goto	OwMatch		; do "match rom" for OwScratch
OwScratch
		Send232(' ')		; send <space> char
		OwWrite(OwReadScratch) 	; send "read scratchpad" command
		OwReadByte 
		movfw	OwByte
		movwf	TempLo
		OwReadByte 
		movfw	OwByte
		movwf	TempHi
ReadLoop
		OwReadByte 
		decfsz	BitMask,F
		goto	ReadLoop

		movf	RomBuffer,W	; get Family ID byte
		xorlw	0x28		; is it 12 bit DS18B20?
		bz	OwTemperature	; yes, branch, else
		rlf	TempLo,F	;                                 |
		rlf	TempHi,F	;
		rlf	TempLo,F	;
		rlf	TempHi,F	;
		rlf	TempLo,F	;
		rlf	TempHi,F	;
		movlw	0xF0		;
		andwf	TempLo,F	;
		movlw	16		;
		movwf	temp		;
		movf	OwByte,W	; Count_Remain
		subwf	temp,W		;
		iorwf	TempLo,F	; now DS18B20 12 bit format
		movlw	4
		subwf	TempLo,F
		skpc
		decf	TempHi,F
OwTemperature
		OwReadByte 
		OwReadByte 

		Send232	" "
		btfss	TempHi,7	;is it negative
		goto	NoNegate
		Send232	'-'		;Send minus sign
		comf	TempLo,F	;and negate temperature
		comf	TempHi,F
		incfsz	TempLo,F
		goto	NoNegate
		incf	TempHi,F
NoNegate
		movfw	TempLo		;keep fraction part
		movwf	TempFrac	;and move it to seperate var
		movlw	0x0f
		andwf	TempHi,F	;move nibbles around so
		swapf	TempLo,F	;that the temperature is 
		andwf	TempLo,F	;contained in TempLo
		swapf	TempHi,W
		iorwf	TempLo,F

		clrf	TempHi		;will contain tens digit
		movlw	0x100-.100
		addwf	TempLo,W
		bnc	NoHundreds
		movwf	TempLo
		Send232	'1'
		bsf	BitMask,0	;no longer suppressing zeros
NoHundreds
		movlw	.10
		subwf	TempLo,F
		incf	TempHi,F
		bc	NoHundreds
		decfsz	TempHi,F	;is it zero
		goto	PrintIt		;no so print it
		btfss	BitMask,0	;are we suppressing zeros
		goto	SkipZero	;yes so skip it
PrintIt		movlw	'0'
		addwf	TempHi,W	;print 10s digit
		call	Put232
SkipZero	movlw	'0'+10
		addwf	TempLo,W
		call	Put232		;always print units digit
		Send232	'.'
		call	FracDigit
		call	FracDigit
		call	FracDigit
		call	FracDigit

		Send232(0x0D) 		; send <cr> char
		Send232(0x0A) 		; send <lf> char
		goto	OwSearchNext	; search and process next device

FracDigit
		movlw	0x0f
		andwf	TempFrac,F
		clrc
		rlf	TempFrac,W	;*2
		movwf	temp
		rlf	temp,W		;*4
		addwf	TempFrac,F	;*5
		rlf	TempFrac,F	;*10
		swapf	TempFrac,W
		goto	PutNybble

It now occupies 254 words so we have a full word free.

Mike.
P.S. this is what I see in Hyperterminal,

You can see where I hit them (3 sensors) with a freezing spray.
 
Last edited:
Excellent work! Incredible work!

I found some 10F206 samples in PDIP-8 package. Can't wait to try this but would like to print leading spaces to align the decimal points.

Mike
 
Hey Mike,

I wonder if this revised sequence would work? If so, I think there's a one word savings over the way we're doing it now and it eliminates our double "match rom" operation.

Code:
OwMatch
        [COLOR=Blue]OwReset[/COLOR]                 ; reset all 1-wire devices
        [COLOR=Blue]OwWrite[/COLOR](OwSkipRom)      ; send "skip rom" command
        [COLOR=Blue]OwWrite[/COLOR](OwConvert)      ; send "convert" command
        [COLOR=Blue]OwPowerBus[/COLOR]              ; power OW pin during conversion
        [COLOR=Blue]Send232[/COLOR](0x0D)           ; send <cr>
        [COLOR=Blue]Send232[/COLOR](0x0A)           ; send <lf>
        [COLOR=Blue]OwReset[/COLOR]                 ; reset all 1-wire devices
        [COLOR=Blue]OwWrite[/COLOR](OwMatchRom)     ; send "match rom" command
        [COLOR=Blue]OwWriteBuffer[/COLOR]           ; send and print rom serial number
        [COLOR=Blue]OwWrite[/COLOR](OwReadScratch)  ; send "read scratchpad" command
 
Hi Mike,

My only worry about using the SkipRom command is that all devices would draw power at the same time and possibly overload the (very dodgy) RS232 power scheme. But it may work. I got rid of the double ROM id write with a bit test in OwWriteBuffer. As for the leading spaces, you're just never satisfied are you. I might play with that tomorrow.

BTW, if you use a 10F206 then you need to turn off the comparator. It was in the original code but got optimized.

Mike.
 
The serial port on my Dell Latitude D800 notebook cannot supply more than a couple hundred microamps so I dismissed the idea of powering this device from the serial port early on and completely forgot that's what you're doing. Sorry. We definitely need to keep your original double "matchrom" method to keep current manageable.

As for the leading spaces, you're just never satisfied are you?
I just though it would be nice if the Host application on the PC could always find the nine character temperature string in the same place; str[x+0] == " " or "-", str[x+1] == " " or "1" (hundreds), str[x+2] == " " or "n" (tens), str[x+3] == "n" (ones), str[x+4] == ".", etc.

BTW, if you use a 10F206 then you need to turn off the comparator. It was in the original code but got optimized.
Your original program occupies a place of honor in my work folder and so I have easy access to it and I see the reference for turning off the comparator. Thank you.

I apologize for my style changes to your excellent program. Those movfw instructions drive me crazy and I prefer using the STATUS "branch" and "skip" pseudo opcodes to the more traditional bit test instructions because they seem much more intuitive (to me). I'm sure you probably cringe at some of my preferences (grin). I suspect my preferences have changed over time as I've become more familiar with some of the C implementations.

Later, Mike
 
Hi Mike,

My comment "but got optimized" was in no way a criticism. I hold your optimization skills in high esteem and I like your programming style. So, no need to apologize, please just carry on as was.

As for the leading zeros, in VB you would simply do Temperature=Val(mid(record,36)) to get the full floating point number, I hadn't considered how you would do it in C. Isn't there a atof (ascii to float) type instruction?

Anyway, you find me a few more words and I'll sort out the spaces problem.

Mike.
 
Just kickin' around some ideas here so bear with me. I was thinking that we should only have up to 12 "tens" for temperature readings up to 125° so why not just count up and print the number of "tens" as two digits?

I just typed this code into the post window so I haven't tested it but I think it's the same size as the code it would replace and I believe it will give me my decimal point alignment and my leading zero suppression. I wonder if there's a better way to do it that produces even smaller tighter code?

I'll test later and get back.

Mike

Code:
[COLOR=DarkOrchid] [COLOR=Blue]       clrf    TempHi          ; will contain tens digit
        bsf     BitMask,5       ; BitMask = 0x20 = " "
Bin2Dec                         ; print values "  0".."125"
        movlw   10              ;
        subwf   TempLo,F        ;
        incf    TempHi,F        ;
        bc      Bin2Dec         ;
        addwf   TempLo,F        ; fix "ones", 0x00..0x09
        decf    TempHi,F        ; fix "tens", 0x00..0x0C
        movlw   6               ;
        addwf   TempHi,W        ; packed BCD, 0x00..0x09?
        skpndc                  ; yes, skip, no digit carry, else
        movwf   TempHi          ; packed BCD, 0x10..0x12
        swapf   TempHi,W        ; get hundreds digit
        call    PutDigit        ; prints " " or "1"
        movf    TempHi,W        ; get tens digit
        call    PutDigit        ; prints " " or "0".."9"
        movf    TempLo,W        ; get ones digit
        call    PutOnes         ; always print ones, "0".."9"[/COLOR][/COLOR]
        Send232 '.'             ;
        call    PutFraction     ;
        call    PutFraction     ;
        call    PutFraction     ;
        call    PutFraction     ;
        [COLOR=Black]Send232[/COLOR](0x0D)           ; send <cr> char
        [COLOR=Black]Send232[/COLOR](0x0A)           ; send <lf> char
        goto    OwSearchNext    ; search and process next device

[COLOR=Blue]PutDigit
        andlw   0x0F            ;
        skpz                    ; zero? yes, skip, else
PutOnes bsf     BitMask,4       ; BitMask = 0x30 = "0"
        iorwf   BitMask,W       ; " " or "0".."9"
        goto    Put232          ; print digit[/COLOR]
;
 
Last edited:
That works well but there is a slight problem with negative numbers. Here is how the alignment works at the moment,
Code:
123.1234 < no space
 12.1234
  1.1234
-  1.1234
This will stop the use of Val in basic. I can correct it but it ends up 5 words too long.

I'm assuming the preferred alignment would be,
Code:
 123.1234 < leading space
  12.1234
   1.1234
  -1.1234


BTW, making BitMask 0x20 or 0x30 is brilliant.

Mike.
 
Thank you for the nice comment. I suspect there are better ways to do it though.

There's a bit of code I didn't show that goes in front of the section I posted that prints a " " or "-" sign so right now it's doing this;

Code:
 123.1234
  12.1234
   0.1234
-  0.1234
-  1.1234
- 12.1234
I think I would prefer to print it one of the following ways;
Code:
 123.1234°C      123.1234°C
  12.1234°C       12.1234°C
   1.1234°C        1.1234°C
  -1.1234°C      - 1.1234°C
 -12.1234°C      -12.1234°C
With a couple other structure changes and the new code sections (abs, putfraction, etc) the program is 249 bytes long.

Mike
 
Last edited:
We can save another couple words in your genius PutFraction subroutine with a subtle change in your x10 code and by letting the routine fall into the PutDigit subroutine;

Code:
PutFraction
        movlw   0x0F            ;
        andwf   TempLo,F        ; toss integer, leave remainder
[COLOR=Blue]        movf    TempLo,W        ;
        addwf   TempLo,F        ; *2, C=0
        rlf     TempLo,F        ; *4, C=0
        addwf   TempLo,F        ; *5, C=0
        rlf     TempLo,F        ; *10
[/COLOR]        swapf   TempLo,W        ; integer portion in lo nybble
PutDigit
        andlw   0x0F            ;
        skpz                    ; zero? yes, skip, else
PutOnes bsf     BitMask,4       ; BitMask = 0x30 = "0"
        iorwf   BitMask,W       ; " " or "0".."9"
        goto    Put232          ; print digit
I've included the code that subtracts 1/4° from the sign extended two's complement temperature value for DS1820/DS18S20 devices but I'm still not convinced it's necessary. This effectively adds 1/4° to a negative temperature or subtracts 1/4° from a positive temperature. Why would this be necessary? Is it because the 1/2° bit in the original 9-bit temperature value is "rounded up" or "rounded down" based on the value in the Count_Remain byte? I'm not using the 1/2° bit, just the 4 bit Count_Remain value, so do you think it's still necessary to subtract 1/4°?

The program is 255 bytes long in its current form (256 bytes with the clrf CMCON0 instruction for my 10F206) and should print out something like this;

Code:
10SSSSSSSSSSSSCC TTTTTTTTTTTTTTTTCC  125.0000°C
28SSSSSSSSSSSSCC TTTTTTTTTTTTTTTTCC   23.1234°C
28SSSSSSSSSSSSCC TTTTTTTTTTTTTTTTCC    0.1234°C
10SSSSSSSSSSSSCC TTTTTTTTTTTTTTTTCC -  4.1234°C
10SSSSSSSSSSSSCC TTTTTTTTTTTTTTTTCC - 24.1234°C
 
Last edited:

The code always subtracts 1/4° because it is done before the absolute function. However, I understand the confusion but assume that the data sheet should be taken literally. The only code I could find on the internet is DS1820.asm which does do the subtraction. (you should study that code - it has a full stack based 16 bit maths system on a 12F509 ).

Anyway, I implemented your optimizations and I had another play with swapping things around. After much head scratching I finally managed to implement the space before minus solution. I removed the earlier print space and minus sign and made Put232 return 0x0a. The final 2 words I found were the initial write to the tris register.

Code:
FracDigit
		movlw	0x0f
		andwf	TempFrac,F
		movf	TempFrac,W
		addwf	TempFrac,F	;*2	C=0
		rlf	TempFrac,F	;*4
		addwf	TempFrac,F	;*5
		rlf	TempFrac,F	;*10
		swapf	TempFrac,W

PutDigit	bsf	BitMask,5	; BitMask = 0x20 = " "
		andlw	0x0F		;
		skpnz			; zero? yes, skip, else
		goto	PutIt		
PutOnes		btfsc	BitMask,4	;done leading zeros
		goto	PutIt		;yes so just print it		
		movwf	FSR		;skip zeros over - keep copy of W
		movlw	'-'		;print minus maybe
		btfss	STATUS,7	;was it minus
		movlw	' '		;No, so print space instead
		call	Put232
		movfw	FSR		
		andlw	0x0F		;and out top bits from FSR
		bsf	BitMask,4	; BitMask = 0x30 = "0"
PutIt		iorwf	BitMask,W	; " " or "0".."9"
		goto	Put232		; print digit

The code ended up 0xfd long and so we have 0xfe free again.

If you can find 1 more word I can reinstate the space between the ROMid and the Scratchpad.

<edit> I mistakenly thought we could corrupt the RomBuffer after it had been used but have just noticed it is reading chips in a random order. I changed it to use FSR and so now there are zero words free</edit>

<edit2> I just realised that we can corrupt the last byte of the ROMid (the CRC) as by the time the algorithm has reached that point only one device can be answering. </edit2>

Mike.
This is the output in Hyperterminal. Again with freezer spray.
 
Last edited:
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…