;***************************************************************************************** ; 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 ;*****************************************************************************************
Comments: