; *****************************************************************************************
; 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 11:04, local time: 2024/11/14 08:27,
|
| ©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://linistepper.com/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?
|