Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Help testing a "pwm steering" 4-channel Servo method

Mike - K8LH

Well-Known Member
Can I impose on anyone to help me test a new Servo driver method, please? After all these years coming up with novel new Servo driver methods, I still don't have a Servo that I can use for testing code (anyone wish to donate a Servo?).

Anyway, I'm adapting my old "pwm frame" servo driver method to use the "pwm steering" capability available on some newer PIC devices. I'm using a 16F1823 (16 MHz) for testing and I'm setting up TMR2 and ECCP for 250-us (1000 cycle) pwm period "frames" (prescaler 4, pr2 = 250-1). I'm using 20 "frames" per Servo channel (5-ms) so all four Servo channels are updated once per 20-ms Servo period. Servo pulse width resolution is 1-us but this could easily be changed to 250-ns resolution. ISR overhead is 26 cycles (2.6%) for 19 of every 20 Servo channel frames and 42 cycles (4.2%) for the 20th frame.

I see one potential caveat using this "pwm steering" method. Only one of four 'steering' bits is set in PSTR1CON at any given time to direct the PWM output. That leaves the other three 'steering' bits clear with the corresponding output pins reverting back to "digital I/O" mode. This means that the output latch bits for all four outputs must be maintained at a '0'. It also means that we're susceptible to RMW (read-modify-write) problems if we're not careful when we read or write other pins on that port.

I've attached the demo code. Currently, it just drives all four channels with a 1500-us pulse width (center position) but I would be happy to add switch and/or pot code if someone is interested in testing the code on actual servo hardware.

Thanks guys. Cheerful regards, Mike
 
Last edited:
I have lots of servos and would happily test your code but unfortunately don't have a 16F1823 (BTW, according to **broken link removed** it doesn't exist!. I don't see why RMW will be a problem due to the chip having latch registers. I'm assuming (I know, dangerous) that the PWM module will override a zero in the latch register otherwise this would cause a problem in all PWM code.

Edit, just placed an order for this chip and 12F1822 as both look like very interesting chips.

Mike.
 
Last edited:
I have lots of servos and would happily test your code but unfortunately don't have a 16F1823 (BTW, according to **broken link removed** it doesn't exist!.

Thank you for the offer, Mike. Here's the link for the 12F1822/16F1823 Datasheet (41413B).

I don't see why RMW will be a problem due to the chip having latch registers. I'm assuming (I know, dangerous) that the PWM module will override a zero in the latch register otherwise this would cause a problem in all PWM code.

Yes, but "pulse steering" to the P1B, P1C, and P1D pins is a little different. When a steering bit for a pin is '1' the PWM module controls the pin and when a steering bit for a pin is '0' the pin goes back to being a digital I/O. If you're using active high steering output polarity you need to make sure the output latch for the inactive outputs are '0's. At least that's how I understand it.

Edit, just placed an order for this chip and 12F1822 as both look like very interesting chips.

Cool! I would add the 12F1840 to your shopping list.

Also, have you done anything with the 10F322 (14-bit core) which has 512 words program memory, 64 SFR's and 64 bytes of RAM (everything in a single bank), and a 16 MHz INTOSC (and too many other features to mention here)?

Regards, Mike
 
Hi Mike,

I did find the datasheet but just found it incredulous that Microchip hasn't a mechanism to tag datasheets with the full part number. The other two you mention above can't be found by entering the full part number either, I had to enter 322 and 1840.

I have just ordered both parts and think I'm going to have lots of fun when they arrive.

Mike.
 
If you are serious about a donation, will some older Airtronics full size (e.g, 94102) work? How many? Do you want standard signal-power-ground leads or will the Airtronics older power-ground-signal sequence be OK. It is just a matter of swapping pins.

If interested, PM me your address, and I will drop in a Priority Mail box.

John

CORRECTED AIRTRONICS PIN-OUT. Added bolding.
 
Last edited:
That's great, Bean. It's nice to be able to use our library of routines for 14-bit core devices on a 10F' device for the first time, isn't it (grin)?

Since you mentioned 1-wire routines, and if you're interested... I updated my low level 1-wire routines last year to a single set of routines that handle both "read" and "write" functions as well as any clock from 4 to 32 MHz. The excerpt below is from a 10F322 program.

Regards, Mike

Code:
;******************************************************************
;  Ow.rwByte, send byte in WREG (send 0xFF to read a byte with    *
;  the read result in 'OwByte').                                  *
;                                 author: Mike McLaren, K8LH      *
        radix   dec             ;**********************************
Ow.rdByte
        movlw   0xFF            ; for read operation
Ow.rwByte
        movwf   OwByte          ; 
        movlw   8               ;
        movwf   BitCtr          ;
rwloop  rrf     OwByte,W        ; put bit 0 in Carry
        call    Ow.rwBit        ; send bit in Carry
        rrf     OwByte,F        ; 
        decfsz  BitCtr,F        ; done (all 8 bits)?
        goto    rwloop          ; no, loop, else
        movf    OwByte,W        ; return "OwByte" in WREG
        return                  ; exit
;
;
Code:
;******************************************************************
;  Ow.rwBit (4..32 MHz clock), send bit in Carry (use carry = 1   *
;  to read bit into carry).                                       *
;                                 author: Mike McLaren, K8LH      *
        radix   dec             ;**********************************
Ow.rdBit
        setc                    ; set C to read a bit
Ow.rwBit
        movlw   DataLo          ; start 60 us rw slot
        tris    PORTA           ; falling edge             0 us
        goto    $+1             ;
        goto    $+1             ;
        skpnc                   ; skip if bit = '0', else
        movlw   DataHi          ; mask to release buss
        tris    PORTA           ; low pulse is 1..8 us
        inDlyCy(14*usecs-8)     ; 14 us minus 8 cycles
        btfss   PORTA,owpin     ; sample owpin at exactly 14 us
        clrc                    ; clear Carry if '0'
        inDlyCy(47*usecs-3)     ; balance of 60 us slot
        movlw   DataHi          ; mask to release buss
        tris    PORTA           ; read/write slot ends at 61 us
        return                  ; for search rom routine
;
;
;
 
Last edited:
Nice trick...

Mike,
That is a nice trick to get able to read by writing $FF. I never thought of that.
I made a seperate libary for 16MHz, 8MHz, 4MHz etc. I guess is would be nice to have everything in one file though.

Yeah, the 2-leve stack on the 12-bit 10F devices really sucks. Plus no interrupts either.
How do they get all that in a SOT-23 package ???

P.S. Another nice thing on the 10F322 is the self-write ability.

Bean
 
Mike, I've been incorporating your method into my code.

I think the line:
Code:
rrf     OwByte,W        ; put bit 0 in Carry
Should be
Code:
rrf     OwByte,F        ; put bit 0 in Carry

As it is above won't it send bit 0 twice (and not send bit 7 at all) ? Or maybe I'm missing something...

Bean
 
Thanks Bean.

Sorry! Looks like I placed the loop label in the wrong place when I was typing in those code snippets. Please check out the corrected listing in post #7...

Would you like the inDlyCy() macro listing?

Regards, Mike
 
Last edited:
Okay, I can understand now.

Yeah, I'd love to see the code for the inDlyCy() macro. I'm not very familiar with macros. I don't really use them much.

Bean
 
The inDlyCy() macro uses one 8-bit variable and preserves the Carry status bit;

Code:
        radix   dec
clock   equ     16              ; 4, 8, 12, 16, 20,.. 32 MHz clock
usecs   equ     clock/4         ; cycles/microsecond multiplier

inDlyCy macro   delay           ; 0..1027 cycle range
        local   loop
     if delay >= 4
        movlw   delay/4         ;
loop    movwf   temp            ; a 4 cycle loop
        decfsz  temp,W          ;
        goto    loop            ;
     endif
     if delay%4 >= 2            ; delay%4 == 2 or delay%4 == 3
        goto    $+1             ; delay 2 additional cycles
     endif
     if delay&1                 ; delay%4 == 1 or delay%4 == 3
        nop                     ; delay 1 additional cycle
     endif
        endm
 
Last edited:
Here is my library so far. (I2C not done yet). Save it as "10f322_routines_16MHz.inc".
Comments welcome...

Code:
;-----------------------------------------------------------
; Subroutines for PIC10F322 
; Author: Terry Hitt
; Last change: Feb 2, 2012
;-----------------------------------------------------------
; NOTE: !!! Routines are timed for 16MHz clock !!!
;-----------------------------------------------------------
; Routines Provided:
;  Delay_uSec_W   - Delays "W" microseconds (min = 3)
;  Delay_mSec_W   - Delays "W" milliseconds
;  Delay_mSec     - Delays "delay" milliseconds. Destroys "delay"
;  Delay_10mSec_W - Delays "W" x 10 milliseconds
;  Delay_10mSec   - Delays "delay" x 10 milliseconds. Destroys "delay"
;
;  Serial_In      - Receives a byte, stores it in "serialData"
;                     You must define SERINPIN in your code before include.
;                     Default baud rate is 9600. Define InBaud to change.
;
;  Serial_Out_W   - Sends byte value in "W" to serial out pin as character
;                     You must define SEROUTPIN in your code before include.
;                     Default baud rate is 9600. Define OutBaud to change.
;  Serial_Out     - Sends byte value in "serialData" to serial out pin as character. Destroys "serialData"
;                     You must define SEROUTPIN in your code before include.
;                     Default baud rate is 9600. Define OutBaud to change.
;  Print_Byte_W   - Sends byte value in "W" to serial out pin as ASCII
;                     You must define SEROUTPIN in your code before include.
;                     Default baud rate is 9600. Define OutBaud to change.
;  Print_Byte     - Sends byte value in "printValue" to serial out pin as ASCII. Destroys "printValue"
;                     You must define SEROUTPIN in your code before include.
;                     Default baud rate is 9600. Define OutBaud to change.
;  Print_Word     - Sends word value in "printValue_LSB" "printValue_MSB" to the serial out pin.
;                     Destroys "printValue_LSB", "printValue_MSB"
;                     You must define SEROUTPIN in your code before include.
;                     Default baud rate is 9600. Define OutBaud to change.
;
;  OW_Reset       - Resets the 1-Wire bus
;                     You must define OWPIN in your code before include.
;  OW_Read        - Read a byte from the 1-wire bus. Value returned "W" and  "owData"
;                     You must define OWPIN in your code before include.
;  OW_Write_W     - Write value in "W" to the 1-wire bus
;                     You must define OWPIN in your code before include.
;  OW_Write       - Write the value in "owData" to the 1-wire bus
;                     You must define OWPIN in your code before include.
;
;  I2C_Start      - Creates I2C start condition
;  I2C_Stop       - Creates I2C stop condition
;  I2C_Read       - Reads a byte from the I2C bus. Value in W and i2cData
;  I2C_Write_W    - Writes "w" to the I2C bus
;  I2C_Write      - Writes value in "i2cData" to I2C bus
;-----------------------------------------------------------
; You may save code space by omitting code by defining these:
;  #DEFINE No_Delay_mSec
;  #DEFINE No_Delay_10mSec
;  #DEFINE No_Print_Byte
;  #DEFINE No_Print_Word
;  #DEFINE No_I2C_Read
;-----------------------------------------------------------
; Serial pin is high when at idle
; Default baud rate is 9600
; Set baud rate by defineing OutBaud and InBaud (Range 19200 to 4800)
; For Example:
;  #DEFINE OutBaud d'19200'
;-----------------------------------------------------------
; YOU MUST DEFINE THE PINS IN YOUR CODE
;  If a pin is not defined, the routines that use that pin are not included
;
;SEROUTPIN       EQU d'0'

;SERINPIN        EQU d'1'

;SCLPIN          EQU d'0'
;SDAPIN          EQU d'1'

;OWPIN           EQU d'2'

  radix dec

; Subroutine variables. Uses RAM from 0x74 to 0x7f
delayuSec      EQU 0x74
delay          EQU 0x75      ; mSec value for delay_mSec routine
delayTemp      EQU 0x76      ; Temp for delay_mSec routine
printValue     EQU 0x77      ; Value to be printed (destroyed by subroutine)
printValue_LSB EQU 0x78      ; LSB of 16-bit value to be printed
printValue_MSB EQU 0x79      ; MSB of 16-bit value to be printed
printChar      EQU 0x7a      ; Char (digit) to be printed
tempW_LSB      EQU 0x7b      ; LSB of temporary variable
tempW_MSB      EQU 0x7c      ; MSB of temporary variable
serialData     EQU 0x7d      ; Data for Serial_Out routine
bitCnt         EQU 0x7e      ; Bit count for Serial_Out routine
owData         EQU 0x7f      ; Data for 1-wire device
; --------------------------------------------------------------
; Delay microSeconds
; CALL Delay_uSec_W  ; value is in "W" min=3
; "W" is preserved; all flags are preserved
; --------------------------------------------------------------
Delay_uSec_W:
  movwf delayuSec            ; Save value
  decfsz delayuSec,f         ; Adjust for movlw,call,return overhead without affecting flags
  decfsz delayuSec,f
  nop
    nop
    decfsz delayuSec,f
    goto $-2
  return

#IFNDEF No_Delay_mSec
; --------------------------------------------------------------
; Delay milliSeconds
; CALL Delay_mSec_W  ; value is in "W" 0 = 256mSec
; CALL Delay_mSec    ; value to send is in "delay" 0 = 256mSec
; Destroys value in "delay"
; --------------------------------------------------------------
Delay_mSec_W:                ; Delay "W" milliseconds
  movwf delay                ; Set delay variable
Delay_mSec: 
    movlw 4                  ; (1)
    movwf delayTemp          ; (1)
      movlw 249              ; (1)
      call Delay_uSec_W
      decfsz delayTemp,F     ; (1)
      goto $-3               ; (2)
    decfsz delay,F           ; Wait another millisecond ?
    goto Delay_mSec          ;   Yes, go back again
  retlw 0x00                 ;   No, Done

#ENDIF ; #IFNDEF No_Delay_mSec

; --------------------------------------------------------------
; Delay 10 milliSeconds
; CALL Delay_10mSec_W  ; value is in "W" 0 = 2560mSec
; CALL Delay_10mSec    ; value to send is in "delay" 0 = 2560mSec
; Destroys value in "delay"
; --------------------------------------------------------------
#IFNDEF No_Delay_10mSec

Delay_10mSec_W:              ; Delay "W" milliseconds
  movwf delay                ; Set delay variable
Delay_10mSec:
    movlw 40                 ; (1)
    movwf delayTemp          ; (1)
      movlw 249              ; (1)
      call Delay_uSec_W
      decfsz delayTemp,F     ; (1)
      goto $-3               ; (2)
    decfsz delay,F           ; Wait another millisecond ?
    goto Delay_10mSec        ;   Yes, go back again
  retlw 0x00                 ;   No, Done

#ENDIF ; #IFNDEF No_Delay_10mSec

#IFDEF SEROUTPIN

#IFNDEF No_Print_Word
; -----------------------------------------------------
; Send word value as ASCII to Serial_Out routine
; Does not print leading zeros
; CALL Print_Word    ; value to be printed in "printValue"
; Destroys value in "printValue"
; Uses subroutine Serial_Out_W
; -----------------------------------------------------
Print_Word:
  clrf printChar             ; Set char to zero
  ; Do 10,000's digit
  movlw 39                   ; Set value to subtract to 10,000 for 10,000's digit
  movwf tempW_MSB
  movlw 16 
  movwf tempW_LSB
  call WDigit                ; Returns with "W" set to 48d
  andwf printChar,F          ; Zero the char, but keep the 32&16 bits.
  ; Do 1,000's digit
  movlw 3                    ; Set value to subtract to 1,000 for 1,000's digit
  movwf tempW_MSB
  movlw 232 
  movwf tempW_LSB
  call WDigit                ; Returns with "W" set to 48d
  andwf printChar,F          ; Zero the char, but keep the 32&16 bits
  ; Do 100's digit
  clrf tempW_MSB             ; Set value to subtract to 100 for 100's digit
  movlw 100
  movwf tempW_LSB
  call WDigit                ; Returns with "W" set to 48d
  andwf printChar,F          ; Zero the char, but keep the 32&16 bits
  ; Do 10's digit
  movlw 10                   ; Set value to subtract to 10 for 10's digit
  movwf tempW_LSB
  call WDigit                ; Returns with "W" set to 48d
  andwf printChar,F          ; Zero the char, but keep the 32&16 bits
  ; Do 1's digit
  addwf printValue_LSB,W     ; 1's digit always prints, just add 48 to value left
  goto Serial_Out_W          ; Print digit and return

WDigit:
    incf printChar,F         ; Increment print char
    movf tempW_LSB,W         ; Prepare to subtract LSB values
    subwf printValue_LSB,F   ; Subtract LSB values
    movf tempW_MSB,W         ; Prepare to subtract MSB values (no borrow on LSB)
    btfss STATUS,C           ; If LSB caused a underflow,
    incf tempW_MSB,W         ;   subtract 1 more from MSB
    subwf printValue_MSB,F   ; Subtract MSB of digit value from value to be printed
    btfsc STATUS,C           ; Did we underflow ?
    goto WDigit              ;  No, then repeat  
  addwf printValue_MSB,F     ;  Yes, then add MSB digit value back to value to be printed
  movf tempW_LSB,W           ;    and LSB as well
  addwf printValue_LSB,F
  decf printChar,W           ; Print char will be 1 higher than value, dec it
  btfsc STATUS,Z             ; If print char is zero, then don't print it
  retlw 48                  ; Digit was leading zero, don't print it, just return
  iorlw 48                  ; Make char ASCI by setting 32 & 16 bits
  movwf printChar            ; Save value so next digit knows this wasn't a leading zero
  goto Serial_Out_W          ; Falls into Serial_Out_W

#ENDIF ; #IFNDEF No_Print_Word

#IFNDEF No_Print_Byte
; -----------------------------------------------------
; Send byte value as ASCII to Serial_Out routine
; Does not print leading zeros
; CALL Print_W  ; value to be printed in "W"
; CALL Print    ; value to be printed in "printValue"
; Destroys value in "printValue"
; Uses subroutine Serial_Out_W
; -----------------------------------------------------
Print_Byte_W:
  movwf printValue           ; Save value to print
Print_Byte:
  clrf printChar             ; Set char to zero
  movlw 100                  ; Set value to subtract to 100 for 100's digit
  call BDigit                ; Returns with "W" set to 48d
  andwf printChar,F          ; Zero the char, but keep the 32&16 bits then we can tell that
                             ;  the 100's character WASN'T a zero if they are set
  movlw 10                   ; Set value to subtract to 10 for 10's digit
  call BDigit                ; Returns with "W" set to 48d
  addwf printValue,W         ; 1's digit always prints, just add 48 to value left
  goto Serial_Out_W          ; Print digit and return
BDigit:
    incf printChar,F         ; Increment print char
    subwf printValue,F       ; Subtract digit value from value to be printed
    btfsc STATUS,C           ; Did we underflow ?
    goto BDigit              ;  No, then repeat  
  addwf printValue,F         ;  Yes, then add digit value back to value to be printed
  decf printChar,W           ; Print char will be 1 higher than value, dec it
  btfsc STATUS,Z             ; If print char is zero, then don't print it
  retlw 48                   ; Digit was leading zero, don't print it, just return
  iorlw 48                   ; Make char ASCI by setting 32 & 16 bits
  movwf printChar            ; Save value so next digit knows this wasn't a leading zero
  ; Falls into Serial_Out_W

#ENDIF ; #IFNDEF No_Print_Byte

; -----------------------------------------------------
; Sends a character to "SEROUTPIN" as serial data
; If clock is 4MHz baud rate is 19200
; CALL Serial_Out_W  ; value to send is in "W"
; CALL Serial_Out    ; value to send is in "serialData"
; Destroys "serialData" & "bitCnt"
; -----------------------------------------------------

#IFNDEF OutBaud
#DEFINE OutBaud 9600
#ENDIF

Serial_Out_W:                ; Sends value in "W" on pin "SEROUTPIN"
  movwf serialData
Serial_Out:
  movlw 10                   ; Send 10 bits (including start bit and stop bit)
  movwf bitCnt
  bcf LATA,SEROUTPIN         ; Set pin to start bit state (same as data 0 bit state)
SerOut_NextBit:
    movlw (960000 / OutBaud) ; Delay for each bit
    call Delay_uSec_W
    btfsc serialData,0       ; set pin to data bit state
    bsf LATA,SEROUTPIN
    btfss serialData,0
    bcf LATA,SEROUTPIN
    bsf STATUS,C             ; rotate data bits and shift in stop bit value (1 bit)
    rrf serialData,F
    decfsz bitCnt,F          ; loop until all bits are done
  goto SerOut_NextBit
  retlw 48                   ; Support "Print" subroutine by setting "W" to 48d on return

#ENDIF ; IFDEF SEROUTPIN

; -----------------------------------------------------
; Receives a character from "SERPIN" as serial data
; If clock is 16MHz baud rate is 19200
; CALL Serial_In  ; value received is in "serialData"
; -----------------------------------------------------
#IFDEF SERINPIN

#IFNDEF InBaud
#DEFINE InBaud 9600
#ENDIF

Serial_In:                    
  clrf serialData
  btfss PORTA,SERINPIN       ; Wait for idle (HIGH)
  goto $-1
  btfsc PORTA,SERINPIN       ; Wait for start bit (LOW)
  goto $-1
  movlw (1440000 / InBaud)   ; Wait 1.5 bit times
  call Delay_uSec_W
  movlw 8                    ; Receive 8 bits
  movwf bitCnt
SerIn_NextBit:
    bcf STATUS,C             ; Make carry match pin state
    btfsc PORTA,SERINPIN
    bsf STATUS,C
    rrf serialData,F         ; Shift in received bit  
    movlw (960000 / InBaud)  ; Wait 1 bit time
    call Delay_uSec_W
    decfsz bitCnt,F
    goto SerIn_NextBit
  movf serialData,w
  return

#ENDIF ; IFDEF SERINPIN

; -----------------------------------------------------
; Reset 1-Wire device
; CALL OW_Reset  ; Resets 1-wire device
; -----------------------------------------------------
#IFDEF OWPIN

OW_Reset:
  bcf LATA,OWPIN             ; Make pin low
  bcf TRISA,OWPIN
  movlw 250                  ; Delay 500uSec
  call Delay_uSec_W
  movlw 250  
  call Delay_uSec_W
  bsf TRISA,OWPIN            ; Make pin input
  movlw 250                  ; Delay 500uSec
  call Delay_uSec_W
  movlw 250
  call Delay_uSec_W
  return

; -----------------------------------------------------
; Read a byte from 1-Wire device
; CALL OW_Read  ; Read byte from 1-wire device, value in owData
; -----------------------------------------------------
OW_Read:
  movlw 0xff
; -----------------------------------------------------
; Write a byte to 1-Wire device
; CALL OW_Write  ; Write byte to 1-wire device
; -----------------------------------------------------
OW_Write_W:
  movwf owData
  
OW_Write:
  ; 0 = Low for 60uSec; input for 10uSec
  ; 1 = Low for 5uSec; input for 15uSec, sample, input for 55uSec
  movlw 8
  movwf bitCnt

OW_Write_Next
    rrf owData,f
    bcf TRISA,OWPIN          ; Make pin low
    movlw 5
    call Delay_uSec_W
    btfsc STATUS,C           ; If C=1 make pin input
    bsf TRISA,OWPIN
    movlw 10
    call Delay_uSec_W
    btfss PORTA,OWPIN        ; If pin is low
    bcf STATUS,C             ;   then clear carry
    movlw 45
    call Delay_uSec_W
    bsf TRISA,OWPIN          ; Make pin input
    movlw 10
    call Delay_uSec_W
    decfsz bitCnt,f          ; Do all bits
    goto OW_Write_Next
    rrf owData,f             ; Get last bit from carry
    movf owData,w            ; Return with value read in "W"
  return

#ENDIF ; IFDEF OWPIN    


#IFDEF SCLPIN
#IFDEF SDAPIN

I2C_Start:
  return

I2C_Stop:
  return

#IFNDEF No_I2C_Read
I2C_Read:
  return
#ENDIF ; #IFNDEF No_I2C_Read

I2C_Write_W:
  movwf i2cData
I2C_Write:
  return

#ENDIF ; #IFDEF SDAPIN
#ENDIF ; #IFDEF SCLPIN

Bean
 
Last edited:
And here is a demo program using the library.
It reads the temperature from a DS18B20 (1-wire) and display it on a parallax serial LCD.

Code:
; Read DS18B20 on pin RA.2 (needs 4.7K pull-up)
; Display on Serial LCD (Parallax) on pin RA.0
 
  #include "p10f322.inc"

  radix dec

  __CONFIG _CP_OFF & _FOSC_INTOSC & _WDTE_OFF & _MCLRE_OFF & _LVP_OFF

; Pins
SEROUTPIN     equ 0
OWPIN         equ 2

; Variables (start at 0x40)
strPtr        equ 0x40
tempLSB       equ 0x41
tempMSB       equ 0x42

; Startup code
  ORG 0x0
  movlw 0x70                 ; Setup for 16MHz clock
  movwf OSCCON
  goto Start                 ; Jump to start of code

; Interrupt code
  ORG 0x4
  retfie

  #DEFINE No_Print_Word
  #DEFINE No_Delay_mSec

  #include "10f322_routines_16MHz.inc"

  radix dec

; --------------------------------------------------
; PrintStr is used to retrieve strings from program memory
; --------------------------------------------------
GetChar:
  movf strPtr,w
  addwf PCL,f
TableStart:
InitStr:
  DT d'22', d'17', "PIC10F322", d'13', "LCD Demo", 0
TempStr:
  DT d'128', "Temp=", 0

PrintStr:
  addlw (d'256'-TableStart)  ; Adjust for table offset
  movwf strPtr
PrintStrNext:
  call GetChar
  addlw 0                    ; Check if W is zero
  btfsc STATUS,Z
  return                     ; If W is zero, then return
  call Serial_Out_W
  incf strPtr,f
  goto PrintStrNext

; --------------------------------------------------

Start:

  ; Setup serial output pin
  bcf TRISA,SEROUTPIN
  bcf ANSELA,SEROUTPIN
  bsf LATA,SEROUTPIN

  ; Setup 1-wire pin
  bsf TRISA,OWPIN
  bcf ANSELA,OWPIN
  bcf LATA,OWPIN

  movlw d'10'                  ; Wait for LCD to startup
  call Delay_10mSec_W

  call ClearScreen  

  movlw InitStr
  call PrintStr

  movlw d'200'
  call Delay_10mSec_W
  
  call ClearScreen

Main:
  movlw TempStr
  call PrintStr

  ; Start temperature measurement
  call OW_Reset
  movlw 0xCC                 ; SkipROM command
  call OW_Write_W
  movlw 0x44                 ; Convert command
  call OW_Write_W
  movlw d'80'
  call Delay_10mSec          ; Conversion takes 750mSec


  ; Read temperature
  call OW_Reset
  movlw 0xCC                 ; Send "SkipROM" command
  call OW_Write_W
  movlw 0xBE                 ; Send "Read scratchpad" command
  call OW_Write_W
  call OW_Read
  movwf tempLSB
  call OW_Read
  movwf tempMSB  

  ; Convert value to degrees
  btfss tempMSB,7            ; Check if negative
  goto Positive
  movlw "-"                  ; print a "-" sign
  call Serial_Out_W 
  comf tempMSB,f             ; negate value
  comf tempLSB,f
  incf tempLSB,f
  btfsc STATUS,Z
  incf tempMSB,f
  
Positive:
  ; Rearrange bits so MSB hold whole value and LSB holds fractional value
  movf tempLSB,w             ; Get LSB bits
  andlw 0xf0                 ; only want high nibble
  iorwf tempMSB,f            ; put high nibble in MSB high nibble 
  swapf tempMSB,f            ; exchange MSB high/low nibble
  movlw 0x0f                 ; only want low nibble of LSB
  andwf tempLSB,f

  ; Print whole temperature value (MSB)
  movf tempMSB,w
  call Print_Byte_W

  ; Print decimal point
  movlw "."
  call Serial_Out_W

  ; Print 1 digit of fractional temperature value (LSB)
  ; digit=(LSB*10)/16
  bcf STATUS,C               ; Clear carry for shift
  rlf tempLSB,w              ; w=LSB * 2
  swapf tempLSB,f            ; LSB=LSB * 16
  bcf STATUS,C               ; Clear carry for shift
  rrf tempLSB,f              ; LSB=LSB * 8 (* 16 / 2)
  addwf tempLSB,f            ; LSB = LSB * 10 (* 8 + * 2)
  swapf tempLSB,w            ; W = LSB / 16
  andlw 0x0f                 ; Only use lower nibble of W
  call Print_Byte_W          ; Print digit

  ; Print "C" plus two spaces to overwrite old digits
  movlw "C"
  call Serial_Out_W
  movlw " "                
  call Serial_Out_W
  movlw " "
  call Serial_Out_W

  goto Main

ClearScreen:
  movlw d'12'                ; Clear Screen
  call Serial_Out_W
  movlw d'1'                 ; Must wait at least 5mSec after clear screen command
  call Delay_10mSec_W
  return

  END

Bean
 
Last edited:
This is not a criticism, but a question for my guidance. I have worried when using comf that its operand must be , <=127. How do you avoid that limit when you complement the LSB from the DS18B20?

John
 
Last edited:
Bean (Terry),

Just wanted to say that your library and demo' program look pretty nice.

When I simulated your serial TX routine the bit timing was 102.25 usecs instead of 104 usecs so you're drifting about 15 usecs off center by the time you sample the eighth data bit. That's not a big deal at 9600 baud, but, if you're interested, I'd be happy to share a "cycle accurate" delay method.

Cheerful regards, Mike
 
Mike,
Yeah, for now I just chose a value that worked. I didn't around to cycle counting it yet. So I guess I need 7 more cycles in the loop. At least that is easier than getting cycles out :)
I haven't timed the millisecond delay either. I'm sure that is not as accurate as it could be either.

It is a work in progress. Thanks for trying it out.

Bean
 
Bean (Terry),

Your code looks great and I hope it didn't seem like I was criticizing it.

If you're interested, Mike (Pommie) came up with a very nice DS18B20 Celsius-to-Fahrenheit algorithm awhile back (below). For those unfamiliar with the DS18B20, please note that the raw temperature data is a 12-bit sign-extended value with 1/16th degree resolution (Celsius * 16, or 16C in the formula).

Regards...

Code:
;==================================================================
;  Pommie's brilliant DS18B20 Celsius to Fahrenheit algorithm
;
;    10F = 18C + 320 = 16C + 2C + 320 = 16C + (16C+4)/8 + 320
;
;                  TempH:L Neg  Output  Display
;     =========================================
;     125.0000°C   h'07D0'  0    2570   257.0°F 
;     100.0000°C   h'0640'  0    2120   212.0°F
;      77.0000°C   h'04D0'  0    1706   170.6°F
;      25.0000°C   h'0190'  0    0770    77.0°F
;      22.0000°C   h'0160'  0    0716    71.6°F
;       0.5000°C   h'0008'  0    0329    32.9°F
;       0.0625°C   h'0001'  0    0321    32.1°F
;       0.0000°C   h'0000'  0    0320    32.0°F
;     - 0.0625°C   h'FFFF'  0    0319    31.9°F
;     - 0.1250°C   h'FFFE'  0    0318    31.8°F
;     - 5.0000°C   h'FFB0'  0    0230    23.0°F
;     -10.0000°C   h'FF60'  0    0140    14.0°F
;     -17.0625°C   h'FEEF'  0    0013     1.3°F
;     -17.6875°C   h'FEE5'  0    0002     0.2°F
;     -17.7500°C   h'FEE4'  0    0001     0.1°F
;     -17.8125°C   h'FEE3'  1    0001   - 0.1°F
;     -25.0000°C   h'FE70'  1    0130   -13.0°F
;     -55.0000°C   h'FC90'  1    0670   -67.0°F
;
        radix   dec
c_to_f
;
;  binh:binl = (TempH:TempL + 4) / 8
;
        rlf     TempH,W         ; preserve sign bit (b15)   
        rrf     TempH,W         ;
        movwf   binh            ;
        rrf     TempL,W         ;
        addlw   2               ; rounding
        movwf   binl            ;
        skpnc                   ;
        incf    binh,F          ;
        rlf     binh,W          ; preserve sign bit (b15)
        rrf     binh,F          ;
        rrf     binl,F          ;
        rlf     binh,W          ; preserve sign bit (b15)
        rrf     binh,F          ;
        rrf     binl,F          ;
;
;  binh:binl = binh:binl + TempH:TempL
;
        movf    TempL,W         ;
        addwf   binl,F          ;
        movf    TempH,W         ;
        skpnc                   ;
        incf    TempH,W         ;
        addwf   binh,F          ;
;
;  binh:binl = binh:binl + 320
;
        movlw	low(320)        ;
        addwf   binl,F          ;
        movlw   high(320)       ;
        skpnc                   ;
        addlw   1               ;
        addwf   binh,F          ;
;
;  get absolute value, set NegFlag
;
        bcf     NegFlag         ;
        btfss   binh,7          ; negative? yes, skip, else
        goto    bin2dec         ; branch
        bsf     NegFlag         ;
        comf    binl,F          ; twos complement result
        comf    binh,F          ;
        incf    binl,F          ;
        skpnz                   ;
        incf    binh,F          ;
;
;  bin-to-bcd (13 bits -> 0000..9999)
;
bin2dec
        clrf    thou            ;
        movlw   -10             ; wreg = -10
resHuns movwf   huns            ; set huns = -10
resTens movwf   tens            ; set tens = -10
div10z  addwf   binl,F          ; binl = binl - 10, borrow?
        skpc                    ; no, skip, else
        decf    binh,F          ; decrement hi byte
        btfsc   binh,7          ; negative? no, skip, else
        goto    b2dwrap         ; wrap up (underflow)
        incfsz  tens,F          ; tens overflow? yes, skip, else
        goto    div10z          ; loop
        incfsz  huns,F          ; huns overflow? yes, skip, else
        goto    resTens         ; reset 'tens' & loop
        incf    thou,F          ; bump 'thou'
        goto    resHuns         ; reset 'huns' & 'tens' & loop
b2dwrap subwf   huns,F          ; fix huns
        subwf   tens,F          ; fix tens
        subwf   binl,W          ; fix ones
        movwf   ones            ;
;
 
Last edited:

Latest threads

Back
Top