ME Labs, Inc.
719-520-5323
 
Home:
  Developer Resources:

Programming Clues
    Sample Programs
   
    PICBASIC PRO™
Compiler Manual
    PICBASIC™ Compiler
Manual
    Serin2/Serout2 Modes
    ASCII Character Set
    Number Conversion
    Floating Point
Routines
    PBP Debug Monitor
    Articles and Tutorials

Hardware Clues
    Parts / Vendor List
    PICPROTO™ Boards
    LAB-X1 Docs
    LAB-X2 Docs
    LAB-X20 Docs
    LAB-X3 Docs
    LAB-X4 Docs
    LAB-XUSB Docs
    LAB-XT Docs
     
 

ME Labs, Inc. | 1-719-520-5323 | Example Program - PHONE2XT.pbp

PICBASIC PRO program the same as phone1xt.pbp, but with Caller ID display.
' Name        : PHONE2XT.pbp
' Compiler    : PICBASIC PRO Compiler 2.6
' Assembler   : PM or MPASM
' Target PIC  : PIC16F877-20, PIC18F452 or similar type
' Hardware    : LAB-XT Experimenter Board
' Oscillator  : 20MHz external
' Keywords    : DCD, HPWM, LCDOUT, LOOKUP, NCD, SELECT CASE
' Description : PICBASIC PRO program the same as phone1xt.pbp, but
' with Caller ID display.
'

' MeLabs PIC programmer should not be connected to the ICSP header
' when running this code. It may interfere with the operation
' of the keypad.  

Define LOADER_USED 1  ' Only required for melabs Loader

Define OSC 20         ' Match the crystal on the board

' Define LCD registers and bits
Define LCD_DREG  PORTD
Define LCD_DBIT  4
Define LCD_RSREG PORTE
Define LCD_RSBIT 0
Define LCD_EREG  PORTE
Define LCD_EBIT  1

led3            Var PORTC.0   ' LED3 output pin
led4            Var PORTC.1   ' LED4 output pin
sieze           Var PORTD.1   ' Relay control pin (sieze line)
dtmf_ready      Var PORTA.4   ' Data ready signal from MT8870
voice_int       Var PORTA.5   ' End of Message signal from voice chip
select_dtmf     Var PORTD.3   ' Data output enable to MT8870
loop_current    Var PORTA.3   ' Loop current present (active low)
ring_detect     Var PORTA.2   ' Ring detect pin (active low, square wave)
dtmf_out        Var PORTE.2   ' DTMF signal output pin
cid_serial_data Var PORTA.0   ' Caller ID data from FSK chip
tone_detect     Var PORTA.1   ' Indicates audio signal present on line
speaker         Var PORTC.2   ' Speaker output pin
select_voice    Var PORTD.2   ' Chip select for voice recorder
serial_mcu_out  Var PORTC.5   ' SPI data output
serial_clk      Var PORTC.3   ' SPI clock pin

' Define program variables
h            Var Byte     ' Loop counter, misc
i            Var Byte     ' Loop counter, misc
j            Var Byte     ' Loop counter, misc
col          Var Byte     ' Keypad column
row          Var Byte     ' Keypad row
key          Var Byte     ' Key value
disp_key     Var Byte     ' Key label
entry_string Var Byte[20] ' Array for storing user keyboard entry
ring_freq    Var Word     ' Frequency of ring signal (speaker PWM)
length       Var Byte     ' Length of caller id string
cid_string   Var Byte[96] ' Array for storing caller id string

   ADCON1 = 7             ' Set PORTA and PORTE to digital
   TRISD = %11110000      ' Set low bits of PORTD to output
   TRISC = %11111000      ' Set LED and Speaker pins to output
   PORTD = 0              ' Intialize output state of PORTD
   Pause 100              ' Wait for LCD to start up
   LCDOut $fe,1           ' Clear display
   OPTION_REG.7 = 0       ' Enable PORTB pullups
   'INTCON2.7 = 0

initialize:
   For i = 0 TO 19         ' Clear the user-entry array variable
     entry_string[i] = $FF ' Store $FF in each location of array
   Next i
   led3 = 0
   i = 0                   ' Set user-entry to location 0
   key = $FF               ' Set key value to $FF (cleared)
   sieze = 0               ' Deactivate the line-sieze relay (Hang Up)
   Pause 250
   LCDOut $fe, 1, "A=Off Hook  D=Dial", $FE, $C0  ' Display sign-on message

mainloop:
   GoSub getkey              ' Check for user keypad entry
   IF (ring_detect = 0) Then ' Check for ring-detect (incoming call, active low)
      j = 0                  ' Clear j variable (time elapsed since last ring-detect)
ring_cycle:
      led3 = 1               ' Light LED to indicate incoming call
      Pause 1                ' Pause 1mS so we can time this loop
      GoSub getkey           ' Check for user entry
      j = j + 1              ' Each count of j represents 1mS of time 
      ' If user pressed "A", he wants to answer the call. Ignore other keys.
      ' If 60mS has elapsed without a ring-detect, the ring has ended
      IF (j > 60) OR (key = 12) Then stop_ringing 
      IF ring_detect = 1 Then ring_cycle ' Stay in this loop if no ring-detect (60mS max)
                
      IF ring_freq = 2150 Then ' On each ring detect, change the speaker frequency.
         ring_freq = 2500      ' This gives us the familiar 20Hz warble tone. The 
      Else                     ' frequencies used (2150 and 2500) gave the most
         ring_freq = 2150      ' speaker volume when tested.
      EndIF
      HPwm 1,127,ring_freq     ' Initiate the speaker tone using HPWM. The output will continue until silenced.
      led3 = 0
      Pause 30                 ' Pause 30mS.  (Low period of ring-detect)               
      j = 30                   ' Set j to 30 (30mS)
      GoTo ring_cycle          ' Loop to keep monitoring the ring-detect
stop_ringing:                  ' Jump here at end of ring or to answer call
      HPwm 1,0,0               ' Silence the speaker
      led3 = 0                 ' Shut off the LED
      IF key = $FF Then GoSub caller_id ' If end of ring, check for caller id
   EndIF                
                
   Select Case key              ' Take action based on user keypad entry
          Case 12               ' User entry "A" - Off Hook
                sieze = 1       ' Activate sieze relay
                Pause 250       ' Pause 250mS to establish loop current
                LCDOut $FE, 2, "B=Hang Up, Dial #'s", $FE, $C0  ' New prompt on LCD
          Case 13,14            ' User entries "B" or "C" - Hang Up or Clear
                GoTo initialize ' Hang Up and initialize variables
          Case 15               ' User entry "D" - Dial user-entered string
                LCDOut $FE, 2, "Dial Tone Detect... "   ' Indicate status on LCD
                IF sieze = 1 Then  ' If already Off Hook, get new dial tone
                   sieze = 0       ' Deactivate sieze relay (hang up)
                   Pause 750       ' Pause 750mS to drop line
                EndIF   
                sieze = 1       ' Activate sieze relay (off hook)
                Pause 150       ' Pause 150mS to establish loop current
                j = 0           ' Clear j (number of tone-detect attempts)
detect:                         ' Jump here to try again
                h = 0           ' Clear h (tone cycles)
                Count tone_detect,150,h ' Count tone cycles for 150mS
                j = j+1         ' Increment number of tries
                IF (j < 50) AND (h < 50) Then detect ' Were there 50 tone cycles in 150mS? (>333Hz)
                ' If tone detected or no tone for 50 tries, continue
                LCDOut $FE, 2, "Dialing:            " 'Indicate status on LCD
                For i = 0 TO 19  ' Loop through the user-entry array
                  IF entry_string[i] = $FF Then end_string   ' $FF in array indicates end-of-string
                  DTMFOut dtmf_out,100,100,[entry_string[i]] ' Dial the DTMF digit in each location
                  entry_string[i] = $FF ' Clear the location after dialed
                Next i          ' Next location
end_string:                     ' Jump here is end-of-string
                i = 0           ' Set user-entry location back to 0
                LCDOut $FE, 1, "B=Hang Up, Dial #'s", $FE, $C0  ' Display prompt on LCD 
          Case Is < 12          ' User pressed number or *# key
                IF i = 0 Then
                   LCDOut $FE,$C0, REP 32\20,$FE,$C0
                EndIF
                LCDOut disp_key         ' Display key label on LCD
                IF sieze = 1 Then       ' If Off Hook, dial the digit
                   DTMFOut dtmf_out,200,10,[key] ' Dial DTMF for user-entered digit
                EndIF
                entry_string[i] = key   ' Save digit in current location
                i = i + 1               ' Increment location
        End Select                      ' Continue program
   GoTo mainloop                        ' Repeat main loop


' Subroutine to get a key from keypad
getkey:
   ' If Off Hook and the loop_current drops, Hang-Up the phone 
   ' and initialize variables.  Placed here because getkey is
   ' frequently called, and we want to avoid delays that hold
   ' the line off-hook.
   IF (sieze = 1) AND (loop_current = 1) Then initialize

   ' Check for keypress
   For row = 0 TO 3        ' Loop for 4 rows in keypad
       PORTB = 0           ' All output pins low
       TRISB = ~(DCD row)  ' Set one row pin to output
       '@ NOP              ' Fudge for 18F452
       col = PORTB >> 4 ' Read column pins

       IF col != $0F Then   ' Check if key is pressed
          HPwm 1,127,7500   ' Begin speaker tick
          GoTo gotkey       ' If any keydown, exit
       EndIF
   Next row                 ' Repeat for the next row
   key = $FF                ' No keys down, set key to $FF
   Return                   ' Return to main loop
        
gotkey:                     ' Change row and column to key number 0 - 15
   Pause 15                 ' Debounce
   HPwm 1,0,0               ' End speaker tick
        
   ' Wait for all keys up
   PORTB = 0                ' All output pins low
   TRISB = $F0              ' Least significant 4 pins out, top 4 pins in
   IF ((PORTB >> 4) != $0F) Then gotkey   ' If any keys down, loop
   key = (row * 4) + (NCD (col^$0F)) - 1  ' Combine row and column into numeric value
        
   ' Translate key to display label:
   ' 1  2  3  A
   ' 4  5  6  B
   ' 7  8  9  C
   ' *  0  #  D
   LookUp key,["123A456B789C*0#D"],disp_key
        
   ' Translate key to DTMFOUT tone
   ' 1   2   3   12
   ' 4   5   6   13
   ' 7   8   9   14
   ' 10  0   11  15
   LookUp key,[1,2,3,12,4,5,6,13,7,8,9,14,10,0,11,15],key
   Return                       ' Return to main loop

caller_id:
   Pause 200    ' Wait 200mS for line to settle

   ' Look for CID data, abort if no data for 500mS
   SerIn2 cid_serial_data, 813, 500, mainloop,  [wait($80),length,STR cid_string\length]
   LCDOut $fe,1                                 ' Clear the display
   For i = 0 TO length                          ' Begin loop, each count represents a character
                                                ' in the recorded CID data.
       Select Case cid_string[i]  ' For each character, check for known parameter types
                   Case $01                     ' Parameter type 01 (hex) is date and time.
                        i = i + 1               ' Move index pointer to parameter length character.
                                                ' This avoids the length character being mistaken for
                                                ' a parameter type code.

                        LCDOut $fe,$80,cid_string[i+1],cid_string[i+2],_ ' Display date and time
                        $FE,$C0, cid_string[i+3],cid_string[i+4], " ",_
                        $FE,$83, cid_string[i+5],cid_string[i+6],":",_
                        cid_string[i+7],cid_string[i+8]
                                
                   Case $02, $04                ' Parameter type 02 is the phone number.
                                                ' Parameter type 04 is the reason, if there is no number
                        LCDOut $FE,$89          ' Move the LCD cursor to position 9 on line 1
                        GoSub disp_string       ' Display the number string

                   Case $07, $08                ' Parameter type 07 is the caller's name.
                                                ' Parameter type 08 is the reason, if there is no name
                        LCDOut $FE,$C3          ' Move the LCD cursor to position 4, line 2
                        GoSub disp_string       ' Display the name string
        End Select
   Next i
        
   ' Because the caller id information overwrites the user-entry on the display,
   ' we must reset the user-entry array and pointer.
   For i = 0 TO 19                              ' Clear the user-entry array variable
     entry_string[i] = $FF                      ' Store $FF in each location of array
   Next i
   i = 0                                        ' Set user-entry to location 0
   Return
        
disp_string:                                            
   i = i + 1                                    ' Move the index pointer to the parameter-length byte
   IF cid_string[i]=1 Then                      ' If the parameter is only 1 character long, then display a reason
      IF cid_string[i+1] = "O" Then LCDOut "Out Of Area" ' "O" is Out Of Area
         IF cid_string[i+1] = "P" Then LCDOut "Blocked"  ' "P" is Private Caller
      Else                                      ' If the parameter is longer than 1 character
         For h = 1 TO cid_string[i]             ' Loop from 1 to the length of the parameter
           LCDOut cid_string[i+h]               ' Display each character in the parameter
        Next h                                  ' Do it for the next character
   EndIF
   Return

   End

           

Download the program file.