;*****************************************************************************************
; Copyright © [4/01/2000] Scenix Semiconductor, Inc. All rights reserved.
;
; Scenix Semiconductor, Inc. assumes no responsibility or liability for
; the use of this [product, application, software, any of these products].
; Scenix Semiconductor conveys no license, implicitly or otherwise, under
; any intellectual property rights.
; Information contained in this publication regarding (e.g.: application,
; implementation) and the like is intended through suggestion only and may
; be superseded by updates. Scenix Semiconductor makes no representation
; or warranties with respect to the accuracy or use of these information,
; or infringement of patents arising from such use or otherwise.
;*****************************************************************************************
;
; Filename:
; DTMF_det.src
;
; Authors:
; Chris Fogelklou
; Applications Engineer
; Scenix Semiconductor, Inc.
;
; Revision:
; 1.01
;
; Target Part(s):
; AB9929AA
;
; Default Oscillator Frequency:
; 50MHz
;
; Assemblers:
; SXKey28L Version 1.09 for SX18/20/28AC
; SASM Version 1.45.2
; SXKey52 Version 1.19 for SX48/52BD (only assembled, not _properly_ tested)
;
; Date Written:
; Feb 24, 2000
;
; Program Description:
; This DTMF Detection code contains a new reference algorithm. It is a ported
; customer code, and it is not a "revised" version of any of the previous versions
; of the DTMF Detection.
; It contains a 9600bps UART for output of received digits to a terminal screen.
; This program is ready to run on V.23 Modem board (tested on Rev. 1.2) with a minor
; modification to the board; the input filters has to be disabled;
; The LP filter has to be disabled by moving the top lead of R25 to pin 14 of U4.
; By doing this, the two OP-amps doing the LP-filtering is bypassed. The HP-filter
; is taken care of in the program by setting the "cntrl_3" pin on RB.5 low. This action
; forces the output from this filter to gnd.
;
; Interface Pins:
; rs232RxPin equ ra.1 ; RS-232 reception pin
; rs232TxPin equ ra.2 ; RS-232 transmission pin
; ledPin equ rb.0 ; LED flasher pin
; dtmfHook equ rb.4 ; drive hook low to go off-hook
; cntrl_3 equ rb.5 ; drive cntrl_3 low to disable the output of the HPF
; dtmfdADInPin equ rc.2 ; A/D Input pin
; dtmfdADFdbkPin equ rc.3 ; A/D feedback pin
; dtmfdImp600Pin equ rc.5 ; To cancel output signals on a 600ohm line
;
; Revision History:
;
; 1.0 Ported customer DTMF detection code to a demo program.
; 1.01 Implemented according to VP guide 1.06, checkPowers routine updated to
; check for low level and high level according to Bellcore. Improved A/D
; routine by adding offset, tri-stating feedback pin when not active and removed
; hardcoding of pins (not "destroying" the whole port for this routine).
; dtmfDetectSine is rewritten to reduce cyclecount/efficiency. DTMF_SAMPL_HI is
; increased from 105 to 115 cycles for better results at high frequencies.
; Modified dtmfdGetValid so that it requieres 30ms of silence to detect a valid
; digit. Also, the ":redoHighFrequencies" is modified to only check that
; the freq. detected is the same as the previous -if not start all over again...
; JEE/AEH/OG, SX-Design, Aug 11, 2000
;
; Virtual Peripherals:
; UART transmitt
; LED flasher
; 5ms Timer
; DTMF detection routines including a simple Sigma Delta A/D module.
; The simple Sigma delta A/D that takes 36 samples @5MHz (1-bit) and with a Nyquist
; frequency of Fs=10kHz. This routine is not the same as the Sigma Delta
; Virtual Peripheral.
;
;*****************************************************************************************
; Target SX
; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC,
; SX48BD, or SX52BD.
;*****************************************************************************************
;SX18_20
SX28
;SX48_52
;*****************************************************************************************
; Assembler Used
; Uncomment the following line if using the Parallax SX-Key assembler. SASM assembler
; enabled by default.
;*****************************************************************************************
SX_Key
;*****************************************************************************************
; Runnable Demo?
; Uncomment the following line to enable the main program, creating a runnable demo
;*****************************************************************************************
DEMO
;*********************************************************************************
; Assembler directives:
; high speed external osc, turbo mode, 8-level stack, and extended option reg.
;
; SX18/20/28 - 4 pages of program memory and 8 banks of RAM enabled by default.
; SX48/52 - 8 pages of program memory and 16 banks of RAM enabled by default.
;
;*********************************************************************************
IFDEF SX_Key ;SX-Key Directives
;VP_BEGIN: DTMF Detection
watch dtmfdSinAcc1Lo,16,sdec
watch dtmfdCosAcc1Lo,16,sdec
watch dtmfdSinAcc2Lo,16,sdec
watch dtmfdCosAcc2Lo,16,sdec
watch dtmfdSinAcc3Lo,16,sdec
watch dtmfdCosAcc3Lo,16,sdec
watch dtmfdSinAcc4Lo,16,sdec
watch dtmfdCosAcc4Lo,16,sdec
watch wreg,1,fstr
watch wreg,8,sdec
watch wreg,8,udec
watch dtmfdAtoDVal,8,sdec
watch dtmfdHiFreqPow,8,udec
watch dtmfdLoFreqPow,8,udec
watch dtmfdRefAcc1Lo,8,udec
watch dtmfdSampleCnt,8,udec
watch dtmfdResult1,8,udec
watch dtmfdResult2,8,udec
watch dtmfdResult3,8,udec
watch dtmfdResult4,8,udec
watch dtmfdHighScore,8,udec
watch dtmfdRunUpScore,8,udec
watch dtmfdRunUpIndex,8,udec
watch dtmfdWinnIndex,8,udec
watch dtmfdSampleLows,1,ubin
;VP_END: DTMF Detection
IFDEF SX18_20 ;SX18AC or SX20AC device directives for SX-Key
device SX18L,oschs2,turbo,stackx_optionx
ENDIF
IFDEF SX28 ;SX28AC device directives for SX-Key
device SX28L,oschs2,turbo,stackx_optionx
ENDIF
IFDEF SX48_52 ;SX48/52/BD device directives for SX-Key
device oschs2
ENDIF
freq 50_000_000 ; Debugger frequency
ELSE ;SASM Directives
IFDEF SX18_20 ;SX18AC or SX20AC device directives for SASM
device SX18,oschs2,turbo,stackx,optionx
ENDIF
IFDEF SX28 ;SX28AC device directives for SASM
device SX28,oschs2,turbo,stackx,optionx
ENDIF
IFDEF SX48_52 ;SX48BD or SX52BD device directives for SASM
device SX52,oschs2
ENDIF
ENDIF
id 'DTMFDET4'
reset resetEntry ; set reset vector
;*****************************************************************************************
; Macros
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; To support compatibility between source code written for the SX28 and the SX52,
; use macros.
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;*********************************************************************************
; Macro: _bank
; Sets the bank appropriately for all revisions of SX.
;
; This is required since the bank instruction has only a 3-bit operand, it cannot
; be used to access all 16 banks of the SX48/52. For this reason, FSR.7
; (SX48/52bd production release) needs to be set appropriately, depending
; on the bank address being accessed.
;
; So, instead of using the bank instruction to switch between banks, use _bank instead.
;
;*********************************************************************************
_bank macro 1
bank \1
IFDEF SX48_52
IF \1 & %10000000 ;SX48BD and SX52BD (production release) bank instruction
setb fsr.7 ;modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software.
ELSE
clrb fsr.7
ENDIF
ENDIF
endm
;*****************************************************************************************
; Macros for SX28/52 Compatibility
;*****************************************************************************************
;*********************************************************************************
; Macro: _mode
; Sets the MODE register appropriately for all revisions of SX.
;
; This is required since the MODE (or MOV M,#) instruction has only a 4-bit operand.
; The SX18/20/28AC use only 4 bits of the MODE register, however the SX48/52BD have
; the added ability of reading or writing some of the MODE registers, and therefore use
; 5-bits of the MODE register. The MOV M,W instruction modifies all 8-bits of the
; MODE register, so this instruction must be used on the SX48/52BD to make sure the MODE
; register is written with the correct value. This macro fixes this.
;
; So, instead of using the MODE or MOV M,# instructions to load the M register, use
; _mode instead.
;
;*********************************************************************************
_mode macro 1
IFDEF SX48_52
expand
mov w,#\1 ;loads the M register correctly for the SX48BD and SX52BD
mov m,w
noexpand
ELSE
expand
mov m,#\1 ;loads the M register correctly for the SX18AC, SX20AC
noexpand ;and SX28AC
ENDIF
endm
;*****************************************************************************************
; INCP/DECP macros for incrementing/decrementing pointers to RAM
; used to compensate for incompatibilities between SX28 and SX52
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; To support compatibility between source code written for the SX28 and the SX52,
; use macros. This macro compensates for the fact that RAM banks are contiguous in
; the SX52, but separated by 0x20 in the SX18/28.
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
INCP macro 1
inc \1
IFNDEF SX48_52
setb \1.4 ; If SX18,20 or SX28, keep bit 4 of the pointer = 1
ENDIF ; to jump from $1f to $30, etc.
endm
DECP macro 1
IFDEF SX48_52
dec \1
ELSE
clrb \1.4 ; If SX18,20 or SX28, forces rollover to next bank
dec \1 ; if it rolls over. (Skips banks with bit 4 = 0)
setb \1.4 ; Eg: $30 --> $20 --> $1f --> $1f
ENDIF ; AND: $31 --> $21 --> $20 --> $30
endm
;*****************************************************************************************
; Error generating macros
; Used to generate an error message if the label is unintentionally moved into the
; second half of a page. Use for lookup tables.
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; Surround lookup tables with the tableStart and tableEnd macros. An error will
; be generated on assembly if part of the table is in the second half of a page.
;
; Example:
; lookupTable1
; add pc,w ; Add w register to program counter
; tableStart
; retw 0
; retw 4
; retw 8
; retw 4
; tableEnd
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
tableStart macro 0 ; Generates an error message if code that MUST be in
; the first half of a page is moved into the second half.
if $ & $100
ERROR 'Must be located in the first half of a page.'
endif
endm
tableEnd macro 0 ; Generates an error message if code that MUST be in
; the first half of a page is moved into the second half.
if $ & $100
ERROR 'Must be located in the first half of a page.'
endif
endm
;*****************************************************************************************
; Data Memory address definitions
; These definitions ensure the proper address is used for banks 0 - 7 for 2K SX devices
; (SX18/20/28) and 4K SX devices (SX48/52).
;*****************************************************************************************
IFDEF SX48_52
global_org = $0A
bank0_org = $00
bank1_org = $10
bank2_org = $20
bank3_org = $30
bank4_org = $40
bank5_org = $50
bank6_org = $60
bank7_org = $70
ELSE
global_org = $08
bank0_org = $10
bank1_org = $30
bank2_org = $50
bank3_org = $70
bank4_org = $90
bank5_org = $B0
bank6_org = $D0
bank7_org = $F0
ENDIF
;*****************************************************************************************
; Global Register definitions
; NOTE: Global data memory starts at $0A on SX48/52 and $08 on SX18/20/28.
;*****************************************************************************************
org global_org
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; Use only these defined label types for global registers. If an extra temporary
; register is required, adhere to these label types. For instance, if two temporary
; registers are required for the Interrupt Service Routine, use the labels isrTemp0
; and isrTemp1.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
flags0 equ global_org + 0 ; stores bit-wise operators like flags
; and function-enabling bits (semaphores)
;VP_BEGIN: DTMF detection
dtmfdDetectEn equ flags0.1
dtmfdSign equ flags0.2 ; Flag that indicates the sign of the mult.result
dtmfdInputSign equ flags0.3 ; used by DTMF detection for A/D sample
dtmfdSampleLows equ flags0.4 ; To sample low frequencies
dtmfdDetectDone equ flags0.5 ; Flag that indicates to main program that the
; sampling period is over.
;VP_END: DTMF detection
;VP_BEGIN : 5ms Timer
timer5msFlag equ flags0.6
;VP_END : 5ms Timer
flags1 equ global_org + 1 ; stores bit-wise operators like flags
; and function-enabling bits (semaphores)
localTemp0 equ global_org + 2 ; temporary storage register
; Used by first level of nesting
; Never guaranteed to maintain data
localTemp1 equ global_org + 3 ; temporary storage register
; Used by second level of nesting
; or when a routine needs more than one
; temporary global register.
localTemp2 equ global_org + 4 ; temporary storage register
; Used by third level of nesting or by
; main loop routines that need a loop
; counter, etc.
isrTemp0 equ global_org + 5 ; Interrupt Service Routine's temp register.
; Don't use this register in the mainline.
isrTemp1 equ global_org + 6 ; Interrupt Service Routine's temp register.
; Don't use this register in the mainline.
;*****************************************************************************************
; RAM Bank Register definitions
;*****************************************************************************************
;*********************************************************************************
; Bank 0
;*********************************************************************************
org bank0_org
bank0 = $
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Avoid using bank0 in programs written for SX48/52.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;*********************************************************************************
; Bank 1
;*********************************************************************************
org bank1_org
bank1 = $
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; Tip 1:
; Indicate which Virtual Peripherals a portion of source code or declaration belongs
; to with a ;VP_BEGIN: VirtualPeripheralName and VP_END: comment.
;
; Tip 2:
; All RAM location declaration names should be
; - left justified
; - less than 2 tabs in length
; - written in hungarian notation
; - prefixed by a truncated version of the Virtual Peripheral's name
;
; Examples:
;
; ;VP_BEGIN: RS232 Transmit
; rs232TxBank = $ ;UART Transmit bank
; rs232TxHigh ds 1 ;hi byte to transmit
; rs232TxLow ds 1 ;low byte to transmit
; rs232TxCount ds 1 ;number of bits sent
; rs232TxDivide ds 1 ;xmit timing (/16) counter
; ;VP_END: RS232 Transmit
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;VP_BEGIN: VP Scheduler
isrMultiplex ds 1 ; The isrMultiplex register is used to switch to a new
;VP_END: VP Scheduler ; execution thread on each pass of the ISR.
;VP_BEGIN : 5ms Timer
Timer5msBank = $
Timer5ms ds 1
Timer5msDiv ds 1
;VP_END : 5ms Timer
;VP_BEGIN: RS232 Transmit
rs232TxBank = $ ;UART Transmit bank
rs232TxHigh ds 1 ;hi byte to transmit
rs232TxLow ds 1 ;low byte to transmit
rs232TxCount ds 1 ;number of bits sent
rs232TxDivide ds 1 ;xmit timing (/16) counter
;VP_END: RS232 Transmit
;*********************************************************************************
; Bank 2
;*********************************************************************************
org bank2_org
bank2 = $
strDialBytes ds 4
strDialCount ds 1
;*********************************************************************************
; Bank 3
;*********************************************************************************
org bank3_org
bank3 = $
;*********************************************************************************
; Bank 4
;*********************************************************************************
org bank4_org
bank4 = $
;*********************************************************************************
; Bank 5
;*********************************************************************************
org bank5_org
bank5 = $
;VP_BEGIN: DTMF Detection
dtmfdRefBank = $ ; Accumulator/counters used to run the references.
dtmfdRefAcc1Lo ds 1 ; the low byte of the phase accumulator for the lowest frequency being generated
dtmfdRefAcc1Hi ds 1
dtmfdRefAcc2Lo ds 1
dtmfdRefAcc2Hi ds 1
dtmfdRefAcc3Lo ds 1
dtmfdRefAcc3Hi ds 1
dtmfdRefAcc4Lo ds 1
dtmfdRefAcc4Hi ds 1 ; the high byte of the phase accumulator for the highest frequency being generated
dtmfdHiFreqInd ds 1
dtmfdLoFreqInd ds 1
dtmfdHiFreqPow ds 1
dtmfdLoFreqPow ds 1
dtmfdSampleCnt ds 1
dtmfdDigitIndex ds 1 ; the index that points to a valid DTMF digit
;VP_END: DTMF Detection
;*********************************************************************************
; Bank 6
;*********************************************************************************
org bank6_org
bank6 = $
;VP_BEGIN: DTMF Detection
dtmfdCh1 = $
dtmfdSinAcc1Lo ds 1 ; Sine and cosine accumulators for accumulating the results of the DFT
dtmfdSinAcc1Hi ds 1
dtmfdCosAcc1Lo ds 1
dtmfdCosAcc1Hi ds 1
dtmfdSinAcc2Lo ds 1
dtmfdSinAcc2Hi ds 1
dtmfdCosAcc2Lo ds 1
dtmfdCosAcc2Hi ds 1
dtmfdSinAcc3Lo ds 1
dtmfdSinAcc3Hi ds 1
dtmfdCosAcc3Lo ds 1
dtmfdCosAcc3Hi ds 1
dtmfdSinAcc4Lo ds 1
dtmfdSinAcc4Hi ds 1
dtmfdCosAcc4Lo ds 1
dtmfdCosAcc4Hi ds 1
dtmfdResult1 equ dtmfdSinAcc1Lo
dtmfdResult2 equ dtmfdSinAcc1Hi
dtmfdResult3 equ dtmfdCosAcc1Lo
dtmfdResult4 equ dtmfdCosAcc1Hi
dtmfdHighScore equ dtmfdSinAcc2Lo
dtmfdRunUpScore equ dtmfdSinAcc2Hi
dtmfdRunUpIndex equ dtmfdCosAcc2Lo
dtmfdWinnIndex equ dtmfdCosAcc2Hi
;VP_END: DTMF Detection
;*********************************************************************************
; Bank 7
;*********************************************************************************
org bank7_org
bank7 = $
;VP_BEGIN: DTMF Detection
dtmfdMathBank = $
dtmfdLoopCount ds 1
dtmfdMathFlags ds 1
dtmfdNeg equ dtmfdMathFlags.0
dtmfdRootMask ds 1
dtmfdALo ds 1
dtmfdAHi ds 1
dtmfdBLo ds 1
dtmfdBHi ds 1
dtmfdInput ds 1
dtmfdAtoDVal ds 1
dtmfdAtoDCount ds 1
;VP_END: DTMF Detection
IFDEF SX48_52
;*********************************************************************************
; Bank 8
;*********************************************************************************
org $80 ;bank 8 address on SX52
bank8 = $
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - This extra memory is not available in the SX18/28, so don't use it for Virtual
; Peripherals written for both platforms.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;*********************************************************************************
; Bank 9
;*********************************************************************************
org $90 ;bank 9 address on SX52
bank9 = $
;*********************************************************************************
; Bank A
;*********************************************************************************
org $A0 ;bank A address on SX52
bankA = $
;*********************************************************************************
; Bank B
;*********************************************************************************
org $B0 ;bank B address on SX52
bankB = $
;*********************************************************************************
; Bank C
;*********************************************************************************
org $C0 ;bank C address on SX52
bankC = $
;*********************************************************************************
; Bank D
;*********************************************************************************
org $D0 ;bank D address on SX52
bankD = $
;*********************************************************************************
; Bank E
;*********************************************************************************
org $E0 ;bank E address on SX52
bankE = $
;*********************************************************************************
; Bank F
;*********************************************************************************
org $F0 ;bank F address on SX52
bankF = $
ENDIF
;*********************************************************************************
; Pin Definitions:
;*********************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Store all initialization constants for the I/O in the same area, so
; pins can be easily moved around.
; - Pin definitions should follow the same format guidelines as RAM definitions
; - Left justified
; - No underscores. Indicate word separation with capital letters
; - Less that 2 tabs in length
; - Indicate the Virtual Peripheral the pin is used for
; - All pin definitions for a specific VP must have the same prefix
; - Only use symbolic names to access a pin/port in the source code.
; - Example:
; ; VP_BEGIN: RS232 Transmit
; rs232TxPin equ ra.3
; ; VP_END: RS232 Transmit
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;VP_BEGIN: rs232 Receive
rs232RxPin equ ra.1 ; RS-232 reception pin
;VP_END: rs232 Receive
;VP_BEGIN: rs232 Transmit
rs232TxPin equ ra.2 ; RS-232 transmission pin
;VP_END: rs232 Transmit
RA_latch equ %11111111 ;SX18/20/28/48/52 port A latch init
RA_DDIR equ %11111011 ;SX18/20/28/48/52 port A DDIR value
RA_LVL equ %00000000 ;SX18/20/28/48/52 port A LVL value
RA_PLP equ %11111111 ;SX18/20/28/48/52 port A PLP value
;VP_BEGIN: LED flasher
ledPin equ rb.0 ; LED flasher pin
;VP_END: LED flasher
;VP_BEGIN: DTMF Detection
dtmfHook equ rb.4 ; drive hook low to go off-hook
cntrl_3 equ rb.5 ; drive cntrl_3 low to disable the output of the HP-Filter
;VP_END: DTMF Detection
RB_latch equ %00010000 ;SX18/20/28/48/52 port B latch init
RB_DDIR equ %11001110 ;SX18/20/28/48/52 port B DDIR value
RB_ST equ %11111111 ;SX18/20/28/48/52 port B ST value
RB_LVL equ %00000000 ;SX18/20/28/48/52 port B LVL value
RB_PLP equ %11111111 ;SX18/20/28/48/52 port B PLP value
IFNDEF SX18_20 ; There is no C port on SX18/20
;VP_BEGIN: DTMF Detection
dtmfdPort equ rc ; Port used for DTMF
ADIn equ 2 ; Pin number AD Input pin (pin 2 has low gain)
ADFdbk equ 3 ; Pin number for ADFeedback pin
Imp450 equ 4 ; Make low impedance to cancel output signals on a 450ohm line
Imp600 equ 5 ; Make low impedance to cancel output signals on a 600ohm line
Imp750 equ 6 ; Make low impedance to cancel output signals on a 750ohm line
Imp900 equ 7 ; Make low impedance to cancel output signals on a 900ohm line
; Do changes above
dtmfdADInPin equ dtmfdPort.ADIn
dtmfdADFdbkPin equ dtmfdPort.ADFdbk
dtmfdImp450Pin equ dtmfdPort.Imp450
dtmfdImp600Pin equ dtmfdPort.Imp600
dtmfdImp750Pin equ dtmfdPort.Imp750
dtmfdImp900Pin equ dtmfdPort.Imp900
;VP_END: DTMF Detection
RC_latch equ %00000000 ;SX18/20/28/48/52 port C latch init
RC_DDIR equ %11010111 ;SX18/20/28/48/52 port C DDIR value
RC_ST equ %11111111 ;SX18/20/28/48/52 port C ST value
RC_LVL equ %00000000 ;SX18/20/28/48/52 port C LVL value
RC_PLP equ %11111111 ;SX18/20/28/48/52 port C PLP value
IFDEF SX48_52 ;SX48BD/52BD Port initialization values
RD_latch equ %00000000 ;SX48/52 port D latch init
RD_DDIR equ %11111111 ;SX48/52 port D DDIR value
RD_ST equ %11111111 ;SX48/52 port D ST value
RD_LVL equ %00000000 ;SX48/52 port D LVL value
RD_PLP equ %11111111 ;SX48/52 port D PLP value
RE_latch equ %00000000 ;SX48/52 port E latch init
RE_DDIR equ %11111111 ;SX48/52 port E DDIR value
RE_ST equ %11111111 ;SX48/52 port E ST value
RE_LVL equ %00000000 ;SX48/52 port E LVL value
RE_PLP equ %11111111 ;SX48/52 port E PLP value
ENDIF ;(SX18_20)
ENDIF ;(SX48_52)
;*****************************************************************************************
; Program constants
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; To calculate the interrupt period in cycles:
; - First, choose the desired interrupt frequency
; - Should be a multiple of each Virtual Peripherals sampling frequency.
; - Example: 19200kHz UART sampling rate * 16 = 307.200kHz
; - Next, choose the desired oscillator frequency.
; - 50MHz, for example.
; - Next calculate the
; - Perform the calculation intPeriod = (cyclesPerSecond / interruptFrequency)
; = (50MHz / 307.2kHz)
; = 162.7604
; - Round intPeriod to the nearest integer:
; = 163
; - Now calculate your actual interrupt rate:
; = cyclesPerSecond / intPeriod
; = 50MHz / 163
; = 306.748kHz
; - This interrupt frequency will be the timebase for all of the Virtual
; Peripherals
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Prescaler is set to 4, this can be changed to 2 if the
; intPeriod is set to 250.
intPeriod = 125 ; Gives an interrupt period at 50MHz of (125 * 4 * (1/50000000)s) = 10us
; Which gives an interrupt frequency of (1/10us)Hz = 100.000kHz
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Include all calculations for Virtual Peripheral constants for any sample
; rate.
; - Relate all Virtual Peripheral constants to the sample rate of the Virtual
; Peripheral.
; - Example:
; ; VP_BEGIN: 5ms Timer
; TIMER_DIV_CONST equ 192 ; This constant = timer sample rate/200Hz = 192
; ; VP_END: 5ms Timer
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;VP_BEGIN: RS232 Transmit
RS232TX_FS = 10000 ; Thread rate of UART; called on every 10th pass of the Interrupt Service Routine
RS232TX_BAUD = 9600 ;
RS232TX_DIVIDE = RS232TX_FS/RS232TX_BAUD ; Divide rate constant used by the program
;VP_END: RS232 Transmit
;VP_BEGIN: DTMF Detection
;**************************************************************************
; CALCULATING CONSTANTS FOR DTMF DETECTION
;
; The constant = 2^n * Ts * FREQ,
; where n is the number of bits in the phase accumulator for each
; signal generator, Ts is the sample rate, and FREQ is the desired
; output frequency.
; We know that the phase accumulator (phaseAcc) is 16 bits, so n = 16
;
; = 1/([cyclesperinterrupt] * [instructiontime] * [ISR passes])
;
; Therefore, the constant = 2^n * (1/DTMF_FS) * FREQ
;
; Convert the result of the calculation to a hexadecimal number and load
; the upper byte into the phaseAccHi register and the lower byte
;**************************************************************************
DTMF_FS = 10000 ; sampling frequency for DTMF detection (interrupt rate of DTMF Detection threads)
DTMF_BITS = 65536 ; 2^16 is the value of the phase accumulator
TIMER_COUNTER = 5*DTMF_FS/1000; Counts to 5ms
DTMF_FREQ697HI equ ((DTMF_BITS * 697)/DTMF_FS) >> 8 ; high 8 bits of 16 bit constant
DTMF_FREQ697LO equ ((DTMF_BITS * 697)/DTMF_FS) & $0ff ; low 8 bits of 16 bit constant
DTMF_FREQ770HI equ ((DTMF_BITS * 770)/DTMF_FS) >> 8
DTMF_FREQ770LO equ ((DTMF_BITS * 770)/DTMF_FS) & $0ff
DTMF_FREQ852HI equ ((DTMF_BITS * 852)/DTMF_FS) >> 8
DTMF_FREQ852LO equ ((DTMF_BITS * 852)/DTMF_FS) & $0ff
DTMF_FREQ941HI equ ((DTMF_BITS * 941)/DTMF_FS) >> 8
DTMF_FREQ941LO equ ((DTMF_BITS * 941)/DTMF_FS) & $0ff
DTMF_FREQ1209HI equ ((DTMF_BITS * 1209)/DTMF_FS) >> 8
DTMF_FREQ1209LO equ ((DTMF_BITS * 1209)/DTMF_FS) & $0ff
DTMF_FREQ1336HI equ ((DTMF_BITS * 1336)/DTMF_FS) >> 8
DTMF_FREQ1336LO equ ((DTMF_BITS * 1336)/DTMF_FS) & $0ff
DTMF_FREQ1477HI equ ((DTMF_BITS * 1477)/DTMF_FS) >> 8
DTMF_FREQ1477LO equ ((DTMF_BITS * 1477)/DTMF_FS) & $0ff
DTMF_FREQ1633HI equ ((DTMF_BITS * 1633)/DTMF_FS) >> 8
DTMF_FREQ1633LO equ ((DTMF_BITS * 1633)/DTMF_FS) & $0ff
DTMF_SCALING equ 4 ; The number of bits to shift the accumulated results
; to the right to squeeze them into 8 bits. Decrease this
; for sensitivity to smaller signals. If so, consider doubling DTMF_MIN_SCORE.
DTMF_ADOFFSET equ 7 ; Offset correction value for ADC. Adjust this value only when there is no
; input signal,and poll the code and check the dtmfdAtoDVal to be near zero.
DTMF_ADNOSAMPL equ 36 ; Count 36 feedback bits per sample. Higher values take longer to process,
; and the minimum time to detect a valid digit will increase.
; Also, if this value is decreased further, the dynamic range of the A/D
; will also be decreased.
DTMF_MIN_SCORE equ 50 ; The minimum score required to detect a digit. However, a valid digit
; requires the measured value in ResultN to be twice this value...
; To decrease the scaling factor, increase this value as well.
; (approx. double the value if the scaling factor is increased by 1)
; Accumulated results will be higher.
DTMF_SAMPL_LO equ 140 ; The number of samples to take to detect a low frequency
DTMF_SAMPL_HI equ 115 ; The number of samples to take to detect a high frequency
;VP_END: DTMF Detection
;-------------------------------------------------------------------------------------
IFDEF SX48_52
;*********************************************************************************
; SX48BD/52BD Mode addresses
; *On SX48BD/52BD, most registers addressed via mode are read and write, with the
; exception of CMP and WKPND which do an exchange with W.
;*********************************************************************************
; Timer (read) addresses
TCPL_R equ $00 ;Read Timer Capture register low byte
TCPH_R equ $01 ;Read Timer Capture register high byte
TR2CML_R equ $02 ;Read Timer R2 low byte
TR2CMH_R equ $03 ;Read Timer R2 high byte
TR1CML_R equ $04 ;Read Timer R1 low byte
TR1CMH_R equ $05 ;Read Timer R1 high byte
TCNTB_R equ $06 ;Read Timer control register B
TCNTA_R equ $07 ;Read Timer control register A
; Exchange addresses
CMP equ $08 ;Exchange Comparator enable/status register with W
WKPND equ $09 ;Exchange MIWU/RB Interrupts pending with W
; Port setup (read) addresses
WKED_R equ $0A ;Read MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_R equ $0B ;Read MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_R equ $0C ;Read Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_R equ $0D ;Read Port Level setup, 0 = CMOS, 1 = TTL
PLP_R equ $0E ;Read Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_R equ $0F ;Read Port Direction
; Timer (write) addresses
TR2CML_W equ $12 ;Write Timer R2 low byte
TR2CMH_W equ $13 ;Write Timer R2 high byte
TR1CML_W equ $14 ;Write Timer R1 low byte
TR1CMH_W equ $15 ;Write Timer R1 high byte
TCNTB_W equ $16 ;Write Timer control register B
TCNTA_W equ $17 ;Write Timer control register A
; Port setup (write) addresses
WKED_W equ $1A ;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W equ $1B ;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W equ $1C ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W equ $1D ;Write Port Level setup, 0 = CMOS, 1 = TTL
PLP_W equ $1E ;Write Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_W equ $1F ;Write Port Direction
ELSE
;*********************************************************************************
; SX18AC/20AC/28AC Mode addresses
; *On SX18/20/28, all registers addressed via mode are write only, with the exception of
; CMP and WKPND which do an exchange with W.
;*********************************************************************************
; Exchange addresses
CMP equ $08 ;Exchange Comparator enable/status register with W
WKPND equ $09 ;Exchange MIWU/RB Interrupts pending with W
; Port setup (read) addresses
WKED_W equ $0A ;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W equ $0B ;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W equ $0C ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W equ $0D ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
PLP_W equ $0E ;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
DDIR_W equ $0F ;Write Port Direction
ENDIF ;(SX48_52)
;*****************************************************************************************
; Program memory ORG defines
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Place a table at the top of the source with the starting addresses of all of
; the components of the program.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
INTERRUPT_ORG equ $0 ; Interrupt must always start at location zero
RESET_ENTRY_ORG equ $1FB ; The program will jump here on reset.
SUBROUTINES_ORG equ $200 ; The subroutines are in this location
STRINGS_ORG equ $300 ; The strings are in location $300
DTMF_SUBS_ORG equ $400 ; DTMF calculation subroutines
MAIN_PROGRAM_ORG equ $600 ; The main program is in the last page of program memory.
;****************************** Beginning of program space *******************************
org INTERRUPT_ORG ; First location in program memory.
;*****************************************************************************************
;------------------------------------------------------------------------------
; Interrupt Service Routine
;------------------------------------------------------------------------------
; Note: The interrupt code must always originate at address $0.
;
; Interrupt Frequency = OSC Frequency / ((-retiw value)*RTCC Prescaler) For example:
; With a retiw value of -163, a prescaler of 1, and an oscillator frequency of 50MHz,
; this Interrupt Routine runs every 3.26us.
;------------------------------------------------------------------------------
ISR ;3 The interrupt service routine...
;------------------------------------------------------------------------------
;VP_BEGIN: VP Scheduler
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Schedule tasks in the Interrupt Service Routine
; - Produces a FAR smaller worst-case cycle time count, and enables a larger number
; of VP's to run simultaneously. Also produces "empty" slots that future VP's
; can be copied and pasted into easily.
; - Determine how often your tasks need to run. (9600bps UART can run well at a
; sampling rate of only 38400Hz, so don't run it faster than this.)
; - Strategically place each "module" into the threads of the Scheduler. If a
; module must be run more often, just call it's module at double the rate or
; quadruple the rate, etc.
; - Split complicated Virtual Peripherals into several modules, keeping the
; high-speed portions of the Virtual Peripherals as small and quick as possible,
; and run the more complicated, slower processing part of the Virtual Peripheral
; at a lower rate.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;------------------------------------------------------------------------------
; Virtual Peripheral Scheduler: up to 16 individual threads, each running at
; the interrupt rate/16. Change the table
; to modify the rate at which each of the threads
; is run.
;
; Input variable(s): isrMultiplex: variable used to choose threads
; Output variable(s): None, executes the next thread
; Variable(s) affected: isrMultiplex
; Flag(s) affected: None
; Program Cycles: 10 cycles (turbo mode SX52)
;------------------------------------------------------------------------------
_bank isrMultiplex ;1/2
inc isrMultiplex ;1 ; toggle interrupt rates
mov w,isrMultiplex ;1
; The code between the tableStart and tableEnd statements MUST be
; completely within the first half of a page. The routines
; it is jumping to must be in the same page as this table.
tableStart ; Start all tables with this macro.
add pc,w ;3
jmp isrThread3 ;3, 12/13 cycles + 16/17 + 392/400 + 7 = 415/424 cycles
; ;Thread3 contains UART, A/D, 5ms Timer and LED flasher
jmp isrThread1 ;3, 12/13 cycles + 490 + 7 = max 510 cycles
; ; NOTE: For the SX18/28 = max 483 cycles
; ;Thread1 contains DTMF
jmp isrThread2 ;
jmp isrThread4 ;
jmp isrThread5 ;
jmp isrThread6 ;
jmp isrThread7 ;
jmp isrThread8 ;
jmp isrThread9 ;
jmp isrThread16 ; ; table threading ends here
jmp isrThread8 ;
jmp isrThread9 ;
jmp isrThread1 ;
jmp isrThread10 ;
jmp isrThread3 ;
jmp isrThread11 ;
jmp isrThread1 ;
jmp isrThread2 ;
jmp isrThread12 ;
jmp isrThread13 ;
jmp isrThread1 ;
jmp isrThread14 ;
jmp isrThread15 ;
jmp isrThread16 ;
tableEnd ; End all tables with this macro.
;VP_END: VP Scheduler
;VP_BEGIN: DTMF Detection
;******************************************************************************
; Portions of DTMF Detection ISR that must be in the first 1/2 page... (Tables, etc.)
;******************************************************************************
;------------------------------------------------------------------------------
sineTableTiny
;------------------------------------------------------------------------------
tableStart ; trap an error if this is not in the first half
;3 ; of a page
and w,#%00000111 ;1 keep w within range
add pc,w ;3
retw 0 ;3 ;3,10
retw 1
retw 1
retw 2
retw 2
retw 2
retw 1
retw 1
tableEnd ; trap an error if this is not in the first half
; of a page
;------------------------------------------------------------------------------
; Jump table for the DTMF detection routines
;------------------------------------------------------------------------------
tableStart ; trap an error if this is not in the first half
; of a page
dtmfDetectSine jmp _dtmfDetectSine
dtmfDetectCosine jmp _dtmfDetectCosine
tableEnd ; trap an error if this is not in the first half
; of a page
;------------------------------------------------------------------------------
;VP_END: DTMF Detection
;------------------------------------------------------------------------------
;VP_BEGIN: VP Scheduler
; ISR TASKS
;------------------------------------------------------------------------------
isrThread1 ; Serviced at ISR rate / 10
;------------------------------------------------------------------------------
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; The sample rate of this section of code is the isr rate / 4, because it is jumped
; to in every 4th entry in the VP Schedulers table. To increase the
; sample rate, put more calls to this thread in the Scheduler's jump table.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;VP_BEGIN: DTMF Detection
sb dtmfdDetectEn ;1,
jmp dtmfdDone ;3, If DTMF Detection is not enabled, skip the DTMF detection ISR
;******************************************************************************
;******************************************************************************
;******************************************************************************
; Portions of DTMF Detection ISR that can be in the second half of a page...
;******************************************************************************
dtmfDetIsr
;------------------------------------------------------------------------------
; Update Reference frequencies
;------------------------------------------------------------------------------
; NOW UPDATE THE REFERENCES!!!
_bank dtmfdRefBank ;1/2
snb dtmfdSampleLows ;1
jmp :doLowFrequencies ;3,8
mov w,#DTMF_FREQ1209LO ;1,7 ;High Band Frequencies
add dtmfdRefAcc1Lo,w ;1 ;1209 Hz
snb c ;1
inc dtmfdRefAcc1Hi ;1
mov w,#DTMF_FREQ1209HI ;1
add dtmfdRefAcc1Hi,w ;1,12
mov w,#DTMF_FREQ1336LO ;1 ;1336 Hz
add dtmfdRefAcc2Lo,w ;1
snb c ;1
inc dtmfdRefAcc2Hi ;1
mov w,#DTMF_FREQ1336HI ;1
add dtmfdRefAcc2Hi,w ;1,18
mov w,#DTMF_FREQ1477LO ;1 ;1477
add dtmfdRefAcc3Lo,w ;1
snb c ;1
inc dtmfdRefAcc3Hi ;1
mov w,#DTMF_FREQ1477HI ;1
add dtmfdRefAcc3Hi,w ;1,24
mov w,#DTMF_FREQ1633LO ;1 ;1633
add dtmfdRefAcc4Lo,w ;1
snb c ;1
inc dtmfdRefAcc4Hi ;1
mov w,#DTMF_FREQ1633HI ;1
add dtmfdRefAcc4Hi,w ;1
jmp dtmfdCalcFreq ;3, 33 cycles
:doLowFrequencies ;0,8
mov w,#DTMF_FREQ697LO ;1 ;Low Band Frequencies
add dtmfdRefAcc1Lo,w ;1 ;697 Hz
snb c ;1
inc dtmfdRefAcc1Hi ;1
mov w,#DTMF_FREQ697HI ;1
add dtmfdRefAcc1Hi,w ;1,14
mov w,#DTMF_FREQ770LO ;1 ;770 Hz
add dtmfdRefAcc2Lo,w ;1
snb c ;1
inc dtmfdRefAcc2Hi ;1
mov w,#DTMF_FREQ770HI ;1
add dtmfdRefAcc2Hi,w ;1,20
mov w,#DTMF_FREQ852LO ;1 ;852 Hz
add dtmfdRefAcc3Lo,w ;1
snb c ;1
inc dtmfdRefAcc3Hi ;1
mov w,#DTMF_FREQ852HI ;1
add dtmfdRefAcc3Hi,w ;1,26
mov w,#DTMF_FREQ941LO ;1 ;941 Hz
add dtmfdRefAcc4Lo,w ;1
snb c ;1
inc dtmfdRefAcc4Hi ;1
mov w,#DTMF_FREQ941HI ;1
add dtmfdRefAcc4Hi,w ;1, 32 cycles
;------------------------------------------------------------------------------
; Update Reference frequencies
;------------------------------------------------------------------------------
dtmfdCalcFreq ;32/33cycles to here in thread...starts over from zero
:loop
_bank dtmfdMathBank ;1/2
;test dtmfdAtoDVal ;1 ; TODO: Can be left out?
;snb z ;1
;jmp dtmfdDone ;3
mov w,#dtmfdSinAcc1Lo ;1
mov fsr,w ;1
_bank dtmfdRefBank ;1/2
mov w,dtmfdRefAcc1Hi ;1,5
call dtmfDetectSine ;3 + 25/45 (25/43cycles for SX18/28)
mov w,#dtmfdCosAcc1Lo ;1
mov fsr,w ;1
_bank dtmfdRefBank ;1/2
mov w,dtmfdRefAcc1Hi ;1
call dtmfDetectCosine ;3 + 31/51 (31/49cycles for SX18/28)
mov w,#dtmfdSinAcc2Lo ;1
mov fsr,w ;1
_bank dtmfdRefBank ;1/2
mov w,dtmfdRefAcc2Hi ;1
call dtmfDetectSine ;3 + 25/45 (25/43cycles for SX18/28)
mov w,#dtmfdCosAcc2Lo ;1
mov fsr,w ;1
_bank dtmfdRefBank ;1/2
mov w,dtmfdRefAcc2Hi ;1
call dtmfDetectCosine ;3 + 31/51 (31/49cycles for SX18/28)
mov w,#dtmfdSinAcc3Lo ;1
mov fsr,w ;1
_bank dtmfdRefBank ;1/2
mov w,dtmfdRefAcc3Hi ;1
call dtmfDetectSine ;3 + 25/45 (25/43cycles for SX18/28)
mov w,#dtmfdCosAcc3Lo ;1
mov fsr,w ;1
_bank dtmfdRefBank ;1
mov w,dtmfdRefAcc3Hi ;1
call dtmfDetectCosine ;3 + 31/51 (31/49cycles for SX18/28)
mov w,#dtmfdSinAcc4Lo ;1
mov fsr,w ;1
_bank dtmfdRefBank ;1
mov w,dtmfdRefAcc4Hi ;1
call dtmfDetectSine ;3 + 25/45 (25/43cycles for SX18/28)
mov w,#dtmfdCosAcc4Lo ;1
mov fsr,w ;1
_bank dtmfdRefBank ;1
mov w,dtmfdRefAcc4Hi ;1
call dtmfDetectCosine ;3 + 31/51 (31/49cycles for SX18/28)
; Total max cycle count in dtmfdCalcFreq = 5 + 53*4 + 59*4 = 453 cycles
; For the SX18/28 the max count is = 4 + 50*4 + 56*4 = 428 cycles
; To here in thread the max count is = 32 + 453 = 485 cycles
_bank dtmfdSampleCnt ;1/2
dec dtmfdSampleCnt ;1
snb z ;1
setb dtmfdDetectDone ;1,4/5 + 485 = max 490 cycles total
; ; For the SX18/28, max=464cycles
;VP_END: DTMF Detection
dtmfdDone jmp isrOut ;7
;------------------------------------------------------------------------------
isrThread2 ; Serviced at ISR rate / 5
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread3 ; Serviced at ISR rate / 10
;------------------------------------------------------------------------------
;VP_BEGIN: RS232 Transmit
;------------------------------------------------------------------------------
; Virtual Peripheral: Universal Asynchronous Receiver Transmitter (UART)
; These routines send and receive RS232 serial data, and are currently
; configured (though modifications can be made) for the popular
; "No parity-checking, 8 data bit, 1 stop bit" (N,8,1) data format.
; TRANSMITTING: The transmit routine requires the data to be inverted
; and loaded (tx_high+tx_low) register pair (with the inverted 8 data bits
; stored in tx_high and tx_low bit 7 set high to act as a start bit). Then
; the number of bits ready for transmission (10=1 start + 8 data + 1 stop)
; must be loaded into the tx_count register. As soon as this latter is done,
; the transmit routine immediately begins sending the data.
; This routine has a varying execution rate and therefore should always be
; placed after any timing-critical virtual peripherals such as timers,
; adcs, pwms, etc.
; Note: The transmit and receive routines are independent and either may be
; removed, if not needed, to reduce execution time and memory usage,
; as long as the initial "BANK serial" (common) instruction is kept.
;
; Input variable(s) : tx_low (only high bit used), tx_high, tx_count
; Variable(s) affected : tx_divide
; Program cycles: 17 worst case
; Variable Length? Yes.
;
;------------------------------------------------------------------------------
rs232Transmit
_bank rs232TxBank ;1/2 switch to serial register bank
decsz rs232TxDivide ;1 ; only execute the transmit routine
jmp :rs232TxOut ;3,5/6
mov w,#RS232TX_DIVIDE ;1 ; load UART baud rate (50MHz)
mov rs232TxDivide,w ;1
test rs232TxCount ;1 ; are we sending?
snz ;1
jmp :rs232TxOut ;1
:txbit clc ;1 ; yes, ready stop bit
rr rs232TxHigh ;1 ; and shift to next bit
rr rs232TxLow ;1
dec rs232TxCount ;1 ; decrement bit counter
snb rs232TxLow.6 ;1 ; output next bit
clrb rs232TxPin ;1
sb rs232TxLow.6 ;1
setb rs232TxPin ;1,16/17
:rs232TxOut
;VP_END: RS232 Transmit
;------------------------------------------------------------------------------
; This A/D module comes after the UART because the UART only uses 17cycles out of
; 500 possible. This UART module is inserted in this thread ahead of this A/D
; module to avoid waste of MIPS and ensure timing of the UART
;------------------------------------------------------------------------------
;VP_BEGIN: DTMF Detection
;------------------------------------------------------------------------------
; Do A/D Conversion:
;------------------------------------------------------------------------------
_bank dtmfdMathBank ;1/2
mov w,#DTMF_ADNOSAMPL ;1
mov dtmfdAtoDCount,w ;1 ; numbers of sample to take
mov w,#DTMF_ADOFFSET ;1
mov dtmfdAtoDVal,w ;1 ; clear and correct AtoDVal for minor DC offset
mov isrTemp0,m ;1 ; Store Mode register
_mode DDIR_W ;1/2 ; point MODE to write DDIR register
mov w,#RC_DDIR ;1 ; load direction setting for port C
clrb wreg.ADFdbk ;1 ; set ADfdbkpin to output
mov !dtmfdPort,w ;1, 10/12
:AtoDLoop2
snb dtmfdADInPin ;1 ; check AD input pin
jmp :above ;3 ; if input is above threshold
:below nop ;1 ; Make :below and :above symmetrical with equal length
nop ;1 ;
setb dtmfdADFdbkPin ;1 ; -if not: feedback pin=1 (inverted AD input)
dec dtmfdAtoDVal ;1 ; decrement AD value
decsz dtmfdAtoDCount ;1 ; decrement number of samples to take
jmp :AtoDLoop2 ;3,10 ; if not done, do again
jmp :loopDone ;3 ; when finished
:above
clrb dtmfdADFdbkPin ;1 ; feedback pin=0 (inverted AD input)
inc dtmfdAtoDVal ;1 ; increment AD value
decsz dtmfdAtoDCount ;1 ; decrement number of samples to take
jmp :AtoDLoop2 ;3,10; if not done, do it again
:loopDone ; loop time = (10 * ADNOSAMPL)+3 = 10*36+3=363 cycles
clrb dtmfdADFdbkPin ;1 ; Make the A/D feedback pin low to next time
clrb dtmfdInputSign ;1
sb dtmfdAtoDVal.7 ;1 ; check the sign bit of dtmfdAtoDVal
jmp :positive ;3
:negative
setb dtmfdInputSign ;1 ; set inputsign to negative
not dtmfdAtoDVal ;1
inc dtmfdAtoDVal ;1 ; 2's compliment (make negative positive)
:positive
mov w,#RC_DDIR ;1 ; load direction setting for port C
setb wreg.ADFdbk ;1 ; set direction of ADfdbkpin to input (tristate)
mov !dtmfdPort,w ;1 ;
_mode isrTemp0 ;1/2; Restore mode register
; = 10/12+363+10/12=383/387cycles
;VP_END: DTMF Detection
;------------------------------------------------------------------------------
;Do timers and return from interrupt.
;------------------------------------------------------------------------------
;VP_BEGIN : 5ms Timer
_bank Timer5msBank ;1/2
decsz Timer5msDiv ;1
jmp :noInc ;3,5/6 Interrupt rate is 1/100_000
mov w,#TIMER_COUNTER ;1 This interrupt occurs every 100uS,
mov Timer5msDiv,w ;1 There are 50 * 20uS in 5mS
inc Timer5ms ;1
snb z ;1
setb timer5msFlag ;1
:noInc ;0,5/9
;VP_END : 5ms Timer
;VP_BEGIN : LED flasher
sb Timer5ms.3 ;1 ; Use these timers to flash the LED
setb ledPin ;1
snb Timer5ms.3 ;1
clrb ledPin ;1,9/13 = 383/387 + 9/13 = 392/400 cycles
;VP_END : LED flasher ;
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread4 ; Serviced at ISR rate / 5
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread5 ; Serviced at ISR rate / 5
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread6 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread7 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread8 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread9 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread10 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread11 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread12 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread13 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread14 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread15 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
jmp isrOut ;7 cycles until mainline program resumes execution
;------------------------------------------------------------------------------
isrThread16 ; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; The final thread in the ISR Multi-Threader must load the isrMultiplex
; register with a value of 255, so it will roll-over to 0 on the next ISR.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
_bank isrMultiplex
mov w,#255 ; Reload isrMultiplex so isrThread1 will be run
mov isrMultiplex,w ; on next interrupt.
jmp isrOut
;VP_END: VP Scheduler
;------------------------------------------------------------------------------
isrOut
;------------------------------------------------------------------------------
mov w,#-intPeriod ;1 ; return and add -intPeriod to the RTCC
retiw ;3 ; using the retiw instruction.
;VP_BEGIN: DTMF Detection
;------------------------------------------------------------------------------
; DTMF DETECT ISR SUBROUTINES HERE.
;------------------------------------------------------------------------------
;------------------------
_dtmfDetectCosine
; INPUTS: WREG: High byte of cosine-index in w register
; FSR: Location of low byte of accumulator
;------------------------
mov isrTemp0,w ;1,4 (jmp after call; 3cycles)
mov w,#$40 ;1
add w,isrTemp0 ;1
; =6 ; These cycles are added to sine cycle-count
;------------------------
_dtmfDetectSine
; INPUTS: WREG: High byte of sine/cosine-index in w register
; FSR: Location of low byte of accumulator
;------------------------
sb wreg.7 ;1,4 ; Sine-reference sign
clrb dtmfdSign ;1 ; Keep track of the reference sign. dtmfdSign is always set when
; leaving/entering this routine, see below (saves one cycle)
swap wreg ;1 ; Bits 6-4 are the sine-index, for table index use bits 2-0
call sineTableTiny ;10; Use bit 2-0 to get the value from the tiny sine table
test wreg ;1 ; Check if sine-reference is zero
snb z ;1
jmp :next ;3,21 ;if reference is zero, skip this
snb dtmfdSign ;1,20
jmp :negRef ;3,23 ; reference is negative
:posRef
snb dtmfdInputSign ;1,22
setb dtmfdSign ;1 ; if input sample value is negative=> pos*neg=neg
jmp :refSignOut ;3,26 ; dtmfdSign have now the sign of the multiplication
:negRef
snb dtmfdInputSign ;1
clrb dtmfdSign ;1,25 ; input sample value is negative=> neg*neg=pos
; dtmfdSign have now the sign of the multiplication
:refSignOut ;0,25/26
_bank dtmfdMathBank ;1/2
mov isrTemp0,w ;1 reference to isrTemp0
mov w,dtmfdAtoDVal ;1 A/D sample value to wreg,
snb isrTemp0.1 ;1 If reference is one, skip multiply by 2(sine-reference is either 1 or 2)
add w,dtmfdAtoDVal ;1 wreg=2*AtoDVal (never exceeds FF; #$FF > (DTMF_ADNOSAMPL+ADOFFSET)*2)
_bank dtmfdCh1 ;1/2 ;
snb dtmfdSign ;1,32/35 check if we shall add or subtract
jmp :decrease ;3 ;35/38
:increase
add indf,w ;1 ; add the input value * reference to the accumulator
inc fsr ;1
snb c ;1
inc indf ;1
:next setb dtmfdSign ;1 ; Always set before leaving this routine, see above
ret ;3,25/44 ; return to isr-thread (25/42cycles for SX18/28)
:decrease
sub indf,w ;1 ; add the input value * reference to the accumulator (negative)
inc fsr ;1
sb c ;1 ; the result is negative if C is cleared
dec indf ;1,
ret ;3,42/45 ;and return to caller. (42/43cycles for SX18/28)
; Total for sineCalcs = 25/45 (25/43cycles for SX18/28)
; Total for cosineCalcs= 31/51 (31/49cycles for SX18/28)
;VP_END: DTMF Detection
;*****************************************************************************************
org RESET_ENTRY_ORG
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; The main program operation should be easy to find, so place it at the end of the
; program code. This means that if the first page is used for anything other than
; main program source code, a resetEntry must be placed in the first page, along
; with a 'page' instruction and a 'jump' instruction to the beginning of the
; main program, wherever that may be.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;------------------------------------------------------------------------------
resetEntry ; Program starts here on power-up
page _resetEntry
jmp _resetEntry
;------------------------------------------------------------------------------
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; ORG statements should use predefined labels rather than literal values.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
org SUBROUTINES_ORG
;*****************************************************************************************
; Subroutines
;*****************************************************************************************
;VP_BEGIN: RS232 Transmit
;
rs232TxSendByte
; Description:
; Sends the byte in w via the Asynchrounous Serial Transmit VP.
; Will block if the transmit VP is still busy.
; Load W with the byte to send before calling rs232TxSendByte
;
; Global Ram Destroyed:
; none
;
; Inputs:
; w - The byte to be sent
;
; Outputs:
; Initializes the Asynchrounous Serial Transmit VP and sends the
; byte out serially.
;------------------------------------------------------------------------------
_bank rs232TxBank
:wait test rs232TxCount ; wait for not busy
sz
jmp :wait
not w ; ready bits (inverse logic)
mov rs232TxHigh,w ; store data byte
setb rs232TxLow.7 ; set up start bit
mov w,#10 ; 1 start + 8 data + 1 stop bit
mov rs232TxCount,w
retp ; leave and fix page bits
;VP_END: RS232 Transmit
;------------------------------------------------------------------------------
;VP_BEGIN : RS232 Transmit
;
rs232TxSendString
; Description:
; Sends the null-terminated string at location "w" serially.
;
; Global Ram Destroyed:
; localTemp1, mode
;
; Inputs:
; w - The location of the string to be sent. The string must
; be located in the same half of a page as the STRINGS_ORG label.
; Outputs:
; Terminal - Outputs the string serially.
;------------------------------------------------------------------------------
_bank rs232TxBank
mov localTemp1,w ; store string address
:loop
_mode STRINGS_ORG>>8 ; with indirect addressing
mov w,localTemp1 ; read next string character
iread ; using the mode register
test w ; are we at the last char?
snz ; if not=0, skip ahead
jmp :out ; yes, leave & fix page bits
call rs232TxSendByte ; not 0, so send character
_bank rs232TxBank
inc localTemp1 ; point to next character
jmp :loop ; loop until done
:out _mode DDIR_W ; reset the mode register to point
; to the data latches
retp
;VP_END: RS232 Transmit
;VP_END: DTMF Detection
;------------------------------------------------------------------------------
clearBank ; Clears an entire bank of RAM.
; To use, first load the FSR with the starting address
; of the bank to clear.
;--------------------------------------------------------------------------
:loop
clr indf
inc fsr
mov w,fsr
and w,#%00001111
snb z
retp
jmp :loop
;VP_END: DTMF Detection
;VP_BEGIN : 5ms Timer
;--------------------------------------------------------------------------
delayW100Ms
; This subroutine delays 'w'*100 milliseconds. (not exactly, but pretty close)
; This subroutine uses the TEMP register
; INPUT w - w/100 milliseconds to delay for.
; OUTPUT Returns after 100 * n milliseconds.
;--------------------------------------------------------------------------
mov localTemp0,w
_bank Timer5msBank
mov w,#48 ; This loop delays for 100ms
mov Timer5msDiv,w ;
:loop
mov w,#-20 ; delay 100ms
mov Timer5ms,w ;
clrb timer5msFlag ; clear the flag and
:wait sb timer5msFlag ; wait until the timer5msFlag is set.
jmp :wait
decsz localTemp0 ; and do this w times.
jmp :loop
retp
;VP_END : 5ms Timer
;*****************************************************************************************
org STRINGS_ORG ; This label defines where strings are kept in program space.
;*****************************************************************************************
;------------------------------------------------------------------------------
; Put String Data Here
;------------------------------------------------------------------------------
;VP_BEGIN: DTMF Detection
_hello dw 13,10,'Press anykey to detect DTMF>>',0
_DTMFstring dw 13,10,'DTMF>',0
;VP_END: DTMF Detection
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; - Routines that use location-dependant data, such as in example below, should
; use a LABEL rather than a literal value as their input. Example:
; instead of
; mov m,#3 ; move upper nibble of address of strings into m
; use
; mov m,#STRINGS_ORG>>8; move upper nibble of address of strings into m
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;*****************************************************************************************
org DTMF_SUBS_ORG
;*****************************************************************************************
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
; To ensure that several Virtual Peripherals, when pasted together, do not cross
; a page boundary without the integrator's knowledge, put an ORG statement and one
; instruction at every page boundary. This will generate an error if a pasted
; subroutine moves another subroutine to a page boundary.
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
jmp $ ; This instruction will cause an assembler error if the source code before
; the org statement inadvertantly crosses a page boundary.
tableStart
dtmfdGetValid jmp _dtmfdGetValid
tableEnd
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
asciiTable ; Ascii value at index (0-15)
; INPUT: w - Index into the table (0-15 value)
; OUTPUT: w - Constant at that index
;--------------------------------------------------------------------------
tableStart
add pc,w
retw '1'
retw '2'
retw '3'
retw 'A'
retw '4'
retw '5'
retw '6'
retw 'B'
retw '7'
retw '8'
retw '9'
retw 'C'
retw '*'
retw '0'
retw '#'
retw 'D'
tableEnd
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
index2digit
; This subroutine converts a digit from 0-9, '*' or '#' or the
; characters 'A'-'D' to a table.
;--------------------------------------------------------------------------
call asciiTable
retp
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
dtmfdSample
; Get the result of one accumulation. Store the result
; in dtmfdResult1, dtmfdResult2, dtmfdResult3 and dtmfdResult4
; inputs: before calling:
; - Disable PWM output
; - Set or clear the dtmfdSampleLows bit
; outputs: These are affected:
; - ALL temporary globals are changed.
; - FSR is destroyed
; - calculated results of all accumulations
; in the result registers.
; - w register returns an index to the winning frequency.
; if no frequency won, wreg will = #4 (bit 2 set.)
; - high score in localTemp0 register
;--------------------------------------------------------------------------
_bank dtmfdSampleCnt ; use isrMultiplex as a sample counter
mov w,#DTMF_SAMPL_LO ; if we are sampling low frequencies, take lowSamples samples
sb dtmfdSampleLows ;
mov w,#DTMF_SAMPL_HI ; else take highSamples samples.
mov dtmfdSampleCnt,w ; move this value from w
mov w,#dtmfdCh1 ; point to DTMF channel 1's accumulators
mov fsr,w
page clearBank
call clearBank ; and clear it
clrb dtmfdDetectDone ; clear the dtmfdDone indicator
setb dtmfdDetectEn ; enable DTMF detection
:wait sb dtmfdDetectDone ; wait until the accumulation cycle is finished.
jmp :wait
clrb dtmfdDetectEn ; disable DTMF Detection
mov w,#dtmfdSinAcc1Lo ; do the calculations on acc1
page dtmfDetectCalcs
call dtmfDetectCalcs
_bank dtmfdCh1
mov dtmfdResult1,w ; and store result in dtmfdResult1
mov w,#dtmfdSinAcc2Lo ; ditto for 2
page dtmfDetectCalcs
call dtmfDetectCalcs
_bank dtmfdCh1
mov dtmfdResult2,w
mov w,#dtmfdSinAcc3Lo ; ditto
page dtmfDetectCalcs
call dtmfDetectCalcs
_bank dtmfdCh1
mov dtmfdResult3,w
mov w,#dtmfdSinAcc4Lo ; ditto
page dtmfDetectCalcs
call dtmfDetectCalcs
_bank dtmfdCh1
mov dtmfdResult4,w
mov w,#dtmfdCh1 ; point to dtmfdCh1
page dtmfGetWinner
call dtmfGetWinner ; and get the winner in this bank.
mov w,dtmfdWinnIndex ; store the index to the winner in w.
retp
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
absolute16 ; absolutes the 16-bit value passed to it
; inputs: -pointer to the high-byte in w
; -must have wreg enabled in !option register
; outputs: -absolute 16-bit value in passed pointer location
; -FSR points to the high byte
;--------------------------------------------------------------------------
mov fsr,w
sb indf.7 ; return if high byte is positive
retp
mov w,indf ; else store high byte in w
xor w,#$ff ; and negate w
dec fsr ; point to low byte
not indf ; 1's complement low byte
inc indf ; and increment it
snb z
inc wreg ; increment high byte if there was roll-over
inc fsr
mov indf,w ; and store the new high-byte
retp
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
scale16ByN ; Shifts right the 16-bit value passed to it by n bits
; If the MSByte is not equal to zero after the final shift,
; it is cleared and the lower byte is set to 255.
; inputs: -#high-byte in FSR
; -n= bits to shift right in w
; -must have wreg enabled in !option register
; outputs: -value shifted right n bits
; -fsr = high-byte
; destroys: -localTemp0
;--------------------------------------------------------------------------
mov localTemp0,w
mov w,indf ; from the "absoluteScaleAndSquare"->"absolute16", fsr contains #dtmfdBHi
clr indf ; clear the upper byte since we want EVERYTHING in the lower byte
dec fsr
test localTemp0 ; check the input variable for zero
:loop
snb z
jmp :done ; jump to :done if finished
clrb c
rr wreg ; rotate the MSbyte right
rr indf ; rotate the LSbyte right
dec localTemp0 ; decrement the counter
jmp :loop
:done
test wreg ; test the upper byte for zero.
snb z
retp
mov w,#255 ; if the upper byte was not = 0, make the lower byte = 255
mov indf,w ;
retp
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
add16Plus16 ; adds the 16-bit value pointed to by w to the location
; in w+2 and divides by two.
;
; inputs: - #valuel in w
; outputs: - the location in memory passed in w is added
; the next contiguous 16 bits and the result
; is stored in the next contiguous 16 bits.
; The result is divided by two to keep in range.
; - fsr = value2h
;--------------------------------------------------------------------------
clrb dtmfdNeg
mov fsr,w ; indf = valuel
mov w,indf
inc fsr ; indf = valueh
inc fsr ; indf = value2l
add indf,w
dec fsr ; indf = valueh
mov w,indf
inc fsr ; indf = value2l
inc fsr ; indf = value2h
snb c
inc indf
snb z
setb dtmfdNeg ; indicate the rollover
add indf,w
snb dtmfdNeg ; if there was a roll-over, set the carry bit
setb c
rr indf ; divide by two to keep in range.
dec fsr ; indf = value2l
rr indf ; divide by two to keep in range.
inc fsr ; fsr = value2h
retp
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
Multiply8x8 ; Multiply W by dtmfdInput
; INPUTS: W * dtmfdInput
; OUTPUTS: 16-bit output value in dtmfdBLo and dtmfdBHi
;--------------------------------------------------------------------------
clr dtmfdLoopCount
setb dtmfdLoopCount.3; move 8 to dtmfdLoopCount without affecting w
clr dtmfdBLo ;1
clr dtmfdBHi ;1 ;3
:loop
clrb c ;1
snb dtmfdInput.0 ;1
add dtmfdBHi,w ;1
rr dtmfdBHi ;1
rr dtmfdBLo ;1
rr dtmfdInput ;1
decsz dtmfdLoopCount ;1
jmp :loop ;3 10=looptime (78 on exit)
; 78 + 3 = 81
retp ;3 82 + 3 = 84 ALWAYS!!!
; for 16 bit result.
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
; sqRoot ; By John Keenan
; Routine to take the square root of a 16 bit unsigned number
; entry: dtmfdBLo - low byte of number
; dtmfdBHi - high byte of number
; exit: W register returns 8 bit result
;--------------------------------------------------------------------------
sqRoot16 mov w,#$c0 ; initialise dtmfdRootMask
mov dtmfdRootMask,w ;
mov w,#$40 ; initialise answer
:sq1 setb c
sub dtmfdBHi,w ; subtract the root develped so far
sb c ; restore subtraction if carry cleared
jmp :sq5
:sq2 or w,dtmfdRootMask ; set the current bit
:sq3 nop
rl dtmfdBLo ; shift number left one position
rl dtmfdBHi
rr dtmfdRootMask ; picks up ms bit of input2
snb dtmfdRootMask.7
jmp :sq6
xor w,dtmfdRootMask ; append 01 to the root developed so far
sb c ; if the lsb of dtmfdRootMask was shifted into carry,
jmp :sq1 ; then we're done. Otherwise loop again
setb c
sub dtmfdBHi,w
sb c
retp
snb z
snb dtmfdBLo.7
xor w,#1
retp
:sq6 snb c
retp
clrb dtmfdRootMask.7
xor w,dtmfdRootMask
setb c
sub dtmfdBHi,w
jmp :sq2
:sq5 add dtmfdBHi,w ; carry=0 here
jmp :sq3
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
dtmfDetectCalcs ; _____________
; OUTPUT = W = \/(a^2) + (b^2)
; Do all processing for DTMF detection, using FSR
; as index into the bank to process in w register
; output result of this calculation in the w register
;--------------------------------------------------------------------------
mov localTemp1,w
call absoluteScaleAndSquare ; a = a^2
_bank dtmfdMathBank
mov w,dtmfdBLo ; store result in a
mov dtmfdALo,w
mov w,dtmfdBHi
mov dtmfdAHi,w
inc localTemp1 ; increment pointer to register location
inc localTemp1
mov w,localTemp1
call absoluteScaleAndSquare ; b = b^2
mov w,#dtmfdALo ; now add a^2 and b^2
call add16Plus16 ; result in dtmfdBLo and dtmfdBHi
; y = a^2 + b^2
call sqRoot16 ; and get the square root of the result.
; result returned in w register.
; _____________
; y = \/(a^2) + (b^2)
retp ; return with result in w register
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
absoluteScaleAndSquare
; absolute, scale, and square the 16-bit value pointed
; to by the w register. Results are in dtmfdBLo and dtmfdBHi of
; of the math bank
;--------------------------------------------------------------------------
mov fsr,w
mov w,indf ; and store value at this location in localTemp2
mov localTemp2,w
inc fsr ; get the high byte
mov w,indf ; and move it to w
_bank dtmfdMathBank
mov dtmfdBHi,w ; store high byte in dtmfdBHi
mov w,localTemp2 ; store low byte in dtmfdBLo
mov dtmfdBLo,w
mov w,#dtmfdBHi ; w-->dtmfdBHi
call absolute16 ; absolute b
mov w,#DTMF_SCALING ; and shift b right by DTMF_SCALING
call scale16ByN ;
mov w,dtmfdBLo
mov dtmfdInput,w
call Multiply8x8 ; square b
retp ; return with result in dtmfdBLo and dtmfdBHi
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
checkPowers ; Lo-frequency power (L) and high frequency power (H)
; shall, according to Bellcore, be within:
; L-8 =< H <= L+4 [dB]
; This routine narrows the check to:
; L-6 =< H <= L+3.5 [dB]
; which is the same as 2*H > L and 1.5*L > H in power levels (voltage)
; Disabling this routine increases sensitivity to DTMF digits.
; INPUTS: dtmfdHiFreqPow and dtmfdLoFreqPow are
; already loaded with their corresponding
; results from the last sample period.
; OUTPUTS: Returns with 0 in the W register if the values
; are within 1.5 of each other, and $ff if they
; are not.
;--------------------------------------------------------------------------
_bank dtmfdRefBank
:loLimit ; Verify that Hi-freq power not too small; 2*H > L
mov w,dtmfdLoFreqPow
clrb c ; instead of 2*dtmfdHiFreqPow:
rr wreg ; = (dtmfdLoFreqPow/2); to avoid overflow
mov w,dtmfdHiFreqPow-w ; if 2*H < L then this is invalid detection
sb c
jmp :invalid
:hiLimit mov w,dtmfdLoFreqPow ; Verify that Lo-freq power not too small; 1.5*L > H
mov localTemp0,w
clrb c
rr wreg ; w=0.5*dtmfdLoFreqPow
add localTemp0,w ; = (1.5 * dtmfdLoFreqPow) might overflow
rr localTemp0 ; = (1.5 * dtmfdLoFreqPow)/2 in case of overflow
clrb c
mov w,dtmfdHiFreqPow
mov localTemp1,w
rr localTemp1 ; = (dtmfdHiFreqPow/2) to make it fair.
mov w,localTemp1 ; if 1.5 * the lower power is not greater
mov w,localTemp0-w ; than or equal to the higher power, this is invalid
sb c
jmp :invalid
:valid
clr w
retp
:invalid
mov w,#$ff
retp
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
dtmfGetWinner ; Compare the 4 frequencies and find the winner and
; runner-up. Pre-load the high-score to create a threshold.
; The winner must be at least 2 times larger than the threshold
; or than the next highest score, whichever is larger.
; INPUTS: Load a pointer to the first result register into W
; OUTPUTS: - dtmfdHighScore, dtmfdRunUpScore indicate power present
; at each frequency
; - W holds an index to the winning result.
; W = 4 if no result was valid.
;--------------------------------------------------------------------------
mov fsr,w
mov w,#DTMF_MIN_SCORE ; DTMF_MIN_SCORE is the score to beat
mov dtmfdHighScore,w
clr dtmfdRunUpScore
mov w,#4 ; if this is still 4 at the end, then nothing beat the high score.
mov dtmfdWinnIndex,w
clr localTemp0 ; clear the index
:loop
mov w,dtmfdHighScore ; check if the result is higher than the high score
mov w,indf-w
sb c
jmp :checkRunnerUp
; if yes, save the result.
mov w,dtmfdWinnIndex ; save as the runner-up
mov dtmfdRunUpIndex,w
mov w,dtmfdHighScore
mov dtmfdRunUpScore,w
mov w,localTemp0 ; save the winner and new high score
mov dtmfdWinnIndex,w
mov w,indf
mov dtmfdHighScore,w
jmp :next
:checkRunnerUp
mov w,dtmfdRunUpScore
mov w,indf-w
sb c
jmp :next
mov w,indf ; this one beat the runner-up
mov dtmfdRunUpScore,w
mov w,localTemp0
mov dtmfdRunUpIndex,w
:next
inc fsr ; point to the next result
inc localTemp0 ; and increment the register (index)
sb localTemp0.2 ; if localTemp0.2 is set, we are done. (we've done 4 results)
jmp :loop
mov w,dtmfdRunUpScore ; multiply the runner-up by 2.
add dtmfdRunUpScore,w ; winner's score must be 2 times larger than the runner up to win.
snb c
jmp :invalid ; if carry, the high score must be invalid.
mov w,dtmfdHighScore ; store the high score
mov localTemp0,w
mov w,dtmfdRunUpScore ; and return if the high score is above or equal to the (dtmfdRunUpScore * 2)
mov w,dtmfdHighScore-w
snb c
retp
:invalid
mov w,#4 ; else move 4 into the winner index
mov dtmfdWinnIndex,w ; to indicate that nothing won.
retp
;VP_END: DTMF Detection
;VP_BEGIN: DTMF Detection
;--------------------------------------------------------------------------
_dtmfdGetValid
; This routine waits for a valid DTMF digit, followed by silence. Once
; a valid DTMF digit is found, the power difference between the two components
; are checked (according to Bellcore spec.). Then it must wait for silence
; (invalid frequency) before a index to the digit can be calculated.
;
;--------------------------------------------------------------------------
:getHighFrequencies ; wait until a valid high frequency comes in.
clrb dtmfdSampleLows
page dtmfdSample
call dtmfdSample
snb wreg.2 ; if invalid (=4), start over
jmp :getHighFrequencies
_bank dtmfdRefBank
mov dtmfdHiFreqInd,w ; store result in the highFrequency storage register
:redoHighFrequencies ; now check it to make sure it is still the
page dtmfdSample ; same frequency.
call dtmfdSample
_bank dtmfdRefBank
xor w,dtmfdHiFreqInd ; compare with result in the highFrequency storage register
sb z ; if the same freq. is detected, do low freq.
jmp :getHighFrequencies
:getLowFrequencies ; now get the low frequency component
mov w,localTemp0 ; store the power in the high frequency
mov dtmfdHiFreqPow,w
setb dtmfdSampleLows
page dtmfdSample
call dtmfdSample
snb wreg.2 ; if invalid, start over
jmp :getHighFrequencies
_bank dtmfdRefBank
mov dtmfdLoFreqInd,w ; store result in the highFrequency storage register
mov w,localTemp0 ; store the power in the high frequency
mov dtmfdLoFreqPow,w
page checkPowers
call checkPowers
snb wreg.2
jmp :getHighFrequencies
:getSilence ; if valid up to this point, wait until there is silence
; before returning
clrb dtmfdSampleLows
page dtmfdSample
call dtmfdSample
sb wreg.2 ; check if valid
jmp :getSilence ; if not, recheck
page dtmfdSample
call dtmfdSample
sb wreg.2
jmp :getSilence ; If valid freq., wait some more for silence
page dtmfdSample
call dtmfdSample
sb wreg.2
jmp :getSilence ; If valid freq., wait some more for silence
_bank dtmfdRefBank
clrb c
mov w,<<dtmfdLoFreqInd ; create the index to the valid digit
rl wreg
or w,dtmfdHiFreqInd
mov dtmfdDigitIndex,w ; and save it
retp
;VP_END: DTMF Detection
;*****************************************************************************************
org MAIN_PROGRAM_ORG
;*****************************************************************************************
;------------------------------------------------------------------------------
; RESET VECTOR
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
; Program execution begins here on power-up or after a reset
;------------------------------------------------------------------------------
_resetEntry
;------------------------------------------------------------------------------
; Initialize all port configuration
;------------------------------------------------------------------------------
_mode ST_W ;point MODE to write ST register
mov w,#RB_ST ;Setup RB Schmitt Trigger, 0 = enabled, 1 = disabled
mov !rb,w
IFNDEF SX18_20
mov w,#RC_ST ;Setup RC Schmitt Trigger, 0 = enabled, 1 = disabled
mov !rc,w
ENDIF ;(SX18_20)
IFDEF SX48_52
mov w,#RD_ST ;Setup RD Schmitt Trigger, 0 = enabled, 1 = disabled
mov !rd,w
mov w,#RE_ST ;Setup RE Schmitt Trigger, 0 = enabled, 1 = disabled
mov !re,w
ENDIF ;(SX48_52)
_mode LVL_W ;point MODE to write LVL register
mov w,#RA_LVL ;Setup RA CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !ra,w
mov w,#RB_LVL ;Setup RB CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !rb,w
IFNDEF SX18_20
mov w,#RC_LVL ;Setup RC CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !rc,w
ENDIF ;(SX18_20)
IFDEF SX48_52
mov w,#RD_LVL ;Setup RD CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !rd,w
mov w,#RE_LVL ;Setup RE CMOS or TTL levels, 0 = TTL, 1 = CMOS
mov !re,w
ENDIF ;(SX48_52)
_mode PLP_W ;point MODE to write PLP register
mov w,#RA_PLP ;Setup RA Weak Pull-up, 0 = enabled, 1 = disabled
mov !ra,w
mov w,#RB_PLP ;Setup RB Weak Pull-up, 0 = enabled, 1 = disabled
mov !rb,w
IFNDEF SX18_20
mov w,#RC_PLP ;Setup RC Weak Pull-up, 0 = enabled, 1 = disabled
mov !rc,w
ENDIF ;(SX18_20)
IFDEF SX48_52
mov w,#RD_PLP ;Setup RD Weak Pull-up, 0 = enabled, 1 = disabled
mov !rd,w
mov w,#RE_PLP ;Setup RE Weak Pull-up, 0 = enabled, 1 = disabled
mov !re,w
ENDIF ;(SX48_52)
_mode DDIR_W ;point MODE to write DDIR register
mov w,#RA_DDIR ;Setup RA Direction register, 0 = output, 1 = input
mov !ra,w
mov w,#RB_DDIR ;Setup RB Direction register, 0 = output, 1 = input
mov !rb,w
IFNDEF SX18_20
mov w,#RC_DDIR ;Setup RC Direction register, 0 = output, 1 = input
mov !rc,w
ENDIF ;(SX18_20)
IFDEF SX48_52
mov w,#RD_DDIR ;Setup RD Direction register, 0 = output, 1 = input
mov !rd,w
mov w,#RE_DDIR ;Setup RE Direction register, 0 = output, 1 = input
mov !re,w
ENDIF ;(SX48_52)
mov w,#RA_latch ;Initialize RA data latch
mov ra,w
mov w,#RB_latch ;Initialize RB data latch
mov rb,w
IFNDEF SX18_20
mov w,#RC_latch ;Initialize RC data latch
mov rc,w
ENDIF ;(SX18_20)
IFDEF SX48_52
mov w,#RD_latch ;Initialize RD data latch
mov rd,w
mov w,#RE_latch ;Initialize RE data latch
mov re,w
ENDIF ;(SX48_52)
;------------------------------------------------------------------------------
; Clear all Data RAM locations
;------------------------------------------------------------------------------
zeroRam
IFDEF SX48_52 ;SX48/52 RAM clear routine
mov w,#$0a ;reset all ram starting at $0A
mov fsr,w
:zeroRam clr indf ;clear using indirect addressing
incsz fsr ;repeat until done
jmp :zeroRam
_bank bank0 ;clear bank 0 registers
clr $10
clr $11
clr $12
clr $13
clr $14
clr $15
clr $16
clr $17
clr $18
clr $19
clr $1a
clr $1b
clr $1c
clr $1d
clr $1e
clr $1f
ELSE ;(SX48_52) ;SX18/20/28 RAM clear routine
clr fsr ;reset all ram banks
:zeroRam sb fsr.4 ;are we on low half of bank?
setb fsr.3 ;If so, don't touch regs 0-7
clr indf ;clear using indirect addressing
incsz fsr ;repeat until done
jmp :zeroRam
ENDIF ;(SX48_52)
;------------------------------------------------------------------------------
; Initialize program/VP registers
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
; Setup and enable RTCC interrupt, WREG register, RTCC/WDT prescaler
;------------------------------------------------------------------------------
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
; Virtual Peripheral Guidelines Tip:
;
; The suggested default values for the option register are:
; - Bit 7 set to 0: location $01 addresses the W register (WREG)
; - Bit 3 set to 1: Prescaler assigned to WatchDog Timer
;
; If a routine must change the value of the option register (for example, to access
; the RTCC register directly), then it should restore the default value for the
; option register before exiting.
;
;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
RTCC_ON = %10000000 ;Enables RTCC at address $01 (RTW hi)
;*WREG at address $01 (RTW lo) by default
RTCC_ID = %01000000 ;Disables RTCC edge interrupt (RTE_IE hi)
;*RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT = %00100000 ;Sets RTCC increment on RTCC pin transition (RTS hi)
;*RTCC increment on internal instruction (RTS lo) is default
RTCC_FE = %00010000 ;Sets RTCC to increment on falling edge (RTE_ES hi)
;*RTCC to increment on rising edge (RTE_ES lo) is default
RTCC_PS_ON = %00000000 ;Assigns prescaler to RTCC (PSA lo)
RTCC_PS_OFF = %00001000 ;Assigns prescaler to WDT (PSA hi)
PS_000 = %00000000 ;RTCC = 1:2, WDT = 1:1
PS_001 = %00000001 ;RTCC = 1:4, WDT = 1:2
PS_010 = %00000010 ;RTCC = 1:8, WDT = 1:4
PS_011 = %00000011 ;RTCC = 1:16, WDT = 1:8
PS_100 = %00000100 ;RTCC = 1:32, WDT = 1:16
PS_101 = %00000101 ;RTCC = 1:64, WDT = 1:32
PS_110 = %00000110 ;RTCC = 1:128, WDT = 1:64
PS_111 = %00000111 ;RTCC = 1:256, WDT = 1:128
OPTIONSETUP equ PS_001 ; the default option setup for this program.
mov w,#OPTIONSETUP ; setup option register for RTCC interrupts enabled
mov !option,w ; and prescaler assigned to WDT.
page mainLoop
jmp mainLoop
;------------------------------------------------------------------------------
; MAIN PROGRAM CODE
;------------------------------------------------------------------------------
mainLoop
IFDEF DEMO ; Put source code here that would create a runnable
; demo using this Virtual Peripheral
; clrb dtmfHook ; go off hook
mov w,#_hello ; Send out string.
call @rs232TxSendString
:wait snb rs232RxPin ; Wait for the rxPin to go low (start bit)
jmp :wait
mov w,#_DTMFstring
call @rs232TxSendString
clrb dtmfHook ; go off hook
;----------------My Code
;**************************************
mov w,#10
call @delayW100Ms ; delay 1s.
:dtmfLoop
call @dtmfdGetValid ; Wait for a valid DTMF digit
_bank dtmfdRefBank
mov w,dtmfdDigitIndex ; Get the ASCII equivalent
call @index2digit
call @rs232TxSendByte ; Send it
jmp :dtmfLoop ; get another digit
ENDIF
jmp mainLoop
;*****************************************************************************************
END ;End of program code
;*****************************************************************************************
| file: /Techref/scenix/lib/io/osi1/dfs/dtmf_det_src.htm, 87KB, , updated: 2003/11/8 18:04, local time: 2025/10/24 16:12,
216.73.216.180,10-8-63-169:LOG IN
|
| ©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://techref.massmind.org/techref/scenix/lib/io/osi1/dfs/dtmf_det_src.htm"> DTMF Detection</A> |
| Did you find what you needed? |
Welcome to massmind.org! |
Welcome to techref.massmind.org! |
.