In SX Microcontrollers, SX/B Compiler and SX-Key Tool, Peter Van der Zee wrote: Hi Beau; In the Parallax contest I entered a "Small Hardware and Development Board" project which incidentally had dual sinewaves with separately selectable frequencies generated by the "single pin PWM" method. While the sine waves were not the primary purpose of the entry, they demonstrate a reasonable way of getting good results, albeit quite a bit slower than what you are looking for. If you poke at that entry (sorry, I don't know how to make the link for you), you can see the photograph of two sine traces on the 'scope. Take a peek at the code to see how the ISR updates each PWM accumulator, and how the scheduler selects the sine value. The scheduler also drives the raise/lower buttons to control each frequency. It can very easily be modified to have the sceduler sweep through a range of frequencies. And of course, the amplitude if frequency independent. Again, that project was not as fast as you would like, but by carving off the "niceties", I think it's possible to get things down to the speeds I indicated; about 400 KHz. If you would like me to spend some time poking at that, I'd be happy to oblige. [/code] ;============================================================================== ;TITLE: Sines.src ; ;PURPOSE: Demonstrate a the effectiveness of an SX development board by ; implementing a simple non-preemptive multi tasking scheduler ; operating a dual pulse density modulation sine wave generator. ; ;AUTHOR: Peter Van der Zee, Datek Industries Inc. ; ;REVISIONS: Feb 27, 2005 Original. ; ;CONNECTIONS: Port: b.0 button to lower frequency 1 ; b.1 button to raise frequency 1 ; b.2 button to lower frequency 2 ; b.3 button to raise frequency 2 ; ; Port: c.0 output as frequency 1 PWM output to RC filter 1. ; c.2 output as frequency 2 PWM output to RC filter 2. ; ;DETAILS: Each of two independent simple sine wave generators operate by ; pulse density modulating an output bit in a deterministic ; Interrupt Service Routine. A tick based task scheduler controls ; frequency selection control and sine value calculation for each ; of the generators. ; ; The scheduler demonstrates multiple independent tasks operating ; without much concern of each other with the exception of being ; non-preemptive in nature. In other words, a task that requires ; more rapid response will not interrupt a slower task already ; running or scheduled to run. For greater determinism it is ; important that no task "hogs" a lot of processor time in any ; run instance, and it is absolutely crucial that no task uses ; long delay loops. The purpose of the scheduler is to remove the ; in-line requirement for delays by letting the scheduler provide ; those instead. ; ; In the generators, the sine value resolution is purposely left ; coarse so on an oscilloscope the user can see the fixed effect ; of frequency adjustment through raise/lower buttons. ; Finer resolution can be conveniently made by expanding access ; and granularity of the sine lookup table, albeit at the expense ; of maximum frequency. ; ; It should be obvious that replacing the sine lookup table with ; a ramp value table, a sawtooth table or any random function table ; that other functions can be equally easily generated. ; ; The scheduler time ticks are set to convenient numbers, in this ; case permitting task threads to be executed at even decades of ; time from the base tick of 1 usec for the ISR, to 10 and 100 usec, ; 1, 10 and 100 msec, and 1 sec. The scheduler can be easily altered ; for more or less resolution, the major stipulation being that each ; slower tick is an integer multiple of the previous tick. ; More complicated arrangements can of course be made. Where mutiple ; tick (non-decade) delays are required in a thread, then the thread ; itself is tasked with the requirement to do so. ;============================================================================== [code] ;---------------DEVICE DIRECTIVES---------------------------------------------- [code]id 'Sines' [code] DEVICE SX28,oschs3,stackx,turbo [code] FREQ 50_000_000 ;default run speed = 50MHz RESET ResetEntry ;jump to start label on reset [code];---------------CONSTANTS------------------------------------------------------ [code]Dac1Bit equ rc.0 ;pulse density modulator 1 output to RC integrator Dac2Bit equ rc.2 ;pulse density modulator 2 output to RC integrator IntValue equ -50 ;interrupt reload value for 1 micro second; 50 instructions at 50 MHz Ram1 equ $10 ; [code];---------------VARIABLES------------------------------------------------------ [code] org 8 Flags ds 1 Intflag equ Flags.0 ;interrupt occurred flag [code] org Ram1 Timer10uS ds 1 ;counter to get to 10uSec Timer100uS ds 1 ;counter to get to 100uSec Timer1mS ds 1 ;counter to get to 1mSec Timer10mS ds 1 ;counter to get to 10mSec Timer100mS ds 1 ;counter to get to 100mSec Timer1S ds 1 ;counter to get to 1Sec [code]Dac1Value ds 1 ;value for the PWM 1 output Dac1Accum ds 1 ;accumulator for PWM 1 Period1 ds 1 ;duration of one cycle of frequency 1 Period1Load ds 1 ;duration of one cycle load source for frequency 1 F1index ds 1 ;index into sine table for frequency 1 Dac2Value ds 1 ;value for the PWM 2 output Dac2Accum ds 1 ;accumulator for PWM 2 Period2 ds 1 ;duration of one cycle of frequency 2 Period2Load ds 1 ;duration of one cycle load source for frequency 2 F2index ds 1 ;index into sine table for frequency 2 [code];---------------INTERRUPT ROUTINE---------------------------------------------- [code] org 0 Intsvc ;For each of two one byte PWMs, calculate the rollover carry and then clear or set the PWM bit accordingly ;The add-with-carry option must be disabled unless carry is specifically cleared before the add. [code] setb Intflag ;advise scheduler an interrupt has occurred add Dac1Accum,Dac1Value ;calculate PWM 1 overflow sc ; clrb Dac1Bit ;clear PWM 1 snc ; setb Dac1Bit ;set PWM 1 add Dac2Accum,Dac2Value ;calculate PWM 2 overflow sc ; clrb rc.2 ;clear PWM 2 snc ; setb rc.2 ;set PWM 2 mov w,#IntValue ; retiw ;return from interrupt and reset for 50 instructions [code];---------------INITIALIZATION------------------------------------------------- [code]ResetEntry ;Initialize the ports SetLevels mov m,#$0d ;Set 0 for CMOS levels mov !ra,#%0000 ; mov !rb,#%0000_0000 ; mov !rc,#%0000_0000 ; SetPullups mov m,#$0e ;Set 0 for pullups mov !ra,#%0000 ;port a not used mov !rb,#%0000_0000 ;input buttons mov !rc,#%1111_1111 ; SetTris mov m,#$0f ;Set 0 for output clr ra mov !ra,#%1111 ;port a not used clr rb ; mov !rb,#%0000_1111 ;X,X,X,X _ F2up,F2dn,F1up,F1dn clr rc ; mov !rc,#%0000_0000 ;X,X,X,X _ X,DAC2,X,DAC1 [code] ;Clear memory Clearmem mov fsr,#$10 ;point to first memory bank Clearone setb fsr.4 ;stay in proper half clr ind ;clear this location incsz fsr ;point to next location jmp Clearone ;not at end so clear one more [code] ;Initialize the scheduler timers mov w,#10 ;timer decade value mov Timer10uS,w ;10 microseconds mov Timer100uS,w ;100 microseconds mov Timer1mS,w ;1 millisecond mov Timer10mS,w ;10 milliseconds mov Timer100mS,w ;100 milliseconds mov Timer1S,w ;1 second [code] ;Initialize the variables clr rtcc ; mov !option,#%1000_1000 ;internal rtcc clr Flags ; mov Dac1Value,#128 ;set initial value of dac1 half way mov Dac2Value,#128 ;set initial value of dac2 half way ;---------------MAIN PROGRAM--------------------------------------------------- [code] ;The scheduler keeps time for the whole system and triggers sine calculations ;for both generators each 10 microseconds. ;Every 100 milliseconds it looks for raise/lower buttons being pushed, and if so, ;calls the corresponding generator's raise/lower routine. [code]Main sb Intflag ;test for interrupt occurred jmp Main ;wait for interrupt bank Ram1 ; Usec1 clrb Intflag ;clear that fact decsz Timer10uS ;scheduler 1 usec base tick jmp Main ;wait for occurrence of next interrupt Usec10 mov Timer10uS,#10 ;reload 10usec timer ;put 10 uSec routines here call Sine1 ;determine freq 1 step call Sine2 ;determine freq 2 step decsz Timer100uS ;scheduler 10 usec tick jmp Main ;wait for occurrence of next interrupt Usec100 mov Timer100uS,#10 ;reload 10usec timer ;put 100 uSec routines here decsz Timer1mS ;scheduler 100 usec tick jmp Main ;wait for occurrence of next interrupt Msec1 mov Timer1mS,#10 ;reload 100usec timer ;put 1 mSec routines here decsz Timer10mS ;scheduler 1 msec tick jmp Main ;wait for occurrence of next interrupt Msec10 mov Timer10mS,#10 ;reload 1msec timer ;put 10 mSec routines here decsz Timer100mS ;scheduler 1 usec base tick jmp Main ;wait for occurrence of next interrupt Msec100 mov Timer100mS,#10 ;reload 10usec timer ;put 100 mSec routines here sb rb.0 ;test button for lower frequency 1 call Lower1 ;decrease frequency 1 sb rb.1 ;test button for higher frequency 1 call Higher1 ;increase frequency 1 sb rb.2 ;test button for lower frequency 2 call Lower2 ;decrease frequency 2 sb rb.3 ;test button for higher frequency 2 call Higher2 ;increase frequency 2 decsz Timer1S ;scheduler 1 usec base tick jmp Main ;wait for occurrence of next interrupt Sec1 mov Timer1S,#10 ;reload 10usec timer ;put 1 Sec routines here jmp Main ;wait for occurrence of next interrupt [code];---------------SUBROUTINES---------------------------------------------------- [code]Lower1 ;reduce frequency of generator 1 but not below zero incsz Period1Load ;increase the period of frequency 1 skip ; dec Period1Load ;underflow not permitted retp ; [code]Higher1 ;increase frequency of generator 1 but not above $ff decsz Period1Load ;decrease the period of frequency 1 skip ; inc Period1Load ;overflow not permitted retp ; [code]Lower2 ;reduce frequency of generator 2 but not below zero incsz Period2Load ;increase the period of frequency 2 skip ; dec Period2Load ;underflow not permitted retp ; [code]Higher2 ;increase frequency of generator 2 but not above $ff decsz Period2Load ;decrease the period of frequency 2 skip ; inc Period2Load ;overflow not permitted retp ; [code]Sine1 ;calculate lookup time for generator 1, and if so, get sine value decsz Period1 ;step frequency 1 period duration retp ;not time for next sine lookup; return to scheduler mov Period1,Period1Load ;reload period 1 timer inc F1index ;step to next sine value in lookup table mov w,F1index ; call SineLookup ;get sine value for this index mov Dac1Value,w ;setup new dac 1 value for the ISR retp ;done freq1; return to scheduler [code]Sine2 ;calculate lookup time for generator 2, and if so, get sine value decsz Period2 ;step frequency 2 period duration retp ;not time for next sine lookup; return to scheduler mov Period2,Period2Load ;reload period 2 timer inc F2index ;step to next sine value in lookup table mov w,F2index ; call SineLookup ;get sine value for this index mov Dac2Value,w ;setup new dac 2 value for the ISR retp ;done freq2; return to scheduler [code]SineLookup ;lookup the sine value of index in w and w,#%0000_1111 ;only use 16 steps in lookup table add pc,w ;calculate offset into lookup table Sin0 retw 128 ;$80 Sin1 retw 177 ;$b1 Sin2 retw 218 ;$da Sin3 retw 245 ;$f5 Sin4 retw 255 ;$ff Sin5 retw 245 ;$f5 Sin6 retw 218 ;$da Sin7 retw 177 ;$b1 Sin8 retw 128 ;$80 Sin9 retw 79 ;$4f SinA retw 38 ;$26 SinB retw 11 ;$b SinC retw 1 ;$1 SinD retw 11 ;$b SinE retw 38 ;$26 SinF retw 79 ;$4f [code] [/code] Cheers, Peter (pjv) ---------- End of Message ---------- You can view the post on-line at: http://forums.parallax.com/forums/default.aspx?f=7&p=1&m=108186#m108417 Need assistance? Send an email to the Forum Administrator at forumadmin@parallax.com The Parallax Forums are powered by dotNetBB Forums, copyright 2002-2006 (http://www.dotNetBB.com)