; +====================================================================+ ; | Name: MAIN.ASM | ; | Description: DTMF -> RS232 converter (for use with PC NummerViser)| ; | Detects DTMF and ringing, and transmits to a std. | ; | RS232 port, at 2400 baud. | ; | | ; | Version: V1.04r | ; | Written by: Jacob Blichfeldt | ; | Date: 18.03.99 | ; +--------------------------------------------------------------------+ ; | Platform: PIC12C508(A) (using internal 4MHz RC-oscillator) | ; | Uses: 90-342W/512W program-mem (all 'switches' set to 0/1) | ; | 6B/25B data-mem | ; | Assembler: MPASM 2.20 (MPLAB 4.00.00) | ; | Comments: - | ; +====================================================================+ TITLE 'DTMF to RS232 converter' PROCESSOR 12C508 __CONFIG _CP_OFF & _MCLRE_OFF & _IntRC_OSC & _WDT_ON INCLUDE RADIX DEC __IDLOCS h'0104' OTP SET 1 ;Use factory-stored cal.-value (on OneTimeProgrammable) TMR0CLK SET 1 ;Use TMR0 external clock at 3,579545MHz (instead of IntRC). PARITY SET 1 ;Use even parity-bit when transmitting to the serial port. CLIONLY SET 1 ;Transmits CLI (A-numbers) only. RING SET 1 ;Transmits ringing. IDENTTX SET 1 ;Transmits 'ident' when pressing '***' (DTMF). PWRUPID SET 1 ;Transmits short 'ident' at powerup. ASCII SET 1 ;Transmits ASCII-chars instead of binary: ;Digit ASCII Binary Digit ASCII Binary ;0 0 (48d) 00000000 9 9 (57d) 00001001 ;1 1 (49d) 00000001 A A (65d) 00001010 ;2 2 (50d) 00000010 B B (66d) 00001011 ;3 3 (51d) 00000011 C C (67d) 00001100 ;4 4 (52d) 00000100 D D (68d) 00001101 ;5 5 (53d) 00000101 * * (42d) 00001110 ;6 6 (54d) 00000110 # # (35d) 00001111 ;7 7 (55d) 00000111 Ring R (82d) 00010000 ;8 8 (56d) 00001000 ;---+++*** Constants ***+++--- IF TMR0CLK == 0 ;Internal oscillator OPTION_REG equ b'10000001' ;'Default' timer/prescaler setup ENDIF IF TMR0CLK == 1 ;External TMR0 source (3,579545MHz) OPTION_REG equ b'10100011' ;'Default' timer/prescaler setup ENDIF #define DTMF0 GPIO, 0 ;Bit0 (LSB) from MT8870 #define DTMF1 GPIO, 1 ;Bit1 from MT8870 #define DTMF2 GPIO, 3 ;Bit2 from MT8870 / _Ring-detector #define DTMF3 GPIO, 4 ;Bit3 from MT8870 / TxD-output (high = space) #define DTMFOK GPIO, 5 ;DTMF detected, and ready at DTMF0-3 ;---+++*** Variable declaration ***+++--- cblock 0Ch DTMFval identval misc temp1, temp2, temp3 ;Temp-variables endc #define CLIP misc, 0 ;Set when CLIP detected #define CLIPend misc, 1 ;Set by CLIPdet when # received #define IdentOK misc, 2 ;Set when 'ident' detected (***) ;---+++*** Reset vector ***+++--- ORG 0h rst_vector: movwf OSCCAL ;Set oscillator calibration. call init IF PWRUPID == 1 goto pwrupident pwrupidentreturn: ENDIF goto main ;---+++*** Identity (located 'low' because of no CP < 64b) ***+++--- identtable: IF IDENTTX == 1 addwf PCL, F dt " +++ PC NummerViser +++ ", 13 dt " Firmware v1.04 ", 13, 13 dt " Jacob Blichfeldt '98 ", 13, 13, 13, 0 ENDIF pwrupidenttable: IF PWRUPID == 1 addwf PCL, F dt "DTMFTORS232", 60, "0104", 62, 0 ENDIF ;---+++*** Initialization ***+++--- init: ; Setup I/O-direction (GP0-3, 5 = input, GP4 = output) movlw b'11101111' tris GPIO ; TxD = Space (GP4 = low) movlw b'00000000' movwf GPIO ; Enable port B pull-up, disable wake-up on pin change ; Setup T0 = inc. on either OSC. or TMR0, prescaler = 4/16 (depends on TMR0CLK) clrwdt movlw OPTION_REG option clrf misc clrf identval retlw 0 ;---+++*** Power-up identity transmission ***+++--- IF PWRUPID == 1 pwrupident: movlw 4 ;wait approx. 1 second movwf temp3 pwrup11: clrf temp2 pwrup12 clrf temp1 pwrup13: clrwdt decfsz temp1, F goto pwrup13 decfsz temp2, F goto pwrup12 decfsz temp3, F goto pwrup11 movlw h'0FF' movwf temp3 pwrup2: clrwdt incf temp3, F movf temp3, W call pwrupidenttable ;Fetch text clrf temp2 subwf temp2, F btfsc STATUS, Z ;End of table? goto pwrupend call txser goto pwrup2 pwrupend: goto pwrupidentreturn ENDIF ;---+++*** Main ***+++--- main: goto DTMFdet DTMFdetreturn: goto ringdet ringdetreturn: goto main ;Repeat main ; Read DTMF-bits from 8870, converts to nibble, and returns in W. readDTMF: movf GPIO, W andlw b'00000011' ;Get bit0-1 movwf DTMFval bsf DTMFval, 2 ;Get bit 2 btfss DTMF2 bcf DTMFval, 2 movlw b'11111111' ;Get bit 3 (DTMF3 = input) bcf DTMFval, 3 tris GPIO movlw b'11101111' btfsc DTMF3 bsf DTMFval, 3 tris GPIO ;DTMF3 = Output bcf DTMF3 movf DTMFval, W ;Convert from 8870 -> 'normal' andlw b'00001111' call DTMFtable movwf DTMFval retlw 0 DTMFtable: addwf PCL, F retlw b'00001101' ;DTMF 'D' retlw b'00000001' ;DTMF '1' retlw b'00000010' ;DTMF '2' retlw b'00000011' ;DTMF '3' retlw b'00000100' ;DTMF '4' retlw b'00000101' ;DTMF '5' retlw b'00000110' ;DTMF '6' retlw b'00000111' ;DTMF '7' retlw b'00001000' ;DTMF '8' retlw b'00001001' ;DTMF '9' retlw b'00000000' ;DTMF '0' retlw b'00001110' ;DTMF '*' retlw b'00001111' ;DTMF '#' retlw b'00001010' ;DTMF 'A' retlw b'00001011' ;DTMF 'B' retlw b'00001100' ;DTMF 'C' ; Converts received DTMF to ASCII (returns in W) conASCII: IF ASCII == 1 addwf PCL, F retlw A'0' retlw A'1' retlw A'2' retlw A'3' retlw A'4' retlw A'5' retlw A'6' retlw A'7' retlw A'8' retlw A'9' retlw A'A' retlw A'B' retlw A'C' retlw A'D' retlw A'*' retlw A'#' ENDIF ; Tests for 3 subsequent '*'. ident: IF IDENTTX == 1 bcf IdentOK movlw b'00001110' ;DTMF '*' subwf DTMFval, W btfss STATUS, Z goto ident1 incf identval, F movlw d'3' subwf identval, W btfss STATUS, Z goto identend bsf IdentOK ident1: clrf identval identend: retlw 0 ENDIF ; Transmits W at 2400bps/std. RS232. Calculates parity-bit. Returns when done. txser: movwf temp1 bcf STATUS, C ;Transmit start-bit call txcarry movlw d'8' ;Transmit 8 databits movwf temp2 txbits: clrwdt rrf temp1, F call txcarry decfsz temp2, F goto txbits IF PARITY == 1 rrf temp1, F ;calculate parity (even) swapf temp1, W xorwf temp1, W movwf temp1 rrf temp1, F rrf temp1, F xorwf temp1, F rrf temp1, W xorwf temp1, F bcf STATUS, C btfsc temp1, 0 bsf STATUS, C call txcarry ENDIF bsf STATUS, C ;Transmit 2*stop-bit call txcarry bsf STATUS, C call txcarry retlw 0 ;transmit carry-flag IF TMR0CLK == 0 txcarry:movlw (256-104) ;2400 = 416,666us = 1666,666c/16 = 104,17 ENDIF IF TMR0CLK == 1 txcarry:movlw (256-93) ;2400 = 416,666us = 1491,477c/16 = 93,22 ENDIF txndone:btfsc TMR0, 7 ;Last bit transmitted (TMR0 ovfl.)? goto txndone bsf DTMF3 ;Space btfsc STATUS,C ;Set to mark (-12 V) if Carry = 1 ;(RS232 inverts output) bcf DTMF3 ;Mark movwf TMR0 retlw 0 ; Recognizes CLIP (DTMF received in A), and sets/clears CLIP. IF CLIONLY == 1 CLIPdet: movwf temp1 movlw b'00001010' ;DTMF 'A' subwf temp1, W btfss STATUS, Z goto CLIPdet2 bsf CLIP goto CLIPdetend CLIPdet2:movlw b'00001101' ;DTMF 'D' subwf temp1, W btfss STATUS, Z goto CLIPdet3 bsf CLIP goto CLIPdetend CLIPdet3:btfss CLIP goto CLIPdetend movlw b'00001111' ;DTMF '#' subwf temp1, W btfss STATUS, Z goto CLIPdetend bcf CLIP bsf CLIPend CLIPdetend: retlw 0 ENDIF ; DTMF-detection - Calls readDTMF, CLIPdet, conASCII and txser if DTMF is present. ; Returns when DTMF is removed. DTMFdet:clrwdt btfss DTMFOK goto DTMFdetreturn goto $+1 ;Double nop btfss DTMFOK goto DTMFdetreturn call readDTMF ;DTMF present, read! IF IDENTTX == 1 ;Transmit ident if '***' received call ident btfss IdentOK goto DTMFdet2 movlw h'0FF' movwf temp3 DTMFdet1:clrwdt incf temp3, F movf temp3, W call identtable ;Fetch text clrf temp2 subwf temp2, F btfsc STATUS, Z ;End of table? goto DTMFdet2 call txser goto DTMFdet1 ENDIF DTMFdet2: movf DTMFval, W IF CLIONLY == 1 ;Transmit only CLI call CLIPdet btfss CLIPend ;Last digit in CLI (#)? goto DTMFdet21 bcf CLIPend goto DTMFdet22 DTMFdet21: btfss CLIP goto DTMFoff DTMFdet22: movf DTMFval, W ENDIF IF ASCII == 1 ;Convert to ASCII movf DTMFval, W call conASCII ENDIF call txser ;Transmit to serial port DTMFoff:clrwdt ;Wait for DTMF to stop btfsc DTMFOK goto DTMFoff DTMFend:goto DTMFdetreturn ; Ring-detection - If ringing is present, calls txser with W = (ascii) 'R'. ; Returns when ringing ends or DTMF is present. ringdet:clrwdt IF RING == 1 btfsc DTMF2 goto ringend ringdet2: ;Wait for ringing-pulse to clrwdt ;stop (or DTMF present). btfsc DTMFOK goto ringend btfss DTMF2 goto ringdet2 movlw b'10000111' ;Setup timer: 50ms before overflow. option movlw d'50' movwf TMR0 ringdet3:clrwdt ;Wait for new ringing-pulse movlw 0 subwf TMR0, W btfsc STATUS, Z goto ringend btfsc DTMF2 goto ringdet3 ;New pulse present. movlw OPTION_REG ;Restore old prescaler option movlw A'R' ;Transmit 'R' call txser bcf CLIP ;Terminate CLIP-sequence (ring det.) movlw b'10000111' ;Setup timer: 50ms before overflow. option ringdet4:movlw d'50' movwf TMR0 ringdet5:clrwdt ;Wait for ringing to end btfss DTMF2 goto ringdet4 movlw 0 subwf TMR0, W btfsc STATUS, Z goto ringend btfss DTMFOK goto ringdet5 ringend: movlw OPTION_REG ;Restore old prescaler option ENDIF goto ringdetreturn IF OTP == 0 ORG h'1FF' ;(old 12C508 calibration-val.) movlw h'0A0' ENDIF END +---------------------------------------------------------------------+ | History | +---------------------------------------------------------------------+ Rev. Change Reason Date -------------------------------------------------------------------------------- 0.0a Source-file created. - 24.03.98 0.5a Hierarchisch diagram finished. - 25.03.98 1.0a Everything implemented, ready for test. - 26.03.98 1.0b Tested in 'real life'... Everything ok, - 27.03.98 (minor bugs removed). 1.0 First version, 'in use'. - 27.03.98 1.01 Fixed '#' not transmitted when CLIP=1. - 29.03.98 Fixed 'D'-sequences not recognized as CLIP (not transmitted when CLIP=1). Implemented PwrUpIdent. Gives possibility of auto-detection, and FW-version detection. 1.02 Implemented possibility of using ext. More precise than 09.04.98 TMR0 source, as timebase for RS232- internal RC-oscillator. communication. Bugfixed CLIPdet. ASCII-table located in upper page when all switches = 1. 1.03r +++ First 'public' release. +++ In case the CLI- 02.05.98 Break CLIP-sequence if ring detected. sequence is not termi- nated probably (because of linenoise etc.) 1.04r Minor changes/bugfixes: 18.03.99 Changed TX-timing. Transmitted at ~2325bps. Changed format from 8E1 to 8E2 Enables receiver to re- (2 stopbits). syncronize between each byte, thereby supressing framing-errors. Minimized 'read-DTMF' timing. Removes unwanted 10us glitch on TX just before transmitting a digit. Ideas/future changes: ------------------------------------------------------------------------------- Add EEPROM and change power-supply, to enable reception/storing of DTMF, when the PC is turned off. Add offhook detector, to enable 'length of conversation', and 'answered/not answered call' logging.