Shop OBEX P1 Docs P2 Docs Learn Events
Prop Sigma ADC — Parallax Forums

Prop Sigma ADC

average joeaverage joe Posts: 795
edited 2012-02-16 10:33 in Propeller 1
Hey guys, I've been reading a lot of material on the propeller and sigma ADC. I understand the theory of operation fairly well and doubt that I will be able to make it work for my purpose. This does not deter me from trying though. I believe my main issue will be the fact I'm using the 40 pin dip. I already have a propRPM v1.0, which I love by the way.
I am sorry for beating a dead horse but most of the threads I have read concerning sigma ADC are fairly old.

What I need: To measure the amplitude of an AC signal, with fair resolution. *I think 8 bit should work*
More info: The ac signal will be the output from my guitar.

I imagine I will need a bit of gain before the ADC to bring voltage within range.

My main question is: Am I wasting my time trying to make this work? Are there any tips, tricks or advice from the community? If this will NOT work, what ADC chip would you recommend?

I have already built my circuit from the microphone to vga source *omitting the mic and 10k pullup resistor* on pins 25 and 26. Since I do not have a vga out, I can't test with this source code.
I am still unclear how to transform the sigma ADC pulse train into a useable value. Furthermore, I am questioning how to turn this usable value into an amplitude. I am fairly new to the propeller so this is all a bit confusing.
«1

Comments

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-12-01 09:12
    The sigma-delta ADC should work fine with audio frequencies from your guitar with 8-bit resolution, and you should need no prior amplification. In fact, if you look at the schematic of the Propeller Demo Board, you will see that the output from the onboard microphone goes directly into a sigma-delta circuit. Just be sure to mount the required passive components very close to the Propeller pins that they connect to. For more details on the sigma-delta capabilities of the Propeller, see Parallax's appnote AN008:

    http://www.parallaxsemiconductor.com/an008

    -Phil
  • average joeaverage joe Posts: 795
    edited 2011-12-01 11:43
    Phil, an008 was very helpful. It's nice to hear that it "SHOULD" work, now getting to that point will be the fun part! ;) But still, am I going to have problems using the propRPM? The picture is my current "SMD" circuit which is still untested. I used the exact circuit you mentioned, except mic and 10k pullup resistor. I assume the pullup resistor is there just to power the mic. I am thinking about re-locating the feedback pin so as to minimize lead length. I have also considered the possibilities of removing the traces from the circuit board as close to the chip as I can and changing ground source for the 1n cap.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-12-01 11:59
    You'll probably do okay with that. The loop in the 100K resistor is a bit of a concern. (A surface mount resistor soldered directly between the pins would be better.) You'll just have to try it and see what happens.

    -Phil
  • average joeaverage joe Posts: 795
    edited 2011-12-02 13:42
    Well my first attempt isn't looking too good. I used Beau's ADC driver with slight modification to use pins 26 and 27. First thing I noticed is the initial value seems wrong? PST displays 0 or 255 with nothing connected. I *THINK* it should be around 127, near midpoint. I tried changing the PLLX from 8 to 4 and 2. I will try relocating the feedback pin to allow for a shorter lead and not use adjacent pins. I'm just wondering if I'm doing something wrong. This is my first real project with the propeller and I'm not too comfortable with spin. My source code is attached below. Any help would be greatly appreciated!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-12-02 13:57
    The behavior you're seeing suggests that the input pin is not getting sufficient feedback from the counter. This could be due to several things:
    1. Poorly-connected feedback resistor.
    2. Counter not programmed to provide feedback or providing feedback to the wrong pin.
    3. Feedback pin not set via DIRA to be an output.
    4. Too high a value of the feedback resistor for the input impedance or signal level.

    -Phil
  • average joeaverage joe Posts: 795
    edited 2011-12-02 14:58
    I believe my problem stems from my changes to Beau's ADC method. I changed this:
    mov dira,#1<<SDF
    to this:
    mov dira,SigmaFBDir
    and added:
    SigmaFBDir long $0400_0000

    because I was using pin 26 as the feedback pin.
    I discovered this after I switched the feedback pin to pin 4 and changed the mov instruction back. I knew I shouldn't have been tinkering around in assembly yet, lol. Baseline now reads 140-145, much better. I still need to figure out how to turn this into a useful amplitude value.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-12-02 15:05
    If you need to increase the range of your readings, there are three things you can do:
    1. Decrease the input impedance (higher input C or lower input R).
    2. Increase the feedback resistance.
    3. Increase the integration time.

    -Phil
  • average joeaverage joe Posts: 795
    edited 2011-12-03 08:03
    I've done some testing and determined that I will need to buffer the signal from my guitar. This is because I need at least two outputs, one to the prop and one to my guitar processor. I would also like to have some level of ESD protection on the input from the guitar. I am currently working on this input circuitry.

    The ADC object is just the first piece of code for this project. I believe the range I'm currently sampling should be sufficient. What I was referring to when I said I still need to turn the sample into a usable value was something like :

    sample := AdcSample - 142
    || sample

    to return the amplitude of the signal. 142 is the approximate offset. I would like to do this part in the ASM code for the ADC if possible.

    I keep looking at AN008, in particular the calibration code. I am considering using a multiplexer on the front end of the prop, with the prop generating a reference signal for calibration. A similar circuit is shown in figure 7 of AN008. I should have a schematic in the next couple days.
  • average joeaverage joe Posts: 795
    edited 2011-12-03 09:04
    I think this is the preamp I'm going to try. The prop connector will be,
    Pin1 Ground
    Pin2 ADC IN
    Pin3 Calibration out
    Pin4 Input Select
    Pin5 Calibration Select
  • average joeaverage joe Posts: 795
    edited 2011-12-03 12:50
    I believe I have figured out how to prepare each sample as an amplitude. I modified the ASM Sigma-Delta loop to include:

    mov out_sample,asm_sample
    subs out_sample,#offset
    abs out_sample,out_sample

    wrlong out_sample,par

    Offset is set in the CON block. Things are slow going due to my unfamiliarity with the propeller.
    The challenge now is to implement an envelope generator triggered by the amplitude of the guitar in. I'm still working out the theory of operation for this method or methods. I am considering using the counters to do this.
  • average joeaverage joe Posts: 795
    edited 2011-12-05 01:09
    After a bit more reading, it doesn't seem that counters will help much with the envelope generator. The main problem is the counters only increment. I could do the attack in the counter with little work. Once decay starts, things get more complicated though. I would need to reset phase and then subtract phase from 255 until I reach Sustain level. I'm not seeing how the counters would be helpful since they would require quite a bit of oversight. Now I must go back to the drawing board.
  • kuronekokuroneko Posts: 3,623
    edited 2011-12-05 01:21
    The main problem is the counters only increment.
    If the emphasis here is on increment then I'd argue that you could easily add negative numbers (not sure if it helps with your problem though). If not, please ignore this comment :)
  • average joeaverage joe Posts: 795
    edited 2011-12-05 06:20
    Kuroneko, I have thought about this but I'm not sure how to assign a negative to the FREQ register. I would still need to sample the PHASE register to be output to my DAC. I'm currently attempting to write my envelope generator in spin without using counters. I'm thinking something like:
    PUB RunADSR | time
      Time := cnt     
       if AdcSample & TriggerMask >= Trigger
         repeat DAC1 from 0 to 255
            if AdcSample & GateMask > Gate
               waitcnt(time += AttackCks)
            else
            {skip to release?}
         repeat DAC1 from 254 to SustainLevel
            if AdcSample & GateMask > Gate
              waitcnt(time += DecayCks)
            else
            {skip to release?}  
            {wait till AdcSample & GateMask < Gate}
         repeat DAC1 from SustainLevel to 0
           waitcnt(time += ReleaseCks)
    
    Still not sure how to skip to and wait for release. This is my first real project with the prop so it is very slow going.
  • average joeaverage joe Posts: 795
    edited 2011-12-05 07:24
    I've got something like this so far. Not sure how well this will work. I still need to finish my DAC method in order to test.
    PUB LoadADSR (GateLevel, AttackTime, DecayTime, ReleaseTime)
      Gate := GateLevel << 16
      AttackCks := AttackTime   * CKperMS / 256
      DecayCks  := DecayTime    * CKperMS / 256
      ReleaseCks := ReleaseTime * CKperMs / 256
      RunADSR
      
    PUB RunADSR | time
      Time := cnt     
       if AdcSample & TriggerMask => Trigger
         repeat DAC from 0 to 255
           if AdcSample & GateMask > Gate
            waitcnt(time += AttackCks)
           else
            EarlyRelease := DAC
            repeat DAC from EarlyRelease to 0
             waitcnt(time += ReleaseCks)
            RunADSR  
         repeat DAC from 254 to SustainLevel
           if AdcSample & GateMask > Gate
            waitcnt(time += DecayCks)
           else
            EarlyRelease := DAC
            repeat DAC from EarlyRelease to 0
             waitcnt(time += ReleaseCks)
            RunADSR
         repeat until AdcSample & GateMask <= Gate
         repeat DAC from SustainLevel to 0
           waitcnt(time += ReleaseCks)
           RunADSR
    
  • kuronekokuroneko Posts: 3,623
    edited 2011-12-05 17:01
    ... I have thought about this but I'm not sure how to assign a negative to the FREQ register.
    frqa := -1
    
    Assuming phsa holds 137, adding -1 (or $FFFFFFFF) will give you $100000088. Cutting off the overflow leaves us with $88 (136).

    As for your latest example code, calling a method (being equivalent to GOSUB) from within itself may eventually eat all you stack space and make your program fall over in interesting ways. If you're lucky it will return from the call and continue processing after the method call which doesn't seem to be what you want. You could restructure your method or simply return from it and have the original call run in an endless loop, e.g.
    ...
    
      [COLOR="blue"]repeat
        RunADSR[/COLOR]
    
    PUB RunADSR
    
      ...
    
      return {instead of RunADSR}
    
    The return will exit the method and return to the repeat loop, then call the method again. One last thing, you should use =< for comparisons instead of <= (assignment). HTH
  • AribaAriba Posts: 2,690
    edited 2011-12-05 19:55
    average joe

    It would help a lot if you describe what you want to do.
    I guess you want trigger an envelope generator from the guitar signal. But what does this envelope modulate?
    I think first you need to extract the envelope of the guitar signal, to get a useful signal to trigger your envelope generator.
    Then when this "guitar envelope" is over a trigger level the attack of your generator is startet, and when it's under the sustain level it goes to release.
    What you have in your code does not do that:
    if AdcSample & TriggerMask => Trigger
    
    This tests a bit (or several bits) of the current ADC sample, I dont see how this can be a trigger criteria.
    The same for the GateMask.

    I would code an Envelope generator as a state machine, something like this pseudo code:
    case envState
        ATTACK:  envValue + AttackRate
                 if >= max: envState = DECAY
        DECAY:   envValue - DecayRate
                 if <= sustainLevel: envState = SUSTAIN
        SUSTAIN: if gate=0: envState = RELEASE
        RELEASE: envValue - ReleaseRate
                 if < 0: envValue = 0
                 if gate > 0: envState = ATTACK
    
    This is called in a loop and generates one "sample" of the envelope.

    Andy
  • average joeaverage joe Posts: 795
    edited 2011-12-06 08:26
    Thanks kuroneko. I knew I was doing something wrong in my ADSR method. I am still considering using counters for the bulk of the envelope generation process. I will probably implement this further down the road, as I become more familiar with Spin and Pasm. I still have a significant amount of work to do, and will be commenting code over the next couple days.
    PUB LoadADSR (GateIn, AttackTime, DecayTime, ReleaseTime)
    
      Gate := GateIn << 16
      AttackCks := AttackTime   * CKperMS / 256
      DecayCks  := DecayTime    * CKperMS / 256
      ReleaseCks := ReleaseTime * CKperMs / 256
    return
    
    
    PUB RunADSR  | time, EarlyRelease
      Time := cnt     
       if AdcSample & TriggerMask > Trigger
         repeat DACByte FROM 0 to 255
           if AdcSample & GateMask > Gate
             waitcnt(time += AttackCks)
           else
            EarlyRelease := DACByte
            repeat DACByte from EarlyRelease to 0
             if AdcSample & TriggerMask < Trigger
              waitcnt(time += ReleaseCks)
             return  
         repeat DACByte from 254 to SustainLevel
           if AdcSample & GateMask > Gate
            waitcnt(time += DecayCks)
           else
            EarlyRelease := DACByte
            repeat DACByte from EarlyRelease to 0
             if AdcSample & TriggerMask < Trigger 
              waitcnt(time += ReleaseCks)
             else
              return 
             return
         repeat until AdcSample & GateMask < Gate
        repeat DACByte from SustainLevel to 0
         waitcnt(time += ReleaseCks)
         Return
       else
       Return  
    


    Ariba
    I apologize for being so obscure. You are correct about triggering the envelope generator from my guitar. I am using a modified version of Beau's generic ADC object. I added this code before writing out the long to prepare my "trigger" and "gate" values.
                  mov       out_sample,asm_sample           'compute amplitude from sample, copy sample in
                  subs      out_sample,#offset                    'and remove offset
                  abs       out_sample,out_sample             'then return absolute value
    
                  add       average,out_sample                  'compute average, add sample to average
                  shr       average,#1                                'and devide by 2
                  mov       average_out,average                 'copy average to out buffer
                  shl       average_out,#16                        'and shift 16 bits
                  add       out_sample,average_out           'and add to out sample
    
                  wrlong    out_sample,par                       'write sample back to Spin variable "sample" 
    
    The masks are then used to chose either amplitude *for trigger* or average *for gate*. Please note in the load section *which converts the times in milliseconds, into clocks per step* the selected gate value is shifted left 16 bits to match the average output from the ADC. I did this to limit the number of hub writes for the ADC *since it already passed a long back to the hub*
    Ariba wrote:
    I guess you want trigger an envelope generator from the guitar signal. But what does this envelope modulate?

    The envelope will be output to a Spi DAC *specifically AD7303, which generates a 0-5v control signal. This control signal will be fed into VCA, more specifically an Alesis MicroLimiter that has been hacked to receive a 0-5v CV. I actually need to generate TWO envelopes, programed independently *please don't ask why, lol*. They will both receive the same AdcSample, but will each get their own Trigger, Gate, Attack, Decay, Sustain and Release values. I'm still working out how to call these methods. Also, thanks for the state machine example. This should prove very helpful when re-writing in Pasm.

    A big thank you to everyone who has contributed. You guys have been a huge help as always. I will update this thread as I continue my work. I expect progress to be slow right now for several reasons. First, the new semester started yesterday. Second, I'm about to have a son any day :) Thanks again!
    Joe
  • average joeaverage joe Posts: 795
    edited 2011-12-22 05:00
    I've been able to spend a couple hours on this project and made some decent headway, I THINK. I haven't had a chance to test this yet, but it should work. So, here's what I did.
    I re-wrote Beau's ADC driver to accept 4 - 8bit "levels" *Trigger and Gate, A and B* and store them into a single long. The address of this long is passed into assembly. The assembly program samples the input, then removes dc offset and returns absolute value.*Amplitude* Then it adds the amplitude into a running average and divides by 2. This is where things get a little tricky. The assembly program should read the levels, one byte at a time and then compare this with the amplitude and average. *Triggers compared with peak, Gate compared with average* It modifies Trigger and Gate pins accordingly. I used 4 - rdbyte instructions instead of 1 rdlong instruction because the long would need masking and shifting. Not sure which is more efficient.

    I'm not sure if anyone else can use this for something? I plan on posting this object to OBEX when finished. Any suggestions before posting? Any suggestions period?

    I have several more objects to work on before I can test my project in full. Next is a modification of SPI driver needed to run my DAC. Then ADSR methods done in ASM as well as UI done in Spin. I will keep you guys updated as I continue.
  • average joeaverage joe Posts: 795
    edited 2012-02-16 05:37
    Hopefully Phil or someone else can answer this.
    I will be hooking up the sigma-delta adc to a 4558 op amp driven from dual 15v rails. The op-amp is configured as a voltage follower to buffer an audio signal from a previous stage. I think I need a resistor between the op-amp output and input cap to protect the prop against voltage spikes during power on, etc. I'm a little unsure how to figure for this resistor.
  • CircuitsoftCircuitsoft Posts: 1,166
    edited 2012-02-16 07:04
    This will give you a +/-18.5v full scale.
    3.3V
                 &#61463;
           10K&#937;  &#61628; 10K&#937;
      Vin &#61609;&#9472;&#61629;&#61630;&#9472;&#9472;&#9472;&#9531;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9523;&#9472;&#9472;&#9472;&#9472;&#61610; APIN
                           &#9492;&#9472;&#61629;&#61630;&#9472;&#61610; BPIN
                             1K&#937;
    
  • average joeaverage joe Posts: 795
    edited 2012-02-16 07:31
    Hey Circuitsoft, thanks for the reply. I don't need 18.5v full scale. I just want to protect against full scale spikes from power on, or other planned failure events. Also, the signal is a/c coupled. The schematic is taken from microphone in. Just thinking about protecting those valuable prop pins. Here's a schematic..
  • CircuitsoftCircuitsoft Posts: 1,166
    edited 2012-02-16 07:44
    +/- 16.5v makes things easier because it's a 10:1 resistor ratio. The extra resistor I added to +3.3v provides an offset so that 0 is 0v rather than 1.65v.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-16 07:56
    In an AC-coupled environment, the pull-up is not necessary. You still need the caps to Vdd and Vss, though.

    -Phil
  • lardomlardom Posts: 1,659
    edited 2012-02-16 08:31
    I ran your code and it doesn't change from 255. I uploaded an object that will at least change values. Note that even though the start method baud rate is set to 9600 you have to set the PST to 4800. I don't know why that is but it will give you something else to look at.
  • lardomlardom Posts: 1,659
    edited 2012-02-16 08:51
    @average joe, I think there may be an error in your schematic. Your feedback needs a resistor to form a voltage divider. I could be wrong but it doesn't look like your +/- inputs are balanced.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-16 09:01
    Larry,

    He's doing it right. It's a unity-gain buffer amp.

    -Phil
  • average joeaverage joe Posts: 795
    edited 2012-02-16 09:10
    lardom wrote: »
    I ran your code and it doesn't change from 255.
    Not sure what code you ran, but I have made significant revisions. I HAD a working adc. I had the DAC working as well. I was then sidetracked by finishing my audio hardware. Now that the analog hardware is in final revision, I needed to work on the user interface.I then ripped my propRPM apart and did this... I have not re-installed DAC yet, and have not power-on tested yet, but fingers crossed.

    I am working on low-level drivers right now. I want to get the screen working first, then touch, sd card after that. The final integration should be painless. I'm drawing up schematics later this week.
  • average joeaverage joe Posts: 795
    edited 2012-02-16 09:18
    phil, I have the caps and fb resistor soldered under the board. It's not pretty, but it worked.

    I made changed to accommodate the new display.
    I need a value for R1. X. Or how to figure it. My thought is it's not entirely necessary, but. IF something happens and the signal input gets driven to either rail, or other faults, I don't wanna burn a prop pin. ?
    191 x 320 - 14K
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-16 09:29
    33K for R1 would be the bare minimum for protection from a 15V surge. But that also gives the ADC a 3X gain, making 1V P-P your maximum convertible signal level. If your signal level is higher than that, R1 will have to be increased accordingly.

    -Phil
  • lardomlardom Posts: 1,659
    edited 2012-02-16 09:49
    Phil, bear with me just this once. I'm asking as a student, how did you derive 33k for R1 @15v? My calculator stays nearby.
    If the equation/s have a name I can look it up. Thanks.
Sign In or Register to comment.