;----------------------------------------------------- ; ; 12/12/99 K Staton Initial Coding. Derived from PICEVAL.ASM ; 12/17/99 K Staton Re-coded for 16F873 ; 04/22/01 K Staton merged pwm84 (16f84) for pwm servos ; 04/24/01 K Staton added fake channel for timing ; put pwm completely in isr ; restructured serial i/o to be single line ; list p=16f873 #include "p16f873.inc" __CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _XT_OSC & _LVP_OFF ; ; ---------- ; MCLR* 1 --| |-- 28 pwm1 ; a/d 0 2 --| P |-- 27 pwm2 ; 3 --| 1 |-- 26 pwm3 ; 4 --| 6 |-- 25 pwm4 ; 5 --| F |-- 24 ; 6 --| 8 |-- 23 ; 7 --| 7 |-- 22 ; GND 8 --| 3 |-- 21 DQ 1820 ; osc1 9 --| |-- 20 VCC ; osc2 10 --| |-- 19 GND ; 11 --| |-- 18 RX-DATA ; 12 --| |-- 17 TX-DATA ; 13 --| |-- 16 ; 14 --| |-- 15 ; ---------- ; pwm outputs: ; ; 4 on port B ; single positive going pulses ; with one output active at a time and outputs ; follow each other in rotation, so any one ; output has a period that depends on the duration ; of the other outputs ; MUST SET DEFAULT RADIX TO DECIMAL! CONSTANT DQ = 0 ; DS1820 is on PIC I/O RB.0 ; 16F873 user memory: ; BANK 0 USER $20..$7F (032..127) ; BANK 1 USER $A0..$FF (160..255) ScratchPadRam equ 0x20 w_tmp equ ScratchPadRam+0 ;isr context saved here status_tmp equ ScratchPadRam+1 AA equ ScratchPadRam+2 BB equ ScratchPadRam+3 CC equ ScratchPadRam+4 SerOutData equ ScratchPadRam+5 SerInData equ ScratchPadRam+6 n equ ScratchPadRam+7 d100 equ ScratchPadRam+8 d10 equ ScratchPadRam+9 d0 equ ScratchPadRam+10 flags equ ScratchPadRam+11 PRODL equ ScratchPadRam+12 PRODH equ ScratchPadRam+13 mulplr equ ScratchPadRam+14 ddr equ ScratchPadRam+15 reg equ ScratchPadRam+16 dat equ ScratchPadRam+17 err equ ScratchPadRam+18 R1 equ ScratchPadRam+19 R0 equ ScratchPadRam+20 T equ ScratchPadRam+21 tmp1 equ ScratchPadRam+22 NBYTES equ ScratchPadRam+23 NBITS equ ScratchPadRam+24 O_BYTE equ ScratchPadRam+25 I_BYTE equ ScratchPadRam+26 LOOP1 equ ScratchPadRam+27 TMP2 equ ScratchPadRam+28 dlyreg equ ScratchPadRam+29 tmp equ ScratchPadRam+30 macstat equ ScratchPadRam+31 DATA_BUFF equ ScratchPadRam+32 ; temperature data TL equ DATA_BUFF+0 ;32 TH equ DATA_BUFF+1 ;33 UB1 equ DATA_BUFF+2 ;34 UB2 equ DATA_BUFF+3 ;35 RB1 equ DATA_BUFF+4 ;36 RB2 equ DATA_BUFF+5 ;37 REM equ DATA_BUFF+6 ;38 CPC equ DATA_BUFF+7 ;39 times equ ScratchPadRam+40 chan equ ScratchPadRam+41 ;index = {0..3} -> channel 1..4 flags2 equ ScratchPadRam+42 ch_base equ ScratchPadRam+43 ch1H equ ScratchPadRam+43 ;indexH = base + (ch)*2 ch1L equ ScratchPadRam+44 ;indexL = base + (ch)*2 + 1 ch2H equ ScratchPadRam+45 ch2L equ ScratchPadRam+46 ch3H equ ScratchPadRam+47 ch3L equ ScratchPadRam+48 ch4H equ ScratchPadRam+49 ch4L equ ScratchPadRam+50 chdlyH equ ScratchPadRam+51 ; use this fake channel to control update rate chdlyL equ ScratchPadRam+52 ; since pwm cycles thru channels scratch equ ScratchPadRam+53 tmpH equ ScratchPadRam+54 tmpL equ ScratchPadRam+55 fsr_tmp equ ScratchPadRam+56 ; BANK 1 w_tmp w_tmp_b1 equ 0xA0 ; must exist in BOTH BANKS! #define ClkFreq 4000000 #define baud(x) (ClkFreq/(64*x))-1 #define TXSTA_INIT 0xA0 ;10100000 = intern brg/8bit/txen/async/lowspeed #define RCSTA_INIT 0x90 ;10010000 = spen/8bit/rxen ; BANK DEFINES FOR 16F873... ; ; ALWAYS LEAVE IN BANK0... #define BANK0 bcf status,rp0 #define BANK1 bsf status,rp0 ; ; define flags ; err_bit equ 1 echo_bit equ 0 ; ; define flags2 ; done_pw equ 7 ; ; PORTB PWMout BIT DEFS ; ch1 equ 7 ch2 equ 6 ch3 equ 5 ch4 equ 4 #define reset 0 ; ; define errors ; #define no_err 0 #define port_err 1 #define number_err 2 #define badcmd 3 #define _carry status,C #define EOL 0x0D ;D13 #define CR 0x0D #define LF 0x0A #define NUL 0x00 ; ****** BEGIN MACRO BLOCK ****** BEGIN MACRO BLOCK ****** BEGIN MACRO BLOCK ****** ; ; Make a mullw macro that codes like the hardware ; 8x8 multiply in the 17c42a. ; mullw macro x clrf PRODL clrf PRODH local i=0 movwf mulplr movlw x bcf status,C WHILE i < 8 btfsc mulplr,i addwf PRODH rrf PRODH rrf PRODL i += 1 ENDW endm ; ; Two microsecond delay ; 4MHZ clock. ; wait2us macro nop nop endm PIN_HI macro BANK1 BSF trisb, DQ ; high impedance (input) BANK0 endm PIN_LO macro BCF PORTB, DQ ; make DQ bit zero BANK1 BCF trisb, DQ ; low impedance zero (output) BANK0 endm ; ; CPFSEQ ; ; compares wreg with register skip if f=w ; cpfseq macro register local seqs local seqc local seqr movwf tmp xorwf register,w btfsc status,z goto seqs goto seqc seqs bsf macstat,z goto seqr seqc bcf macstat,z seqr movf tmp,w btfss macstat,z ; movf affects status,z ! endm ; ; CPFSLT ; ; compares wreg with register skip if fw ; cpfsgt macro register local sgteq local sgts local sgtc local sgtr movwf tmp subwf register,w ; f-w btfsc status,z goto sgts goto sgtc sgts bsf macstat,z goto sgtr sgtc bcf macstat,z sgtr movf tmp,w btfsc macstat,z ; z=1: equal goto sgteq btfss status,c ; c=0 borrow; c=1 no borrow (f>w) sgteq endm INTS_ON macro bsf INTCON,GIE ; global int enable endm INTS_OFF macro bcf INTCON,GIE ; global int disable endm ;****** END MACRO BLOCK ****** END MACRO BLOCK ****** END MACRO BLOCK ****** ; ; Begin code ; org 0 goto Start ; org 4 goto isr ; org 0x10 ;; ;; START OF CODE... ;; start bcf status,rp1 ; this bank bit is always 0 ;************************************************************************ ; ; SYSTEM INIT ; ; ;init rs232 ; BANK1 movlw baud(1200) movwf SPBRG movlw TXSTA_INIT movwf TXSTA BANK0 movlw RCSTA_INIT movwf RCSTA ; ; init adc ; BANK1 clrf adcon1 ; left justified all analog in BANK0 movlw B'10000001' ;fosc/32, ch0, on movwf adcon0 bsf adcon0,2 ; initial conversion ; ;init vars ; bcf flags,echo_bit ;initially no echo clrf err ;initially no errors (err result 0) bcf flags,err_bit ;clear error flag ; ; init io ; movlw B'00000000' ;port b ouput movwf portb ;initially low ouput BANK1 movwf trisb BANK0 ; ; init timer 0 (timer mode) ; BANK1 bcf option_reg,t0cs BANK0 ;now init pwm vars movlw 0x00 movwf chan ;current channel bsf flags2,done_pw ;done_pw=true so new one starts up ; 1000us = 0x03E8 = 1000 ; 1500us = 0x05DC = 1500 ; 2000us = 0x07D0 = 2000 ; ; init position to center = 1500us ; ; high byte "off by 1" refers to making the number of interrupts ; correct for the total count correspondence ; ; low byte must be complemented because the timer counts up (not down). ; ;high byte movlw 0x05 addlw 1 ;fix high byte off by 1 movwf ch1H movwf ch2H movwf ch3H movwf ch4H ;low byte movlw 0xDC movwf scratch comf scratch,w ;fix low byte up counter movwf ch1L movwf ch2L movwf ch3L movwf ch4L ; initialize fake channel for update rate control ; total cycle time of > 10 ms 4ch x 1ms -> 6ms delay needed ; 6000 = 0X1770 movlw 0x17 movwf chdlyH movlw 0x70 movwf scratch comf scratch,w movwf chdlyL bsf INTCON,GIE ; global int enable, but all off bsf INTCON,T0IE ; enable timer0 int ;************************************************************************ ; ; MAIN program loop ; main call pr_space call ok main2 ; ; get char (command) ; call getc ; process char ; c='W' write RAM movlw 'W' cpfseq SerInData goto n1 goto write ; c='R' read RAM n1 movlw 'R' cpfseq SerInData goto n2 goto read ; c='H' HELP n2 movlw 'H' cpfseq SerInData goto n3 goto helplist ; c='E' echo n3 movlw 'E' cpfseq SerInData goto n4 goto echo ; c='Y' take temperature measurement n4 movlw 'Y' cpfseq SerInData goto n5 goto meas_temp ; c='Z' read temperature n5 movlw 'Z' cpfseq SerInData goto n6 goto read_temp ; c='T' test routine n6 movlw 'T' cpfseq SerInData goto n7 goto test ; c=NUL n7 movlw NUL cpfseq SerInData goto n8 goto endline ; c=LF n8 movlw LF cpfseq SerInData goto n9 goto endline ; c=CR n9 movlw CR cpfseq SerInData goto n10 goto endline ; c=D n10 movlw 'D' cpfseq SerInData goto n11 goto dump ; c=A n11 movlw 'A' cpfseq SerInData goto n12 goto adcin ; c=M n12 movlw 'M' cpfseq SerInData goto cmderr goto motor_pwm ; c is unknown... cmderr movlw badcmd movwf err ; ERROR HANDLER errorh call pr_space movlw '?' movwf SerOutData call putc call pr_space movf err,W call printn call crlf clrf err ;clear error number bcf flags,err_bit ;clear error flag goto main2 ;don't call ok on err ; ; End of main program (command) loop ; ;************************************************************************ ; ; Command routines... ; ; ; read RAM ; Rnnn ; nnn is decimal address to read ; read call getn ;get address movf n,W movwf FSR btfsc flags,err_bit goto errorh movlw ':' call putchar movf INDF,W ;INDIRECT read data call printn ;send to terminal goto main ; ; write RAM ; Wnnn,mmm ; nnn is decimal address to write ; mmm is decimal data to write ; write call getn ;get address btfsc flags,err_bit goto errorh movf n,W movwf FSR call getc ;get field separator movlw ',' cpfseq SerInData goto errorh call getn ;get data btfsc flags,err_bit goto errorh movf n,W movwf INDF ;INDIRECT write data goto main ; ; echo (toggle) ; toggles flag that is checked in serial input routine. ; echo btfss flags,echo_bit goto eb_c goto eb_s eb_c bsf flags,echo_bit goto main eb_s bcf flags,echo_bit goto main ; ; test ; ; use to test various routines... ; test movlw 0 movwf T testloop movf T,W call printn call crlf incfsz T goto testloop goto main ; ; dump ; ; dumps ram ; ; Daaa,bbb dump call getn ;get start address btfsc flags,err_bit goto errorh movf n,W movwf FSR call getc ;get field separator movlw ',' cpfseq SerInData goto errorh call getn ;get # bytes to dump btfsc flags,err_bit goto errorh movf n,W movwf tmp1 dump1 call pr_space movf INDF,w call printn incf FSR,F decfsz tmp1,f goto dump1 goto main ; ; adcin ; ; adcin bsf adcon0,2 ; start conversion adwait btfsc adcon0,2 goto adwait movf adresh,W ; 8 bit mode, left justified call printn goto main ; ; endline ; ignores end of line characters: CR,LF,NUL ; endline goto main2 ; don't ok crlf on EOL char ; ; ; Mnnn,mmm ; nnn is high byte of data (chan|msb_data) ; mmm is low byte of data ; ; data field is number of timer counts for pulse width ; low byte is loaded into timer first, then ; the timer is allowed to count high byte number of full cycles (256) ; ; 1000us = 0x03E8 = 3,232 16+3 (chan 2) 32+3 (chan 3) ; 1500us = 0x05DC = 5,220 ; 2000us = 0x07D0 = 7,208 ; init position to center = 1500us ;high byte (centered) ; movlw 0x05 ;low byte (centered) ; movlw 0xDC ; movwf scratch ; comf scratch,w ;fix low byte up counter ; movwf ch1L motor_pwm call getn ;get high byte btfsc flags,err_bit goto errorh movf n,W movwf tmpH call getc ;get field separator movlw ',' cpfseq SerInData goto errorh call getn ;get low byte btfsc flags,err_bit goto errorh movf n,W movwf tmpL ; ; now split out a 4 bit address leaving a 11 bit data value. ; the 11 bit value is 8 bits for initial timer count and ; 3 bits for the number of times the timer overflows ; the 8 bit initial count value must be complemented since ; the timer is an up counter. do this when tmpL is transfered ; to channel variable. ; the 4 bit value for the number of interrupts must be incremented, ; since there must always be at least ONE interrupt. ; ; copy tmp to channel variable spec in bits 15..12 movf tmpH,w ; tmpH->W andlw 0xF0 ; 0xF0 & tmpH -> W movwf scratch swapf scratch ; put in low nibble bcf STATUS,C rlf scratch,w ; scratch*2->W addlw ch_base movwf FSR ; load indirect pointer movf tmpH,w ; tmpH->W andlw 0x0F ; 0x0F & tmpH -> W addlw 1 ; fix high byte off by one movwf INDF ; copy high incf FSR ; inc pointer movf tmpL,w ; tmpL->W movwf scratch comf scratch,w ; not tmpL -> W movwf INDF ; copy low goto main ; ; help ; helplist call pr_space movlw 'A' call putchar movlw ',' call putchar movlw 'D' call putchar movlw ',' call putchar movlw 'E' call putchar movlw ',' call putchar movlw 'H' call putchar movlw ',' call putchar movlw 'M' call putchar movlw ',' call putchar movlw 'R' call putchar movlw ',' call putchar movlw 'T' call putchar movlw ',' call putchar movlw 'W' call putchar movlw ',' call putchar movlw 'Y' call putchar movlw ',' call putchar movlw 'Z' call putchar goto main ;************************************************************************ ; ; Subroutines... ; ; ; printn ; converts WREG into ascii and outputs to rs232 ; printn call bin2bcd call print_bcd return ; ; bin2bcd ; converts the bin number in WREG to a 3 digit bcd number ; in R1,R0 (R1 msd) ; bin2bcd clrf R1 ;clear count for sucessive subtract clrf R0 dig2 addlw -100 ;subtract 100 until underflow btfss _carry goto dig1 incf R1 goto dig2 dig1 addlw 100 ;previous subtract underflowed so add it back dig1a addlw -10 ;subtract 10 until underflow btfss _carry goto dig0 incf R0 goto dig1a dig0 addlw 10 ;previous subtract underflowed so add it back swapf R0 iorwf R0 return ; ; print_bcd ; takes the 3 digit bcd number in R1,R0 and converts each to ascii ; then sends them on rs232 ; print_bcd ;; swapf R1,w ;result in WREG ;; andlw 0x0F ;mask digit ;; addlw '0' ;; putchar movf R1,w andlw 0x0F addlw '0' call putchar swapf R0,w ;put result in WREG andlw 0x0F ;mask digit addlw '0' call putchar movf R0,W andlw 0x0F addlw '0' call putchar return ; ; One Second Delay ; ; CURRENTLY NOT USED delay1s movlw .7 movwf CC d3 movlw .255 movwf BB d2 movlw .255 movwf AA d1 decfsz AA goto d1 decfsz BB goto d2 decfsz CC goto d3 retlw 0 ; ; One millisecond delay ; onems movwf tmp1 movlw .250 movwf dlyreg omsl1 nop ; 1 decfsz dlyreg ; 2 (3 when W=0) goto omsl1 ; 3, 4 movf tmp1,W return ; ; GETN ; ; get number (byte) from RS232 as 3 digit decimal in ascii format ; then convert digits to byte ; ; byte result returned in memory "n" ; getn call getc movlw '9'+1 cpfslt SerInData goto nerr movlw '0'-1 cpfsgt SerInData goto nerr movlw '0' ;ascii to int subwf SerInData,w movwf d100 ;save 100's digit call getc movlw '9'+1 cpfslt SerInData goto nerr movlw '0'-1 cpfsgt SerInData goto nerr movlw '0' ;ascii to int subwf SerInData,w movwf d10 ;save 10's digit call getc movlw '9'+1 cpfslt SerInData goto nerr movlw '0'-1 cpfsgt SerInData goto nerr movlw '0' ;ascii to int subwf SerInData,w movwf d0 ; save 1's digit ; ; form byte from int digits... ; movf d100,W mullw 100 ; alt bx100=bx64+bx32+bx4 movf PRODL,W movwf n movlw 0 cpfseq PRODH goto nerr movf d10,W mullw 10 ; alt bx10=bx8+bx2 movf PRODL,W addwf n,1 movf d0,W addwf n,1 return nerr movlw number_err movwf err bsf flags,err_bit return ; ; indicate that command completed correctly ; ok movlw 'O' movwf SerOutData call putc movlw 'K' movwf SerOutData call putc crlf movlw CR movwf SerOutData call putc movlw LF movwf SerOutData call putc return ; ; print blank space ; pr_space movlw ' ' movwf SerOutData call putc return ; ; putchar - output character in WREG ; input: WREG ; putchar movwf SerOutData call putc return ; ; put char - busy wait send char on rs232 ; input: SerOutData ; putc PollTx btfss PIR1,4 ;check the TX Buffer empty bit goto PollTx movf SerOutData,W movwf TXREG return ; ; get char - receive char from rs232 ; ; BUSY WAIT FOR CHARACTER IN ; ; char -> SerInData ; ; optional echo ; output: SerOutData ; getc PollRx btfss PIR1,5 ;check the RX Buffer full bit goto getc ; got char movf RCREG,W movwf SerInData ; test echo btfss flags,echo_bit ; ECHO? goto noecho doecho movf SerInData,W ; yes; do echo movwf SerOutData call putc noecho return ;********************************************************************** ; INTERRUPT SERVICE ROUTINE * ; TIMER is only active interrupt * ; * ; first time thru, does low byte number of counts * ; subsequent times, does full 256 counts * ; (no reload after 1st int) * ; * ; decrement 'times' variable... * ; # times int must occur before done * ; * ; if not done, * ; decrement 'times' & return * ; if done, * ; negate pwm output specd in channel var * ; inc channel var * ; assert pwm output specd in channel var * ; reload timer & int count ('times') * ;********************************************************************** isr ; save status & wreg movwf w_tmp ;w_tmp bank0 or bank1 swapf status,w ;swapped status in w clrf status ;FORCE BANK0. status_tmp only bank0 movwf status_tmp ;save swapped status movf FSR,w movwf fsr_tmp ; isr body bcf INTCON,T0IF ; clr timer int flag decfsz times goto isrdone ;<>0 pwm_done ; =0 ; negate pwm output for channel just completed movlw 0 xorwf chan,w btfsc status,z bcf portb,ch1 movlw 1 xorwf chan,w btfsc status,z bcf portb,ch2 movlw 2 xorwf chan,w btfsc status,z bcf portb,ch3 movlw 3 xorwf chan,w btfsc status,z bcf portb,ch4 ; now set up next channel ; increment channel variable... incf chan ;pre inc movlw 0x05 ; number of channels (0..3)+fake subwf chan,w ; (f)-(W)->(W) btfsc STATUS,Z ;Z=1 -> chan=0x04 clrf chan ;reset chan index to 0 ; set up indirect (indexed) access of channel values bcf STATUS,C rlf chan,w ;chan*2->W addlw ch_base movwf FSR ; assert pwm output for next channel movlw 0 xorwf chan,w btfsc status,z bsf portb,ch1 movlw 1 xorwf chan,w btfsc status,z bsf portb,ch2 movlw 2 xorwf chan,w btfsc status,z bsf portb,ch3 movlw 3 xorwf chan,w btfsc status,z bsf portb,ch4 ; set up # of timer interrupts to expect for this channel movf indf,w ;get variable value->W movwf times ;msb is # times timer rolls ; start timer at correct count for first (partial) count incf FSR movf indf,w ;get variable value->W movwf TMR0 ;lsb is initial timer count isrdone ; restore status & wreg (swap affects no status bits) movf fsr_tmp,w movwf FSR swapf status_tmp,w ;unswapped status in w movwf status ;restores bank swapf w_tmp,f ;swapped result in w_tmp swapf w_tmp,w ;unswapped result in w retfie ;###################################################################### ; ; DS1820 routines... ; READ_TEMP: ; read bytes from scratch pad ; DISABLE TIMER INT TO READ! INTS_OFF CALL INIT MOVLW 0CCH MOVWF O_BYTE CALL OUT_BYTE MOVLW 0BEH MOVWF O_BYTE CALL OUT_BYTE call pr_space call in_byte movwf tl call printn call pr_space call in_byte movwf th call printn call pr_space call in_byte movwf ub1 call printn call pr_space call in_byte movwf ub2 call printn call pr_space call in_byte movwf rb1 call printn INTS_ON goto main ;skip original read data.... MEAS_TEMP: ; DISABLE TIMER INT TO MEASURE! INTS_OFF CALL INIT MOVLW 0CCH MOVWF O_BYTE CALL OUT_BYTE MOVLW 044H MOVWF O_BYTE CALL OUT_BYTE INTS_ON goto main ; waiting (for 0FFH) is optional ; The following are common 1-Wire routines used in all applications INIT: PIN_HI ; BE SURE PIN STARTS HI nop PIN_LO MOVLW .50 ; 500 us delay CALL DELAY_10USEC PIN_HI MOVLW .50 ; 500 usec delay CALL DELAY_10USEC RETURN IN_BYTE: ; returns byte in W MOVLW .8 MOVWF NBITS CLRF I_BYTE IN_BYTE_1: PIN_LO ; momentary low on DATA_PIN 1 nop ; 2 PIN_HI ; 3,4 NOP ; 5 NOP ; 6 NOP ; 7 NOP ; 8 NOP ; 9 NOP ; 10 NOP ; 11 MOVF PORTB,W ; 15 usecs after DATA_PIN low, fetch from DATA_PIN MOVwF TMP2 ; WORK ON A STATIC COPY BTFSS TMP2, DQ BCF _carry ; its a zero BTFSC TMP2, DQ BSF _carry ; its a one RRF I_BYTE,F MOVLW .6 ; now delay 60 usecs CALL DELAY_10USEC DECFSZ NBITS, F GOTO IN_BYTE_1 MOVF I_BYTE,W ; return the result in W RETURN OUT_BYTE: MOVLW .8 MOVWF NBITS OUT_BYTE_1: RRF O_BYTE, F BTFSS _carry GOTO OUT_0 GOTO OUT_1 OUT_0: PIN_LO ; bring DATA_PIN low MOVLW .6 ; for 60 usecs CALL DELAY_10USEC PIN_HI GOTO OUT_BYTE_2 OUT_1: PIN_LO ; momentary low nop PIN_HI MOVLW .6 CALL DELAY_10USEC GOTO OUT_BYTE_2 OUT_BYTE_2: DECFSZ NBITS, F GOTO OUT_BYTE_1 RETURN ;;;;;; DELAY_10USEC: ; provides a delay equal to W * 10 usecs MOVWF LOOP1 DELAY_10USEC_1: NOP ; 1 NOP ; 2 NOP ; 3 NOP ; 4 NOP ; 5 NOP ; 6 NOP ; 7 DECFSZ LOOP1, F ; 8 (9 when loop1=0) GOTO DELAY_10USEC_1 ; 9, 10 RETURN ; (10 when loop1=0) end