Tuesday, June 18, 2013

Direct Digital Synthesis (DDS) on the GA144

I wanted a simple demo of the GA144 producing sound. I found a sample DDS app for a SeaForth chip (predecessor to the GA144) but the code syntax is sufficiently different that I couldn't simply copy it.

The concept is simple enough. Calculate the values of a sine wave over a fixed time interval and load those values into a digital-to-analog converter (DAC). Connect the DAC to a loudspeaker and listen to the pretty tone.

Rather than look up tables of (co-)sine values, which can take up lots of memory, this app chooses to approximate the values at each interval using relatively fast calculations. The code takes much less room and easily fits into the limitations of the F18 nodes in the GA144.

But it took me a couple of days to understand the code. It all resolved to two words: cos and scaled. cos in turn uses three other words: triangle, poly and *. while scaled uses interp. Now I knew that each of those words is part of the GA144 ROM library. There was also an intriguing comment in cos: 'Hart, 3300'.

So I googled for 'Hart cosine' and one of the first hits was a page by Chuck Moore about sines and cosines in colorForth. This page also explained the 'Hart, 3300' comment, viz., Computer Approximations by John F Hart, Wiley 1968; function 3300. I googled for 'Computer Approximations Hart' and found that the only copy I could buy was hardcover from Amazon. However I also found a link to an electronic copy which allowed me to read the theory behind the magic numbers that turn up in the code.

And then I (quite by accident) discovered the shadow page for the poly word on the GA144 which contains the exact same code as the cos method but translated into colorForth.

cos f-f'
hart 3300
-0.0043 0.0794 -0.6459 0.5708
 2* 2* . triangle dup *. 2 poly
 -281 , 5203 , -42329 , 37407 ,
 push drop pop *. + ;                       

So now I needed to find out how to use the interp word to scale the cosine values into values between 0 and 511. Lots of test code and simulations later...

The final code was this:
866 list 
sine wave generator,
617 node 0 org,
0 , 40 , 80 , 120 , 170 ,,
220 , 280 , 370 , 511 ,,
hart 3300; -.00433 .07943
          -.64589 1.57079,
cos tri 2* 2* . triangle
   dup *. 2 poly
 -281 , 5203 , -42329 , 37407 ,
 push drop pop *. + ;
 2/ 8000 . + 8191 12 
       interp ;
dac! io b! 155 or !b ;
 700 1 for . . unext ;
 22 128 phaseinc
 dup dup or
 dup cos scaled
 delay dac!
 over . +
A couple of explanations. The delay word in the original was another node running a single timing loop and raising a signal on a comms port at the end. This in turn allows the main node to synchronise with a (relatively) accurate clock signal. However I ran into problems starting multiple nodes on the chip so I brought the synch delay into the node, preferring to sacrifice some accuracy for simplicity. The constant, 700, controls the amount of delay between DAC updates and, hence, the frequency. 500 gives around 550Hz, 700 gives around 447Hz.

interp expects an interpolation table at address 0. In this case it's not quite a linear interpolation. (Refer to DB001 F18A Technology Reference for details.) scaled halves the cos value, adds 0.5 (to scale into positive numbers only) and calls interp with s and m calculated for L=16 bits and n=3 (a 9-entry table i.e. 2**3+1) to interpolate the values between 0 and 511. dac! sets the DAC value on the node.

The output of the DAC needs to connect to a resistive load. The EVB001 kit includes some 47ohm resistors so I soldered one between the DAC pin and earth and then I was able to see a (relatively) clean sine wave output on my 'scope. It also sounded like a sine wave when I connected a speaker.

Make sure block 200 contains a load of block 866 so 'compile' will include it. The block to start the app on the GA144 (copied from 9.4 of the Users Guide) is:

872 list 
demo ide boot empty compile serial load
customize -canon 0 fh orgn !
a-com sport ! a-bps bps ! !nam
run talk 0 617 hook 0 64 617 boot
 upd ?ram panel 22 call ;

To load and run type '872 load' and 'run' in the IDE.

My next task is to get a second node working as a timer to increase the accuracy of the sine wave (possibly using a crystal?). Then I want to port another VentureForth app, Musicbox, to the GA144.

No comments: