; ------------------------------------------------------------------------
;
; Title:
;
;   PD48 -- PIC 48 kHz to 100 Hz frequency divider for DECT
;
; Function:
;
;   This PIC program implements a digital frequency divider: the external
;   input clock is divided by a factor of 480. Given a 48 kHz input clock
;   the output frequency is 100 Hz (10 ms period).
;
;   The output waveform is unusual: the pulse width is 500 us except every
;   16th pulse is 5 ms wide. All periods are aligned at the falling edge.
;
;   For details see Digital Enhanced Cordless Telecommunications (DECT):
;     "this signal establishes the 10 ms DECT frame interval and the
;     160 ms DECT multiframe interval"
;
;   The pins are "T2-mini" compatible (pin2 clock input, pin3 pulse ouput).
;
; Diagram:
;                                ---__---
;                5V (Vdd)  +++++|1      8|=====  Ground (Vss)
;            48 kHz clock  ---->|2  pD  7|-
;           100 Hz output  <----|3  48  6|-
;                              o|4      5|-
;                                --------
; Notes:
;
;   Only 4 pins are required: power (2.0-5.5V), ground, input and output.
;   o Pull or tie unused pin4 to Vdd or Vss.
;   - Other unused pins may be left open.
;   Output frequency accuracy is the same as clock input accuracy.
;   Output drive current is 25 mA maximum per pin.
;   Coded for Microchip 12F675 but any '609 '615 '629 '635 '675 '683 works.
;
; Version:
;
;   21-Sep-2014  Tom Van Baak (tvb)  www.LeapSecond.com/pic
;
; ------------------------------------------------------------------------

; Microchip MPLAB IDE assembler code (mpasm).

        list        p=pic12f675
        include     p12f675.inc
        __config    _EC_OSC & _MCLRE_OFF & _WDT_OFF

; Register definitions.

        cblock  0x20            ; define register base
            count               ; mod-16 counter
        endc

; Pin definitions.

clock_pin   equ     5           ; GP5/pin2 external 48 kHz clock input
pulse_pin   equ     4           ; GP4/pin3 100 Hz output

; One-time PIC 12F675 initialization.

        org     0               ; power-on entry here
        bcf     STATUS,RP0      ; bank 0
        clrf    GPIO            ; set all pins low
        movlw   07h             ; set mode to turn
        movwf   CMCON           ;   comparator off
        bsf     STATUS,RP0      ; bank 1
        clrf    ANSEL-0x80      ; set digital IO (no analog A/D)
        movlw   ~(1<<pulse_pin)
        movwf   TRISIO-0x80     ; set output(0) and input(1) direction
        bcf     STATUS,RP0      ; bank 0
        clrf    count           ;

; With 48 kHz input clock, there are 12,000 PIC instructions per second.
; A 100 Hz pulse rate is 10 ms pulse period, which is 120 instructions.
; Make every 16th pulse wider than usual.
; Loop overhead is fixed at 20 cycles (isochronous).

loop:   call    Delay10         ; loop timing
        nop                     ; loop timing
        incf    count,F         ;
        movf    count,W         ; W = ++count % 16
        andlw   0x0F            ;
        skpz                    ; W=0 if count=0
          movlw   1             ; W=1 if count=1...15
        addwf   PCL,F           ; jump PCL+W (computed goto)
          goto  ms5             ;   sync (wide) pulse
          goto  us500           ;   normal (narrow) pulse

; Each isochronous code block below is exactly 100 cycles, which is 120
; cycles total including the overhead of the loop code above.

; Create 500 us (1/2 ms) pulse (114 cycles LOW, 6 cycles HIGH).
us500:  movlw   d'90'           ;
        call    DelayW1         ; delay (15 <= W <= 255)
        bsf     GPIO,pulse_pin  ; set output high (rising edge)
        call    Delay5          ;
        bcf     GPIO,pulse_pin  ; set output low (falling edge)
        goto    loop

; Create 5 ms pulse (60 cycles LOW, 60 cycles HIGH).
ms5:    movlw   d'36'           ;
        call    DelayW1         ; delay (15 <= W <= 255)
        bsf     GPIO,pulse_pin  ; set output high (rising edge)
        movlw   d'58'           ;
        call    DelayW1         ; delay (15 <= W <= 255)
        bcf     GPIO,pulse_pin  ; set output low (falling edge)
        goto    loop

        include delayw.asm      ; precise delay functions
        end
