;;; ---------------------------------------------------------------------------- ;;; "THE BEER-WARE LICENSE" (Revision 42) (by Poul-Henning Kamp): ;;; Joerg Wunsch wrote this file. As long as you ;;; retain this notice you can do whatever you want with this stuff. If we meet ;;; some day, and you think this stuff is worth it, you can buy me a beer ;;; in return. ;;; ---------------------------------------------------------------------------- ;;; ;;; PIC 12F508 driving an MB1504 PLL generator, ;;; controlled by three switches (A/B/C). ;;; ;;; Processor sleeps most of the time, and wakes ;;; up from a pin change reset. It debounces the ;;; switches, then sets up the MB1504 prescaler ;;; and VCO divider, re-enables pin change reset, ;;; and finally puts the processor to sleep again. ;;; $Id: picpll.s,v 1.4 2015/11/04 20:18:22 j Exp $ PROCESSOR 12c508 INCLUDE ;;;------------------------------------------------------------ ;;; ;;; macro and EQU definitions ;;; ;;; MB1504 GPIO connections mb_clk EQU 5 mb_data EQU 4 mb_le EQU 2 ;;; input key GPIO connections key_a EQU 0 key_b EQU 1 key_c EQU 3 ; /MCLR ;;; clock and VCO divider setup ;;; reference clock 4 MHz fclock EQU 4000000 ;;; reference clock divider => 2.5 kHz R EQU 1600 ;;; internal reference frequency fref EQU fclock/R ;;; VCO prescaler M EQU 32 SW EQU 1 ; prescaler == 32 bit ;;; invert count bits from cw into cwinv invert MACRO cw,count NOLIST cwinv SET 0 REPEAT count cwinv SET cwinv << 1 IF cw & 1 cwinv SET cwinv | 1 ENDIF cw SET cw >> 1 ENDR LIST cwinv SET cwinv ENDM ; invert ;;; produce bit pattern for VCO divider to ;;; run on frequency fvco divider MACRO fvco ;; required divider div SET fvco/fref ;; counter value N N SET div/M ;; swallow-counter value A A SET div%M ;; complete control word (19 bits) cw SET 0 | (A << 1) | (N << 8) ;; now reverse the bit order of cw into cwinv invert cw,19 movlw low cwinv movwf cword movlw high cwinv movwf cword + 1 movlw low msw cwinv movwf cword + 2 movlw 19 movwf nbits ENDM ; divider ;;; produce bit pattern to setup MB1504 reference ;;; clock divider setup MACRO cw SET 1 | (R << 1) | (SW << 15) ;; now reverse the bit order of cw into cwinv invert cw,16 movlw low cwinv movwf cword movlw high cwinv movwf cword + 1 movlw low msw cwinv movwf cword + 2 movlw 16 movwf nbits ENDM ; setup ;;; Compute key pattern kval MACRO a,b,c NOLIST k1 SET 0 IF a k1 SET (1 << key_a) ENDIF IF b k1 SET k1 | (1 << key_b) ENDIF IF c k1 SET k1 | (1 << key_c) ENDIF LIST k1 SET k1 ENDM ; kval ;;; Evaluate key pattern, emit frequency divider ;;; bit pattern on match, and proceed to "exit". setfreq MACRO a,b,c,fmhz,fkhz,fhz kval a,b,c movlw k1 subwf keys, w ;; if Z is clear, not our pattern -> ;; jump to end of macro btfss STATUS, Z goto .sf1 ;; matched: emit bit pattern. divider (fmhz * 1000000 + fkhz * 1000 + fhz) call shiftout goto exit .sf1: ENDM ; setfreq ;;; branch if Z flag set beq MACRO tgt btfsc STATUS, Z goto tgt ENDM ;;; branch if Z flag clear bne MACRO tgt btfss STATUS, Z goto tgt ENDM ;;; branch if C flag set bcy MACRO tgt btfsc STATUS, C goto tgt ENDM ;;; branch if C flag clear bnc MACRO tgt btfss STATUS, C goto tgt ENDM ;;;------------------------------------------------------------ ;;; ;;; data definitions ;;; SEGU "data" ;; start behind the SFRs ORG GPIO+1 ;; 3 bytes to hold current bit pattern cword: DS 3 ;; number of bits to shift out next nbits: DS 1 ;; two delay counters for 10 ms delay ctr0: DS 1 ctr1: DS 1 ;; key pattern keys: DS 1 ;;;------------------------------------------------------------ ;;; ;;; code section ;;; SEG "code" __CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC ORG 0 ;; reset vector goto start ;;; Shift out nbits bits from cword to MB1504; when done, ;;; pulse LE once to transfer the shift register into the ;;; actual PLL logic. shiftout: ;; one shift operation, current bit will be ;; stored into carry flag x1: rrf cword + 2, f rrf cword + 1, f rrf cword, f ;; W holds our GPIO bit pattern to send clrw bcy x2 ;; send 0-bit andlw ~(1 << mb_data) goto x3 x2: ;; send 1-bit iorlw (1 << mb_data) x3: ;; output bit pattern, clk clear movwf GPIO ;; send clock pulse iorlw (1 << mb_clk) movwf GPIO andlw ~(1 << mb_clk) movwf GPIO ;; continue loop until all bits sent decfsz nbits, f goto x1 ;; send LE pulse clrw iorlw (1 << mb_le) movwf GPIO andlw ~(1 << mb_le) movwf GPIO retlw 0 ;;; Simple 10 ms delay, used to debounce keys at ;;; application startup. delay10ms: movlw 13 movwf ctr0 l1: clrf ctr1 l2: decfsz ctr1, f goto l2 decfsz ctr0, f goto l1 retlw 0 ;;;------------------------------------------------------------ ;;; Main program entry. ;;; start: ;; We are supposed to come up with the OSCCAL ;; value in W. movwf OSCCAL ;; Select key A/B/C GPIOs for input, MB1504 ;; pins will default to output. movlw (1 << key_a) | (1 << key_b) | (1 << key_c) tris GPIO ;; Option register: no wakeup on pin change (by now), ;; no pull-ups, timer0 runs on internal clock (this ;; is required to have GP2 available as GPIO pin). movlw (1 << NOT_GPWU) | (1 << NOT_GPPU) option ;; debounce keys call delay10ms ;; extract key pattern to "keys" comf GPIO, w andlw (1 << key_a) | (1 << key_b) | (1 << key_c) movwf keys ;; initialize MB1504 reference clock prescaler setup call shiftout ;; A,B,C, MHz,kHz,Hz ;; LO Rx setfreq 0,0,0, 126,800,000 ; 137.500 setfreq 1,0,0, 126,920,000 ; 137.620 setfreq 0,1,0, 126,400,000 ; 137.100 setfreq 1,1,0, 127,000,000 ; 137.700 setfreq 0,0,1, 127,212,500 ; 137.9125 setfreq 1,0,1, 127,100,000 ; 137.800 setfreq 0,1,1, 127,150,000 ; 137.850 setfreq 1,1,1, 127,200,000 ; 137.900 exit: ;; New option register: wakeup on pin change, then ;; fall asleep. movlw (1 << NOT_GPPU) option movf GPIO, w ; clear pin-change status sleep END