;======================================================================= ;Assembly Language Tutorial for the SX Microcontroller ;Program 8.1 ;======================================================================= device sx28l,stackx,optionx device oschs3,turbo freq 50_000_000 ; default run speed = 50MHz ID 'DIAL' reset start ; JUMP to start label on reset ;*********************************************************************** ; Equates for common data comm frequencies ;*********************************************************************** 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 ;*********************************************************************** ; Pin Definitions ;*********************************************************************** ;PWM_pin equ rb.7 ; DTMF output PWM_pin equ rc.6 ; DTMF output ;*********************************************************************** ; Global Variables ;*********************************************************************** org $8 ; Global registers flags ds 1 dtmf_gen_en equ flags.1 ; Tells if DTMF output is enabled timer_flag equ flags.2 ; Flags a rollover of the timers. temp ds 1 ; Temporary storage register byte ds 1 ; a byte i ds 1 ; loop counter ;*********************************************************************** ; Bank 0 Variables ;*********************************************************************** org $10 sin_gen_bank = $ freq_acc_high ds 1 ; ; 16-bit accumulator which decides when to increment the sine wave freq_acc_low ds 1 freq_acc_high2 ds 1 ; ; 16-bit accumulator which decides when to increment the sine wave freq_acc_low2 ds 1 freq_count_high ds 1 ; freq_count = Frequency * 6.83671552 freq_count_low ds 1 ; 16-bit counter ;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 sin wave sinvel ds 1 ; The velocity of the sin wave curr_sin2 ds 1 ; The current value of the sin wave sinvel2 ds 1 ; The velocity of the sin wave sin2_temp ds 1 ; Used to do a temporary shift/add register PWM_bank = $ pwm0_acc ds 1 ; PWM accumulator pwm0 ds 1 ; current PWM output ;*********************************************************************** ; Bank 1 Variables ;*********************************************************************** org $30 ;bank3 variables timers = $ timer_l ds 1 timer_h ds 1 ;*********************************************************************** ; Interrupt ; ; With a retiw value of -163 and an oscillator frequency of 50MHz, this ; code runs every 3.26us. ;*********************************************************************** org 0 ;*********************************************************************** PWM_OUTPUT ; This outputs the current value of pwm0 to the PWM_pin. This generates ; an analog voltage at PWM_pin after filtering ;*********************************************************************** bank PWM_bank add pwm0_acc,pwm0 ; add the PWM output to the acc 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 ;*********************************************************************** jnb dtmf_gen_en,sine_gen_out call @sine_generator1 sine_gen_out ;*********************************************************************** 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 :timer_out ;*********************************************************************** :ISR_DONE ; This is the end of the interrupt service routine. ; Now load 163 into w and ; perform a retiw to interrupt 163 cycles from the start of this one. ; (3.26us@50MHz) ;*********************************************************************** break ; interrupt 163 cycles after this interrupt mov w,#-163 retiw ; return from the interrupt ;*********************************************************************** start bank sin_gen_bank ; Program starts here on power up ;*********************************************************************** ; Initialize ports and registers ;*********************************************************************** ; use these values for a wave which is 90 degrees out of phase. mov curr_sin,#-4 mov sinvel,#-8 ; use these values for a wave which is 90 degrees out of phase. mov curr_sin2,#-4 mov sinvel2,#-8 call @disable_o mov !option,#%00011111 ; enable wreg and rtcc interrupt mov !rc,#%10111111 mov m,#$D ; make cmos-level mov !rc,#%10111111 mov m,#$F ; load digits clr i digloop call getdigit mov byte,w cje byte,#$FF,done call @load_frequencies ; load the frequency registers call @dial_it ; dial the number for 60ms ; and return. inc i mov w,#20 call @delay_10n_ms jmp digloop done sleep ; get i'th digit to dial getdigit mov w,i jmp PC+W retw 1,8,8,8,5,1,2,1,0,2,4,$FF org $200 ; Start this code on page 1 ;*********************************************************************** ; Miscellaneous subroutines ;*********************************************************************** 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 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 ;*********************************************************************** ; Subroutine - Disable the outputs ; Load DC value into PWM and disable the output switch. ;*********************************************************************** disable_o bank PWM_bank ; input mode. mov pwm0,#128 ; put 2.5V DC on PWM output pin retp org $400 ; This table is on page 2. ; DTMF tone table _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 org $600 ; These subroutines are on page 3. ;*********************************************************************** ; DTMF transmit functions/subroutines ;*********************************************************************** ;*********************************************************************** load_frequencies ; This subroutine loads the frequencies using a table lookup approach. ; The index into the table is passed in the byte register. The DTMF table ; must be in the range of $400 to $500. ;*********************************************************************** 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 :dtmf_load_loop mov m,#4 ; mov 4 to m (table is in $400) mov w,byte IREAD ; get the value from the table bank sin_gen_bank ; and load it into the frequency mov indf,w ; register 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 1000ms, and then stops outputting the frequencies. ;*********************************************************************** cje byte,#$0FF,end_dial_it bank sin_gen_bank ; use these values to start the wave at close to zero crossing. mov curr_sin,#-4 mov sinvel,#-8 ; use these values to start the wave at close to zero crossing. mov curr_sin2,#-4 mov sinvel2,#-8 enable_o ; enable the output mov w,#3 call @delay_10n_ms ; delay 30ms setb dtmf_gen_en mov w,#10 call @delay_10n_ms ; delay 100ms clrb dtmf_gen_en 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 ; advance sine at frequency add freq_acc_low,freq_count_low;2 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 ; then add freq counter to accumulator (which should be zero, ; so move will work) mov freq_acc_high,freq_count_high ; and update sine wave jmp :change_sin :no_carry ; add the upper bytes of the accumulators add freq_acc_high,freq_count_high 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) ;*********************************************************************** ;advance sine at frequency add freq_acc_low2,freq_count_low2 ;2 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 ; then add freq counter to accumulator (which should be zero, mov freq_acc_high2,freq_count_high2 ; so move will work) ; and update sine wave jmp :change_sin :no_carry ; add the upper bytes of the accumulators add freq_acc_high2,freq_count_high2 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 mov pwm0,curr_sin2 ; mov sin2 into pwm0 mov sin2_temp,w ; mov the high_frequency sin wave's current value clc ; into a temporary register ; divide temporary register by four by shifting right snb sin2_temp.7 stc ; (for result = (0.25)(sin2)) rr sin2_temp clc snb sin2_temp.7 stc mov w,>>sin2_temp ; (1.25)(sin2) = sin2 + (0.25)(sin2) add pwm0,w ; add the value of SIN into the PWM output add pwm0,curr_sin ; for result = pwm0 = 1.25*sin2 + 1*sin ; put pwm0 in the middle of the output range (get rid of negative values) add pwm0,#128 retp ; return with page bits intact