EasyADC - call for feedback

Hi, All.

Here is a(nother) simple sigma-delta ADC 2 channel object. The main difference here is that the output for all sample rates is automatically scaled to a 16-bit number, and I tried to give some simple-to-follow theory about the circuit fundamentals.

I would really appreciate any feedback on the writeup or code. Note that I intentionally avoided using the schematic symbols to keep the file simple ASCII.

thanks,
Jonathan

Edited:
* added fast median-of-3 filtering (thanks, Dave Hein!!)
* added Rbias, did easy then complex circuits, changed Tau recommendation again.
* to change the wording around Tau
* to incorporate some clarification, and changed to either frequency or period specification.
{{
        Easy ADC
        Jonathan "lonesock" Dummer

        Sigma-Delta ADC is very easy with the propeller.  This object shows a simple
        implementation of a 2-channel ADC input.  Here's how to wire it up:

        Simplest ADC circuit:

        In ----------+ <= common node, at threshold voltage
                     |
        Out ---Rout--+---Rmeasure---* Voltage_to_measure
                     |
        Gnd ----C----+

        
        Using counter mode POS (or NEG) w/ feedback, the propeller makes Out-pin the opposite of In-pin,
        while counting the number of clock cycles that A was high (or low for NEG mode).  The threshold
        for the input pin is halfway between VDD and Gnd, or ~1.6V. If the common node voltage is above the
        threshold, the In pin will read high, and the propeller's counter will drive the Out pin Low, and
        vice-versa, effectively pulling the common node to the threshold voltage.  Over a given time
        period, the percentage of time spent pulling the common node voltage low is proportional to the
        input Voltage.

        The capacitor acts in conjunction with the resistors to act as a RC low-pass filter for the Out-
        pin's output.  The capacitor is needed, otherwise as soon as the Out pin is set, the voltage at
        the common node changes instantaneously, with no chance for the counter to actually count anything.
        So, the cap and resistor are a simple RC filter, smoothing out high-frequency noise, with the
        corner-frequency of the filter at 1/Tau, where Tau is the time constant of the filter, which for
        a RC filter is simply:

                Tau = Rout*C

        Various values of C seem to work fine, so I am recommending setting the C value as if you were using
        the RC filter for a DAC, so Rout*C ~= 1 / typical_output_sample_frequency.  NOTE: this recommendation
        is subject to change!!

        With a little bit of Kirchhoff's Current Law (KCL, a.k.a. "summing the current into a node should equal 0"),
        and assuming steady state (so the capicitors act as an open circuit):
        * The maximum input is hit when the Out pin is driven low 100% of the time: Voltage = 1.6 * (1 + Rmeasure / Rout)
        * The minimum input is hit when the Out pin is driven high 100% of the time: Voltage = 1.6 * (1 - Rmeasure / Rout)

        The maximum value coming out of the counter is simply the period over which you sample.  For example,
        with a 80 MHz system, running a sampling rate of 44.1 kHz means the sampling period is 1814 clocks.
        The minimum value coming out of the counter is obviously 0, and the maximum possible value of the counter
        is 1814 (if the In pin was high the whole time, the counter would add 1 to PHSx for each clock).  This
        is equivalent to about 10.8 bits.  If you wanted the module to report a 16-bit number exactly, you
        would need to make sure the sampling period was 65535 (or 1.22 kHz on a 80 MHz system).  To make the
        output of the system easier to use, I adjust the FRQx value so that period * FRQx = 2^31-1.  That way,
        after a full period is complete, I can shift right by 15 bits, yielding a 16-bit number.  Note that
        this does _NOT_ mean you are getting 16 significant bits, only that the reading are always scaled to
        that range. 
        
        Things to note:

        * you want large resistor values if you are trying to measure an unbuffered voltage source
                - for example, if you want to measure the voltage on a capacitor...the circuit
                  will bleed current (V_measure_me - 1.6) / Rmeasure.  If you are measuring the
                  output of an op-amp, the resistor values you choose are no big deal (other than
                  setting gain).  Note that the larger the Resistors, the noiser the system can get.
        * keep your traces as short as possible
        * use non-adjascent pins for In and Out if possible (to reduce cross-talk)
        * if possible use a low cog for pins P0..P15 (cog 0 is best, then 1, etc)
        * if possible use a high cog for pins P16..P31 (cog 7 is best, then 6, etc.)
        * there is no point summing then averaging N values...it's the same as using sample-rate / N
        * averaging is still useful if you are smoothing a waveform
        * restart the cog to change the sampling rate
        * the PASM loop takes about 80 clocks, so don't try to go over clkfreq/80 Hz for the sample-rate

        
        Here is a more complex version of the circuit, to add some features:

             +-Rbias-+ 
             |       |
        VDD -+---C---+ <= common node 
                     |
        In -----Rin--+---Rmeasure--- Voltage to measure
                     |
        Out ----Rout-+
                     |
        Gnd -----C---+

        Rin does nothing as the input pin is high impedence, but can be used to switch the function
        of the In and Out pins, changing the ADC's range.  For example, if Rin = 0.5*Rout, switching pin
        functions (Out is now the sense pin, and In is now the feedback pin) would yield a new range that
        is 2x larger.

        Note that if you have 2 capacitors to constant voltages (VDD and GND), the equivalent capacitance
        of the system looks like both capacitors are in parallel, or 2*C (2 caps are very useful since
        noise on either VDD or Gnd can affect the threshold voltage).

        Rbias can be left out of the circuit as well, but since the sum of the current coming into the
        node is 0, you can use Rbias to inject a constant current into the node (since VDD is constant,
        and the threshold voltage is constant (thanks to the prop's counter) we are basically offsetting
        the center point).  As a useful example, if Rbias == Rmeasure, you effectively shifted the input
        range to be +- 1.6 * Rmeasure/Rout, centered around 0 V!  You can verify this using KCL.  The more
        intuitive way to see this, at least for me, is to note that Rmeasure & Rbias form a resistive divider
        between Vin and VDD such that the common node is exactly 1/2 way between the the two, i.e. when the
        input voltage is 0 then the common node voltage would be at VDD/2, which is already the threshold
        voltage, and the counter doesn't have to sink or source any current.  If you want to push the center
        point around you can change Rbias, and/or connect it to Gnd instead of VDD.  For extreme flexibility,
        you could even use a potentiometer with one leg each to Gnd and VDD, or even another output pin via
        a RC filter DAC connected via a resistor to the common node, to affect fine tuning electronically.         
}}

VAR
  long control
  byte cog

PUB start_freq( Ain, Aout, Bin, Bout, SampleHz )
  '' helper function, to convert Sample Hertz to (rounded) period in system clocks
  return start_period( Ain, Aout, Bin, Bout, (clkfreq + (SampleHz >> 1)) / SampleHz )  

PUB start_period( Ain, Aout, Bin, Bout, PeriodClocks )
  '' initialize the sigma-delta ADC cog
  stop
  drive_mask := (|<Aout) + (|<Bout)
  period := PeriodClocks #> 10 ' 80 for non-filtered, 144 for filtered
  ctra_val := constant(%01001 << 26) | (Aout << 9) | (Ain)
  ctrb_val := constant(%01001 << 26) | (Bout << 9) | (Bin)
  frq_val := POSX / (period - 4) ' -4 because I am resetting PHSx immediately after reading it, so that instruction costs me 4 clocks
  control~~
  return cog := cognew( @EasyADC_pasm, @control ) + 1

PUB fill_buffer( buffer_ptr, num_samples, filter )
  '' fill a buffer with alternating channel A & B 16-bit unsigned samples
  '' period must be 80 clocks or more
  repeat
  while control
  if filter AND (period > 143)
    ' THE FILTERED VERSION IS SLOWER!  period must be 144 clocks or more
    control := -((num_samples << 16) + buffer_ptr)
  else
    ' unfiltered...the minimum period was enforced at start
    control := (num_samples << 16) + buffer_ptr

PUB wait_till_full
  '' you can call fill_beffer earlier, then just spin here until the buffer is full 
  repeat
  while control

PUB stop
  '' kill the cog
  if cog
    cogstop( cog~ - 1 )

DAT
ORG 0

EasyADC_pasm            ' initialize
              mov       ctra, ctra_val
              mov       frqa, frq_val
              mov       ctrb, ctrb_val
              mov       frqb, frq_val
              mov       dira, drive_mask
              mov       buf_ptr, #0
              wrlong    buf_ptr, par   

              ' spin until I get a non-zero pointer
get_buffer    rdlong    buf_ptr, par wz
              mov       timeout, cnt
        if_z  jmp       #get_buffer
              ' prime the pump:
              ' - set initial old* values to be 0 and max, so the 1st value will be the median
              ' - start my timer and clear the PHSx values to 0 (note the offset relative to when cnt was sampled)
              mov       phsa, #0
              mov       oldA1, #0              
              mov       phsb, #0
              ' now I have some time to waste, while the 1st sample is gathered              
              neg       oldA2, #1
              mov       oldB1, #0
              neg       oldB2, #1
              add       timeout, period                                
              ' if negative, we want filtering
              abs       buf_ptr, buf_ptr wc     ' C flag holds if I want to filter
              ' the high 16 bits of the pointer are actually the sample count
              ' (note: this won't affect the pointer...the upper bits are ignored (anything above 32kB is wrapped)
              mov       buf_cnt, buf_ptr
              shr       buf_cnt, #16

sample_loop   waitcnt   timeout, period

              ' grab the new values and reset the counters (saves 2 instructions and 2 variables, costs 4 clocks per counter)
              mov       A, phsa
              mov       phsa, #0
              mov       B, phsb
              mov       phsb, #0

              ' should I skip the brilliant median filter? (courtesy of Dave Hein...thanks!) 
        if_nc jmp       #:skip_median_filter

              ' Store the original value of A
              mov       filter0, A   
              ' Get the minimum of each of the 3 possible pairs ( A & oldA1, A & oldA2, oldA1 & oldA2 )              
              mov       filter1, oldA1              
              max       filter1, oldA2
              max       oldA2, A                ' this modifies oldA2, but it will be overwritten soon anyway
              max       A, oldA1                ' A is modified (will be the median filtered value)
              ' find the maximum of those 3 minima               
              min       A, oldA2
              min       A, filter1
              ' update my tracking variables               
              mov       oldA2, oldA1
              mov       oldA1, filter0

              ' repeat for channel B
              mov       filter0, B   
              mov       filter1, oldB1              
              max       filter1, oldB2
              max       oldB2, B                
              max       B, oldB1                
              min       B, oldB2
              min       B, filter1
              mov       oldB2, oldB1
              mov       oldB1, filter0

:skip_median_filter

              ' scale the values
              shr       A, #15              
              shr       B, #15
              { limit the values (I shouldn't need this!)
              max       A, limitFFFF    
              max       B, limitFFFF
              '}

              ' write the value (putting A in the high 2 bytes of the long...Little-Endian)
              shl       A, #16
              or        A, B
              wrlong    A, buf_ptr
              add       buf_ptr, #4

              ' keep going?
              djnz      buf_cnt, #sample_loop

              ' done...signal done-ness
              wrlong    buf_cnt, par
              jmp       #get_buffer
                                                  

{===== PASM Parameters and 'constants' =====}
drive_mask    long      0
ctra_val      long      0
ctrb_val      long      0
period        long      0
frq_val       long      0
limitFFFF     long      $FFFF                                                                 

{===== PASM scratch variables =====}
timeout       res       1
buf_ptr       res       1
buf_cnt       res       1
A             res       1
B             res       1
filter0       res       1
filter1       res       1
oldA1         res       1    
oldA2         res       1      
oldB1         res       1      
oldB2         res       1    
Free time status: see my avatar [8^)
F32 - fast & concise floating point: OBEX, Thread
Unrelated to the prop: KISSlicer
Tagged:
«1

Comments

  • 37 Comments sorted by Votes Date Added
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Nice work, looks good. Good to see nice explanations.

    I don't understand this comment:
    * you want large resistor values if you are trying to measure low impedence sources

    Can you clarify that?

    Nice to see an explanation of tau. I still don't have a good feeling for the time constants at work...

    BTW: Regarding min and max readings, I have a calculator here:
    http://www.pulsedpower.net/Applets/Electronics/SigmaDeltaADC/SigmaDelta.html
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Fixing output to 16 bit is a great idea, I away from home now but will try it in several days. my Poor man's Digital 'scope needs this
    I was playing with moch code trying to make up for the variable A/D timeings allowed.
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Great sudo tutorial. I have long known the math and had a very basic idea of its operation, this actually has me understanding this form of ADC in a functional sense (witch is good as I use it quite a bit).
    PASM The simplest programming language for the propeller.
    3D Printing pure simplicity with the Propeller..
    Multithreading applications that are thought to be to monolithic in nature.
    A computer made using only Propellers for CPU's, that is a goal (once the next gen Prop is out).
  • edited April 2011 Posts: 865Vote Up0Vote Down
    OK, I updated the code above to clarify & fix some stuff. I also changed to 2 start methods, one where you specify the period, and another where you specify the frequency. I also switched to using simple division to calculate the FRQx values, instead of the more complex ratio code.

    @ Rayman: Oops, sorry about that. It was A) confusing, and B) wrong! I meant that if you are measuring the voltage coming off an op-amp or something, then using small resistor values is OK. But if you need to measure something where leaking current will affect the voltage, you should use larger resistors. The voltage at the common node is ~ 1.6V, so you will actually be sinking or sourcing some current with this ADC circuit: (V_measure_me - 1.6) / Rmeasure. In these cases, you may want to make Rmeasure large, like 1 Mohm or so, then adjust your other resistors and caps to match. I really like that calculator! I had found it before, then lost it, so thanks for the (now bookmarked) link!

    @ Perry: Thanks! Your scope is actually why I finally wrote this up ;-) EasyADC is pretty simple to embed, but I actually want to add the option of triggering to EasyADC, as monitoring the values in PASM is much more feasible than in Spin. Let me know when you get back home, we can collaborate [8^)

    @ David: Thanks, I'm glad it helped! Hopefully the recent updates helped more, rather than hindered.


    Thanks for your feedback, more is still welcome! [8^)

    Jonathan
    Free time status: see my avatar [8^)
    F32 - fast & concise floating point: OBEX, Thread
    Unrelated to the prop: KISSlicer
  • edited April 2011 Posts: 0Vote Up0Vote Down
    I've been wondering about tau too. One analysis that makes sense to me requires that the voltage ripple at the summing node be on the order of one bit during one clock period. That comes out to
    tau = RC = 2^bits / clkfreq ----> note that the resolution is a factor.
    That differs from your prescription of
    tau = ~100 / clkfreq.
    We agree for a level of around 7 bits. Suppose you are shooting for 11 bits. In a time interval of 100/clkfreq the summing node can slew the equivalent of 16 bits. Is that important?

    I really don't have a handle of this and I'd like to hear reasoning or empirical experience that leads to one conclusion or another. A longer tau will also slow down the step response.
  • edited April 2011 Posts: 865Vote Up0Vote Down
    Hi, Tracy. Thanks for the feedback. Here's my reasoning for the Tau selection: once you pick your C and R values, you are limiting the physical response of the system to input...i.e. you can not respond any faster. Numerically, you can increase the averaging (or with this code, just decrease your sample rate) to go slower, but you have no option of sampling anything of higher frequency, as it's already been filtered out. So, for my purposes, I say that I would never want < ~6-bits resolution, so I pick the RC time constant to match (Ideally, though I don't want to be sampling at my corner frequency, especially with a RC filter, only 3dB falloff per octave).

    I'm sure that's all stuff you knew, it's just my reasoning for the recommendation. If I really wanted to do 1Msps, I'd probably pick a Tau that was only about 10 / clock_rate.

    Jonathan
    Free time status: see my avatar [8^)
    F32 - fast & concise floating point: OBEX, Thread
    Unrelated to the prop: KISSlicer
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Hi Jonathan,

    I suspect that the RC time constant, tau, is not low-pass filtering your input signal in the manner that you describe. The capacitors are sitting at a summing junction, and a suitable analogy would be an inverting op-amp with a capacitor from the inverting input to ground. To first order, the capacitor at virtual ground sees no voltage changes and does not enter into the equations for gain or frequency response. It does enter into the noise gain of the op-amp, but in the sigma-delta that is a different issue (jitter).

    A separate point that I wanted to raise is that the input to the summing junction is a current. Voltage in series with a resistor is just one way to supply that current. The current might instead come from a photodiode, or a transistor collector, or an AD592 temperature sensor, or a charge displacement device like a piezo film tab, etc..

    This is related to the tau issue. Suppose there happens to be a step change in current at the input. That might be a step change in the voltage at your resistor input, or a step change in light level falling on a photodiode. The response of the sigma delta is immediate. That is, if the ADC synchronously starts counting at the onset of the step, the count is correct for the new step value at all bit levels. (I'm currently working with a system where the prop controls the flash of a laser and also reads the photodiode response, so it can be synchronous in that manner.) On the other hand, if the ADC sampling is asynchronous, it will take one sampling period for the step to resolve, but that is a digital issue that has nothing to do with the input RC. My point again is that the choice of C does not affect the step or frequency response in the analog domain, pre-delta. The sigma in the prop is done by the summation.

    I know, this does not resolve the issue of how to choose the capacitors!
  • edited April 2011 Posts: 865Vote Up0Vote Down
    Hi, Tracy.

    Thanks for the feedback. I definitely need to think about this some more, but here's my current take on it:

    * regarding the filtering, I was saying that the RC lowpass filter is essentially for the feedback Out pin. As such, it does limit the response speed of counter-driven "keep the common node at 1.65V", so I would think that if the input changes faster than circuit can keep up, I'd have an issue. I will definitely do some experimentation and simulation this coming weekend.

    * regarding the input to the common node actually being a current, that is an excellent point! I was writing this assuming someone wished to measure voltage, but I should definitely include this. (I've used some piezos' current as a cheap electronic drum sensor before in a similar circuit...fun stuff!) Thanks!

    Thanks for all the help & insight!

    Jonathan
    Free time status: see my avatar [8^)
    F32 - fast & concise floating point: OBEX, Thread
    Unrelated to the prop: KISSlicer
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Here is a graphic that I hope shows the effect of the capacitor, and also what I mean about its non-effect as a low-pass filter for the analog input.
    sigmaDeltaSeeSaw1.gif

    The blue line is an exaggerated graph of voltage fluctuations around the threshold associated with three different voltage inputs, 1/2, 3/4 and 7/8 of the power supply voltage Vdd. This assumes that your Rin = Rout, so that the full scale range is nominally 0 to 3.3 V. Feedback holds the Prop input pin close to its threshold, nominally 1.65V. The fluctuations are exaggerated and would normally be much smaller in amplitude, but they are in scale with one another.

    On the left is the case where the summing node input is at Vdd/2, right at threshold, so the voltage across Rin is zero and it contributes no net current. The feedback pin flips with every clock cycle, and the voltage moves up and down accordingly, at clkfreq/2. I won't get quantitative about the amplitude right away, but suffice it to say that the amplitude easy to calculate from the current through Rout, the capacitance, and the clock period.

    The fluctuations have a bit of exponential character, but in this situation a linear approximation will be very good. Also, noise will add a chaotic element to the real, observed pattern, but the average of 50% high and 50% low holds as if it were the ideal pattern. The model system has zero noise and a razor sharp threshold.

    When the input voltage takes a step up to 3/4 * Vdd (red line in the middle segment of the graph), the feedback pin immediately goes into a cycle of 4, with 1 high clock period and 3 low clock periods, a base frequency now of clkfreq/4 instead of clkfreq/2. That comes from charge balance, the requirement that current coming into the summing node has to balance with current leaving. In this case both Rin and Rout enter into the calculation, and it turns out (not surprisingly) that the current when the feedback pin is high is 3 times the current when the feedback pin is low. The voltage cycle around threshold has a steep rise and a slow fall. The amplitude is higher than the previous case, but not by much (* 1.5 to be exact).

    The third step goes up to 7/8 of Vdd. The analysis again comes out with a steep rise and a slow fall, due to resistor currents adding in one direction and subtracting in the other direction. It is a cycle of 8 with a base frequency of clkfreq/8, consisting of 1 high period and 7 low periods, and the amplitude is 7/4 of the amplitude when Vin = Vdd/2.

    As Vin goes closer and closer to the limit at Vdd, the compensating fluctuations continue so long as feedback is capable of maintaining the charge balance. Frequency tends to zero (impossible to filter!) and amplitude goes to twice the value it had when Vin = Vdd/2. (The math is basic Ohm's law and I = C dV/dt).

    The effect of increasing the value of the input capacitor is only to decrease the amplitude of the fluctuations around the threshold, as seen here...
    sigmaDeltaSeeSaw2.gif

    The threshold is ideally razor sharp and detects the transitions no matter how closely they split on either side. The graph below shows a "small" capacitor with small RC on top versus a "big" capacitor with long RC on the bottom. Same timing, squashed amplitude. Conclusion: The input time constant has nothing to do with low-pass filtering the input signal.

    It must be said also that in reality the pattern will be subject to dithering due to noise and the ADC result is an average ratio of high to low that comes out in the count over many cycles. The dithering may be a good thing up to a point.

    Note that there is not a period where the feedback pattern has to "catch up" with the input step due to the time constant of either Rin or Rout with the input capacitor. Depending on where the step happens within one clock period, there may be a brief period of adjustment, but that will resolve within a few clock cycles. Other than that, the pattern associated with the new input level starts immediately, so you might say that the step response is on the same order as clkfreq. Of course, practically that is irrelevant, because nothing can get around the time required to accumulate enough counts for the desired resolution.

    There are situations that can put the system in a state where it takes time to recover. Examples are: 1) taking the input voltage out of range, or 2) replacing the input resistor with a capacitor and trying to feed in an AC square wave (which takes it out of range because I = C dV/dt) or 3) making a sudden change in the value of C (which also takes it out of range, because I = V dC/dt).

    I think the choice of capacitor comes down to second order effects. It has to be large enough to keep the fluctuations small around the threshold. There may be a wide optimum having to do with noise around the threshold. The totem pole input is biased in its linear region at threshold,so it will be generating power supply spikes. Non-ideal characteristics of the capacitor might be important, things like ESR and soakage.
    489 x 294 - 12K
    480 x 114 - 8K
  • edited April 2011 Posts: 865Vote Up0Vote Down
    Tracy, beautiful analysis, thanks!

    I will immediately remove the portion about the speed of the "response to an input" regarding the capacitor value. However, I would still like to make some recommendation as to the minimum value of C (as that seems to be missing from most sigma-delta adc math I've seen, perhaps for good reason!). Do you have a recommendation for a value that will be large enough to keep the fluctuations small around the threshold? This will be a function of at least the clock speed of the counter (so people running at PLL1x would need to adjust their minimum values, for example), and the Rout value.

    Just another quick thought...the larger the value of C, to more the threshold will jump around if there is noise on VDD or GND, right? So I'm guessing there's a "large large enough to keep the fluctuations small around the threshold" factor, but also a "small enough to minimize any effects of noise on the supply lines" factor.

    Again, I really appreciate the feedback, Tracy! Thanks!!

    Jonathan
    Free time status: see my avatar [8^)
    F32 - fast & concise floating point: OBEX, Thread
    Unrelated to the prop: KISSlicer
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Well, I'd like to come up with an answer to that question this time around, but I'm still not sure on what basis. I went back and read through the 5 page thread Troubles with Sigma-Delta ADC, which brought a lot of Prop sigma-delta issues to the fore. It did not resolve the issue of capacitor selection, at least for some of us. There were many contributors, including very interesting thoughts and empirical results from Ray (Rayman--Hey Ray, it still makes me smile to revisit rejoinders to your questioning from the illimitable deSilva!)

    Ray experimented with capacitor values from zero (+ parasitics) up to (his words, responding to Peter Verkaik, middle of page 3): "Peter, I tried using a really big C, and it didn't work very well... Individual samples all had values of 0, 50, or 100%. (This is with DC input). So, I think there must be some upper limit on RC as well, regardless of the input frequency."

    That might have something to do with the less than ideal characteristics of really big C's, and the fact that the Prop threshold is less than razor sharp. Maybe there is small hysteresis due to internal wiring. Question up in the air.

    For the lower limit of capacitor value, another factor may be how closely the signal has to approach to the full scale rails. The period between compensating pulses become longer as the signal approaches full scale, so a larger capacitor value (RC product) is required to keep the voltage within some delta of the threshold. For example, if the signal stays within a factor of 7/8 of the full scale, then the minimum base frequency that has to be filtered is 10 MHz, and the time constant RC should be >> 0.1µs. Suppose Rin=10k and Rout=10k, parallel equivalent = 5k. That makes RC >> 20pF. So a choice of 470 pF or 1 nF should be good. Probably NPO/COG. A corollary is that signals should be centered around the threshold if possible and avoid coming too close to full scale.

    That is a less stringent requirement than the criterion that involves resolution. There the RC product should be chosen so that the summing node voltage changes at most by one bit during one clock tick, and comes down to RC = 2^n / clkfreq. For R=5k and n=11 bits and clkfreq = 80MHz, that would be C = 5 nF.

    I'm not sure if either of those criteria stand on firm ground or are just rules of thumb.
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Tracy, I just had to read that thread again... Good times...

    I learned a lot from deSilva's comments in other threads. But, in that one, I still don't know for sure what he was trying to say (except that he was right all the time :)
  • edited April 2011 Posts: 865Vote Up0Vote Down
    Just read through the other thread...seems there's nothing new under the sun (hey, that's a cool quote, bet it's never been used before [8^).

    So, in a politician-style issue dodge, maybe I'll change my writeup to say "Pick the value C so that if you want, you could use the circuit as a DAC with the RC low-pass filter", that way people can use the circuit for multiple purposes. [8^)

    Jonathan
    Free time status: see my avatar [8^)
    F32 - fast & concise floating point: OBEX, Thread
    Unrelated to the prop: KISSlicer
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Tracy Allen:
    Thank you for that link to the old Post. I learned a lot from that.

    Also to note: using what I have learned here from Lonesock, Tracy, and deSilva I have managed to get the poor mans Oscilloscope working on bread board with a 1.2MHz sample rate. This was accomplished using 0.001uf caps, with R1 = 5.6K and R2 = 11.2K, with a signal diode on the input, and a ground line on one side, a Vdd line on the other side of the line between R2 and the connector for the 'Probe'. works great this way, and is stable. I am going to modify my PerfBoard version of the Pore Mans Oscilloscope to reflect these changes and see how well it does. Of course it is limited to measuring positive signals.
    PASM The simplest programming language for the propeller.
    3D Printing pure simplicity with the Propeller..
    Multithreading applications that are thought to be to monolithic in nature.
    A computer made using only Propellers for CPU's, that is a goal (once the next gen Prop is out).
  • edited April 2011 Posts: 0Vote Up0Vote Down
    If you look at the calcultator on that website I mentioned, there's an "alternate version" link that can help you pick resistors to measure bipolar signals...
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Actually, now that I think about it, the idea lonesock floated about using two different feedback resistors to adjust measurement range could also be used to switch between monopolar and bipolar sampling...
  • edited April 2011 Posts: 0Vote Up0Vote Down
    I do not know how well that would work with the diode.
    PASM The simplest programming language for the propeller.
    3D Printing pure simplicity with the Propeller..
    Multithreading applications that are thought to be to monolithic in nature.
    A computer made using only Propellers for CPU's, that is a goal (once the next gen Prop is out).
  • edited April 2011 Posts: 865Vote Up0Vote Down
    I just posted an updated version, added info about a bias resistor to center the input range around 0. Cheated on my C value recommendation ;-). Etc.

    Jonathan

    P.S. At the risk of hijacking this thread, David, I'd love to help on the scope code, if you'd like...just let me know!
    Free time status: see my avatar [8^)
    F32 - fast & concise floating point: OBEX, Thread
    Unrelated to the prop: KISSlicer
  • edited April 2011 Posts: 0Vote Up0Vote Down
    I am now using the Easy_ADC in the Poor Man's Digital Oscilloscope"

    The only thing I am concerned about is the loop waiting for more to do. That should be wraped around a waitcnt as when doing nothing the cog will be rdlongs on memory as fast as it can affecting the memory bandwidth available for other tasks.

    You can find the latest version at:
    http://forums.parallax.com/showthread.php?130582-Poor-Man-s-Digital-Oscilloscope-

    Perry
    576 x 480 - 29K
    576 x 480 - 36K
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Perry wrote: »
    ... rdlongs on memory as fast as it can affecting the memory bandwidth available for other tasks.
    Hub RAM bandwidth is a constant (20MB/s @80MHz using rdlong/wrlong) regardless of what other cogs are doing. If you mean something else can you elaborate?
  • edited April 2011 Posts: 0Vote Up0Vote Down
    kuroneko wrote: »
    Hub RAM bandwidth is a constant (20MB/s @80MHz using rdlong/wrlong) regardless of what other cogs are doing. If you mean something else can you elaborate?

    This code is poor practice.
    get_buffer    rdlong    buf_ptr, par wz
            if_z  jmp       #get_buffer
    

    It will use up the bandwidth of the memory buss doing nothing.
    There should be a waitcnt in the loop at least about 1/2 the size of the expected interval
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Perry wrote: »
    It will use up the bandwidth of the memory buss doing nothing.
    Poor practice or not, it's the best solution with minimal response latency. And it really doesn't matter whether you use up the bandwidth or not. It's there and doesn't affect the other cogs. The only valid argument against this style would be power consumption.
  • edited April 2011 Posts: 0Vote Up0Vote Down
    kuroneko wrote: »
    Poor practice or not, it's the best solution with minimal response latency. And it really doesn't matter whether you use up the bandwidth or not. It's there and doesn't affect the other cogs. The only valid argument against this style would be power consumption.


    Phil's software radio does this in two places.

    1 the quadrature code checks for frequency change
    2 the output to the sound DAC


    It will not go to shortwave frequencys as promised unless you wrap the offending code with waitcnts.
  • edited April 2011 Posts: 0Vote Up0Vote Down
    lonesock wrote:
    P.S. At the risk of hijacking this thread, David, I'd love to help on the scope code, if you'd like...just let me know!
    Thank you, though I am just using the version by Perry (with some mods). I am currently playing with the idea of increasing the sample rate using multiple sample and hold circuits (If i can get this working well the sample rate should reach 15MS/s for a Nyquist freq of 7.5MHz) or if just go with a single channel 30MS/s for a Nyquist of 15MHz. So far not much luck, and I am just doing this when I get a bit of a brain lock while working on my other projects, so not much time is spent on it.

    To note: either way if I succeed this will require 6 cogs (because of the limited number of counters available).
    PASM The simplest programming language for the propeller.
    3D Printing pure simplicity with the Propeller..
    Multithreading applications that are thought to be to monolithic in nature.
    A computer made using only Propellers for CPU's, that is a goal (once the next gen Prop is out).
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Perry wrote: »
    It will not go to shortwave frequencys as promised unless you wrap the offending code with waitcnts.
    Did you raise this issue with Phil?
  • edited April 2011 Posts: 865Vote Up0Vote Down
    Perry wrote: »
    This code is poor practice.
    get_buffer    rdlong    buf_ptr, par wz
            if_z  jmp       #get_buffer
    
    It will use up the bandwidth of the memory buss doing nothing.
    There should be a waitcnt in the loop at least about 1/2 the size of the expected interval
    Hi, Perry. Two things to note:

    1 - as kuroneko points out, that memory access is available whether I choose to use it or not, it doesn't affect any other cog's hub access at all.

    2 - I have found with both a DAC (single pin plus a RC low-pass) and sigma-delta ADC, that I get small glitches in the output / input when other cogs enter or exit any low-power sleep state. Try this: on a demo board, set up a simple sine-wave output on the headphones. Next, start up a dummy cog that does some dummy work (like a simple djnz loop), then does a waitcnt, where the waitcnt period is specified in a Hub variable. Listen to the output in the headphones as you dial that period around.

    Assuming the scope isn't going to run on batteries, you _might_ want to consider minimizing the use of any low-power wait* commands.

    Jonathan
    Free time status: see my avatar [8^)
    F32 - fast & concise floating point: OBEX, Thread
    Unrelated to the prop: KISSlicer
  • edited April 2011 Posts: 865Vote Up0Vote Down
    I just uploaded a new version: thanks to Dave Hein you can have a fast median-of-3 noise filtering on both channels. (Median filtering is a nice cheap way to get rid of spiky noise, while preserving quick response time.) Just pass in a non-zero value for "filter" when calling:

    PUB fill_buffer( buffer_ptr, num_samples, filter )

    Note that the filtering method takes more time (144 clocks, to be precise, so ~ 0.55 MHz with the prop clock @ 80 MHz).

    Jonathan
    Free time status: see my avatar [8^)
    F32 - fast & concise floating point: OBEX, Thread
    Unrelated to the prop: KISSlicer
  • edited April 2011 Posts: 0Vote Up0Vote Down
    Just tried your new version with this little test program with my latest PMDO. I use 470k resistors.
    AsmTest       mov       t2,     #1
                  shl       t2,     #7              'Pin Number
                  or        dira,   t2              'Make Pin an Output
    
    
                  mov       t,cnt
                  add       t,t1
    
    :aulp         waitcnt   t,t1
                  xor       outa,   t2              '4 Clocks (50nS)
                  jmp       #:aulp                  'Keep doing it.
    
    t             long  0
    t1            long  2000
    t2            long  1
    

    There seems to be some interaction between channels, the B channel is hooked to nothing.

    Any explanations for the false signal on B?
    576 x 480 - 14K
  • edited April 2011 Posts: 865Vote Up0Vote Down
    Hi, Perry, thanks for testing this!

    Are your pins for B right next to the pins for A? You may be getting cross-talk...can you try it with another set of pins for B (or A), preferably a distance away from the other channel's pins?

    Jonathan
    Free time status: see my avatar [8^)
    F32 - fast & concise floating point: OBEX, Thread
    Unrelated to the prop: KISSlicer
  • edited April 2011 Posts: 0Vote Up0Vote Down
    lonesock wrote: »
    Hi, Perry, thanks for testing this!

    Are your pins for B right next to the pins for A? You may be getting cross-talk...can you try it with another set of pins for B (or A), preferably a distance away from the other channel's pins?

    Jonathan

    these are my channels
      InPinA     = 18 ' A/D converter A
      opinA      = 17
    
      InPinB     = 8  ' A/D converter A
      OpinB      = 10
    

    I'll upload the latest version when I can get the output od the time difference correct.
Sign In or Register to comment.