; ***************************************************************************************** ; Copyright © [01/26/1999] 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: bell202_modem_at.src ; ; Author: Chris Fogelklou ; Applications Engineer ; Scenix Semiconductor Inc. ; ; Revision: 3.63 ; ; Date: January 26, 1999. ; ; Part: SX28AC rev. 2.5 ; ; Freq: 50Mhz ; ; Compiled using Parallax SX-Key software v1.0 ; ; Program Description: ; This program is a full implementation of a half-duplex bell202 modem ; which transmits and receives at 1200bps. The baud rate of the uart ; connected to it should be set to 1200,N,8,1. ; The necessary baud rate can be changed easily in this software by ; using the defines present in the sx_demo software, which is available ; on the web. On power-up, this software generates a prompt with some ; instructions on usage. This version of of the software has these ; functions implemented... ; - DTMF output for dialing ; - FSK output ; - FSK input ; - 1200 baud UART for communication with a PC or terminal ; - Small AT command set ; - Ring detection ; - Buffer for AT command storage ; - String parser for AT command processing. Allows easy addition of ; - AT commands. ; ; Authors: Chris Fogelklou, Scenix Semiconductor Inc. ; Written: 98/12/18 to 98/12/21 ; Version: 3.63 ; Last revised: 11/29/99 ; Revisions: 3.0 began with the tx_modem_1_0.src software and combined it ; with the fsk_rx_1_0 software. ; 3.1 modified so that UART speed = Transmit speed and this ; is settable by changing the divider_bit ; 3.2 has much better documentation inside the software, and ; it also includes a transmit-only mode for error-free file ; transmission at 1200 baud and a receive-only mode for ; error-free file reception at 1200 baud. Future revisions ; will fix the 1200-baud file transfer errors. Debugging work ; is involved. ; 3.4 fixes the bug in the DTMF generation code, which was ; causing the output signal to clip and it also adds "twist" ; to the DTMF generation code, which is required. The high ; frequency signal should be 1.25 times greater in amplitude ; than the low frequency signal. (1/5/99) ; 3.5 Fixes the bug in the UART code which is causing ; the modem to miss characters during file transfer. ; (UART process) ; 3.51 Fixes a bug caused by the bug fix in 3.5, in which the ; pwm DC value is not reset to 2.5V when the output is disabled ; 3.6 Adds an AT-command set and RING DETECT to the modem, for ; auto-answer in the AT command set. ; 3.61 Adds ATO command to switch back to data mode and also ; adds additional documentation to the source code. (2/8/99) ; Removed the simplex modes of communication. ; 3.62 Documentation updates. A lot of old comments were left ; in from rev 3.51. Updated ascii buffer to 63 bytes. Ascii ; buffer space can be re-used for other variables, since its ; operations use the FSR only. The actual RAM usage for the ; buffer is limited to the number of characters stored + 1. ; but it is capable of handling up to 63 bytes.(2/23/99) ; 3.63 Modified format for new Scenix template, and to compile for ; SX28 or SX48/52 target devices. ; ; ; I/O USAGE: INPUTS: ; ; rx_pin equ ra.1 ; RS-232 input pin ; fsk_in equ rb.1 ; FSK input pin ; ; OUTPUTS: ; ; PWM_pin equ ra.0 ; PWM output for D/A ; tx_pin equ ra.2 ; RS-232 output pin ; in_out equ ra.3 ; Enables/Disables output ; ; on SX DTMF DEMO boards. ; led_pin equ rb.0 ; LED output pin ; fsk input pin is rb.1 ; does not need a define, uses a bitmask ; hook equ rb.4 ; Selects on-hook/off-hook ; ; RESOURCES: ; Program Memory: TBD ; Data Memory: TBD ;***************************************************************************************** ;***************************************************************************************** ; Target SX ; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, SX48BD/ES, ; SX48BD, SX52BD/ES or SX52BD. For SX48BD/ES and SX52BD/ES, uncomment both defines, ; SX48_52 and SX48_52_ES. ;***************************************************************************************** ;SX18_20 ;SX28 SX48_52 ;SX48_52_ES ;***************************************************************************************** ; Assembler Used ; Uncomment the following line if using the Parallax SX-Key assembler. SASM assembler ; enabled by default. ;***************************************************************************************** SX_Key ;********************************************************************************* ; 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 IFDEF SX18_20 ;SX18AC or SX20AC device directives for SX-Key device SX18L,oscxt4,turbo,stackx_optionx ENDIF IFDEF SX28 ;SX28AC device directives for SX-Key device SX28L,oscxt4,turbo,stackx_optionx ENDIF IFDEF SX48_52_ES ;SX48BD/ES or SX52BD/ES device directives for SX-Key device oschs,turbo,stackx,optionx ELSE IFDEF SX48_52 ;SX48/52/BD device directives for SX-Key device oschs2 ENDIF ENDIF freq 50_000_000 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_ES ;SX48BD/ES or SX52BD/ES device directives for SASM device SX52,oschs,turbo,stackx,optionx ELSE IFDEF SX48_52 ;SX48BD or SX52BD device directives for SASM device SX52,oschs2,stackx,optionx ENDIF ENDIF ENDIF id ' ' ; reset reset_entry ; set reset vector IFDEF SX_Key ;***************************************************************************************** ; Watches (For Debug in SX_Key software V.1.0 +) ;***************************************************************************************** watch freq_acc_high,16,uhex watch freq_count_high,16,uhex watch freq_count_high2,16,uhex watch byte,1,fstr watch curr_sin,8,sdec watch sinvel,8,sdec watch pwm0,8,udec watch ring_duration,8,udec watch timer_flag,1,ubin watch timer_l,16,uhex watch temp,8,uhex watch fsk_bit_delay,8,uhex watch fsk_last_bit,1,ubin watch fsk_rx_en,1,ubin watch flags,8,ubin watch ascii_buffer,16,zstr watch ascii_buffer2,16,zstr watch ascii_buffer3,16,zstr watch ascii_buffer4,16,zstr watch ascii_index,8,udec watch fsr,8,udec watch indf,1,fstr watch wreg,1,fstr watch rings,8,udec watch plus_count,8,udec ENDIF ;***************************************************************************************** ; 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.4 (for SX48/52BD/ES) ; or FSR.7 (SX48/52bd production release) needs to be set appropriately, depending ; on the bank address being accessed. This macro fixes this. ; ; So, instead of using the bank instruction to switch between banks, use _bank instead. ; ;********************************************************************************* _bank macro 1 noexpand bank \1 IFDEF SX48_52 IFDEF SX48_52_ES IF \1 & %00010000 ;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction expand setb fsr.4 ;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software. noexpand ENDIF ELSE IF \1 & %10000000 ;SX48BD and SX52BD (production release) bank instruction expand setb fsr.7 ;modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software. noexpand ELSE expand clrb fsr.7 noexpand ENDIF ENDIF ENDIF endm ;********************************************************************************* ; 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 noexpand 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 ;and SX28AC noexpand ENDIF endm ;************************************************************* ; Macros ;************************************************************* ; old_board ; uncomment this if using with old boards. ; ; Boards marked REV1.4 are newer. Boards ; ; Boards with no rev number are older. ; ; If board is green and uses a DIP package, ; ; keep the oldboard macro commented. ;********************************************************************************* ; Macro: enable_o ; ; This macro enables the output ;********************************************************************************* enable_o macro 0 ifdef old_board clrb in_out ; switch on the new modem boards. else setb in_out endif clr flags 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 flags equ global_org+0 rx_flag equ flags.0 ; Signifies a bit recieved via. RS-232 dtmf_gen_en equ flags.1 ; Signifies whether or not DTMF output is enabled fsk_tx_en equ flags.2 ; These flags are the same and they both fsk_transmitting equ flags.3 ; indicate when the UART is transmitting timer_flag equ flags.4 ; Flags a rollover of the timers. fsk_rx_en equ flags.5 ; Enables the FSK receiver. fsk_rx_flag equ flags.6 ; Signifies reception of FSK ring_det_en equ flags.7 ; Enables the ring detector temp equ global_org+1 ; Temporary storage register for use by the main program divider equ global_org+2 ; used to divide down the UART to 1200 baud IRQ_temp equ global_org+3 ; Temporary register for use by the interrupt service routine ascii_index equ global_org+4 ; Register used for the ascii buffering command_index equ global_org+5 ; Register used as an index to the command to compare to. ;***************************************************************************************** ; RAM Bank Register definitions ;***************************************************************************************** ;********************************************************************************* ; Bank 0 ;********************************************************************************* org bank0_org bank0 = $ sin_gen_bank = $ freq_acc_high ds 1 ; freq_acc_low ds 1 ; 16-bit accumulator which decides when to increment the sine wave freq_acc_high2 ds 1 ; freq_acc_low2 ds 1 ; 16-bit accumulator which decides when to increment the sine wave freq_count_high ds 1 ; freq_count = Frequency * 6.83671552 freq_count_low ds 1 ; 16-bit counter which decides which frequency for the sine wave freq_count_high2 ds 1 ; freq_count = Frequency * 6.83671552 freq_count_low2 ds 1 ; 16-bit counter which decides which frequency for the sine wave curr_sin ds 1 ; The current value of the imitation sin wave sinvel ds 1 ; The velocity of the sin wave curr_sin2 ds 1 ; The current value of the imitation sin wave sinvel2 ds 1 ; The velocity of the sin wave PWM_bank = $ pwm0_acc ds 1 ; PWM accumulator pwm0 ds 1 ; current PWM output timers = $ timer_l ds 1 timer_h ds 1 ;********************************************************************************* ; Bank 1 ;********************************************************************************* org bank1_org bank1 = $ serial = $ ;UART bank tx_high ds 1 ;hi byte to transmit tx_low ds 1 ;low byte to transmit tx_count ds 1 ;number of bits sent tx_divide ds 1 ;xmit timing (/16) counter rx_count ds 1 ;number of bits received rx_divide ds 1 ;receive timing counter rx_byte ds 1 ;buffer for incoming byte string ds 1 byte ds 1 plus_count ds 1 ;counts the number of '+'s received ;while in FSK_IO mode. ;********************************************************************************* ; Bank 2 ;********************************************************************************* org bank2_org bank2 = $ fsk_transmit_bank = $ fsk_bit_delay ds 1 fsk_tx_byte ds 1 fsk_flags ds 1 fsk_last_bit equ fsk_flags.0 fsk_tx_counter ds 1 fsk_receive_bank = $ fsk_trans_count ds 1 ; This register counts the number of counts ; between transitions at the pin rb_past_state ds 1 ; This register keeps track of the previous fsk_rx_count ds 1 ; number of bits received fsk_rx_divide ds 1 ; bit delay fsk_rx_byte ds 1 ; buffer for incoming byte fsk_current_in equ fsk_flags.1 ; The bit represented by the current input frequency fsk_trans equ fsk_flags.2 ;********************************************************************************* ; Bank 3 ;********************************************************************************* org bank3_org bank3 = $ ring_detect_bank = $ ring_timer_low ds 1 ring_timer_high ds 1 ring_flags ds 1 ring_timer_flag equ ring_flags.0 ringing_1 equ ring_flags.1 ringing_2 equ ring_flags.2 long_ring equ ring_flags.3 ring_pause equ ring_flags.4 ring_captured equ ring_flags.5 ring_off_timer_1 ds 1 ring_timer ds 1 ring_duration ds 1 ring_duration_timer ds 1 answer_rings ds 1 rings ds 1 ;********************************************************************************* ; Bank 4 ;********************************************************************************* org bank4_org bank4 = $ ascii_buffer = $ ;********************************************************************************* ; Bank 5 ;********************************************************************************* org bank5_org bank5 = $ ascii_buffer2 = $ ;********************************************************************************* ; Bank 6 ;********************************************************************************* org bank6_org bank6 = $ ascii_buffer3 = $ ;********************************************************************************* ; Bank 7 ;********************************************************************************* org bank7_org bank7 = $ ascii_buffer4 = $ IFDEF SX48_52 ;********************************************************************************* ; Bank 8 ;********************************************************************************* org $80 ;bank 8 address on SX52 bank8 = $ ;********************************************************************************* ; 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 ;***************************************************************************************** ; Port Assignment ;***************************************************************************************** RA_latch equ %00001000 ;SX18/20/28/48/52 port A latch init RA_DDIR equ %11110010 ;SX18/20/28/48/52 port A DDIR value RA_LVL equ %00001110 ;SX18/20/28/48/52 port A LVL value RA_PLP equ %11111111 ;SX18/20/28/48/52 port A PLP value RB_latch equ %11101110 ;SX18/20/28/48/52 port B latch init RB_DDIR equ %00101110 ;SX18/20/28/48/52 port B DDIR value RB_ST equ %11111101 ;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 RC_latch equ %00000000 ;SX18/20/28/48/52 port C latch init RC_DDIR equ %11111010 ;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 ;********************************************************************************* ; Pin Definitions ;********************************************************************************* PWM_pin equ ra.0 ; PWM output for D/A rx_pin equ ra.1 ; RS-232 Input pin tx_pin equ ra.2 ; RS-232 Output pin in_out equ ra.3 ; Switches between output ; and input on SX DTMF DEMO boards. led_pin equ rb.0 ; Flashes while characters are ; being received. ;fsk input pin is rb.1 ; does not need a define, uses a bitmask ring equ rb.3 ; Ring detection pin hook equ rb.4 ; Goes on/off-hook. ;***************************************************************************************** ; Program constants ;***************************************************************************************** int_period equ 163 ;RTCC Interrupt rate ;********************************************************************************* ; Equates for the FSK receive part of the modem ;********************************************************************************* glitch_th equ 10 ; The threshold which defines a glitch (small spike which should be ignored) low_count_error_th equ 30 ; The lowest count allowed for a high frequency low_high_th equ 95 ; The lowest count allowed for a low frequency high_count_error_th equ 150 ; The highest count allowed for a low frequency ; *** 1200 baud using a 1/2 counter. baud_bit = 7 ;for 1200 baud fsk start_delay = 128+64+1 ; " " " divider_bit = 1 ;1 for 1200 baud, 2 for 600 baud, 3 for 300 baud. ;************************************************************************** ; Equates for common data comm frequencies (DTMF generation) ;************************************************************************** f697_h equ $012 ; DTMF Frequency f697_l equ $09d f770_h equ $014 ; DTMF Frequency f770_l equ $090 f852_h equ $016 ; DTMF Frequency f852_l equ $0c0 f941_h equ $019 ; DTMF Frequency f941_l equ $021 f1209_h equ $020 ; DTMF Frequency f1209_l equ $049 f1336_h equ $023 ; DTMF Frequency f1336_l equ $0ad f1477_h equ $027 ; DTMF Frequency f1477_l equ $071 f1633_h equ $02b ; DTMF Frequency f1633_l equ $09c f1300_h equ $022 ; 1300Hz Signifies HIGH data in Bell202 Spec f1300_l equ $0b7 f2100_h equ $038 ; 2100Hz Signifies LOW data in Bell202 Spec f2100_l equ $015 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 $02 ;Read Timer Capture register low byte TCPH_R equ $02 ;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 Schmitt Trigger setup, 0 = enabled, 1 = disabled PLP_R equ $0E ;Read Port Schmitt Trigger setup, 0 = enabled, 1 = disabled DDIR_R equ $0F ;Read Port Direction ; Timer (write) addresses CLR_TMR equ $10 ;Resets 16-bit Timer 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 Schmitt Trigger setup, 0 = enabled, 1 = disabled PLP_W equ $1E ;Write Port Schmitt Trigger 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 ;***************************************************************************************** ; Interrupt Service Routine ;***************************************************************************************** ; Note: The interrupt code must always originate at address $0. ; ; Interrupt Frequency = (Cycle Frequency / -(retiw value)) For example: ; With a retiw value of -217 and an oscillator frequency of 50MHz, this ; code runs every 4.34us. ;***************************************************************************************** org $0 ;********************************************************************************* ; Virtual Peripheral: ; ; ; Input variable(s): ; Output variable(s): ; Variable(s) affected: ; Flag(s) affected: ;********************************************************************************* ;****************************************************************************** PWM_OUTPUT ; This outputs the current value of pwm0 to the PWM_pin. This generates ; an analog voltage at PWM_pin after filtering. ; INPUTS: ; pwm0 - The value from 0-255 representing the analog voltage to be ; output by the PWM_pin ;************************************************************************** bank PWM_bank add pwm0_acc,pwm0 ; add the PWM output to the accumulator snc jmp :carry ; if there was no carry, then clear ; the PWM_pin clrb PWM_pin jmp PWM_out :carry setb PWM_pin ; otherwise set the PWM_pin PWM_out ;************************************************************************** TASK_SWITCHER ; Now decide which task to do... we may only do one, and the transmit ; takes priority over the receive functions. ; INPUTS: ; flags - Depending on which of the flags are set in the flags register, ; either FSK transmission, FSK reception, or DTMF generation ; will occur. ;************************************************************************** snb ring_det_en call @ring_detect jnb fsk_tx_en,:fsk_tx_out ; If FSK transmit is enabled call @sine_generator2 ; only use one of the sine generators call @FSK_TX_UART ; perform the transmit UART first jmp :TASK_OUT ; and then skip over DTMF because can't do :fsk_tx_out ; both at once jnb dtmf_gen_en,:sine_gen_out ; if dtmf generation is enabled call @sine_generator1 ; do it. jmp :TASK_OUT :sine_gen_out jnb fsk_rx_en,:fsk_rx_out ; jump out if the FSK receiver is not enabled call @FSK_RECEIVE jmp :TASK_OUT :fsk_rx_out :TASK_OUT ;************************************************************************** :transmit ; This is an asynchronous transmitter for RS-232 transmission ; INPUTS: ; divider.divider_bit - Transmitter/receiver only executes when this bit is = 1 ; tx_divide.baud_bit - Transmitter only executes when this bit is = 1 ; tx_high - Part of the data to be transmitted ; tx_low - Some more of the data to be transmitted ; tx_count - Counter which counts the number of bits transmitted. ; OUTPUTS: ; tx_pin - Sets/Clears this pin to accomplish the transmission. ;************************************************************************** jnb divider.divider_bit,:rxdone ; cut the UART speed down to 1200/600/300 ; depending on divider bit bank serial clrb tx_divide.baud_bit ;clear xmit timing count flag inc tx_divide ;only execute the transmit routine STZ ;set zero flag for test SNB tx_divide.baud_bit ; every 2^baud_bit interrupt test tx_count ;are we sending? JZ :receive ;if not, go to :receive clc ;yes, ready stop bit rr tx_high ; and shift to next bit rr tx_low ; dec tx_count ;decrement bit counter movb tx_pin,/tx_low.6 ;output next bit ;************************************************************************** :receive ; This is an asynchronous receiver for RS-232 reception ; INPUTS: ; rx_pin - Pin which RS-232 is received on. ; OUTPUTS: ; rx_byte - The byte received ; rx_flag - Set when a bRyte is received. ;************************************************************************** movb c,rx_pin ;get current rx bit test rx_count ;currently receiving byte? jnz :rxbit ;if so, jump ahead mov w,#9 ;in case start, ready 9 bits sc ;skip ahead if not start bit mov rx_count,w ;it is, so renew bit count mov rx_divide,#start_delay ;ready 1.5 bit periods :rxbit djnz rx_divide,:rxdone ;middle of next bit? setb rx_divide.baud_bit ;yes, ready 1 bit period dec rx_count ;last bit? sz ;if not rr rx_byte ; then save bit snz ;if so setb rx_flag ; then set flag :rxdone ;************************************************************************** do_timers ; The timer will tick at the interrupt rate (3.26us for 50MHz.) To set up ; the timers, move in FFFFh - (value that corresponds to the time.) Example: ; for 1ms = 1ms/3.26us = 306 dec = 132 hex so move in $FFFF - $0132 = $FECD ;************************************************************************** bank timers ; Switch to the timer bank mov w,#1 add timer_l,w ; add 1 to timer_l jnc :timer_out ; if it's not zero, then add timer_h,w ; don't increment timer_h snc setb timer_flag movb led_pin,timer_h.6 ; once timer_h is changed, update the LED :timer_out clrb divider.divider_bit inc divider ; do nothing unless divider_bit is a '1' ;********************************************************************************* ; Set Interrupt Rate ;********************************************************************************* isr_end mov w,#-int_period ;refresh RTCC on return retiw ;return from the interrupt ; = 1/(int_period*RTCC prescaler*1/50MHz) ; = 1/(217*1*20ns) = 4.34us ;***************************************************************************************** ; End of the Interrupt Service Routine ;***************************************************************************************** ;***************************************************************************************** ; RESET VECTOR ;***************************************************************************************** ;********************************************************************************* ; Program execution begins here on power-up or after a reset ;********************************************************************************* reset_entry jmp @init ;***************************************************************************************** ; MAIN PROGRAM CODE ;***************************************************************************************** ;********************************************************************************* ; Main ;********************************************************************************* main jmp start_2 _FSK_IO jmp FSK_IO ; Jump table for FSK_IO start_2 ;************************************************************************** ; Main Code: ; -Sends Prompt ; -Waits for input from UART ; -Pushes incoming characters onto the buffer. ; -If incoming character is a backspace, deletes last character from ; buffer ; -On CR character, jumps to the parse_string routine ; -Outputs RING if a ring is detected ; -Answers if the modem is set for auto-answer mode and a ring is detected, ; after number of rings specified occurs. ;************************************************************************** mov w,#_hello ; send hello string call @send_string mov w,#_instructions ; send instructions call @send_string _send_prompt mov w,#_CR call @send_string mov w,#_COMMAND_MODE call @send_string mov w,#_prompt ; send prompt call @send_string clr flags _cmd_loop jb hook,:not_auto_answer ; If we are off-hook, don't setb ring_det_en ; watch for ring. bank ring_detect_bank jnb ring_captured,:not_auto_answer clrb ring_captured cjb ring_duration,#27,:not_auto_answer ; If the ring was less than inc rings ; 200ms, ignore it, otherwise mov w,#_RING ; increment the ring count. call @send_string ; and send 'RING' to the terminal bank ring_detect_bank mov w,answer_rings ; If answer_rings is 0, then this jz :not_auto_answer ; is not auto answer. xor w,rings ; Else, if # rings = answer_rings, answer jz :answer :not_auto_answer jnb rx_flag,_cmd_loop ; wait for an input character clrb rx_flag ; from terminal bank serial mov byte,rx_byte call @uppercase ; convert it to uppercase cje byte,#$20,_cmd_loop ; if it equals a space, ignore it. cje byte,#$0d,:enter ; if it equals a carriage return, parse the string. mov w,byte ; if it does not resemble the above characters, echo it. call @send_byte cje byte,#$08,:backspace ; if it equals a backspace, delete one character in the buffer. call @buffer_push ; otherwise, store it jmp _cmd_loop ; and come back for more. :answer setb hook ; answer the phone!!! clrb ring_det_en ; disable ring detect. mov w,#_ANSWERING ; send 'answering' to the terminal call @send_string call _FSK_IO ; go to half-duplex mode. jmp _cmd_loop ; and go back for more. :backspace call @buffer_backspace jmp _cmd_loop :enter ; If the user presses enter, then parse the string. ;************************************************************************** ; String parser (Checks to see if buffer = any commands) ; -Checks contents of ascii buffer against any commands stored in ROM ; -If a command = the contents of the ascii buffer, a routine will be called ; -Each routine MUST perform a retw 0 on exit, or parse_string will not ; know that a routine has run and it should exit back to command mode. ; -Exits back to command mode when it detects a zero after the table look-up. ; -Outputs 'OK' if no commands are matched. ;************************************************************************** parse_string clr ascii_index ; Clear the index into the ascii buffer clr command_index ; And the index into the commands :loop call @buffer_get ; Get a vale from the buffer at ascii_index call command_table ; Get a character from one of the commands test w ; If the return value is 0, then this matched jz :done ; the command and ran a routine. Exit. bank serial xor w,byte ; compare the command's character with the jnz :not_equal ; buffer's character. call @inc_ascii_index ; Increment the index into the buffer. jmp :loop :not_equal inc command_index ; If the buffer did not equal the command, clr ascii_index ; start from the beginning of a new command cjne command_index,#6,:loop ; and the buffer. (This number = # of commands) mov w,#_OK ; If we have checked all 4 commands, then this call @send_string ; did not equal any so send an 'OK' message. :done bank ascii_buffer clr ascii_index clr ascii_buffer jmp _send_prompt ;************************************************************************** command_table mov w,command_index add pc,w jmp command_1 jmp command_2 jmp command_3 jmp command_4 jmp command_5 jmp command_6 ;************************************************************************** command_1 ; Dial command mov w,ascii_index add PC,w retw 'A' retw 'T' retw 'D' retw 'T' jmp DIAL_MODE ;************************************************************************** command_2 ; Hang up command mov w,ascii_index add PC,w retw 'A' retw 'T' retw 'H' jmp HANG_UP ;************************************************************************** command_3 ; Initialize mov w,ascii_index add PC,w retw 'A' retw 'T' retw 'Z' jmp INITIALIZE ;************************************************************************** command_4 ; Answer/ Auto answer mov w,ascii_index add PC,w retw 'A' retw 'T' retw 'A' jmp AUTO_ANSWER ;************************************************************************** command_5 ; Data mode mov w,ascii_index add PC,w retw 'A' retw 'T' retw 'O' jmp FSK_IO ;************************************************************************** command_6 ; Help mov w,ascii_index add PC,w retw '?' jmp HELP ;************************************************************************** ; END of String parser (Checks to see if buffer = any commands) ;************************************************************************** ;************************************************************************** ; Dial Mode: ; -Dials contents of ascii buffer, starting from location pointed ; to by ascii_index. ; -Responds to these commands: ; 0-9, *, # - Dials the specified number ; , - Pause for 2 seconds ; -Jumps to data mode after dialing. ;************************************************************************** DIAL_MODE clrb ring_det_en mov w,#_CR call @send_string mov w,#_DIALING ; send Dialing call @send_string setb hook ; pick up the line :dial_loop call @buffer_get ; wait for an input character call @uppercase ; convert it to uppercase mov w,byte snz jmp FSK_IO call @send_byte cje byte,#',',:pause ; if the character = ',', pause for 2s call @digit_2_index ; convert the ascii digit to an ; index value call @load_frequencies ; load the frequency registers call @dial_it ; dial the number for 60ms and return. :inc call @inc_ascii_index ; increment the index into the table jmp :dial_loop :pause mov w,#201 ; delay 2s call @delay_10n_ms jmp :inc ;************************************************************************** ; FSK Input/Output mode: ; -Sends Prompt ; -Sends any characters received from the terminal through the phone line ; and sends any characters received from the phone line to the terminal ; -performs retw 0 when it detects incoming '+++' from UART ;************************************************************************** FSK_IO mov w,#_CR call @send_string mov w,#_DATA_MODE ; send FSK I/O string call @send_string mov w,#_PROMPT call @send_string bank sin_gen_bank mov curr_sin2,#-4 ; set up so the wave starts at close to the right spot (0). mov sinvel2,#-8 clr flags setb fsk_rx_en ; enable the FSK detector bank serial :RESET_PLUSES mov plus_count,#3 ; counts the number of '+'s received :TX_LOOP jb fsk_rx_flag,:fsk_byte_received ; if FSK received a byte, go to :fsk_byte_received jb rx_flag,:byte_received ; if the UART received a byte, go to :byte_received jmp :TX_LOOP :byte_received bank serial ; echo the character back to the terminal mov byte,rx_byte clrb rx_flag ; clear the flag call @fsk_transmit_byte ; send the byte via. FSK bank serial mov w,byte xor w,#'+' jnz :RESET_PLUSES dec plus_count snz retw 0 jmp :TX_LOOP ; return to the loop :fsk_byte_received bank fsk_receive_bank ; send the character to the terminal mov w,fsk_rx_byte clrb fsk_rx_flag ; clear the flag call @send_byte jmp :TX_LOOP ; return to the loop ;************************************************************************** HANG_UP ; goes on-hook mov w,#_CR call @send_string mov w,#_OK call @send_string clrb hook retw 0 ;************************************************************************** INITIALIZE ; calls init routine mov w,#_CR call @send_string mov w,#_OK call @send_string jmp @reset_entry ;************************************************************************** HELP ; outputs help stuff mov w,#_plus call @send_string mov w,#_COMMAND_MODE call @send_string mov w,#_CR call @send_string mov w,#_ATO call @send_string mov w,#_DATA_MODE call @send_string mov w,#_ATDT call @send_string mov w,#_DIALING call @send_string mov w,#_CR call @send_string mov w,#_ATA call @send_string mov w,#_AUTO_ANSWER call @send_string mov w,#_ATH call @send_string mov w,#_HANGING_UP call @send_string mov w,#_ATZ call @send_string mov w,#_INIT call @send_string retw 0 ;************************************************************************** AUTO_ANSWER ; Moves a default value of 1 into answer_rings. This ; specifies the number of rings to answer after. If the ; user has entered ATA3, for instance, then a 3 will ; replace the 1 in answer_rings. The modem will answer ; after 3 rings. If the user enters ATA, the modem will ; answer after 1 ring. ATA0 should force the modem to ; pick up immediately. bank ring_detect_bank clr rings mov answer_rings,#1 call @buffer_get ; get the next character from ; the ascii_buffer. bank serial mov w,byte ; If this character is not a null, jz :done ; then use it to indicate how many sub byte,#'0' ; rings to trigger on. mov w,byte bank ring_detect_bank mov answer_rings,w :done mov w,#_CR call @send_string mov w,#_AUTO_ANSWER call @send_string retw 0 org $200 ;************************************************************************** ; Miscellaneous subroutines ;************************************************************************** ;************************************************************************** buffer_push ; This subroutine pushes the contents of byte onto the 32-byte ascii buffer. ;************************************************************************** bank serial ; Move the byte into the buffer mov temp,byte mov fsr,#ascii_buffer add fsr,ascii_index mov indf,temp ; Increment index and keep it in range call @inc_ascii_index mov fsr,#ascii_buffer ; Null terminate the buffer. add fsr,ascii_index clr indf bank serial retp ;************************************************************************** ;************************************************************************** buffer_backspace ; This subroutine deletes one value of the buffer and decrements the index ;************************************************************************** dec ascii_index and ascii_index,#%01101111 mov fsr,#ascii_buffer add fsr,ascii_index clr indf bank serial retp ;************************************************************************** inc_ascii_index ; This subroutine increments the index into the buffer ;************************************************************************** mov w,ascii_index and w,#%00001111 xor w,#%00001111 jnz :not_on_verge inc ascii_index mov w,#16 add w,ascii_index and w,#$7f mov ascii_index,w retp :not_on_verge inc ascii_index retp ;************************************************************************** buffer_get ; This subroutine retrieves the buffered value at index ;************************************************************************** mov fsr,#ascii_buffer add fsr,ascii_index mov w,indf bank serial mov byte,w retp ;************************************************************************** ;************************************************************************** delay_10n_ms ; This subroutine delays 'w'*10 milliseconds. ; This subroutine uses the TEMP register ; INPUT w - # of milliseconds to delay for. ; OUTPUT Returns after 10 * n milliseconds. ;************************************************************************** mov temp,w bank timers :loop clrb timer_flag ; This loop delays for 10ms mov timer_h,#$0f4 mov timer_l,#$004 jnb timer_flag,$ dec temp ; do it w-1 times. jnz :loop clrb timer_flag retp ;************************************************************************** delay_n_ms ; This subroutine delays 'w' milliseconds. ; This subroutine uses the TEMP register ; INPUT w - # of milliseconds to delay for. ; OUTPUT Returns after n milliseconds. ;************************************************************************** mov temp,w bank timers :loop clrb timer_flag ; This loop delays for 1ms mov timer_h,#$0fe mov timer_l,#$0cd jnb timer_flag,$ dec temp ; do it w-1 times. jnz :loop clrb timer_flag retp ;************************************************************************** zero_ram ; Subroutine - Zero all ram. ; INPUTS: None ; OUTPUTS: All ram locations (except special function registers) are = 0 ;************************************************************************** CLR FSR :loop SB FSR.4 ;are we on low half of bank? SETB FSR.3 ;If so, don't touch regs 0-7 CLR IND ;clear using indirect addressing IJNZ FSR,:loop ;repeat until done retp ;************************************************************************** ; Subroutine - Get byte via serial port and echo it back to the serial port ; INPUTS: ; -NONE ; OUTPUTS: ; -received byte in rx_byte ;************************************************************************** get_byte jnb rx_flag,$ ;wait till byte is received clrb rx_flag ;reset the receive flag bank serial mov byte,rx_byte ;store byte (copy using W) ; & fall through to echo char back retp ;************************************************************************** ; Subroutine - Get byte via Bell202 FSK and send it to the serial port ; INPUTS: ; -NONE ; OUTPUTS: ; -received byte in fsk_rx_byte ;************************************************************************** fsk_get_byte jnb fsk_rx_flag,$ ;wait till byte is received clrb fsk_rx_flag ;reset the receive flag bank fsk_receive_bank mov byte,fsk_rx_byte ;store byte (copy using W) ; & fall through to echo char back ;************************************************************************** ; Subroutine - Send byte via serial port ; INPUTS: ; w - The byte to be sent via RS-232 ;************************************************************************** send_byte bank serial :wait test tx_count ;wait for not busy jnz :wait ; not w ;ready bits (inverse logic) mov tx_high,w ; store data byte setb tx_low.7 ; set up start bit mov tx_count,#10 ;1 start + 8 data + 1 stop bit RETP ;leave and fix page bits ;************************************************************************** ; Subroutine - Send string pointed to by address in W register ; INPUTS: ; w - The address of a null-terminated string in program ; memory ; OUTPUTS: ; outputs the string via. RS-232 ;************************************************************************** send_string bank serial mov string,w ;store string address :loop mov w,string ;read next string character mov m,#4 ; with indirect addressing iread ; using the mode register mov m,#$F ;reset the mode register test w ;are we at the last char? snz ;if not=0, skip ahead RETP ;yes, leave & fix page bits call send_byte ;not 0, so send character inc string ;point to next character jmp :loop ;loop until done ;************************************************************************** ; Subroutine - Make byte uppercase ; INPUTS: ; byte - The byte to be converted ;************************************************************************** uppercase csae byte,#'a' ;if byte is lowercase, then skip ahead RETP sub byte,#'a'-'A' ;change byte to uppercase RETP ;leave and fix page bits ;************************************************************************** ; Subroutine - Disable the output (Enable the input) ;************************************************************************** disable_o ifdef old_board setb in_out ; set the analogue switch for else clrb in_out endif bank PWM_bank ; input mode. mov pwm0,#128 ; put 2.5V DC on PWM output pin retp ;************************************************************************** org $400 ;************************************************************************** ; Jump table for page 2 ;************************************************************************** FSK_RECEIVE jmp _FSK_RECEIVE ;************************************************************************** ; String data (for RS-232 output) and tables ;************************************************************************** _hello dw 13,10,'SX Modem V 3.6',13,10,0 _instructions dw '- ? For Help',0 _DIALING dw 'DIAL ',0 _ANSWERING dw 'ANSWERING ',0 _AUTO_ANSWER dw 'AUTO ANSWER ',13,10,0 _RING dw 'RING',13,10,0 _PROMPT dw 13,10,'>',0 _HANGING_UP dw 'HANG UP ',13,10,0 _ATDT dw 13,10,'ATDT=',0 _ATA dw 'ATA =',0 _ATH dw 'ATH =',0 _ATZ dw 'ATZ =',0 _ATO dw 'ATO =',0 _plus dw 13,10,'+++ =',0 _OK dw 'OK',13,10,0 _CR dw 13,10,0 _COMMAND_MODE dw 'COMMAND MODE',0 _DATA_MODE dw 'DATA MODE',0 _INIT dw 'INIT',0 ;************************************************************************** ; FSK transmit/receive functions ;************************************************************************** ring_detect jmp _ring_detect ;************************************************************************** FSK_TX_UART ;(part of interrupt service routine) ; This subroutine creates an internal transmit UART using the data in ; fsk_tx_byte ;************************************************************************** sb fsk_transmitting RETP sb divider.divider_bit ; divide the baud by divider_bit RETP bank fsk_transmit_bank clrb fsk_bit_delay.7 ; multiply the baud by 2 dec fsk_bit_delay ; Decrement the delay counter sz RETP stc ; set the carry bit to create a stop bit rr fsk_tx_byte sc clrb fsk_last_bit snc setb fsk_last_bit jb fsk_last_bit,:new_bit_is_high :new_bit_is_low bank sin_gen_bank mov freq_count_high2,#f2100_h ; output a frequency of 2100Hz mov freq_count_low2,#f2100_l jmp :end_new_bit :new_bit_is_high bank sin_gen_bank mov freq_count_high2,#f1300_h ; output a frequency of 1300Hz mov freq_count_low2,#f1300_l :end_new_bit bank fsk_transmit_bank decsz fsk_tx_counter RETP :FSK_DONE_TRANSMITTING setb fsk_last_bit ; since we're done transmitting, clrb fsk_transmitting ; clear the transmitting flag RETP ;************************************************************************** fsk_transmit_byte ; This subroutine initializes the FSK UART and the sine generator and then ; it transmits data via FSK modulation to an outside source. The byte to ; send is passed in the 'w' register. Returns when byte transmission is ; done and FSK wave is close to zero OR when another character is received ; via. RS-232. If another character is received via. RS-232, it immediately ; exits so that next character can begin transmission without re-initializing. ;************************************************************************** jb fsk_transmitting,$ ; wait until done transmitting. bank fsk_transmit_bank mov fsk_tx_byte,w :enable enable_o ; enable the outputs bank fsk_transmit_bank clr fsk_bit_delay ; since fsk_bit_delay goes from 0 (256) to zero, ; clear fsk_bit_delay to set up for the first bit. clrb fsk_last_bit ; set fsk_last_bit to be an internal low (start bit) mov fsk_tx_counter,#10 ; set up the bit counter for 1 start, 8 data, and 1 stop bank sin_gen_bank mov freq_count_high2,#f2100_h ; set up the sine generator to mov freq_count_low2,#f2100_l ; output 2100 Hz. mov curr_sin,#0 ; make the output of sin gen 1=0 ; so it doesn't interfere with sin gen 2 bank fsk_transmit_bank ; enable the 2nd sin generator and the TX UART. setb fsk_transmitting setb fsk_tx_en bank sin_gen_bank :wait_loop snb rx_flag ; if another character is received, don't disable retp ; output jb fsk_transmitting,:wait_loop ; otherwise, wait until we are done transmitting cjne curr_sin2,#-4,:wait_loop ; and wait until FSK signal is relatively close to zero. clrb fsk_tx_en ; disable the FSK transmitter setb fsk_rx_en ; enable the FSK receiver call @disable_o ; disable the output retp ;************************************************************* ; _FSK_RECEIVE ; FSK receiver starts here. ; ;************************************************************* bank fsk_receive_bank add fsk_trans_count,#1 ; Regardless of what is going on, increment the snc ; transition timer. These get cleared when a transition jmp :roll_over_error ; takes place. cjb fsk_trans_count,#low_high_th,:fsk_timer_out ; as soon as it takes longer than 95 counts setb fsk_current_in ; to transition, this must be a low frequency :fsk_timer_out mov w,rb and w,#%00000010 ; get the current state of rb. xor w,rb_past_state ; compare it with the previous state of the pin jz fsk_rx_out ; if there was no change, then jump out, there is nothing to do. ; Now it is time to determine if the transition that took place indicates a bit was received ; (it must be within some thresholds... below 20, ignore it, below 40, what???, ; below 95, high frequency, below 140, low frequency (already set), above 140, ; what???) cjb fsk_trans_count,#glitch_th,:glitch_so_ignore ; pulse was below specs, ignore it... probably noise cjb fsk_trans_count,#low_count_error_th,:error ; pulse was not a glitch but wasn't long enough to mean anything... huh? cjb fsk_trans_count,#low_high_th,:high_frequency ; pulse was within specs for a high frequency... cjb fsk_trans_count,#high_count_error_th,:fsk_receive_done ; pulse was within specs for a low frequency (don't do anything) jmp :error ; pulse was too long to mean anything, so do nothing. :high_frequency ; a high frequency corresponds to low data. clrb fsk_current_in jmp :fsk_receive_done :roll_over_error ; if the counter rolls over, keep it in range. ;--------------- PUT ERROR HANDLING CODE IN HERE ----------------- mov fsk_trans_count,#high_count_error_th clr fsk_rx_count jmp :glitch_so_ignore :error ; if there is another type of error, just clear ; any UART receive. ;--------------- PUT ERROR HANDLING CODE IN HERE ----------------- clr fsk_rx_count :fsk_receive_done clr fsk_trans_count ; clear the bit counter. :glitch_so_ignore ; don't clear the counter if the data was a glitch mov w,rb ; save the new state of RB. and w,#%00000010 mov rb_past_state,w fsk_rx_out ;************************************************************************** :fsk_uart ; This is an asynchronous receiver. Written by Craig Webb. Modified by ; Chris Fogelklou for use with FSK receive routine. ;************************************************************************** bank fsk_receive_bank jnb divider.divider_bit,fsk_rx_done ; (Divide operation frequency by divider_bit) movb c,fsk_current_in ; get current rx bit test fsk_rx_count ; currently receiving byte? jnz :rxbit ; if so, jump ahead mov w,#9 ; in case start, ready 9 bits sc ; skip ahead if not start bit mov fsk_rx_count,w ; it is, so renew bit count mov fsk_rx_divide,#start_delay ; ready 1.5 bit periods :rxbit djnz fsk_rx_divide,fsk_rx_done ; middle of next bit? setb fsk_rx_divide.baud_bit ; yes, ready 1 bit period dec fsk_rx_count ; last bit? sz ; if not rr fsk_rx_byte ; then save bit snz ; if so setb fsk_rx_flag ; then set flag fsk_rx_done RETP ;************************************************************************** ; END FSK ROUTINES ;************************************************************************** ;************************************************************************** ; DTMF generate lookup tables. Gives the tone required for each of the ; DTMF digits. ;************************************************************************** _0_ dw f941_h,f941_l,f1336_h,f1336_l _1_ dw f697_h,f697_l,f1209_h,f1209_l _2_ dw f697_h,f697_l,f1336_h,f1336_l _3_ dw f697_h,f697_l,f1477_h,f1477_l _4_ dw f770_h,f770_l,f1209_h,f1209_l _5_ dw f770_h,f770_l,f1336_h,f1336_l _6_ dw f770_h,f770_l,f1477_h,f1477_l _7_ dw f852_h,f852_l,f1209_h,f1209_l _8_ dw f852_h,f852_l,f1336_h,f1336_l _9_ dw f852_h,f852_l,f1477_h,f1477_l _star_ dw f941_h,f941_l,f1209_h,f1209_l _pound_ dw f941_h,f941_l,f1477_h,f1477_l ;************************************************************************** _ring_detect ; Ring detect has 3 functions: ; - It filters out the 20Hz pulsing of the ring-line by using ; a software monostable with an 80ms timeout. If the length ; of time between pulses exceeds 80ms, the "ringing_1" flag gets ; cleared. This can also be used to show the pause between ; rings in a distinctive ring pattern. ; - It uses another monostable with a timeout of 530ms to ; filter out distinctive ring pauses, whose largest pause ; between distinctive rings is 525ms. If the pause exceeds ; the 530ms timeout, the "ring_occured" flag is set, indicating ; that a ring is complete. ; - A duration timer is used to find the amount of time the ; line was ringing between pauses. This can be used to ; decode distinctive ring. The register is called ; ring_duration. The duration is saved as soon as a pause is ; found. ;************************************************************************** bank ring_detect_bank ;****************************************************************** ; First run a timer that rolls over every 10ms, so all other timers ; can sync to it. ;****************************************************************** inc ring_timer_low jnz :no_roll_1 inc ring_timer_high jnz :no_roll_1 setb ring_timer_flag mov ring_timer_low,#$04 mov ring_timer_high,#$f4 :no_roll_1 ;****************************************************************** ; Now create a monostable that times out after 80ms, because this ; is the maximum amount of time between two pulses of the ring ; signal. Ringing gets cleared when this monostable times out. ;****************************************************************** jb ring,:check_for_duration :ring_low mov ring_off_timer_1,#9 ; if the ring line is low, set up setb ringing_1 jb ringing_2,:check_for_duration mov ring_timer,#221 setb ringing_2 :check_for_duration jnb ring_timer_flag,:ring_detect_done ; jump out if the timer_flag is not set. clrb ring_timer_flag jnb ringing_1,:not_ringing_1 inc ring_duration_timer ; count the ring duration snz setb long_ring dec ring_off_timer_1 ; while it is ringing. jnz :not_ringing_1 :done_short_ring clrb ringing_1 ; clear the flag that indicates ; it is ringing. mov ring_duration,ring_duration_timer ; and save the duration ; of the ring. clr ring_duration_timer ; reset the timer which times how ; long each ring is. setb ring_pause ; indicate pause between rings. :not_ringing_1 ; Now check if the 530ms monostable ; has timed out. jnb ringing_2,:ring_detect_done dec ring_timer jnz :ring_detect_done :done_whole_ring clrb ringing_2 setb ring_captured :ring_detect_done retp ;************************************************************************** ; Done ring detection interrupt service routine ;************************************************************************** org $600 ;************************************************************************** ; DTMF transmit functions/subroutines ;************************************************************************** ;************************************************************************** digit_2_index ; This subroutine converts a digit from 0-9 or a '*' or a '#' to a table ; lookup index which can be used by the load_frequencies subroutine. To use ; this routine, pass it a value in the 'byte' register. No invalid digits ; are used. (A, B, C, or D) ;************************************************************************** bank serial cja byte,#'9',:error ; if the character is above 9, then error (get another char) cje byte,#'*',:star cje byte,#'#',:pound cjb byte,#'0',:error sub byte,#'0' ; convert to decimal number jmp :got_it :star mov byte,#10 jmp :got_it :pound mov byte,#11 :got_it retp :error mov byte,#$0FF retp ;************************************************************************** load_frequencies ; This subroutine loads the frequencies using a table lookup approach. ; The index into the table is passed in the byte register. ;************************************************************************** bank serial cje byte,#$0FF,:end_load_it clc rl byte rl byte ; multiply byte by 4 to get offset add byte,#_0_ ; add in the offset of the first digit mov temp,#4 mov fsr,#freq_count_high bank serial :dtmf_load_loop mov m,#5 mov w,byte IREAD ; get the value from the table bank sin_gen_bank ; and load it into the frequency mov indf,w ; register bank serial inc byte inc fsr decsz temp jmp :dtmf_load_loop ; when all 4 values have been loaded, :end_load_it retp ; return ;************************************************************************** dial_it ; This subroutine puts out whatever frequencies were loaded ; for 60ms, and then stops outputting the frequencies. ;************************************************************************** bank serial cje byte,#$0FF,:end_dial_it bank sin_gen_bank mov curr_sin,#-4 ; use these values to start the wave at close to zero crossing. mov sinvel,#-8 mov curr_sin2,#-4 ; use these values to start the wave at close to zero crossing. mov sinvel2,#-8 enable_o ; enable the output mov w,#6 call @delay_10n_ms ; delay 20ms setb dtmf_gen_en ; dial the number mov w,#11 call @delay_10n_ms ; delay 100ms clrb dtmf_gen_en ; stop dialing call @disable_o ; now disable the outputs :end_dial_it retp ;************************************************************************** sine_generator1 ;(Part of interrupt service routine) ; This routine generates a synthetic sine wave with values ranging ; from -32 to 32. Frequency is specified by the counter. To set the ; frequency, put this value into the 16-bit freq_count register: ; freq_count = FREQUENCY * 6.83671552 (@50MHz) ;************************************************************************** bank sin_gen_bank add freq_acc_low,freq_count_low;2 ; advance sine at frequency jnc :no_carry ;2,4 ; if lower byte rolls over inc freq_acc_high ; carry over to upper byte jnz :no_carry ; if carry causes roll-over mov freq_acc_high,freq_count_high ; then add freq counter to accumulator (which should be zero, ; so move will work) ; and update sine wave jmp :change_sin :no_carry add freq_acc_high,freq_count_high ; add the upper bytes of the accumulators jnc :no_change :change_sin mov w,++sinvel ;1 ; if the sine wave sb curr_sin.7 ;1 ; is positive, decelerate mov w,--sinvel ;1 ; it. Otherwise, accelerate it. mov sinvel,w ;1 add curr_sin,w ;1 ; add the velocity to sin :no_change ;************************************************************************** sine_generator2 ;(Part of interrupt service routine) ; This routine generates a synthetic sine wave with values ranging ; from -32 to 32. Frequency is specified by the counter. To set the ; frequency, put this value into the 16-bit freq_count register: ; freq_count = FREQUENCY * 6.83671552 (@50MHz) ;************************************************************************** bank sin_gen_bank add freq_acc_low2,freq_count_low2;2 ;advance sine at frequency jnc :no_carry ;2,4 ; if lower byte rolls over inc freq_acc_high2 ; carry over to upper byte jnz :no_carry ; if carry causes roll-over mov freq_acc_high2,freq_count_high2 ; then add freq counter to accumulator (which should be zero, ; so move will work) ; and update sine wave jmp :change_sin :no_carry add freq_acc_high2,freq_count_high2 ; add the upper bytes of the accumulators jnc :no_change :change_sin mov w,++sinvel2 ;1 ; if the sine wave sb curr_sin2.7 ;1 ; is positive, decelerate it mov w,--sinvel2 ;1 ; it. Otherwise, accelerate it. mov sinvel2,w ;1 add curr_sin2,w ;1 ; add the velocity to sin :no_change jb dtmf_gen_en,:do_DTMF mov pwm0,curr_sin ; mov the value of SIN into the PWM output add pwm0,curr_sin2 ; mov the value of SIN2 into the PWM output clc rl pwm0 ; double the value of the PWM output add pwm0,#128 ; put it in the middle of the output range retp ; return with page bits intact :do_DTMF ; If we are doing DTMF generation, then we need to add "twist" to the ; signal (divide sin2 by 4 and add it to it's original value) mov pwm0,curr_sin2 ; mov sin2 into pwm0 mov IRQ_temp,w ; mov the high_frequency sin wave's current value clc ; into a temporary register snb IRQ_temp.7 ; divide temporary register by four by shifting right stc ; (for result = (0.25)(sin2)) rr IRQ_temp clc snb IRQ_temp.7 stc mov w,>>IRQ_temp add pwm0,w ; (1.25)(sin2) = sin2 + (0.25)(sin2) add pwm0,curr_sin ; add the value of SIN into the PWM output ; for result = pwm0 = 1.25*sin2 + 1*sin add pwm0,#128 ; put pwm0 in the middle of the output range (get rid of negative values) retp ; return with page bits intact ;********************************************************************************* ; Initialise all port configuration ;********************************************************************************* init _mode ST_W ;point MODE to write ST register mov w,#RB_ST ;Setup RB Schmitt Trigger, 0 = enabled, 1 = disabled mov !rb,w mov w,#RC_ST ;Setup RC Schmitt Trigger, 0 = enabled, 1 = disabled mov !rc,w 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 _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 mov w,#RC_LVL ;Setup RC CMOS or TTL levels, 0 = TTL, 1 = CMOS mov !rc,w 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 _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 mov w,#RC_PLP ;Setup RC Weak Pull-up, 0 = enabled, 1 = disabled mov !rc,w 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 _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 mov w,#RC_DDIR ;Setup RC Direction register, 0 = output, 1 = input mov !rc,w 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 mov w,#RA_latch ;Initialize RA data latch mov ra,w mov w,#RB_latch ;Initialize RB data latch mov rb,w mov w,#RC_latch ;Initialize RC data latch mov rc,w 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 ;********************************************************************************* ; Clear all Data RAM locations ;********************************************************************************* IFDEF SX48_52 ;SX48/52 RAM clear routine mov w,#$0a ;reset all ram starting at $0A mov fsr,w :zero_ram clr ind ;clear using indirect addressing incsz fsr ;repeat until done jmp :zero_ram _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 ;SX18/20/28 RAM clear routine clr fsr ;reset all ram banks :zero_ram sb fsr.4 ;are we on low half of bank? setb fsr.3 ;If so, don't touch regs 0-7 clr ind ;clear using indirect addressing incsz fsr ;repeat until done jmp :zero_ram ENDIF ;********************************************************************************* ; Initialize program/VP registers ;********************************************************************************* bank sin_gen_bank ; mov curr_sin,#32 ;init variables. A sine starts at 1, a cos wave starts at 0. ; mov sinvel,#0 mov curr_sin,#-4 ; use these values for a wave which is 90 degrees out of phase. mov sinvel,#-8 mov curr_sin2,#-4 ; use these values for a wave which is 90 degrees out of phase. mov sinvel2,#-8 call @disable_o ;********************************************************************************* ; Setup and enable RTCC interrupt, WREG register, RTCC/WDT prescaler ;********************************************************************************* 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 RTCC (PSA lo) 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 mov w,#RTCC_PS_OFF ;setup option register mov !option,w jmp @main
file: /Techref/scenix/lib/io/dev/modem/bell202_modem_at.src, 74KB, , updated: 2000/12/5 12:04, local time: 2024/8/14 20:07,
3.139.235.177:LOG IN
|
©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://massmind.org/techref/scenix/lib/io/dev/modem/bell202_modem_at.src"> scenix lib io dev modem bell202_modem_at</A> |
Did you find what you needed? |
Welcome to massmind.org! |
Welcome to massmind.org! |
.