Reply by Phil Seakins December 27, 20022002-12-27
>Some time ago I developed some fairly rough and ready code to decode the low
>speed modem modulation used by Bell 202, V23 and the old Kansas City tape
>interface. It was primarily developed to allow a PIC to decode Caller ID
>signals from a phone line but can be used for a variety of different
>purposes.

Ken,

This is magic. Without looking too deeply it seems almost exactly what I
was about to start researching for my next project.

Phil Seakins.


Reply by Ken Boak December 27, 20022002-12-27
Listers,

Some time ago I developed some fairly rough and ready code to decode the low
speed modem modulation used by Bell 202, V23 and the old Kansas City tape
interface. It was primarily developed to allow a PIC to decode Caller ID
signals from a phone line but can be used for a variety of different
purposes.

The technique allows a 16F PIC to receive and decode Caller ID
signals, or to allow 2 PICS to communicate over the phone network, or
between local nodes strung together with bell-wire, for home automation
projects.

It allows both the data signal and power to be distributed to all
nodes on the network using just 2 wires. I am working on a low cost DIY
network for HA projects based on intelligent nodes communicating via wire or
low power RF using the popular SNAP protocol www.hth.com

How It Works:

The technique first uses a high gain amplifier to turn the sinusidal modem
tone signal into an approximate squarewave which can then be fed directly
into Port B7 set up to interrupt on both rising and falling edges

I use Timer 0 to time the period between the signal edges - using interrupt
on both edges, and compare these times against an upper and lower threshold
value. If greater than the threshold, I set a Port A bit else I clear it.
This signal on Port A is effectively the demodulated modem signal as a
serial bit stream which can be fed into a terminal program.

If anyone is interested in similar techniques - please drop me a line.
Meanwhile attached is the rudimentary source code, so you can see what's
going
on.

There is also a Yahoo Group for interested enthusiasts of such techniques -
we have about 540 members. Called rat_ring the rat refers to the PIC based
node - the Remote Applications Terminal

http://groups.yahoo.com/group/rat_ring/

We have source code for DTMF generation and decoding on a PIC16F84A, plus
coding and decoding popular modem modulation - again on an F84A.

In the members files area you will find constructional details for the rat
based projects as well as a 40 page tutorial describing how to safely
connect PIC devices to the telephone line and detect and decode Caller ID
and other telephone signals. Merry Christmas & Happy New Year,
Ken Boak

rat_ring


;*********************************************************************************************
; V23 demodulator code - running with a 3.579545 MHz ceramic resonator
;
; This code uses Timer 0 to time the pulse widths of the signal applied to the B7 pin
; Timer 0 is used with a divide by 4 prescaler
; Each time an edge is detected on pin B7 the routine generates the time period of the last half period.
; If the period is short - then it is a high frequency input, long pepriod = low frequency
; It stores the time (in half clock cycles) in a variable assigned to T0-count
; The count is compared with certain limits to see if the input signal frequency matches
; that of V23 high tone 2100Hz or V23 low tone 1300Hz
; The frequency thresholding is centered around 1700Hz so this will work with any modulation
; which uses frequencies either side of 1700Hz including V23, Bell 202 and
; Kansas City tape interface standards.
; Port pin A1 is set to signify 1300Hz (mark) and cleared to signal 2100Hz (space)
; and can thus be taken as the demodulated serial output, provided the routine controls the bit time
; The demod routine is timed to run in approximately 833uS. This is the bit time for 1200baud comms
; Port A2 is used to examine the output of the demod routine
;*********************************************************************************************

demod_init

movlw 0FFh
movwf ALLONES

clrf PORTB ; Init output latches for port B to 0
movlw B'10000001' ; set port B as output except bit 7, 0
tris PORTB ; Set all of PORT B to outputs

MOVLW B'00010001' ; Set A0, A4 as inputs, A1, A2, A3 as output
tris PORTA

; set up timer 0 with a divide 4 on the prescaler

init_rtcc

bsf STATUS,RP0
movlw B'11000001' ; weak pull ups off, rising edge interrupts, internal clock/4
movwf OPTION_REG
bcf STATUS, RP0
clrf TMR0 ; start timer 0

; movlw B'10010000' ; enable INT interrupts
movlw B'10001000' ; enable port B change interrupts
movwf INTCON

; Demodulate the incoming tones into 1200 baud asynchronous serial bits and put them out on port A2
; The demod routine ( plus an allowance for the ISR) should take 833uS to run, to set the correct
; 1200 baud bit time. This next routine re-times the bits into 833uS chunks

demod

movlw 120 ; Is sum >165 then mark
subwf T0_sum,0 ; 2100Hz gives 106, 1300 Hz gives 172
btfsc STATUS,C ; carry is SET IF T0_count > threshhold
bsf PORTA,2 ; set PORTA,2 send the bit to the "Test Pin"
btfss STATUS,C ;
bcf PORTA,2 ;

goto continue
; This next part bit-bangs Port A2 to reconstitute a serial data stream from the
; demodulated data. Connect to hyperterminal or similar running at 1200,8,N,1 to view

movfw T0_last
subwf T0_count,0 ; if T0_count > T0_last then mark
btfsc STATUS,C
bsf PORTA,2 ; raise Port A,2

movfw T0_last
subwf T0_count,0 ; if T0_count > T0_last then mark
btfss STATUS,C
bcf PORTA,2 ; lower Port A,2

goto continue

nop8
nop ; Some Nops to get the right timing delays
nop
nop
nop
nop
nop
nop
nop

nop
nop

continue
nop
nop
nop
nop
nop

movlw 215 ; this gives 833uS delay - put the rest of the user code
movwf bit_count ; into this 833uS time slot
; hold the space for 833uS

redo_A decfsz bit_count,Same
goto redo_A

goto demod ; this whole loop goes every 833uS - as near as possible ;***************************************************************************************
;This is the Interrupt service routine for determining the frequency on pin 13 Port B7

ISR
;**********************************************************************
;
; CONTEXT SAVE
;
;**********************************************************************

;Save STATUS and W registers into RAM before servicing timer interrupt

C_SAVE MOVWF W_TEMP
SWAPF STATUS,W
BCF STATUS,5 ; ENSURE BANK0 SAVE
MOVWF STATUS_TEMP

; BTFSS INTCON,INTF ; Test for external interrupt
; GOTO C_RESTORE

BTFSS INTCON,RBIF ; Exit ISR if it wasn't a port B change
GOTO C_RESTORE

; btfsc PORTB,7 ; Test for rising edge interrupt
; goto clr_rbintf ; exit if falling edge ; The signal applied to pin B7 causes an interrupt on change on both the rising and falling edges.
; On receipt of an interupt we stop the T0 counter and store its value into register T0_count.
; We then clear the counter to set it counting again. We use the divide by 4 prescaler
; 1300Hz gives a half period of 86 in T0_count, 2100Hz gives a T0_count of 53
; So if we test bit 6 of T0_count we can determine whether the signal is mark or space.
; and set Port A1 pin or flag accordingly. Constant mark will keep the port pin high.
; T0_sum contains the sum of the last 2 half periods - so can be used to qualify the signal frequency
; For this to work well, the signal needs to have close to equal high and low periods. If the signal
; M/S ratio varies too much from 1:1, the 1300Hz low period may be falsely interpreted as 2100Hz etc.

Changepin_isr

movfw T0_count ; get the last count
movwf T0_last ; save it

movfw TMR0 ; read Timer 0

movwf T0_count ; save it in T0_count for next time
clrf TMR0 ; start the timer again

btfsc T0_count,6 ; If T0_count <64 then lower Port A1 - a space
bsf PORTA,1
btfss T0_count,6 ; If T0_count >d then raise Port A1 - a mark
bcf PORTA,1

addwf T0_last,0 ; get the sum of last and current in W
movwf T0_sum ; put it in T0_sum for testing clr_rbintf

movf PORTB,1 ; read port b to clear the mismatch
BCF INTCON,RBIF ; clear port B changed interrupt bit
; BCF INTCON,INTF ; clear external interrupt bit
;********************************
;
; Context Restore
;
;********************************
C_RESTORE

BCF STATUS,5 ;ENSURE BANK0 RESTORE
SWAPF STATUS_TEMP,W
MOVWF STATUS
SWAPF W_TEMP,F
SWAPF W_TEMP,W

RETFIE
END