; ---------------------------------------------------------------------- ; ; Title: ; ; 10 MHz frequency divider ; ; Function: ; ; This PIC 16c84 program is designed to divide a 10 MHz frequency ; source down to 1 Hz (1 PPS). ; ; Since several extra output pins are available the program creates ; a total of 9 square wave outputs -- one for each frequency decade ; from 100 kHz to 0.001 Hz (1000 s). ; ; A STOP input and a 1 PPS synchronization input are also provided. ; Raising the STOP input high stops and resets the divider. The ; divider resumes on the leading edge of the 1 PPS SYNC input. The ; 1 PPS output will be synchronized to the 1 PPS SYNC input to less ; than 1.2 us (three PIC instructions at 10 MHz). ; ; The following chip schematic shows the assignment of each pin. ; ; ------ ------ ; 100 kHz <- RA2 |1 --- 18| RA1 -> Red LED ; Green LED <- RA3 |2 17| RA0 <= Stop input ; 1PPS SYNC => T0CKI/RA4 |3 16| OSC1/CLKIN <= 10 MHz input ; +5 VDC -> /MCLR |4 15| OSC2/CLKOUT -- N/C ; GND -> Vss |5 16C84 14| Vdd <- +5 VDC ; 10 kHz <- INT/RB0 |6 13| RB7 -> 1000 s ; 1 kHz <- RB1 |7 12| RB6 -> 100 s ; 100 Hz <- RB2 |8 11| RB5 -> 10 s ; 10 Hz <- RB3 |9 10| RB4 -> 1 Hz / 1 PPS ; --------------- ; ; Implementation: ; ; To generate a 10 kHz square wave at 50% duty cycle an output pin ; must be flipped every 50 us (125 instructions at 10 MHz clock). ; This program does not use TMR0, the pre-scaler, or interrupts. ; Instead it relies on the fact that given an accurate 10 MHz clock ; each PIC instruction takes precisely 400 ns and the main loop has ; been designed to use exactly 125 instructions. ; ; The 100 kHz frequency (10 us period) is generated by setting an ; output pin on and off every 25 cycles. Since 25 is an odd number ; it is not possible for the PIC to generate this square wave with ; a 50% duty cycle. Instead a 20% duty cycle (5 cylcles on and 20 ; cycles off) was chosen for this frequency output. A total of 5 ; pairs of 100 kHz bit set/clear code are carefully interspersed ; within the 50 us main loop. ; ; Pins RA0 and RA4 are not used to drive a LED. RA4 is a Schmidt ; trigger input and O.C. output. It is used as the SYNC input. ; The data sheet says not to toggle RA0 under some conditions so ; it is used as the STOP input. ; ; Version: ; ; 1998-Aug-05, Version 4, tvb ; ; ---------------------------------------------------------------------- ; Using Microhip assembler. list p=16c84 STATUS set 3 PORTA set 5 PORTB set 6 BASE2 equ 2 HALF2 equ 1 BASE10 equ 0x0a HALF10 equ 0x05 ; STATUS bit definitions Zero Equ 2 ; PORTA bit definitions Stop Equ 0 Red Equ 1 F100k Equ 2 Green Equ 3 Sync Equ 4 ; Temporary register definitions OutByte equ 0x0c DelayTemp equ 0x0d ; Decade counter register definitions Digit0 equ 0x10 Digit1 equ 0x11 Digit2 equ 0x12 Digit3 equ 0x13 Digit4 equ 0x14 Digit5 equ 0x15 Digit6 equ 0x16 Digit7 equ 0x17 ; ---------------------------------------------------------------------- org 0 Start GOTO Init org 4 Interrupt GOTO $ Init CLRF PORTA ; set pins low by default BCF PORTA, Green BCF PORTA, Red BSF PORTA, F100k ; start with 100 kHz high MOVLW 0x11 ; set RA0/RA4 input TRIS PORTA MOVLW 0xff ; start with outputs high MOVWF PORTB MOVLW 0x00 ; set all pins output TRIS PORTB CLRF Digit0 CLRF Digit1 CLRF Digit2 CLRF Digit3 CLRF Digit4 CLRF Digit5 CLRF Digit6 CLRF Digit7 MOVLW 0xff ; start with outputs high ; ; The following implements a software decade divider chain much like ; a string of 7490 decade counter chips or an odometer. ; ; The low-order digit is incremented and when it wraps the carry is ; propagated to the next higher-oder digit. Since square waves are ; being generated in this particular case the low-order digit is ; incremented from 0 to 1 to 0. All remaining digits count from ; 0 to 9 and wrap back to 0. The STATUS Z bit is used to propagate ; carry into the next digit: Z mean carry and not-Z means no carry. ; ; The low order digit toggles every 125 instructions (50 us) for ; a period of 100 us and a frequency of 10 kHz. Each next higher ; order digit counts at 1/10 the rate of the preceding digit. ; The 100 kHz output changes too rapidly for the main loop so it ; is implemented independently. ; ; Note all 8 bits of PORTB are set at the same time so that the ; PIC acts like a synchronous, rather than an asynchronous, decade ; counter. The 100 kHz output pin is in PORTA so the phase of the ; 100 kHz frequency output lags the other outputs by one cycle. ; Loop MOVWF PORTB BSF PORTA, F100k ; set 100 kHz high for 5 cycles (1 of 5) INCF Digit0 MOVLW BASE2 SUBWF Digit0, W NOP BCF PORTA, F100k ; set 100 kHz low for 20 cycles BTFSC STATUS, Zero CLRF Digit0 Loop1 BTFSC STATUS, Zero INCF Digit1 MOVLW BASE10 SUBWF Digit1, W BTFSC STATUS, Zero CLRF Digit1 BTFSC STATUS, Zero INCF Digit2 MOVLW BASE10 SUBWF Digit2, W BTFSC STATUS, Zero CLRF Digit2 BTFSC STATUS, Zero INCF Digit3 MOVLW BASE10 SUBWF Digit3, W NOP BSF PORTA, F100k ; set 100 kHz high for 5 cycles (2 of 5) BTFSC STATUS, Zero CLRF Digit3 BTFSC STATUS, Zero INCF Digit4 BCF PORTA, F100k ; set 100 kHz low for 20 cycles MOVLW BASE10 SUBWF Digit4, W BTFSC STATUS, Zero CLRF Digit4 BTFSC STATUS, Zero INCF Digit5 MOVLW BASE10 SUBWF Digit5, W BTFSC STATUS, Zero CLRF Digit5 BTFSC STATUS, Zero INCF Digit6 MOVLW BASE10 SUBWF Digit6, W BTFSC STATUS, Zero CLRF Digit6 BTFSC STATUS, Zero INCF Digit7 MOVLW BASE10 BSF PORTA, F100k ; set 100 kHz high for 5 cycles (3 of 5) SUBWF Digit7, W BTFSC STATUS, Zero CLRF Digit7 ; ; Now compress 8 digits into one byte of 50% duty cycle bits. If the ; odometer digit is less than 5 create a one bit and if the odometer ; digit is 5 or more create a zero bit. ; MOVLW HALF2 BCF PORTA, F100k ; set 100 kHz low for 20 cycles SUBWF Digit0, W RRF OutByte, 1 MOVLW HALF10 SUBWF Digit1, W RRF OutByte, 1 MOVLW HALF10 SUBWF Digit2, W RRF OutByte, 1 MOVLW HALF10 SUBWF Digit3, W RRF OutByte, 1 MOVLW HALF10 SUBWF Digit4, W RRF OutByte, 1 MOVLW HALF10 SUBWF Digit5, W RRF OutByte, 1 MOVLW HALF10 SUBWF Digit6, W BSF PORTA, F100k ; set 100 kHz high for 5 cycles (4 of 5) RRF OutByte, 1 MOVLW HALF10 SUBWF Digit7, W RRF OutByte, 1 BCF PORTA, F100k ; set 100 kHz low for 20 cycles MOVLW 0xff XORWF OutByte ; ; Add delays so that the loop takes exactly 125 cycles (50 us). ; CALL Delay10 CALL Delay6 NOP BSF PORTA, F100k ; set 100 kHz high for 5 cycles (5 of 5) CALL Delay4 BCF PORTA, F100k ; set 100 kHz low for 20 cycles CALL Delay10 CALL Delay4 ; ; Check STOP signal once per loop then exit the loop with the next ; version of OutByte in W. The top of the loop writes PORTB. ; MOVF OutByte, W BTFSS PORTA, Stop ; is STOP pin high? GOTO Loop ; no, continue ; ; STOP-SYNC protocol: ; ; 1) Divider free running (No LEDs) ; - User sets STOP pin high. ; - PIC stops frequency generator. ; ; 2) Divider stopped (Red LED) ; - PIC waits for STOP pin to go low again. ; - User sets STOP pin low. ; - PIC resets counter and output lines. ; ; 3) Waiting for SYNC (Red and Green LEDs) ; - PIC waits for SYNC pin to go from low to high. ; - User sends 1 PPS pulse to SYNC pin. ; - PIC resumes frequency generator. ; ; 4) Divider running in sync (Green LED) ; BCF PORTA, Green BSF PORTA, Red BTFSC PORTA, Stop ; is STOP pin low? GOTO $-1 ; no, keep checking BSF PORTA, Green CLRF Digit0 CLRF Digit1 CLRF Digit2 CLRF Digit3 CLRF Digit4 CLRF Digit5 CLRF Digit6 CLRF Digit7 MOVLW 0xff ; start with outputs high MOVWF PORTB BSF PORTA, F100k ; start with 100 kHz high ; ; Resume on leading edge of SYNC pin. The PIC will be one to three ; instructions behind the actual 1 PPS sync pulse. ; BTFSC PORTA, Sync ; is 1 PPS sync low? GOTO $-1 ; no, keep checking BTFSS PORTA, Sync ; is 1 PPS sync high? GOTO $-1 ; no, keep checking ; ; Now synchronously rejoin main loop. ; BCF PORTA, Red INCF Digit0 NOP BCF STATUS, Zero BCF PORTA, F100k ; set 100 kHz low for 20 cycles GOTO Loop1 ; ---------------------------------------------------------------------- ; ; Delay routines. ; ; - To delay 1 cycle use NOP. ; - To delay 2 cycles use GOTO $+1. ; - To delay 3 cycles use NOP and GOTO $+1. ; - To delay 4 cycles use CALL Delay4. ; - To delay 10 cycles use CALL Delay10, etc. ; ; For other delays use a combination of the above. ; Delay100 GOTO $+1 Delay98 MOVLW 24 MOVWF DelayTemp DECFSZ DelayTemp GOTO $-1 Delay25 GOTO $+1 Delay23 MOVLW 4 MOVWF DelayTemp DECFSZ DelayTemp GOTO $-1 Delay10 GOTO $+1 Delay8 GOTO $+1 Delay6 GOTO $+1 Delay4 RETURN END