MicroElektronics Designs

Home
About Us
Services
Clients
Free stuff
Related Links
Contact Us

PIC Micro Code Snippets

Adjustable Delay
;**********************************************************************
; Adjustable delay from 11-256 cycles (for 14 bit core)
; W = count, cycle count excludes loading of W
; by Regulus Berdin
delay:
        addlw -(.10+1)  ;2+1 remove 1 count more for carry correction
        addlw -4        ; 1
        skpnc           ; 1/2
         goto $-2       ; 2
        sublw 0xFF      ; 1
        addwf PCL,f     ; 2
delay7  nop             ; delay adjuster
delay6  nop             ;
delay5  nop             ;
delay4  return          ;2

Adjustable delay (13-256) with single cycle resolution for 12 bit core
;********************************************************************** 
; Adjustable delay from 13-256 cycles (for 12 bit core) 
; Count = delay count, cycle count excludes loading of count 
; by Regulus Berdin 

delay: 
        movlw .13        ;delay = delay - 13 
        subwf count      ; 
        movlw 4          ; 
        subwf count      ;delay = delay - 4 
        skpnc            ; 
         goto $-2        ; 
        comf count,w     ;burn extra cycles 
        addwf PCL,f      ; 
        nop              ; 
        nop              ; 
        nop              ; 
        retlw 0          ;

Fast bitbanged serial tx
this is 2x faster (500kbps @ 4MHz) :

; serial tx
; assumes PORTB0 initially on high state

        rrf     data,w
        xorwf   data,f

        movlw   1
        xorwf   PORTB,f         ;start bit

        skpnc
         xorwf  PORTB,f         ;b0

        btfsc   data,0
         xorwf  PORTB,f         ;b1

        btfsc   data,1
         xorwf  PORTB,f         ;b2

        btfsc   data,2
         xorwf  PORTB,f         ;b3

        .
        .
        .

        nop
        bsf     PORTB,0         ;

Very fast 8x8 Multiply result in 8bits no carry (31 cycles only)
> Suppose you have:
> 
>  c = a * b
> 
> and all three are bytes.
> 
> Minimize execution time (on a mid-range core).
> 
> Trashing a (or b) I have a routine that is 31 instructions/cycles long.
> 
> Any takers?

I'll try:

 clrf c

 movf b,w      ;b = w
 btfsc a,0
  addwf c

 addwf b,w     ;w = b*2
 btfsc a,1  
  addwf c

 movwf b       ;b = b*2
 addwf b,w     ;w = b*4
 btfsc a,2
  addwf c

 movwf b       ;b = b*4
 addwf b,w     ;w = b*8
 btfsc a,3
  addwf c

 .
 .
 .

 movwf b       ;b = b*64
 addwf b,w     ;w = b*128
 btfsc a,7
  addwf c

32 bit binary to BCD converter / unpacking packed BCD
> i have a VERY urgent problem. I have a 32 bit binary
> to convert in bcd digits (5 registers). Do u have a routine ready ? I
> tried a routine
> from the pic Archive but i have now packed (?) bcd.

First of all 32 bits binary will not fit in a 5 register unpacked bcd.
You need 10 registers to store the unpacked result.

To convert 32bit binary to packed bcd use this:
;**********************************************************************
; bin2bcd - binary to bcd conversion, using bcd divide by 2 (32 bits)
; input -> num1:num2:num3:num4 (msb:lsb)
; output -> bcd1,bcd2,bcd3:bcd4:bcd5 (msb:lsb)
; 
; uses: tmp
;

bin2bcd:
        movlw .32          ;process 32 bits
        movwf tmp 
        clrf bcd1          ;zero bcd digits
        clrf bcd2 
        clrf bcd3 
        clrf bcd4 
        clrf bcd5 
        goto b2b0 

badj    movlw 0x33
        addwf bcd1
        addwf bcd2
        addwf bcd3
        addwf bcd4
        addwf bcd5

        movlw 0x03
        btfss bcd1,3
         subwf bcd1
        btfss bcd2,3
         subwf bcd2
        btfss bcd3,3
         subwf bcd3
        btfss bcd4,3
         subwf bcd4
        btfss bcd5,3
         subwf bcd5

        movlw 0x30
        btfss bcd1,7
         subwf bcd1
        btfss bcd2,7
         subwf bcd2
        btfss bcd3,7
         subwf bcd3
        btfss bcd4,7
         subwf bcd4
        btfss bcd5,7
         subwf bcd5

b2b0    rlf num2
        rlf num1
        rlf bcd5
        rlf bcd4
        rlf bcd3
        rlf bcd2
        rlf bcd1
        decfsz tmp
         goto badj
        return

> Instead of 42 94 96 72 95 in my registers i have
> now 99 06 58 04 80 in my regissters. Is this packed bcd, and if so,
> how can i unpack it to regular bcd ?

That is not packed. For example: the is number 1234567890 (hex
0x499602D2), packed BCD = 0x12 0x34 0x56 0x78 0x90 while unpacked is bcd
= 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x00.

Optimized and configurable up to any number of digits BCD increment routine
;Increment routine for packed BCD 
; 
;6 digit bcd in bcd1:bcd2:bcd3:...(lsb:msb) 
; 
; 
        cblock  0x0c 
                tmp 
                bcd1:3 
        endc 

inc_bcd: 
        movlw   bcd1 
        movwf   FSR 
        movlw   3               ;adjust for number of digits 
        movwf   tmp 
iloop   movlw   0x67 
        addwf   INDF 
        movlw   0x60 
        skpdc 
         movlw  0x66 
        skpc 
         subwf  INDF 
        skpz 
         goto   iquit 
        incf    FSR 
        decfsz  tmp 
         goto   iloop 
iquit   retlw   0 

The routines can accomodate to any number of BCD digits, just change the movlw 3.

Optimized and configurable up to any number of digits BCD decrement routine
;Decrement routine for packed BCD 
; 
;6 digit bcd in bcd1:bcd2:bcd3:...(lsb:msb) 
; 
; 
        cblock  0x0c 
                tmp 
                bcd1:3 
        endc 

dec_bcd: 
        movlw   bcd1 
        movwf   FSR 
        movlw   3               ;adjust for number of digits 
        movwf   tmp 
dloop   movlw   1 
        subwf   INDF 
        addwf   INDF,w 
        movlw   0x6 
        skpnc 
         movlw  0x66 
        skpndc 
         subwf  INDF 
        xorlw   0x66 
        skpz 
         goto   dquit 
        incf    FSR 
        decfsz  tmp 
         goto   dloop 
dquit   retlw   0

The routines can accomodate to any number of BCD digits, just change the movlw 3.

DTMF tone generation using 2 output pins
;********************************************************************** 
;DTMF GENERATION 
; by Regulus Berdin 
; W holds the key to send 
; 10-A  11-B  12-C  13-D  14-*  15-# 
; uses 18 cycles per count optimized for 4MHz crystal 
; 
;DTMF                           ERROR 
;TONE   DELAY   TONE GENERATED  (Hx)    % 
;697    40      694.4444444     2.56    0.37 
;770    36      771.6049383     1.60    0.21 
;852    33      841.7508418     10.25   1.20 
;941    30      925.9259259     15.07   1.60 
;1209   23      1207.729469     1.27    0.11 
;1336   21      1322.751323     13.25   0.99 
;1477   19      1461.988304     15.01   1.02 
;1633   17      1633.986928     0.99    0.06 
        cblock  LASTVAR 
                lowcnt 
                highcnt 
                slowcnt 
                shighcnt 
                hdcnt 
                ldcnt 
        endc 

tlowcnt 
        addwf   PCL,f 
        dt      30, 40, 40, 40 
        dt      36, 36, 36, 33 
        dt      33, 33, 40, 36 
        dt      33, 30, 30, 30 

thighcnt 
        addwf   PCL,f 
        dt      21, 23, 21, 19 
        dt      23, 21, 19, 23 
        dt      21, 19, 17, 17 
        dt      17, 17, 23, 19 

dtmfsend: 
        andlw   0x0f 
        movwf   tmp 
        call    tlowcnt 
        movwf   slowcnt 
        movwf   lowcnt 

        movf    tmp,w 
        call    thighcnt 
        movwf   shighcnt 
        movwf   highcnt 

        clrf    ldcnt 
;       movlw   .22                     ;adjust here for delay 
        movlw   MTXLENGTH 
        call    EE_read 
        addlw   1 
        movwf   hdcnt                   ;approx. 100 ms 

dloop0  clrwdt                          ;2 
        nop                             ; 
dloop   movlw   (DHI_PIN + DLOW_PIN) 
        decfsz  lowcnt,f 
         andlw  DHI_PIN 
        decfsz  highcnt,f 
         andlw  DLOW_PIN 
        movwf   tmp 
        xorwf   PORTB,f 

        movf    slowcnt,w 
        btfsc   tmp,0 
         movwf  lowcnt 

        movf    shighcnt,w 
        btfsc   tmp,1 
         movwf  highcnt                 ;13 

        IF      DTMFTESTING 
        nop 
        goto    dloop0 
        ENDIF 

        decfsz  ldcnt,f                 ;2/1 
         goto   dloop0                  ;2 
        decfsz  hdcnt,f                 ;2/1 
         goto   dloop                   ;2      ;5 

        return 

This routine uses 2 pic pins for high and low tones.  Just add 2 stage 
low pass filters the each of the pins and combine both.  This works well 
and has a low error less than dtmf receiver specs.

32 bit add
Your add routine may not work if the result of da2_l+da_1 has a carry 
and da2_m is equal to 255 since it will become zero and the next 
addition will be wrong. 
Use the standard routine: 

add32: 
        movf    da2_l,w 
        addwf   da1_l,f 
        movf    da2_m,w 
        skpnc 
         incfsz da2_m,w 
           addwf da1_m,f 

        movf    da2_h,w 
        skpnc 
         incfsz da2_h,w 
          addwf da1_h,f

Shift by a register (x <<= y;)
> In C:
> 
>   unsigned  char x;
>   unsigned  char y;
> 
>  ...
> 
>   x <<= y;
> 
> How would you do this in PIC assembly?
> 
> This has been discussed here before (but I couldn't find it in the
> archives) and IIRC a 12 cycle solution was found (by Dmitry?). I've got a
> 12-cycle solution, but thought it would be interesting to propose this as
> a challenge. (The winner may simply find the answer in the archives :).

x <<= y;

 swapf  x,w
 andlw  0xF0
 btfsc  y,2
  movwf x           ;x=x*16

 clrc
 rlf    x,w         ;w = x*2
 addwf  x,w         ;w = w+x = x*2+x = x*3
 btfsc  y,1
  addwf x,f         ;x = w+x = x*3+x = x*4

 clrc
 btfsc  y,0
  rlf   x,f         ;x = x*2

32 bit addition/subtraction
> I'am following PIC list for a while now, and after some small projects with 
> 16C84, I want to build a 9 digits frequency counter (say beyond 100 MHz). 
> something that looks like Peter Alicky's counter, but with an external 
> prescaler. 
> Does any of you have very efficient routines for 4 bytes 32 bits) add  and 
> substract for 16C84? I mean efficient in term of size and/or speed 
add32: 
        movf    a1,w 
        addwf   b1,f 

        movf    a2,w 
        skpnc 
         incfsz a2,w 
          addwf b2,f 

        movf    a3,w 
        skpnc 
         incfsz a3,w 
          addwf b3,f 

        movf    a4,w 
        skpnc 
         incfsz a4,w 
          addwf b4,f 

;---- 

sub32: 
        movf    a1,w 
        subwf   b1,f 

        movf    a2,w 
        skpc 
         incfsz a2,w 
          subwf b2,f 

        movf    a3,w 
        skpc 
         incfsz a3,w 
          subwf b3,f 

        movf    a4,w 
        skpc 
         incfsz a4,w 
          subwf b4,f

Isochronous delay
I make my delay routines somewhat isochronous.  This makes my life 
easier in computing the number of cycles spent on the routine. 
Example, the routine below will have a delay of 256*256*cnt1*7.  To have 
approx. 1 second at 20MHz, cnt1 is set to 11. 

; 
;Delay 1sec@20MHz: 
;  
; cnt1 = Cycles/256/256/7 = (20000000/4)/256/256/7 = 10.899 = 11 
; 
; 

delay: 
        movlw   .11 
        movwf   cnt1 
        clrf    cnt2 
        clrf    cnt3 

dloop   decfsz  cnt3,f 
         goto   $+2 
        decfsz  cnt2,f 
         goto   $+2 
        decfsz  cnt1,f 
         goto   dloop

Manchester Encoding
;---------------------------------------
;Manchester encoding
; Input:	W
; Output:	manch_hi:manch_lo
; Cycles:	20 cycles
; by: Regulus Berdin (untested)

manch_encode:
	movwf	temp			;save to temp

	movlw	B'01010101'		;assume all bits are 0
	movwf	manch_lo
	movwf	manch_hi

	movlw	B'00000011'		;setup invert mask
	btfsc	temp,0			;test bit 0
	 xorwf	manch_lo,f		; invert code if 1
	btfsc	temp,4			;test bit 4
	 xorwf	manch_hi,f		; invert code if 1
		
	movlw	B'00001100'		;same as above, repeated 3 times
	btfsc	temp,1
	 xorwf	manch_lo,f
	btfsc	temp,5
	 xorwf	manch_hi,f

	movlw	B'00110000'
	btfsc	temp,2
	 xorwf	manch_lo,f
	btfsc	temp,6
	 xorwf	manch_hi,f

	movlw	B'11000000'
	btfsc	temp,3
	 xorwf	manch_lo,f
	btfsc	temp,7
	 xorwf	manch_hi,f
	
	return
transparent