Shop OBEX P1 Docs P2 Docs Learn Events
Strategy for Porting "ANALOGIN" (Sigma-Delta) to ISR? — Parallax Forums

Strategy for Porting "ANALOGIN" (Sigma-Delta) to ISR?

ZootZoot Posts: 2,227
edited 2008-12-12 07:34 in General Discussion
I've been playing with ANALOGIN so I can have a few channels of ADC on some SX projects w/o having to add more chips. In these projects the mainline (a state machine) can't really be expected to run it's individual pieces with any particular regular time period, so it tends to add jitter to ANALOGIN readings, so I've been scratching my head over how to run it in the ISR.

It's not much code, I don't think since I only need to keep track of the current loop through the 255 charge/read cycles and if the read is in progress or if it's ready for the mainline to parse....

... but I'm having trouble wrapping my head around time requirements here. Given what I see happening in generated code for ANALOGIN, and given that I'm working with ISR periods of say 13us, it would seem that readings wouldn't be taken quickly enough? Do I have that right? Would I need to use larger cap so that the charge/discharge cycle is long enough to be read? My rough count of analogin time as used normally in the mainline (with 1 priming cycle) is about 1200us (at 20MHZ) so it's not exactly a fast conversion to begin with.

Suggestions? Theory? Ideas?

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST

1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php


Post Edited (Zoot) : 12/4/2008 6:59:24 PM GMT

Comments

  • BeanBean Posts: 8,129
    edited 2008-12-04 00:29
    Zoot,
    When you use it in an interrupt you don't need the priming cycle, since it is continuous.
    So the rate would be 13uS * 256 = 3.328mSec or about 300 Hz.

    Basically every interrupt you sense the input pin. If the input pin is high, you add one to a temp value and make the output pin low. If the input pin was low, then just make the output pin high.
    Next you increase a counter, when the counter rolls over to zero, you copy the temp value to the variable to hold the ANALOGIN value and possibly set a flag to indicate that a new value has been aquired.

    I could whip-up some code if you need more help.

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    "The welfare of the people in particular has always been the alibi of tyrants." ~ Camus
    www.iElectronicDesigns.com

    ·
  • ZootZoot Posts: 2,227
    edited 2008-12-04 01:31
    Bean (Hitt Consulting) said...

    When you use it in an interrupt you don't need the priming cycle, since it is continuous.

    Oh, sure, the cap will always be charging/discharging at a regular interval unlike a mainline analogin which may have a substantial amount of irregular time between calls to the command. I didn't think of that.

    The code is simple, but where I'm scratching my head is how to go about choosing the best cap/resistor values for a given interrupt rate.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • BeanBean Posts: 8,129
    edited 2008-12-04 02:26
    The longer it takes to take a sample, the larger values of cap and/or resistors you will need.
    I'm sure there is a way to calculate it, but I would start with 10K resistors and 1uF cap.
    If you get unsteady readings or use larger values. If the response time is to long use smaller values.
    If the values are too large, you will get· values that skip around alot (like 128 to 192 in one jump).

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    "The welfare of the people in particular has always been the alibi of tyrants." ~ Camus
    www.iElectronicDesigns.com

    ·
  • ZootZoot Posts: 2,227
    edited 2008-12-04 03:03
    Cool. I'll try this out sometime over the next few evenings on an IR ranger.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-12-04 07:01
    Hi Zoot,
    Attached is a document with ADC calculations.
    Step 4 calculates the C for a given resolution N and interrupt rate.
    The calculated C will result in a ripple at the adc input pin (= ripple over C)
    that hoovers between 0.5Vdd-0.5LSB and 0.5Vdd+0.5LSB
    A larger C decreases the ripple, a smaller C increases the ripple.
    The value is a guiding value but it also sets the signal bandwidth that
    you can sample (you must sample at least at 2x the input BW).

    regards peter
  • ZootZoot Posts: 2,227
    edited 2008-12-04 18:56
    Handy file, that.

    Peter, just a few questions on your notes:

    - under step 2, is the following
    2*Vdd - 2*Uinmin
    to be read as
    (2*Vdd) - (2*Uinmin)

    - under step 3, is the following
    Vdd - 2^(N-1) * ( Uinmax - Uinmin )
    to be read as
    Vdd - ( 2^(N-1) * ( Uinmax - Uinmin ) )
    or
    ( Vdd - 2^(N-1) ) * ( Uinmax - Uinmin )

    - under step 4 I'm a bit confused by Fs and N:
    -- for example, N (bit resolution) would be 8 if I'm using a byte as the final ADC value? 16 if a Word? etc?
    -- Fs is how many times per second a converted sample is desired, or how many times per second the state of the output pin will be set? I believe it's the latter?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-12-04 20:44
    Use the normal math·rules:

    Step 2:
    (2*Vdd) - (2*Uinmin)

    Step 3:
    Vdd - ( 2^(N-1) * ( Uinmax - Uinmin ) )

    Step 4:
    N is bit resolution, so·8 for byte, 16 for word, 10 for 10bits ADC.
    Fs is interrupt frequency in Herz.
    For example, if FREQ = 50MHz and intperiod=217 (prescaler 1:1)
    then Fs = 50MHz/217 = 230415 Hz
    With Fs in Herz and R in ohms yields C in Farad
    With Fs in Megahertz and R in ohms yields C in microfarad

    regards peter
  • ZootZoot Posts: 2,227
    edited 2008-12-04 20:46
    You're the man. Thanks.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • ZootZoot Posts: 2,227
    edited 2008-12-08 05:36
    Pretty sweet. Nice doing some ADC w/o too much external circuitry or a dedicated ADC IC. I'm still experimenting with Peter's version which allows for biasing of the input voltage and greater resolution, but with an interrupt rate of 77_000, two 10k resistors and a .01 uf cap, I get nice stable readings at 8 bits. A 1uf cap didn't give me enough range in the reading; .001 was too smalll. .005 or so would be better, but I used what I had on hand.

    AdcIn0   PIN RB.2 INPUT CMOS
    AdcOut0 PIN RB.3 OUTPUT
    
    adcVal  VAR Byte  ' running count of charge/discharge input state
    adcCntr VAR Byte ' 8 bit "frame"
    
    sensed  VAR Byte
    
    WATCH sensed
    
    '...
    
    INTERRUPT 77_000
       GOTO Interrupt_Handler
    
    '...
    
    Main:
    
    '...
    
       GOTO Main
    
    '...
    
    Interrupt_Handler:
    
    '...
    
    Read_Sigma_Delta:
       INC adcCntr
       IF adcCntr = 0 THEN
          sensed = adcVal
          adcVal = 0
       ENDIF
       adcVal = adcVal + AdcIn0
       AdcOut0 = ~AdcIn0
    
    '...
    
    RETURNINT
    
    



    Now, I have a few other questions.

    1. Should I presume that this circuit will be more sensitive to temperature swings and the like than an ADC chip? Ditto that it will be sensitive to tolerances in the resistors and cap(s)?

    2. Is there any way to combine pins if I wanted to set up 4 or 6 sigma delta ADCs? I'm thinking not, because I can't see a way around the need for isolating the input and output of each circuit. And if I had that many it would probably be easier to just hook up a multi-channel ADC IC anyway, but in a pinch it would be nice.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php


    Post Edited (Zoot) : 12/8/2008 5:41:40 AM GMT
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-12-08 13:35
    Zoot,

    For tolerance, consider this: (adcValue =·0 to 2^N - 1)

    ······Uinmax - Uinmin···········································
    Uin =·
    * adcValue +·Uinmin =
    ··········2^N - 1

    ·······R1···· ··· 1····················Vdd········ R1·· R1·· R1
    Vdd·* ----·*
    * adcValue +·
    * (1 - -- - -- + --)
    ·······R2····· 2^N - 1··················2········· R2·· R3·· R4

    You can calculate the minimum and maximum for Uin using minimum and maximum values for
    the resistors and calculate relative tolerance from that.
    But a very good approximation would be

    dUin···dVdd··· dR1·· dR2
    ---- =
    + --- + ---
    ·Uin··· Vdd···· R1··· R2

    In words:
    relative tolerance in Uin equals rel.tol. of Vdd + rel.tol. of R1 + rel.tol. of R2
    So if tol.Vdd =·5%, tol.R1 =·1%, tol.R2 =·1% then tol.Uin =·7%


    regards peter



    Post Edited (Peter Verkaik) : 12/8/2008 1:42:56 PM GMT
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-12-08 13:55
    Zoot,
    You should set the output pin at equal intervals
    so charge and discharge time are equal when
    hoovering around Vdd/2.

    Interrupt_Handler:

    '...

    Read_Sigma_Delta:
    ·· Sample = AdcIn0·· 'set output at equal intervals
    ·· AdcOut0 = ~Sample
    ···adcVal·=·adcVal·+·Sample
    ···INC·adcCntr
    ···IF·adcCntr·=·0·THEN
    ······sensed·=·adcVal
    ······adcVal·=·0
    ···ENDIF

    '...

    RETURNINT


    regards peter
  • ZootZoot Posts: 2,227
    edited 2008-12-08 14:45
    I thought about doing a "capture" of the input state to keep sample times consistent, but given the handful of instructions of difference at 20 or 50 mhz I didn't think it would make too much of a difference, esp. with the relatively slow interrupt rate for 8 bits of resolution (and I wanted to save a register). But I'll try this out. Thanks.

    Actually, Peter, here is another question regarding your Sigma Delta calc. worksheet -- choosing R1 to start -- what should I be considering when choosing this first value?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-12-08 14:57
    Considering that the connection for the adc input pin
    is a virtual ground for small ac signals superimposed on a dc input voltage,
    R1 should be large compaired to the output resistance of·your
    Uin voltage source (those 2 form a divider), but 10k should do
    fine for·most applications.

    regards peter
  • ZootZoot Posts: 2,227
    edited 2008-12-10 13:57
    Update: Peter, that document of yours is the niftiest thing since sliced bread. While the component count ends up being a bit higher than the analogin circuit in the SX/B docs, using the 3 resistors and 2 caps and properly biasing the input yields incredible results -- rock-steady values across the entire range of the chosen bit resolution. In my test last night, a photoresistor that yields an input voltage of about 2.8v-4.9v resulted in 8-bit values of 3-247. Nice. Thank you, thank you!

    While I still sometimes (wistfully) wish that the SX had 1-5 analog channels like some of the PICs, this is a superb way to add one or two ADCs to a project without having to purchase an ADC chip. Sweet.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • RickBRickB Posts: 395
    edited 2008-12-10 15:52
    Does this technique have enough stability (with the appropriate ext. parts and stable Vdd) for 9 or 10 bits?

    Rick
  • ZootZoot Posts: 2,227
    edited 2008-12-10 15:58
    Peter can probably answer that better, but his formula allows for entering desired bit resolution, so yes. I would think the key here would be using resistors and caps with really tight tolerances, esp. for higher bit resolutions.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    When the going gets weird, the weird turn pro. -- HST

    1uffakind.com/robots/povBitMapBuilder.php
    1uffakind.com/robots/resistorLadder.php
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-12-10 16:37
    It should do, but note that the total conversion time
    increases (eg. sample rate decreases) by powers of 2.

    Here is code for a 15bit adc. Note that this code was set up
    to use dynamic ports and pins, so you could simplify it
    when used with fixed pins.

            device SX48
     DEVICE OSCHS1 ;SX48/52
     IRC_CAL IRC_SLOW
     FREQ 20_000_000
     org $0E
    _IsrTemp ds 1 ;temporary storage
    _IsrBank ds 1 ;to select adc rambank
     org $10
    vpAdc_portIn ds 1 ;adc input port (RA=5)
    vpAdc_pinIn ds 1 ;OR pinmask (only one bit set to 1)
    vpAdc_portOut ds 1 ;adc output port (RA=5)
    vpAdc_pinOut ds 1 ;OR pinmask (only one bit set to 1)
    vpAdc_bits ds 2 ;counter reset value, $1000 for 12bits, $0100 for 8bits (eg. 1<<resolution)
    vpAdc_count ds 2 ;counter
    vpAdc_accum ds 2 ;accumulator
    vpAdc_value ds 2 ;resulting adc value, max resolution 15 bits
     org $0
    vpAdc15_isr
    ;------- dynamic adc in pin read and adc out pin update
            MOV   W,_IsrBank ;select adc rambank
            MOV   FSR,W
            MOV   W,#15  ;select adc in port
            AND   W,vpAdc_portIn
            MOV   FSR,W
            MOV   W,/INDF  ;get inverted port value
            MOV   _IsrTemp,W
            MOV   W,_IsrBank ;select adc rambank
            MOV   FSR,W
            MOV   W,vpAdc_pinIn ;extract inverted adc in bit
            AND   _IsrTemp,W
            SETB  C   ;Carry is inverted adc in bit
            SNB   Z
            CLRB  C
            MOV   W,/vpAdc_pinOut ;AND mask
            MOV   _IsrTemp,W
            MOV   W,#15  ;select adc out port
            AND   W,vpAdc_portOut
            MOV   FSR,W
            MOV   W,INDF  ;get current adc out
            AND   W,_IsrTemp ;clear adc out bit
            NOT   _IsrTemp  ;OR mask
            SNB   C
            OR    W,_IsrTemp ;set adc out bit to inverted adc in bit
            MOV   INDF,W
    ;------- adc value calculation
            MOV   W,_IsrBank ;select adc rambank
            MOV   FSR,W
            SNB   C   ;adc in triggered?
            JMP   m026  ;no
            INC   vpAdc_accum ;yes, inc accumulator
            SNB   Z
            INC   vpAdc_accum+1
    m026
            DEC   vpAdc_count ;dec count
            MOV   W,++vpAdc_count
            SNB   Z
            DEC   vpAdc_count+1
            MOV   W,vpAdc_count ;count == 0?
            OR    W,vpAdc_count+1
            SB    Z
            JMP   m028  ;no
            MOV   W,vpAdc_accum ;yes, done
            MOV   vpAdc_value,W ;value = accumulator
            MOV   W,vpAdc_accum+1
            MOV   vpAdc_value+1,W
            CLR   vpAdc_accum ;clear accumulator
            CLR   vpAdc_accum+1
            MOV   W,vpAdc_bits ;reset counter (count = bits)
            MOV   vpAdc_count,W
            MOV   W,vpAdc_bits+1
            MOV   vpAdc_count+1,W
    m028
            RET
    end
    
    

    regards peter
  • RickBRickB Posts: 395
    edited 2008-12-11 15:46
    Actually, I was refering to absolute accuracy, although resolution and monotonicity alone are useful for a lot of tasks.

    Rick
  • Peter VerkaikPeter Verkaik Posts: 3,956
    edited 2008-12-12 07:34
    I would say the absolute accuracy is +-1LSB
    but with the following consideration:
    Rather than the sx I/O pins have a fixed treshold point (eg. Vdd/2)
    they have a small treshold gap around Vdd/2. A pin input voltage
    fluctuating within that gap may not be detected as an input state change.
    So this puts a limit on the value for N.
    If we name the gap value GV then
    Vdd
    ---- > GV·· to make the input state change happen
    2^N

    This yields 2^N < Vdd/GV
    Unfortunately, I don't know the value for GV

    But there is·a simple test:
    If the calculated value for C,·does not give stable adcValues
    of 2^(N-1) when applying Uin = (Uinmax-Uinmin)/2
    (or remove R3 and R4 and do not apply Uin)
    then the value for C is too large (eg. ripple accross C is smaller than GV).
    Then you must decrease resolution N·which·yields a lower value for C.

    regards peter
    ··
Sign In or Register to comment.