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
|