Shop OBEX P1 Docs P2 Docs Learn Events
serial DAC - sine wave — Parallax Forums

serial DAC - sine wave

ceresinceresin Posts: 3
edited 2011-03-11 23:08 in Propeller 1
Hi all,

I am a novice. I want to make sine wave with 12bits serial D/A converter. I would like to use the sine table in ROM, but I dont know how. I read that simples in this table are 16bits and I need 12bits for my converter.
Could you help me with:
1) How read sample from sine table in ROM
2) How make 12bits from 16bits sample and how send this sample to serial converter.

Thanks a lot for all the advice.

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2011-03-06 12:14
    Can't help you much with #2 without further information. Given a 16 bit value and wanting a 12 bit value, you can just divide the 16 bit number by 16 to get a 12 bit number (probably just shifting it right by 4 bits). Maybe you want to round it up or not.

    Regarding sending the 12 bits to the DAC, that depends on the chip you're using. You have to provide a link to a datasheet if you want further suggestions. You might look in the Object Exchange for an existing DAC object for the device you're using or something similar.

    There was an early Prop document called "Propeller Guts". I don't know where it's kept these days, but attached is a copy from my archive. There's a section on the use of the Log / Anti-log and Trig tables.
  • ceresinceresin Posts: 3
    edited 2011-03-06 12:43
    Mike,
    thank you for your response. I apologize for my ignorance, I am a novice.

    I found the manual code for sine:
    [SIZE=2]'' from the propeller manual
    ' Get sine/cosine[/SIZE]
    [SIZE=2][/SIZE][SIZE=2] 
    [LEFT]getcos
                  add       sin, sin_90             'for cosine, add 90°
    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
    getcos_ret
    getsin_ret
                  ret                               '39..54 clocks (variance due to HUB sync on RDWORD)
    sin_90                  long    $0800
    sin_180                 long    $1000
    sin_table               long    $E000 >> 1 'sine table base shifted right[/LEFT]
    [/SIZE]
    

    And this is code for DAC (I use MCP4822):
    VAR
     byte CS, SCK, SDI
      
    PUB Init(inCS, inSCK, inSDI)
      CS := inCS
      SCK := inSCK
      SDI := inSDI  
      dirA[SDI]~~
      dirA[SCK]~~
      dirA[CS]~~
      outA[CS] := 1 
      outA[SCK] := 0
    PUB Set(Channel, Value) | data
    '        Channel,        Gain,       Shutdown,   Value
      data := (Channel << 15) + (1 << 13) + (1 << 12) + Value
      data ><= 16
      outa[CS] := 0
      repeat 16
        outa[SDI] := data & 1
        outa[SCK] := 1
        outa[SCK] := 0
        data >>= 1
      outA[CS] := 1   
    

    And I dont know, how to combine. How to modify the variable "sin" (16bits <-> 12bits) and stored in the variable "Value".
  • Mike GreenMike Green Posts: 23,101
    edited 2011-03-06 13:28
    Both of these subjects are really advanced topics and you say you're a novice. First you have to learn enough Spin to at least understand how to use the DAC Spin code you've shown. You haven't said anything about the frequency and the number of samples per cycle you want for your sinewave synthesizer. If the frequency is low enough, you could use the floating point package to produce the sine values needed (once you understand Spin well enough to write the code to do this). If the frequency is high, you'll need to do the whole thing in assembly language and that's a longer learning process.

    Start with the Propeller Education Kit tutorials. Remember that objects are a little like library routines. You refer to them in your program and call them from your program. You don't cut and paste the code into your program. In the case of objects from the Object Exchange, the documentation for the object is either contained in the comments of the object source code or in a demo program provided in the object's archive or in a separate document included in the archive (like for the floating point package).

    It looks like the MCP4822 is the same as the MCP4922 except that the MCP4822 has an on-chip voltage reference. You should be able to use the same object from the Object Exchange.
  • Tracy AllenTracy Allen Posts: 6,666
    edited 2011-03-06 17:39
    Ceresin,

    Welcome to the forum. I think you will enjoy the Propeller, and I do second Mike's suggestions.

    The method you posted above for the DAC is written in Spin language, whereas the snippet for producing the sine is in assembly language, pasm. There is a huge difference in speed of execution.

    In the Spin method, each value you send to the DAC will take about 0.5 or 0.6 millisecond. That limits the frequency of your sine wave to something like 100Hz depending on how smooth you need the waveform to be. A 1 Hz sine wave would give about 200 level shifts over the course of one second and would look pretty good.

    Each elementary instruction in Spin takes about 5 microseconds. In contrast, each elementary instruction in pasm takes around 50 nanoseconds, so right there you have a factor of 100 difference. If you want to generate high frequency sine waves, both the DAC routine and the sine routine will have to be written in pasm. It is not necessarily harder than Spin, but it is a different skill set, because you have to think closer to the heart of the machine. The most important thing is to understand the logical steps that go into your project.

    Here is a spin method that reads the sine table. The input is a number 0 to 8191 that represents phase around a circle. The result is a number that could feed into your DAC, 12 bits, biased at 2048 center scale. You would need a main method that cycles through phase at your desired frequency, which would call on another method like the one below to find the sine, after which it would call on the method that sends the sine value to the DAC. Repeat around the cycle.
    PUB fullsine(x) | y, q                                  ' x is 0 to 2^13 (0 to 8191) for 0 to 360 degrees
      q := x >> 11                                          ' quadrant, 0 to 3
      y := (x & $7ff) << 1                                    ' 0 to 90- degrees, word index into table
      case q                                                ' by quadrant
        0 : result := word[$E000 + y]                       '  read from hub sine table
        1 : result := word[$F001 - y]
        2 : result := -word[$E000 + y]
        3 : result := -word[$F001 - y]
      return ((result ~> 5) + 2048)    ' return a 12 bit result biased at 2048
    
  • ceresinceresin Posts: 3
    edited 2011-03-11 04:32
    Thank you Mike and Tracy,

    I wrote program in PASM. Sine is nice but the frequency is only 40 Hz and I need frequency about 100kHz. I mean it is neccessary to use more cogs, isn´t it? But I dont know how. Is there some material to learn how to use more cogs? Or could you give me some advice how to do it? Thank you.
    CON
      _CLKMODE = XTAL1 + PLL16X
      _XINFREQ = 5_000_000
    pub main
    cognew(@sine,0)
    dat
            org
    sine    
            mov dira,#$07       'p0-p3  output
            
    :loop   mov count2,val
              mov phs,#0
            
    :lab2  mov outa,#$1        'dac cs 1
            mov count,#16        
            mov count3,#12 
            mov sin,phs
            add phs,#4
           
            call #getsin        'sample sin
            shr sin,#5
            add sin,val       'result biased at 2048
           
    :rev   mov reverse,sin     'bitwise reverse (low 12bits)
            and reverse,#1
            shr sin,#1
            shl shift,#1  
            add shift,reverse
          djnz count3,#:rev  
            shl shift,#4
            add shift,#%1111   'DAC header
                       
    :lab   mov y,shift        '16bits send to DAC (4bits - header + 12bits -data)
            and y,#1
            shl y,#2
            mov outa,y
            add y,#$2
            mov outa,y
            mov outa,#$0
            shr shift,#1
           djnz count,#:lab
          djnz count2,#:lab2
            jmp #:loop
            
    getsin  test sin,sin_90 wc      'sine
            test sin,sin_180 wz      
            negc sin,sin             
            or sin,sin_table         
            shl sin,#1               
            rdword sin,sin           
            negnz sin,sin           
    getsin_ret      ret             
                 
    x long 0
    y long 0
    reverse long 0
    shift long 0
    count long 0
    count2 long 0
    count3 long 0
    sin_90 long $0800
    sin_180 long $1000
    sin_table_end    long      $0000F001
    sin_table long $E000 >> 1 'sine table base shifted right
    sin long 0
    val long 2048
    phs long 0
    
  • Tracy AllenTracy Allen Posts: 6,666
    edited 2011-03-11 09:21
    First off, congratulations on getting it working even at 40Hz!

    One issue is that the phase "count3" advances by one at each iteration, and 2048 counts per each cycle and about 10 or 12 microseconds to run through all the pasm code for each sample, that comes out to about 40 Hz.

    The serial DAC requires a lot of instructions for each update, and by a quick estimate that comes out to about 7+ microseconds right there.

    One cure is to use DDS techniques, and advance the phase by a larger increment. For example, instead of +1, do +8, and then you would have 320 Hz instead of 40 Hz, but fewer samples per cycle.

    But 100 kiloHz as a target requires a different approach.

    Have you considered using the Propeller's own version of a DAC? That is uses the cog counter in the so-called duty mode. That combined with DDS techniques can reach audio frequencies. Here for example is sinewave3.spin, my own reaches about 1 microsecond per sample.

    Another approach would be an external DDS chip instead of a DAC. Or you could use an external ladder network and lots of Prop pins and send the data out in parallel.
  • AriAri Posts: 63
    edited 2011-03-11 23:08
    TDM.....google it

    I think you will have a very limited gain structure with only 12 bits.....but maybe you only need a fixed ref. level?

    the sample rate for a 100khz. sine is relatively high.....and don't forget that you need overhead to eliminate distortion at the filter points....otherwise your sine at 100khz will be full of non linear artifacts....

    how clean of a function do you need to generate??
Sign In or Register to comment.