Shop OBEX P1 Docs P2 Docs Learn Events
Sigma Delta Audio DAC Object — Parallax Forums

Sigma Delta Audio DAC Object

Chris MicroChris Micro Posts: 160
edited 2009-04-16 22:02 in Propeller 1
hello all together,

I'm new to the propeller chip and just began to get the LED's blinking at my Triblade Propeller.

What I want to do next, is, to produce some audio waveforms. To do this, I would need an audio dac object in spin. I couldn't find any object in the objects library. Yes; I know, there is this spatial room demo, which is very advanced to produce sound. But to keep it simple, I need an simple an Object with following inputs: LeftPin, RightPin, ADCValue Left, ADCValueRight. Why isn't there any simple Object to do this? For an beginner like me it is essential to have a simple code example to learn from and to expand it step by step. Probably anybody can give me a hint about this isue.

chris

Comments

  • AribaAriba Posts: 2,690
    edited 2009-04-15 10:12
    Chris Micro said...
    Why isn't there any simple Object to do this? ...

    Perhaps because it's to simple to set up a DAC with the propeller counters. A counter in the DUTY mode generates
    a high frequency PWM output, which is nearly ideal for Audio DACs. See the application note AN0001 for a description
    of the Propeller counters.
    After setting the counters into the right mode, you can simply write the DAC value to the FRQA and FRQB registers.
    The counter produces then the according PWM pulswith. The value should be centered at $8000_0000 to get the
    best possible result ($0000_0000=max negativ, $8000_0000=middle, $FFFF_FFFF=max positiv).

    The DAC outputs should have an R-C lowpass filter to eliminate the high PWM frequencies (in the MHz).

    The following example shows how to set up the counters and produces 2 slightly detuned triangle waves at the left
    and right output. You can play with the DDS values (63_000_000) to get other frequencies.

    {{ DAC Demo }}
    
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      LEFT_DAC_PIN  = 10
      RIGHT_DAC_PIN = 11
    
    PUB Main | phl,phr
      ctra := %00110<<26 + LEFT_DAC_PIN     'init counters for DACs
      ctrb := %00110<<26 + RIGHT_DAC_PIN
      dira[noparse][[/noparse]LEFT_DAC_PIN]  := 1              'set DAC pins to output
      dira[noparse][[/noparse]RIGHT_DAC_PIN] := 1
      repeat
        phl += 63_000_000       'DDS Sawtooth Osc L
        frqa := ||phl           'convert to Triangle and write to DAC L
        phr += 63_200_000       'DDS Sawtooth Osc R
        frqb := ||phr           'convert to Triangle and write to DAC R
    
    



    Andy
  • virtuPICvirtuPIC Posts: 193
    edited 2009-04-15 10:19
    There is also the example microphone_to_headset.spin where you can find an SD-ADC and an SD-DAC in about 100 lines - half of it code, half of it documentation.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Airspace V - international hangar flying!
    www.airspace-v.com/ggadgets for tools & toys
  • Chris MicroChris Micro Posts: 160
    edited 2009-04-15 20:06
    virtuPIC said...
    There is also the example microphone_to_headset.spin where you can find an SD-ADC and an SD-DAC in about 100 lines - half of it code, half of it documentation.

    Thank You, nice example, but I do not really understand it.It seems to be a delta sigma adc and delta dac but only single channel. A Delta Dac would probably produce results which have a higher quality than pure PWM. Therefore I would prefer this to have it in an DAC-Object than only PWM.

    @Ariba
    Thank you for this nice example. At least it was easy to run. For a synthesizer I would prefer to separate the signal generation and the DAC. This is due to the fact that in my opinion the timing conditions could be met easier and in a later stage of programming the DAC part could be enhanced with some filters and mixers. As far as I understood, the DAC value could be transfered to the DAC Object by a pointer to the propeller main memory.

    In your example the loop timing is very unclear. To produce a certain pitch, lets say 440Hz, it would be very useful to have a constant loop cycle of lets say 20KHz. With this to values the increment for the phase accumulator can be easily calculated ( I saw your synthesizer project, congratulations )

    I will try to make a pure DAC Object with PWM and pointer input as learning example. Of course it would be better to have an Object with a Delta DAC but that seems to complicated for now.

    chris
  • Chris MicroChris Micro Posts: 160
    edited 2009-04-15 20:08
    By the way: what is the PWM frequency in the Ariba example?
  • virtuPICvirtuPIC Posts: 193
    edited 2009-04-15 20:14
    Oops! Sorry Chris, you are right. The DAC is PWM. You could make it sigma delta by increasing the frequency or enlarging the capacitor. But take care that signal amplitudes stay small to stay linear since you only have an RC low pass as integrator...

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Airspace V - international hangar flying!
    www.airspace-v.com/ggadgets for tools & toys
  • Chris MicroChris Micro Posts: 160
    edited 2009-04-16 14:42
    Hello,

    I'm still trying to make the simple DAC-Object. And as I'm new to Spin, there seems to be a pointer problem. Has anybody a hint?

    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
      OneSecond = 80_000_000 'SysClk: Number of clk cycles in one second
    
    VAR
      LONG Stack[noparse][[/noparse]9]
      LONG Left 
      LONG Right 
    
    ' private test function to test the object
    PUB TestDualDac  
      Start(10,11)
      'generate 500Hz test pitch
      repeat
        waitcnt(OneSecond/1000+cnt)
        Left:=0
        waitcnt(OneSecond/1000+cnt)
        Left:=$FFFF_0000
    
    '' Start: Initialize dual dac outputs
    PUB Start(LeftDacPin,RightDacPin)
      cognew(Init(LeftDacPin,RightDacPin,@Left,@Right),@Stack)
    
    
    PUB Init(LeftDacPin,RightDacPin,ValueLeft,ValueRight)| phl,phr
      ctra := %00110<<26 + LeftDacPin     'init counters for DACs
      ctrb := %00110<<26 + RightDacPin
      dira[noparse][[/noparse]LeftDacPin]  := 1              'set DAC pins to output
      dira[noparse][[/noparse]RightDacPin] := 1
    
      repeat
        'phl += 63_000_000*16       'DDS Sawtooth Osc L
        frqa := ValueLeft[noparse][[/noparse]0]
        'frqa := ||phl           'convert to Triangle and write to DAC L
        phr += 63_200_000*5       'DDS Sawtooth Osc R
        frqb := ||phr           'convert to Triangle and write to DAC R    
        waitcnt(OneSecond/20_000+cnt) ' sampling frequency is 20KHz  
    
    
  • Chris MicroChris Micro Posts: 160
    edited 2009-04-16 17:40
    After some thinking I implement it a "write method" into the DAC object and didn't try any longer to pass the DAC values by a pointer. With this, the DAC object seems to work fine. The only trade off: it might be slow

    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
      OneSecond = 80_000_000 'SysClk: Number of clk cycles in one second
    
    VAR
      LONG Stack[noparse][[/noparse]9]
      LONG LeftChannel  'analog value left
      LONG RightChannel 'analog value right
    
    PUB TestDualDac
      '' private test function to test the object, don't use it external  
      Start(10,11)
      'generate 250Hz  pitch test sound
      repeat
        waitcnt(OneSecond/500+cnt)
        Write(0,0)
        waitcnt(OneSecond/500+cnt)
        Write($FFFF,0) 
    
    PUB Start(LeftDacPin,RightDacPin)
    ''  Start: Initialize dual dac outputs     
      cognew(Init(LeftDacPin,RightDacPin),@Stack)
    
    PUB Write(LeftValue,RightValue)
    {{  write analog value
        imput range: 0..$FFFF
        }} 
    
      LeftChannel:=LeftValue<<16
      RightChannel:=RightValue<<16
    
    PRI Init(LeftDacPin,RightDacPin)
    {  PRI: start pwm output
        } 
      ctra := %00110<<26 + LeftDacPin     'init counters for DACs
      ctrb := %00110<<26 + RightDacPin
      dira[noparse][[/noparse]LeftDacPin]  := 1              'set DAC pins to output
      dira[noparse][[/noparse]RightDacPin] := 1
      repeat
        frqa := LeftChannel
        frqb := RightChannel 
        'waitcnt(OneSecond/20_000+cnt) ' sampling frequency is 20KHz
    
    
  • mparkmpark Posts: 1,305
    edited 2009-04-16 18:59
    Unlike in C, in Spin you can't dereference a pointer by saying ValueLeft[noparse][[/noparse] 0 ] (ValueLeft[noparse][[/noparse] 0 ] is actually just ValueLeft). You have to write long[noparse][[/noparse]ValueLeft].
  • AribaAriba Posts: 2,690
    edited 2009-04-16 22:02
    Chris Micro said...
    By the way: what is the PWM frequency in the Ariba example?

    The PWM frequency varies with the value. The shorter puls length part is always 1 clock cycle (12.5ns @80MHz).
    The longer pulslength part is adjusted in the length so that the ratio corresponds to the value.
    At value $8000_0000 you have a PWM frequency of 40MHz (1 high, 1 low cycle) ! At $0000_FFFF ou have only 2.4 kHz (1 high, 32767 low cycles).

    Spin is not fast enough for Audio processing, this must be done in Assembly.

    Andy
Sign In or Register to comment.