Forum Update - Announcement about May 10th, 2018 update and your password.

Sine wave generator

Mad AMad A Posts: 17
edited July 2012 in Propeller 1 Vote Up0Vote Down
Hi everyone, I am very new to the propeller, assembly, and this forum. I am trying to build a function generator and so I am trying to output a sin wave on 8 output pins to go to a D/A converter. I have successfully output a ramp, so I know my LSB and MSB are in the right place and for that function the output pins were performing as expected. I am trying to use the sine table with no success. I was hoping someone could point me in a better direction. With some embarrassment I give you what I have so far:

CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000


VAR
byte index
byte angles[360]
'address:=@angles[0]




PUB main
repeat 360
angles[index] := index
index+=1


dira[0..7]~~


cognew(@begin,1)
address:=@angles[0]




DAT


begin mov dira,#$FF
movs ptr,address 'mov ptr,angles[0] 'Point to first array element.
:loop movs :add,ptr 'Put pointer value into add instruction's source field
add ptr,#1 'Increment the pointer. (Due to pipelining, there has to be at least one instruction here.)
rdlong sin,address 'move value stored at address into sin
:add add address,0-0 'add ptr to address



call #getsin 'call get sin
'wrlong sin,sin 'write value of sin to sin


mov outa,sin 'write sin to output pins
djnz ctr,#:loop 'decrement counter and restart loop
movs ctr,#360 'Initialize counter.
movs ptr,address
jmp #:loop




getsin test sin,sin_90 wc 'get quadrant 2|4 into c
test sin,sin_180 wz 'get quadrant 3|4 into nz
negc sin,sin 'if quadrant 2|4, negate offset
or sin,sin_table 'or in sin table address >> 1
shl sin,#1 'shift left to get final word address
rdlong sin,sin 'read word sample from $E000 to $F000
negnz sin,sin 'if quadrant 3|4, negate sample
getsin_ret ret ' (this subroutine adapted from Prop manual)




sin_90 long $0800
sin_180 long $1000
sin_table long $E000 >>1
sin long 0
ptr long 0
ctr long 360
address long @address

I am uncertain I am accessing the array correctly, or even declaring it correctly. On an oscilloscope I get a periodic but non-coherent output from the DAC. I am also fairly sure I am not calling get_sin correctly as when I comment the entire routine out, my output is the same. Any help would be appreciated. Also, does anyone know more about programming the propeller in C? That would be easier for me for sure. Also any tips for debugging propeller asm?

Comments

  • 30 Comments sorted by Date Added Votes
  • AribaAriba Posts: 2,182
    edited June 2012 Vote Up0Vote Down
    First: please surround your code with [ code ] and [ /code ], (without the spaces) so that we see it in a code box. You can also select the code and press the # button in the editor.

    If I understand your code right you want to read the angle array in the PASM cog and write the resulting sine value to P0..P7.
    For that you need to pass the address of angle[0] to the PASM cog in the PAR register:
       cognew(@begin, @angle[0])
    
    remove all the lines with 'address' in the Spin code.

    In the PASM cog you need to move the pointer in par to the variable address, then you can increment this later (PAR is not changeable)
    begin   mov address, par
            mov dira, #$FF
    
    You don't need all the indirect addressing with movs, you can just read the phase with rdbyte sin, address, and then increment address:
            rdbyte sin, address
            add address, #1
            call #getsin
    
    Now you get the sin value in the range -$FFFF...+$FFFF from the sine table in ROM. to fit this to 8 bits you can shift it by 17:
            sar sin, #17
            mov outa, sin
    
    do that in a loop 360 times and with the ctr variable as you have done already and then move address back to the first array element: address=par.

    Andy

    Edit: I just forget that the sin routine needs the angle not from 0 to 359, but from 0 to 8191 for a full circle, so your Spin code must declare a word array for angle and fill it with
    angle[index] := index *8192 / 360
    Then in the PASM code you read the array with rdword instead od rdbyte and increment the address by 2.
  • Mad AMad A Posts: 17
    edited June 2012 Vote Up0Vote Down
    Andy, Thanks. It is still not working but I think I am closer. I added a delay in case it was just going too fast for my ancient oscilloscope. Now I have:
    CON  _clkmode        = xtal1 + pll16x
      _xinfreq        = 5_000_000
    
    
    VAR
    byte index
    word angles[360]
    
    
    PUB main
    repeat 360
      angles[index] := index*8192/360
      index+=1
    
    
    dira[0..7]~~
    
    
    cognew(@begin,1)
    
    
    DAT
    
    
    begin   mov address, par
            mov dira,#$FF
    :loop   rdword sin, address
            add address, #2
            call #getsin 'call get sin
            shr sin, #17
            mov time,cnt
            add time,delay
            waitcnt time,delay
            mov outa, sin 'write sin to output pins
            djnz ctr,#:loop 'decrement counter and restart loop
            mov ctr,360 'Initialize counter.
            mov address,par
            jmp #:loop
            
             
    
    
    getsin  test sin,sin_90 wc       'get quadrant 2|4 into c  
            test sin,sin_180 wz      'get quadrant 3|4 into nz
            negc sin,sin             'if quadrant 2|4, negate offset
            or sin,sin_table         'or in sin table address >> 1
            shl sin,#1               'shift left to get final word address
            rdlong sin,sin           'read word sample from $E000 to $F000
            negnz sin,sin            'if quadrant 3|4, negate sample
    getsin_ret      ret             ' (this subroutine adapted from Prop manual)
    
    
    
    
    sin_90        long      $0800
    sin_180       long      $1000
    sin_table     long      $E000 >>1
    sin long 0 
    ptr long 0
    ctr long 360
    address long @address
    delay long 10
    time res 1
    

    (I hope the code appears in a box this time.) Thanks again, Arna
  • Mad AMad A Posts: 17
    edited June 2012 Vote Up0Vote Down
    and I see a mistake
  • msrobotsmsrobots Posts: 1,917
    edited June 2012 Vote Up0Vote Down
    Mad A,

    it is kind of funny how often you see your mistakes just seconds after clicking Post Quick Reply.

    so its cognew(@begin, @angle[0]) ...

    Enjoy!

    Mike
    I am just another Code Monkey.
    A determined coder can write COBOL programs in any language. -- Author unknown.
    Press any key to continue, any other key to quit

    The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this post are to be interpreted as described in RFC 2119.
  • AribaAriba Posts: 2,182
    edited June 2012 Vote Up0Vote Down
    I see some more ;-)
    But you are close !

    yes, the cognew misses still the array address as second parameter. This second parameter value lands in the PAR register, now it will be 1 and your rdword's access the hubram from that address.

    Then in the Spin code, make index a long, a byte will not go to 360.
    You don't need the dira[0..7]~~, you set the dira in the cog anyway, and the Spin cog gets terminatedat the end of the main methode

    In the PASM code:
    - I think the rdlong in getsin must be a rdword, the sin table is stored in words.
    - I guess you have connected a DAC at P7..P0, then you should add an offset to the sin value, so that zero is in the middle of the DAC range ($80). The sin value from the table is a signed 16bit value, so you should shift right with SAR and not SHR:
        sar  sin, #17
        add sin, #$80
        mov outa, sin
    

    and the delay may be a bit short, I dont remember what is the minimal value so that waitcnt not hangs, but 20 should be safe.

    Andy

    Ah, and mov ctr, # 360 after the loop
  • jmgjmg Posts: 11,256
    edited June 2012 Vote Up0Vote Down
    Mad A wrote: »
    .... but I think I am closer.

    When you have this delay-per-sample working correctly, which will have zero jitter but some granularity, another variant approach I've thought of (on paper) for Sine generation, is to config a COG Counter in NCO mode, and run a fast-polling loop on the upper 12 bits of PHSx value, and apply those bits as the Sine-table index (with the same quadrant shuffle you do now)

    These bits will follow a sawtooth, the same as your present scan counter does.
    Your sine out will have NCO frequency precision, and will scale naturally over frequency.

    At very low NCO output rates, you will read the same PHSx value multiple times, as polling is faster than INC, and at higher Fout, your polling will skip some values, but still give a valid sine.
    I think the cross-over point is ~9765.625Hz, and your frequency granularity is 18.6 milli hertz.

    Above this, the scan loop and sawtooth may 'beat', but that will change the sample 'dots' along the sine wave, and any LPF will smooth that.

    The next step would be to add a Clocked video i2s out, and use a stereo DAC chip..

    There is room in a COG to support both Gen modes.
    The hard-step size may have benefits where super low jitter matters, and the NCO tracker would be easy to sweep.

    Pulling the getsin in-line once you have it working, will shave a couple of cycles off the loop.
  • Tracy AllenTracy Allen Posts: 6,215
    edited June 2012 Vote Up0Vote Down
    I wrote a tutorial program using a free running cog counter to generate the phase of a sine wave, where a loop (pasm or spin, slowly) samples the phase, looks up the sine in the table, and transfers it to the output, which in the example is a second cog counter in duty mode. It is a different approach to look at.

    Whoa! That was way back in the 2006 thread on "Spin code examples for the beginner". The version of that program that runs 8 sine wave generators to the leds on the demo board or the quick start is still mesmerizing.
  • jmgjmg Posts: 11,256
    edited June 2012 Vote Up0Vote Down
    Nice example.
    { Just need it some orders of magnitude faster to feed an i2s DAC ... }
  • Mad AMad A Posts: 17
    edited June 2012 Vote Up0Vote Down
    So this is still not working. I think the trouble may be with the declaration of address....I am afraid it's missing the angles array.......
  • Beau SchwabeBeau Schwabe Posts: 6,341
    edited June 2012 Vote Up0Vote Down
    Nice example.
    { Just need it some orders of magnitude faster to feed an i2c DAC ... }

    What kind of speed are you looking for?

    There is an old object I have in my personal library dated back in 2006 I think that Chip wrote originally that uses PWM and updates the duty with the correct phase position of the desired sinewave. The original file had an update rate of about 250kHz.
    I modified and cleaned that file up so that the update rate to the PWM duty cycle is now over 500kHz.

    Note: The 3dB roll off with the selected components is at about a 90kHz sinewave


    Feel free to use the attached files however you wish. I think there are enough notes within the DEMO file and the driver file to get you started. If not let me know and I will try to help.


    EDIT: I do like Tracy Allen's 'sinewave3.spin'... it seems to be smoother at higher frequencies than what I just posted. I might see If I can get his to work using only one I/O pin. Attached is a second version implementing Tracy Allen's sinewave using only one pin. See the 'cat' variable at the bottom of the Sinewave_v2.spin file. The sample rate was improved from 524kHz to 1.25MHz from the previous version 1 to the new version 2.



    Beau Schwabe -- Submicron Forensic Engineer
    www.Kit-Start.com - bschwabe@Kit-Start.com ෴෴ www.BScircuitDesigns.com - icbeau@bscircuitdesigns.com ෴෴

    Seriously at this point in the game "the ship has sailed" and "I have no expectations" <- said two brothers we ALL know
  • jmgjmg Posts: 11,256
    edited June 2012 Vote Up0Vote Down
    What kind of speed are you looking for?

    Oops, ahem, I'm sure I wrote i2s DAC.... (now corrected)

    There, the logical speed is 'whatever the chip can do' which seems to be 192KHz, so the scan and send loop needs to be above this. (paced with waitvid to match 192KHz ?)

    (ie output two 16/24/32 bit serial samples at this rate : i2s has CLK, Data and a R.L frame toggle)

    Some users might want Sine and Cos, some might want two independent frequencies.

    This one http://www.akm.com/datasheets/ak4388a_f02e.pdf is 86c/100+, and Nuvoton have a nice looking
    NAU8402, that has a -ve charge pump and 2V RMS drive... Shows 82c at Arrow, but no stocks.

    A PWM DAC on one COG, and an i2s DAC on another would be a nice example...
  • AribaAriba Posts: 2,182
    edited June 2012 Vote Up0Vote Down
    Hi Arna

    Here is the code with the latest corrections. I get some output at P0..P7 but have no DAC and no Scope connected.
    There was a bug with the shift of the sine output, we need to shift it by 9 and not 17, that's my fault, sorry!
    CON
      _clkmode        = xtal1 + pll16x
      _xinfreq        = 5_000_000
    
    VAR
    long index
    word angles[360]
    
    PUB main
     
     repeat index from 0 to 359
       angles[index] := index*8192/360
    
     cognew(@begin,@angles[0])
    
    
    DAT
    
    begin   mov address, par
            mov dira,#$FF
    :loop   rdword sin, address
            add address, #2
            call #getsin
            mov time,cnt
            add time,delay
            waitcnt time,delay
            sar sin, #9
            add sin, #$80
            mov outa, sin    'write sin to output pins
            djnz ctr,#:loop  'decrement counter and restart loop
            mov ctr,#360     'Initialize counter.
            mov address,par
            jmp #:loop
    
    getsin  test sin,sin_90 wc       'get quadrant 2|4 into c  
            test sin,sin_180 wz      'get quadrant 3|4 into nz
            negc sin,sin             'if quadrant 2|4, negate offset
            or sin,sin_table         'or in sin table address >> 1
            shl sin,#1               'shift left to get final word address
            rdword sin,sin           'read word sample from $E000 to $F000
            negnz sin,sin            'if quadrant 3|4, negate sample
    getsin_ret      ret             ' (this subroutine adapted from Prop manual)
    
    sin_90        long      $0800
    sin_180       long      $1000
    sin_table     long      $E000 >>1
    sin           long 0 
    ptr           long 0
    ctr           long 360
    address       long 0
    delay         long 10
    time          res 1
    

    Andy
  • kbashkbash Posts: 47
    edited June 2012 Vote Up0Vote Down
    I went through this same process several years ago when the Prop first came out. I needed a nice clean sine wave to drive a voice-coil motor. After many days spent trying to get the propeller to do what I needed using a D2A converter... and still not QUITE being there... I purchased a DDS development board from Analog devices. It was a MUCH better way of doing what I needed. It was very easy to interface to, I got an amazingly clean sine wave at any frequency from zero hertz up to several megahertz. I ran the signal through a digital potentiometer to do scaling. It was MUCH easier, gave an amazing signal, used fewer pins, and once designed onto a board only cost a few dollars.

    The evaluation board from digikey is: EVAL-AD9833SDZ-ND A bit pricy, (~$70) but the chips are in the $10 range. You might want to see if it fits your application like it did mine.

    KB
  • jmgjmg Posts: 11,256
    edited June 2012 Vote Up0Vote Down
    AD9833 is a nice little part, and a great idea if you need results fast, but it has a 10b DAC, and is Mono, and specs a 100mHz step.

    I think a Prop can do 18.6 milli hertz, and drive a 16b value into a cheap i2s DAC - and do that using 1/8 of the chip.
    - but no, it is not a 10 minute task.
  • Mad AMad A Posts: 17
    edited June 2012 Vote Up0Vote Down
    Andy, I realized the bit shift was incorrect as well but there were some other corrections you posted that were transformative. It works now! :) :) :)
  • Mad AMad A Posts: 17
    edited July 2012 Vote Up0Vote Down
    I am such a novice I think I only 50% understand your post, : / I think you are discussing an approach I would like to take though, I know it must be possible to use the system counter modules to control the frequency of a sine wave and that frequencies higher than the 2 kHz I was able to get with this code. I ultimately want to be able to control the frequency with two knobs, one for course adjustment which would set course frequency according to a priority encoder and then a fine adjustment knob using a potentiometer and an ADC. Right now I am trying to get the samples from the sin table into cog memory so that I can cycle through them rapidly using a counter loop as I believe you are describing. I am getting a triangle wave now which makes little sense to me. Here is my code:
    CON
      _clkmode        = xtal1 + pll16x
      _xinfreq        = 5_000_000
    
    
    VAR
    long index
    word angles[360]
    
    
    
    
    PUB Get_samples
     
     repeat index from 0 to 360
       angles[index] := index*8192/360
    
    
     cognew(@begin,@angles[0])
    
    
    
    
    DAT
    
    
    begin   mov address, par
            mov dira,#$FF
    :loop   rdword sin, address
            add address, #2
            call #getsin
            mov time,cnt
            add time,delay
            waitcnt time,delay
            sar sin, #9
            add sin, #$80 
            mov cogmem, sin  'write sin cog memory rather than output pins
            'mov outa,cogmem
            add cogmem, #2    'increment memory location 
            djnz ctr,#:loop
            'mov ctr,cntstart     'Initialize counter.
            'mov address,par
            'jmp #:loop
    
    
            mov ctr,#360
            mov cogmem,start
    :rdmem  mov outa,cogmem
            add cogmem, #2
            djnz ctr,#:rdmem
            mov cogmem, start
            mov ctr,#360
            jmp #:rdmem  
          
    
    
    getsin  test sin,sin_90 wc       'get quadrant 2|4 into c  
            test sin,sin_180 wz      'get quadrant 3|4 into nz
            negc sin,sin             'if quadrant 2|4, negate offset
            or sin,sin_table         'or in sin table address >> 1
            shl sin,#1               'shift left to get final word address
            rdword sin,sin           'read word sample from $E000 to $F000
            negnz sin,sin            'if quadrant 3|4, negate sample
    getsin_ret      ret             ' (this subroutine adapted from Prop manual)
    
    
    sin_90        long      $0800
    sin_180       long      $1000
    sin_table     long      $E000 >>1
    sin           long 0 
    ptr           long 0
    ctr           long 360
    cntstart      long 360
    address       long 0
    cogmem        long $03A0
    start         long $03A0
    delay         long 9
    time          res 1
    
  • jmgjmg Posts: 11,256
    edited July 2012 Vote Up0Vote Down
    Mad A wrote: »
    I know it must be possible to use the system counter modules to control the frequency of a sine wave and that frequencies higher than the 2 kHz I was able to get with this code.

    Above some ceiling, you are going to need to reduce the number of 'dots' in your Sine, and once you have an adder-version working, you can use a Counter in NCO mode (which is just an adder ) and use the upper bits as your quadrant index. Top 2 bits would be Quadrant choice, and next 12 bits are the phase inside that quadrant.

    The Prop manual says this is legal for reading the adder

    mov Result, phsa 'Get current phase value

    Edit: I see in the post above
    http://forums.parallax.com/showthread.php?140784-Sine-wave-generator&p=1105768&viewfull=1#post1105768

    Beau gives Sinewave_v2.spin, which is a PASM version that does exactly this.
    Did you try that ?

    If you want smooth/clickless updates of new Freq values, calculated elsewhere, you would need to move the
    rdlong frqa, pointer
    inside the loop, which will slow it down slightly.

    You may want to run both Software choices, as a skipping adder will always deliver points on a sine wave, but not always the same points every cycle, and if you no not smooth with a LPF that slight difference might be noticed.
  • Mad AMad A Posts: 17
    edited July 2012 Vote Up0Vote Down
    I have yet to dissect sinewave_V2. I intend to as I think understanding it fully will improve my novice status. The only thing is, it seems that it puts out the sin wave on one pin whereas I am looking to put it out onto 8. In my current code I thought I was successfully writing the values from the sin table into cog memory because my original loop modified to have the instruction mov outa,cogmem produced a sine wave on the scope. Then I had it run only once through the angles, get the sine values and write them to memory, I thought. But then when I loop through the memory I thought I wrote to I get a triangle wave.
  • RaymanRayman Posts: 8,465
    edited July 2012 Vote Up0Vote Down
    Not sure if it helps at all, but I have posted my sine wave generator here:
    http://forums.parallax.com/showthread.php?112389
    Prop Info and Apps: http://www.rayslogic.com/
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 21,852
    edited July 2012 Vote Up0Vote Down
    Here's a program inspired by the NCO idea that jmg mentioned:
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      OUT_PIN       = 15
      FREQ          = 100_000
    
    PUB  start | i
    
      repeat i from $01 to $ff
        sine[i] := (sin(i << 5) << 15) + $8000_0000
      frqa0 := frqval(FREQ)
      cognew(@sine, 0)     
    
    
    PUB sin(x) : value | t
    
    '' Sine of the angle x: 0 to 360 degrees == $0000 to $2000,
    
      if (x & $fff == $800)
        value := $ffff
      else
        if (x & $800)
          t := -x & $7ff
        else
          t := x & $7ff
        value := word[$e000][t]
      if (x & $1000)
        value := -value
    
    PRI frqval(a) : f     'Thanks, Tracy!
    
      repeat 32
         a <<= 1
         f <<= 1
         if a => clkfreq
            a -= clkfreq
            f++
    
    DAT
    
    sine          jmp       #start_sine
    sine_table    long      0-0[255]
    
    start_sine    mov       sine,_0x8000_0000
                  mov       frqa,frqa0
                  mov       ctra,ctra0
                  mov       ctrb,ctrb0
                  mov       dira,dira0
    
    :loop         mov       frqb,0
                  mov       acc,phsa
                  shr       acc,#24
                  movs      :loop,acc
                  jmp       #:loop
    
    _0x8000_0000  long      $8000_0000
    ctra0         long      %00001 << 26
    frqa0         long      0-0
    ctrb0         long      %00110 << 26 | OUT_PIN
    dira0         long      1 << OUT_PIN
    
    acc           res       0  
    

    It uses DUTY mode output to produce a pretty good sine wave up to about 500 kHz. Beyond that, things start to fall apart. Here's a plot of the waveform filtered by a simple RC filter (R = 2.2K, C = 220 pF):

    attachment.php?attachmentid=94327&d=1342756112

    Here's the spectrum in the near vicinity of the fundamental, with no apparent spurious sidebands due to phase jitter:

    attachment.php?attachmentid=94326&d=1342756112

    Although it would be possible to shorten the loop a little, it would come at the expense of frequency resolution.

    -Phil
    800 x 480 - 25K
    640 x 480 - 13K
    “Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away. -Antoine de Saint-Exupery
  • jmgjmg Posts: 11,256
    edited July 2012 Vote Up0Vote Down
    Nifty, uses a smaller COG based Full Sine table, which allows the scan loop to shrink to 5 lines.
    Gives more X-Dots at high frequencies, at the cost of less X-dots at low frequencies, but the Y dots can be higher precision, is a DAC is available to use it. They are stored as 32 bit values.

    It uses DUTY mode output to produce a pretty good sine wave up to about 500 kHz. Beyond that, things start to fall apart.

    Not too surprising, at 400KHz you have 10 samples in the X axis, (so are taking stretched strides thru that 256 full sine table), but the DAC only has 20 clocks to build a Y axis value, before a new one arrives.

    If this is insufficient for anyone, and they have deep pockets, this news is topical :

    http://www.eetimes.com/electronics-products/rf-microwave-products/4390550/Analog-Devices-introduces-superfast-direct-digital-synthesizers

    This has a 3.5GHz (!) NCO rate and a 12bit DAC, and claims 271 pico Hz resolution. Yours from just $119 ea/100

    Here's the spectrum in the near vicinity of the fundamental, with no apparent spurious sidebands due to phase jitter:

    At 100KHz, you are very close to /800 (adder will be 99999.997764Hz) - a better test might be one that more evenly runs /800 /801, so try an adder value of 5372066.

    This will output an average of 100062.526762, but achieve it by alternating 100125.15644, & 100000.0 Hz

    Your spectrum then should show two equal peaks 125.156Hz appart

    Although it would be possible to shorten the loop a little, it would come at the expense of frequency resolution.

    It is already only 5 lines long - not much fat there ! :)

    I think Frequency resolution is determined more by the Adder, the loop affects the 'Dots on the Graph', not so much the underlying frequency (or frequencies, as I prefer to think of the NCO output )

    I could see that making the Table 512 x 16, rather than 256 x 32 could give a useful gain of more X axis dots, for little real Y axis cost (not in a Prop DAC anyway)

    A direct table allows any waveform to be synth'd, but as most change slowly, one way to get more resolution, would be to store a signed delta in the table.

    Edit : oops, just dawned on me that delta storage is only possible in a no-skips/no repeats readout, so it would be of no use here, as an adder index design has both skips and repeats, depending on frequency.


    The good thing about a table, is you can take as long as you like creating it, and read-back is very fast.
  • K2K2 Posts: 567
    edited May 1 Vote Up0Vote Down
    Update: Don't wish to bump this post, but problem has been solved.
    It is as though a voice came from Heaven with the answer. ;)

    I've encountered a mystery that has me totally flummoxed.

    Phil's lovely code, two posts up, works great.

    I've added a tiny bit here:
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
      OUT_PIN       = 15
      FREQ          = 100_000
    
    PUB  start | i
    
      repeat i from $01 to $ff
        sine[i] := (sin(i << 5) << 15) + $8000_0000
      frqa0 := frqval(FREQ)
      cognew(@sine, 0)     
    
    
    PUB sin(x) : value | t
    
    '' Sine of the angle x: 0 to 360 degrees == $0000 to $2000,
    
      if (x & $fff == $800)
        value := $ffff
      else
        if (x & $800)
          t := -x & $7ff
        else
          t := x & $7ff
        value := word[$e000][t]
      if (x & $1000)
        value := -value
    
    PRI frqval(a) : f     'Thanks, Tracy!
    
      repeat 32
         a <<= 1
         f <<= 1
         if a => clkfreq
            a -= clkfreq
            f++
    
    DAT
    
    sine          jmp       #start_sine
    sine_table    long      0-0[255]
    
    start_sine    mov       sine,_0x8000_0000
                  mov       frqa,frqa0
                  mov       ctra,ctra0
                  mov       ctrb,ctrb0
                  mov       dira,dira0
    
    :loop         mov       frqb,0
                  mov       fum, foo
                  mov       temp, phsa
    
                  'add       temp, fum         ' <= this fails
                  add       temp, foo          ' <= this works
                  
                  mov       phsa, temp
                  mov       acc,phsa
                  shr       acc,#24
                  movs      :loop,acc
                  jmp       #:loop
    
    _0x8000_0000  long      $8000_0000
    ctra0         long      %00001 << 26
    frqa0         long      0-0
    ctrb0         long      %00110 << 26 | OUT_PIN
    dira0         long      1 << OUT_PIN
    foo           long      10
    
    acc           res       0
    fum           res       0
    temp          res       0
    

    As you can see, inside the endless loop I've incorporated a few lines of code to modify phsa.
    As it sits, it runs perfectly fine.
    But if I comment out add temp, foo
    and uncomment add temp, fum
    it breaks.
    What in the world am I doing wrong?
  • Mark_TMark_T Posts: 1,659
    Hmm, a mystery. counter A (or rather explicit manipulation of PHSA) is controlling the instruction setting
    FRQB from the 256 entry table. All looks reasonable, and fum should always be identical to foo.

    This isn't some kind of name clash for 'fum' on some other part of the code?
  • jmgjmg Posts: 11,256
    K2 wrote: »
    What in the world am I doing wrong?

    Looks like foo and fum are not the same - one is a constant, and the other a register, & you need to tell the assembler that with # or no #

  • Tracy AllenTracy Allen Posts: 6,215
    edited May 1 Vote Up0Vote Down
    I quickly verified K2's observation. It threw me for a bit. A trap.

    You can make it work like this...
    acc           res       1 . ' <<need to res 1, not zero!
    fum           res       1
    temp          res       1
    
  • K2K2 Posts: 567
    Yup. That was the problem, Tracy. I mindlessly followed what Phil had done. Of course what he had actually done was recycle a register that had served its purpose and was no longer needed. I'm easily fooled by subtle acts of cleverness. ;-)
  • K2K2 Posts: 567
    Mark_T wrote: »
    Hmm, a mystery. counter A (or rather explicit manipulation of PHSA) is controlling the instruction setting
    FRQB from the 256 entry table.
    That's right. I sought to introduce abrupt phase shifts of the sinewave, part of a π/4-QPSK modulator.

  • lardomlardom Posts: 1,437
    K2 wrote: »
    Update: Don't wish to bump this post, but problem has been solved.
    It is as though a voice came from Heaven with the answer.
    I, for one, am glad you did: It would allow you to build a 50Hz or a 60Hz inverter without transformer hum. It would also be useful in designing a 'variable frequency drive' for an induction motor.
    Larry

    If the grass is greener on the other side...it's time to water your lawn.
  • The number after the res tells how much to advance the assembly pointer, i.e. res 1 reserves one long and advances the assembly pointer by one. I guess res 0 doesn't advance the assembly pointer at all, so you ended up with three variables, acc, fum, and temp, all pointing to the same location in the cog. The output just sits at 1.65 volts.

    Keep in mind that at 100kHz, phsa advances by 5_368_709 (frqa0) at each clock tick, once every 12.5 nanoseconds. That makes it 800 steps to traverse the phase accumulator.
  • K2K2 Posts: 567
    Keep in mind that at 100kHz, phsa advances by 5_368_709 (frqa0) at each clock tick, once every 12.5 nanoseconds. That makes it 800 steps to traverse the phase accumulator.
    Exactly. My particular application (which uses timing values different than the code examples posted above) increments phsa by more than 10,000,000 between each symbol.
Sign In or Register to comment.