Shop OBEX P1 Docs P2 Docs Learn Events
MCP3208 Analog Chips — Parallax Forums

MCP3208 Analog Chips

danthonydanthony Posts: 45
edited 2009-01-24 14:32 in Propeller 1
Below is an object that is made exactly for my coding needs. I cannot seem to get it to work. I am trying to view the data it produces via the parallax serial debug window. Has anyone ever interfaced this 12 bit analog object?

Can I populate the pin#'s from within this object or must I use another object to do that? I am using pins 13,14 &15 respectively.



''MCP3208_fast.spin
''
''Copyright (c) 2007 Jim Kuhlman
''
'' See end of file for terms of use.
''
''*****************************************************
''* MCP3208 12-bit/8-channel ADC Driver *
''* Works w/indiv channel on chip rather than all 8 *
''* Provides single sample or average of n samples *
''* Provides single mode only, no differential mode *
''* Does not provide DAC output like original code *
''*****************************************************

'' rev 1.0 09-16-07 Fixed bug in start routine for delay value.

CON
#1, _setup, _single, _average, _delay, _div, _adcready
' cmds for assy routine: 1=setup, 2=single adc sample, 3=average of n adc samples, etc.

VAR

long cog, delay

long cmd, chnl, nsamples, data '7 longs (3 longs, 8 words)

PUB start(dpin, cpin, spin, mode) : okay

'' Start driver - starts a cog
'' returns false if no cog available
'' may be called again to change settings
''
'' dpin = pin connected to both DIN and DOUT on MCP3208
'' cpin = pin connected to CLK on MCP3208
'' spin = pin connected to CS on MCP3208
'' mode not used, included for compatability with older program

stop ' stop cog if running from before

cmd := 0 ' tell adc_read to wait for next command
cog := cognew(@adc_read, @cmd) + 1 ' store adc_read and shared mem address
waitcnt(clkfreq/4 + cnt) 'wait 1/4 sec for cog to start

chnl := dpin ' initialize for call to _setup
nsamples := cpin
data[noparse][[/noparse]0] := spin
data := 0

do_cmd(_setup, @chnl) ' initialize assy routine for dpin, cpin and spin pins on MCP3208

if clkfreq => 80_000_000 ' make it work for prop2 also
delay := clkfreq/2_000_000 ' time delay for half cycle of 50KHz sample rate
else ' set for 1MHz clock rate to chip
delay := 40 ' if clkfreq < 80,000,000 then set to 40

do_cmd(_delay, @delay) ' pass time delay parameter to assy routine

return cog


PUB stop

'' Stop driver - frees a cog

if cog
cogstop(cog~ - 1)


PUB in(channel)

'' Read the current sample from an ADC channel (0..7)

chnl := channel

do_cmd(_single, @chnl) ' get single sample from channel

return data.word[noparse][[/noparse]channel]


PUB average(channel, n)

'' Average n samples from an ADC channel (0..7), n <= 16

chnl := channel ' set channel number in xfr array

nsamples := n <# 16 ' limit # of samples to 16 max
nsamples #>= 1 ' must be greater than zero

do_cmd(_average, @chnl) ' get average of n samples from specified channel

return data.word[noparse][[/noparse]channel]

PUB div(divisor, dividend) : quotient

'' Divide 16 bit dividend (in long)
'' by 16 bit divisor (in long)
'' Return 16 bit quotient (in long)

chnl := divisor
nsamples := dividend

do_cmd(_div, @chnl)

quotient := nsamples


PRI do_cmd(adc_command, argptr)

cmd := adc_command << 16 + argptr 'write command and pointer to shared memory loc
repeat while cmd 'wait for command to be cleared, signifying completion


DAT
''
''************************************
''* Assembly language MCP3208 driver *
''************************************

org
'************************************
adc_read rdlong t1,par wz ' wait for command
if_z jmp #adc_read

movd :arg,#arg0
mov t2,t1
mov t3,#4 ' get 4 arguments

:arg rdlong arg0,t2 ' store in arg0, arg1, arg2, arg3
add :arg, hex200 ' update destination addr for next loop
add t2,#4 ' point to next input arg
djnz t3,#:arg

ror t1,#16+2 ' lookup command address
add t1,#jmptable
movs :tabloc,t1
rol t1,#2
shl t1,#3
:tabloc mov t2,0
shr t2,t1
and t2,#$FF
jmp t2 ' jump to command

'************************************
jmptable byte 0 '0 not used
byte setup_ '1 setup adc routine
byte single_ '2 single adc value
byte average_ '3 average adc values
byte delay_ '4 delay for adc clock
byte div_ '5 divide 16 x 16 bits
byte adcready '6 loop for next instruction

'************************************
setup_ mov t1, arg0
call #param 'setup DIN/DOUT pin
mov dmask,t2

mov t1, arg1
call #param 'setup CLK pin
mov cmask,t2

mov t1, arg2
call #param 'setup CS pin
mov smask,t2

jmp #adcready

'************************************
param mov t2,#1 'make pin mask in t2
shl t2,t1
param_ret ret

'************************************
delay_ mov t4, arg0 'set up delay time for chip clock
shr t4, #3 ' divide it by 8 clock cycles/ 2 instr
sub t4, #4 ' adj for 1/2 cycle
mov delayval, t4 ' store in delayval for delay routines
jmp #adcready

'************************************
div_ mov t1, arg0 ' move divisor to t1
mov t2, arg1 ' move dividend to t2

call #divide

mov t3, par ' shared mem ref to t3
add t3, #8 ' point to output
wrlong t2, t3 ' write quotient to nsamples
jmp #adcready

'************************************
single_ mov arg1, #1 'initialize arg1, not supplied with single_ call

'************************************
average_ mov t3, arg1 'set for n sample average (or 1 if single sample)
or dira,cmask 'set CLK line to output
or dira,smask 'set CS line to output

mov command,#$18 'init command to 011000, start/single
add command, arg0 'add in channel number to command bits

mov t2, #0 'initialize data[noparse][[/noparse]channel] accumulator

:bloop mov stream,command 'set up for new sample
or outa,smask ' CS high
or dira,dmask ' make DIN/DOUT output
andn outa,cmask ' CLK MUST be low before CS enabled!!!
andn outa,smask ' CS low
nop ' wait 8 clock cycles for TSUCS
nop

mov bits,#5 'ready 20 bits (cs+1+diff+ch+0+0+data[noparse][[/noparse]12])
:cloop test stream,#$10 wc 'update DIN/DOUT
muxc outa,dmask
andn outa,cmask 'CLK low
call #delay1 ' adjust clock cycle time
rcl stream,#1 ' shift out next bit
or outa,cmask 'CLK high
call #delay2 ' adjust clock cycle time
djnz bits,#:cloop

andn dira,dmask ' make DIN/DOUT input to get data
mov bits,#14 'get the data 2-null plus 12 data
:dloop andn outa,cmask 'CLK low
call #delay1 ' adjust clock cycle time
or outa,cmask 'CLK high
test dmask,ina wc 'sample DIN/DOUT
rcl stream,#1 ' store bit for output
call #delay2 ' adjust clock cycle time
djnz bits, #:dloop 'next data bit

and stream,mask12 'trim sample
add t2, stream 'accumulate sum in t2
djnz t3, #:bloop 'more samples for average?

cmp arg1, #1 wz ' see if n > 1
if_nz mov t1, arg1 ' set up for divide if needed, arg1 = number of samples, n
if_nz call #divide ' divide t2 by t1 or sum/n -> t2

mov t3, arg0 'load channel number, 0..7
shl t3, #1 'mult by 2 for word offset in data[noparse][[/noparse] ]
add t3, #12 'add 12 for 3 long offset (cmd + chnl + nsamples)
mov t1,par 'reset sample pointer
add t1, t3 'add offset, t1 now points to data[noparse][[/noparse]channel]
wrword t2,t1 'write sample to data[noparse][[/noparse]channel]
'fall through to adcready and exit
'************************************
adcready wrlong zero,par 'zero command to tell caller we're done
jmp #adc_read 'back to wait for another command

'************************************
delay1 mov t4, delayval ' load delayval, clock cycles
nop ' adj to half cycle
nop
:tloop1 nop
djnz t4, #:tloop1 ' delay 1/2 uSec = 1/2 MCP3208 clk cycle
delay1_ret ret

'************************************
delay2 mov t4, delayval ' load delayval, clock cycles
:tloop2 nop
djnz t4, #:tloop2 ' delay 1/2 uSec = 1/2 MCP3208 clk cycle
delay2_ret ret

'************************************
' Divide
' in: t1 = 16-bit divisor in long
' t2 = 16-bit dividend in long

' out: t2 = 16-bit quotient (in long), truncated

' temp: t3 = loop counter

divide shl t1,#15 'get divisor into t1[noparse][[/noparse]30..15]
mov t3,#16 'loop counter for 16 bits

:loop cmpsub t2,t1 wc 'if t1 =< t2 subtract it, quotient bit -> c bit
rcl t2,#1 'rotate c into quotient and shift dividend
djnz t3,#:loop 'loop until done

and t2, hexFFFF 'mask quotient, drop the remainder in [noparse][[/noparse]31..16]
divide_ret ret

'************************************
' Initialized data
'
hex200 long $200 'constants
hexFFFF long $FFFF
mask12 long $FFF
zero long 0

'************************************
' Uninitialized data
'
arg0 res 1 ' arguments passed from high-level
arg1 res 1
arg2 res 1
arg3 res 1

dmask res 1 ' MCP3208 pin masks
cmask res 1
smask res 1

t1 res 1 ' temps
t2 res 1
t3 res 1
t4 res 1

dx res 1 ' for atn routine
dy res 1

command res 1 ' for 3208 routine
stream res 1
bits res 1
delayval res 1

{ Terms of use:

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

}
«13

Comments

  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2008-12-24 13:17
    I have used MCP3208s in a tablet counter project where I scanned multiple channels and filtered and buffered the samples. There is nothing much to say as the chips are very easy to work with. Perhaps if you could repost your code using the CODE command button to preserve the style and indentation then someone might be able to comment further. Here is a bit of the code I adapted below.

    *Peter*



    DAT
    ''
    ''************************************
    ''* Assembly language MCP3208 driver *
    ''************************************
    
                            org
    '************************************
    adc_scan
                            mov     delayval,#1
                            or      dira,_sclk              'set CLK line to output
                            or      dira,_scsl              'set CS  line to output
    :startscan
                            mov     t3,par
                            sub     t3,#4                   'point to led power
                            rdlong  ledmode,t3
                            mov     chan,#6                 'repeat channel from 5 to 0
                            mov     adcbufP,par
                            add     adcbufP,#5*4            ' point to end of adcbuf
                            mov     ledptr,led5             'point to channel 5 led
    :mloop
                            mov     stream,#$18             'init command to 011000, start/single
                            add     stream, chan            'add in channel number to command bits
                            sub     stream,#1               'index offset
                            or      outa,_scsl              ' CS high
                            or      dira,_sdio              ' make DIN/DOUT output
                            andn    outa,_sclk              ' CLK MUST be low before CS enabled!!!
                            andn    outa,_scsl              ' CS low
                            nop                             ' wait 8 clock cycles for TSUCS              
                            nop
                            mov     bits,#5                 'ready 20 bits (cs+1+diff+ch+0+0+data[noparse][[/noparse]12])
    :cloop                  test    stream,#$10     wc      'update DIN/DOUT
                            muxc    outa,_sdio
                            andn    outa,_sclk              'CLK low
                            call    #delay1                 ' adjust clock cycle time
                            rcl     stream,#1               ' shift out next bit
                            or      outa,_sclk              'CLK high                        
                            call    #delay2                 ' adjust clock cycle time                        
                            djnz    bits,#:cloop
    
                            andn    dira,_sdio              ' make DIN/DOUT input to get data
                            mov     bits,#14                'get the data 2-null plus 12 data
    :dloop                  andn    outa,_sclk              'CLK low                        
                            call    #delay1                 ' adjust clock cycle time                        
                            or      outa,_sclk              'CLK high
                            test    _sdio,ina       wc      'sample DIN/DOUT
                            rcl     stream,#1               ' store bit for output         
                            call    #delay2                 ' adjust clock cycle time                        
                            djnz    bits, #:dloop           'next data bit
                            test    ledmode,#_multiplexed      wz
                            andn    outa,_sclk              'CLK low
                            and     stream,mask12           'trim sample to 12 bits
                            wrlong  stream,adcbufP          ' write sample to adcbuf
                            or      outa,_scsl              ' CS high
    ' end of sample
                            shl     ledptr,#1
                            sub     adcbufP,#4               ' work down from 5 to 0
                            djnz    chan,#:mloop
                            jmp     #:startscan
    
    '************************************
    delay1                  mov     dlycnt, delayval        ' load delayval, clock cycles
                            nop                             ' adj to half cycle
                            nop
    :tloop1                 nop
                            djnz    dlycnt, #:tloop1        ' delay 1/2 uSec = 1/2 MCP3208 clk cycle
    delay1_ret              ret
    
    '************************************
    delay2                  mov     dlycnt, delayval        ' load delayval, clock cycles
    :tloop2                 nop                             
                            djnz    dlycnt, #:tloop2        ' delay 1/2 uSec = 1/2 MCP3208 clk cycle
    delay2_ret              ret
    
    
    '************************************
    param                   mov     t2,#1                   'make pin mask in t2
                            shl     t2,t1
    param_ret               ret
                            
    
    '************************************
    ' Initialized data
    '
    mask12                  long    $FFF
    _scsl                   long    %001000000000000
    _sclk                   long    %010000000000000
    _sdio                   long    %100000000000000
    _leds                   long    %111111_00000000_00000000
    
    led5                    long    %000001_00000000_00000000
    
    '************************************
    ' Uninitialized data
    '
    
    t1                      res     1                       ' temps
    t2                      res     1
    t3                      res     1
    dlycnt                  res     1
    
    ledmode                 res     1
    chan                    res     1
    adcbufP                 res     1
    auxbufP                 res     1
    ledptr                  res     1
    
    dx                      res     1                       ' for atn routine
    dy                      res     1
    
    stream                  res     1
    bits                    res     1
    delayval                res     1
    
    
    

  • danthonydanthony Posts: 45
    edited 2008-12-24 13:29
    Hi Peter, Thanks

    I worked with the MCP3208 chips in a stamp form using shift in and shift out. You are right they work great. I am trying to transfer the same project into a propeller to increase scanning speed of the analog channels. What I posted was right from the object download portion of the site and may not even be best for what I need.

    I like your commented code above it makes sense.

    One question: is this where the final data is written to? wrlong stream,adcbufP ' write sample to adcbuf
  • shanghai_foolshanghai_fool Posts: 149
    edited 2008-12-24 14:41
    Hi danthony,
    I could not tell by your post if you were trying to use the object as stand-alone. It is a driver that must be call from another main program. This is a demo for the same object you are using. You can use any pins as long as they are in the correct order. I was using this to determine some timing condition.
    I use PC_terminal as I/O but you can use whatever you are confortable with.
    {{ IMU Demo }}
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
      #0, WHITE,CYAN,RED,PINK,BLUE,GREEN,YELLOW,GRAY        'Standard colors
      us =  80
      ms = 80_000
    OBJ
      term   :       "PC_Interface"
      ad    :       "MCP3208_fast"
      
    VAR
      long CS 
      long DIO
      long CLK
      long    txa, txb ,txc, txd ,txe 
    PUB Main | b , r ,ra 
    
      'start the interface
      term.start(31,30)
      term.setcol(YELLOW)
      term.str(string("MPC3208 Demo..."))
      ad.Start(26, 27, 25, %01111111)                       
      waitcnt((80_000_000*1) + cnt)
      term.setcol(CYAN)
      term.out(0)
      repeat
        txe := cnt
        b :=0 
        repeat while b <8
          if b < 3
            txa := cnt
            r := ad.in(b)-2048
            txb := cnt
            txc :=cnt
            ra := ad.average(b,8)-2048
            txd :=cnt
          else
            r := ad.in(b)    '  87us
            ra := ad.average(b,8)   '  237us
          
          if b == 4
            r := r - y0
            ra := ra - y0
          if b ==  5
            r := r -  x0
            ra := ra - x0
          setpos (0,b++)
          term.dec(b-1)
          term.str(string(" = "))
          term.dec (r)
          term.str(string("     "))
          setpos (10,b-1)
          term.str(string(" Avg = "))
          term.dec (ra)
          term.str(string("     ",13))
        term.dec ((txb - txa) / us)
        term.str(string(" us  1 read ",13))
        term.dec ((txd - txc) /us)
        term.str(string(" us  avg 8 reads ",13))
        term.dec ((cnt - txe) /us)
        term.str(string(" us  whole routine ",13))
        waitcnt(txe + 8_000_000)
    PRI setpos(px, py)
      term.out(10)
      term.out(px)
      term.out(11)
      term.out(py)
    
  • mynet43mynet43 Posts: 644
    edited 2008-12-24 20:24
    Hi danthony,

    I'm the author of MCP3208_fast. I've used it many times on quite a few robotics and other projects.

    As far as I know, it works fine. I've done extensive value and timing tests on it, including counting the clock cycles needed for each conversion.

    If you have any questions, or need sample code, please let me know and I'll be happy to help.

    Merry Christmas,

    Jim
  • danthonydanthony Posts: 45
    edited 2008-12-24 21:58
    Hi Jim,

    I am sure it works well, I just need to figure out how to call it 24 times. (3 chips to scan) I am new to propeller and worked through the labs, still nothing is jumping out at me on how to get started. This application was easy in a stamp but jumping to propeller was harder than I thought.

    My Christmas wish would be to look at the (24) MCP3208 values graphically with view port. Should I be looking at data[noparse][[/noparse]0].

    Thanks,
    Don
  • mynet43mynet43 Posts: 644
    edited 2008-12-25 01:00
    Hi Don,

    I sent you a private message with sample test code and comments on how you might use it to read three chips.

    If anyone else wants it, let me know.

    Jim
  • danthonydanthony Posts: 45
    edited 2008-12-25 21:41
    Thanks Jim. I am on it now.

    Merry Christmas
  • danthonydanthony Posts: 45
    edited 2008-12-25 23:21
    Jim,
    I spoke to soon. The chip is resonding, but not on all the channels. I see in the comments of MCP3208_Fast it says it only samples 1 channel.

    Here is my main code that calls it. When I drive channel 7 with 5V it shows up on channels 4 to 7. None of the other channels seem to work
    {{ IMU Demo }}
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
      #0, WHITE,CYAN,RED,PINK,BLUE,GREEN,YELLOW,GRAY        'Standard colors
      us =  80
      ms = 80_000
    OBJ
      term   :    "PC_Interface"
      ad    :     "MCP3208_fast"
      
    VAR
      long CS 
      long DIO
      long CLK
      long    txa, txb ,txc, txd ,txe, y0, x0 
      
    PUB Main | b , r ,ra 
    
      'start the interface
      term.start(31,30)
      term.setcol(YELLOW)
      term.str(string("MPC3208 Demo..."))
      ad.Start(26, 27, 25, %11111111)                       
      waitcnt((80_000_000*1) + cnt)
      term.setcol(CYAN)
      term.out(0)
      repeat
        txe := cnt
        b :=0 
        repeat while b <8
        
            r := ad.in(b)    '  87us
            
          setpos (0,b++)
          term.dec(b-1)
          term.str(string(" = "))
          term.dec (r)
          term.str(string("     "))
          setpos (10,b-1)
          term.str(string(" Avg = "))
          term.dec (ra)
          term.str(string("     ",13))
        term.dec ((txb - txa) / us)
        term.str(string(" us  1 read ",13))
        term.dec ((txd - txc) /us)
        term.str(string(" us  avg 8 reads ",13))
        term.dec ((cnt - txe) /us)
        term.str(string(" us  whole routine ",13))
        waitcnt(txe + 8_000_000)
    PRI setpos(px, py)
      term.out(10)
      term.out(px)
      term.out(11)
      term.out(py)
    
    
    


    Let me know what you think
    Thanks,
    Don
  • mynet43mynet43 Posts: 644
    edited 2008-12-26 00:02
    Hi Don,

    The code you showed is a little confusing. It looks like you started with shanghai_fool's code and cut out some of it.

    1. For the initialization call, you're using the same pin numbers he used. Is this correct for your setup?
    Remember, the order should be: DIN/DOUT, CLK, CS.

    2. You're not computing several of the values you're displaying. E.g. ra, txa, txb, txc, etc.

    If I get a chance, I'll take the code you have, modify it to use my output and let you know.

    Hang in there, you're off to a good start.

    Jim
  • danthonydanthony Posts: 45
    edited 2008-12-26 00:24
    Yeah I tried his pins when the results were coming back like they were. I am going to stay in the right order of DIN/DOUT, CLK, CS

    Thanks for encouragement
  • mynet43mynet43 Posts: 644
    edited 2008-12-26 01:26
    Hi Don,

    I took your code, modified it and tried it.

    The code below shows what I tested. I commented out my original test routine and substituted yours using my display routine. Yours is the last routine in the listing.

    The way I tested the code was with two voltage reference wires, one at 3.2V and the other at 5.0V. I started out with channels 0 and 1, then moved the wires to 2 and 3, 4 and 5 and finally 6 and 7.

    I took screen shots of the output from each of these tests and attached them.

    Here's the code. Keep at it, you're almost there.

    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    ' VGA and Keyboard info for vga.start
      BasePin  = 16                 ' base pin for video output (0, 8, 16, 24) - 16 on proto board
      
      kbd_dpin = 26                 ' keyboard dpin number - pin 26 on proto board
      kbd_cpin = 27                 ' keyboard cpin number - pin 27 on proto board
      kbd_lock = %0_001_000         ' keyboard locks: disable scroll lock key, NumLock, CapsLock, ScrollLock all off
      kbd_auto = %01_01000          ' keyboard auto repeat: 0.5 sec delay, 15 chars/sec repeat (?)
      kbdflag  = 0                  ' set this to 1 if a keyboard is connected/needed
    
    ' MCP3208 adc stuff
      dpin     = 25                 ' ADC data pin on Propeller
      cpin     = 26                 ' ADC clock pin on Propeller
      spin     = 27                 ' ADC Chip Select pin on Propeller
      mode     = $FF                ' ADC mode, set to use all 8 ADC's, not used in fast routine
    
    ' Don's conversion factor to get us from count
      us =  80
      
    VAR
      long CS 
      long DIO
      long CLK
      long    txa, txb ,txc, txd ,txe, y0, x0 
    
    DAT
    ' Misc Values
      AOK         byte  "All COGs Started, ready to rock!", 0
      OOPS        byte  "Oops, cog start failed...",0
    
    ' Values for Test_adc routine
      ADCdisp     byte  "ADC", 0
      VALdisp     byte  "val:     ", 0
      AVGdisp     byte  "avg of 10 samples:     ", 0
      CLKcycl     byte  "clk cycles:       ", 0
    
    OBJ
    '' Include VGA/Keyboard, MCP3208 ADC and Floating Point Objects
      adc : "MCP3208_fast"
      fp  : "Float32Full"
      fps : "FloatString"
      vga : "VGA_1024"
    
    PUB Init | char
    
      vga.start(BasePin, kbdflag, kbd_dpin, kbd_cpin, kbd_lock, kbd_auto)
      '   start the VGA and Keyboard routines
    
      '' start ADC chip MCP3208
        if adc.start(dpin, cpin, spin, mode)'mode not used by fast program, here for compatibility
          waitcnt(clkfreq * 1 + cnt)        'wait 1 second for cog to start
        else
          vga.cursloc(50,18)
          vga.str(@OOPS)
    
      fp.start                              ' start the floating point routine
      
      vga.cursloc(45,20)                    ' position cursor
      vga.str(@AOK)                         ' AOK message
    
    '' run Test_adc if requested
        Test_adc
    
      repeat while TRUE                     ' loop forever to keep COGs running
    
    {
    PRI Test_adc | chan, val, avg, cntsave, cntdiff, avgsave, avgdiff
      repeat while TRUE
        waitcnt(clkfreq/2 + cnt)      ' wait between each read of adc   
        repeat chan from 0 to 7 step 1       ' select the 8 adc channels
          cntsave := cnt
          val := adc.in(chan)            ' get single adc value
          cntdiff := cnt - cntsave
    
          avgsave := cnt
          avg := adc.average(chan, 10) ' get average adc value (10 samples)
          avgdiff := cnt - avgsave
    
          vga.cursloc(20, 24 + 2 * chan)  ' position cursor      
          vga.str(@ADCdisp)
          vga.dec(chan)
    
          vga.cursloc(28, 24 + 2 * chan)  ' position cursor
          vga.str(@VALdisp)
          vga.cursloc(33, 24 + 2 * chan)  ' position cursor
          vga.dec(val)
    
          vga.cursloc(40, 24 + 2 * chan)
          vga.str(@CLKcycl)
          vga.cursloc(52, 24 + 2 * chan)
          vga.dec(cntdiff)
       
          vga.cursloc(65, 24 + 2 * chan)  ' position cursor
          vga.str(@AVGdisp)
          vga.cursloc(84, 24 + 2 * chan)  ' position cursor
          vga.dec(avg)
              
    
          vga.cursloc(91, 24 + 2 * chan)
          vga.str(@CLKcycl)
          vga.cursloc(103, 24 + 2 * chan)
          vga.dec(avgdiff)
    }
    
    PUB Test_adc | b , r ,ra 
    
      vga.cursloc(20, 44)  ' position cursor      
      vga.str(string("MPC3208 Demo..."))
      
    '  adc.Start(26, 27, 25, %11111111)   ' ADC.START called in main program with my pin #'s...                    
    '  waitcnt((80_000_000*1) + cnt)
    
      repeat
        txe := cnt
        b :=0 
        repeat while b <8
        
          r := adc.in(b)    '  87us
            
          vga.cursloc(20, 24 + 2 * b++)  ' position cursor      
          vga.dec(b-1)
          vga.str(string(" = "))
          vga.dec (r)
          vga.str(string("     "))
    
    
        vga.cursloc(20, 46)  ' position cursor      
        vga.dec ((cnt - txe) /us)
        vga.str(string(" us  whole routine ",13))
        waitcnt(txe + 8_000_000)         ' wait approx 100 ms
    
    
    800 x 600 - 63K
    800 x 600 - 82K
    800 x 600 - 63K
    800 x 600 - 71K
  • mynet43mynet43 Posts: 644
    edited 2008-12-26 15:33
    Hi Don,

    You asked me for a copy of the VGA_1024 display driver, so you can test the MCP3208.

    I've attached it here. It's a driver I wrote that has the features I wanted at the time like cursor loc, etc.

    By the way, I'm looking at the changes needed to read three MCP3208's from the same driver, with only one initialization call.
    I think this should be what you're looking for. It will still use only one COG.

    Let me know how it's going.

    Jim
  • danthonydanthony Posts: 45
    edited 2008-12-26 15:59
    Hey Jim,

    Attached is pic you asked for...

    Let me know your thoughts

    Thanks,
    Don
    480 x 640 - 204K
  • mynet43mynet43 Posts: 644
    edited 2008-12-26 16:48
    Looks good. Watching you with those 24 IR sensors while you lift those weights ...

    My only thought is the possible problem with the response time of the sensors. Try it and see...

    I've just about finished the multi-MCP3208 driver.

    I'll test it and post it for you to try.

    Jim
  • mynet43mynet43 Posts: 644
    edited 2008-12-26 18:38
    OK, the first cut at the new MCP3208 driver is ready to try.

    It supports up to three MCP3208 chips, based on the same algorithms as MCP3208_fast.

    The calling sequences for the multi driver have changed, they are now:

        adc.start(dpin1, cpin1, spin1, dpin2, cpin2, spin2, dpin3, cpin3, spin3)
    
    '   dpin is the DIN/DOUT signal, cpin is the CLK pin and spin is the CS pin of chips 1, 2, 3.
    
    
        val := adc.in(chan, chipnum)  ' retrieve single adc sample: chan = {0,1,...7} = adc channel, and chipnum = {1,2,3} = chip number
    
        avg := adc.average(chan, chipnum, nsamples) ' get average adc value, nsamples =   # samples per average {1,2,...16}
    
    
    



    I've tested it with the attached program, DonTest_multi.
    The pin numbers and chip number are in a CON segment, so they can be changed without changing the code.
    I purposely left the pin numbers wrong for chip #3 to make sure it was accessing the chip I asked for.
    Feel free to change them to match your pin assignments.

    I'd appreciate anyone testing it to see if you can spot any bugs.
    You can use either Don's test routine or my original routine, which also tests the averaging.
    If you want to run it as-is, you can download my VGA_1024 driver from a few posts above.

    If it seems to work for everyone, I'll post it to the object exchange.

    Thanks for the help.

    Jim
  • danthonydanthony Posts: 45
    edited 2008-12-27 01:40
    Hi Jim,
    I have it up and running. Looks great! All channels of each chip check out except for the last one on each chip[noparse][[/noparse]7]. I think it is the way I am linking them to view port and need to take another look at that part again.

    Thanks again,
    Don
  • danthonydanthony Posts: 45
    edited 2008-12-27 02:17
    Jim,
    Why is 80us the divisor in the program timer? Is that what it takes for one channel?

    Don
  • mynet43mynet43 Posts: 644
    edited 2008-12-27 02:28
    The 80 us is from shanghai_fool's original test code. It's used only to convert from cnt cycles to microseconds.

    The count in cycles divided by cycles/us gives us...

    If you divide it by 80,000, you get ms, but you'd lose the fraction, since it's integer math.

    Jim
  • danthonydanthony Posts: 45
    edited 2008-12-29 15:41
    Hi Jim,
    Attached is the sketch of the MCP3208 update rate question I had for you.

    Thanks,
    Don
  • mynet43mynet43 Posts: 644
    edited 2008-12-29 17:31
    Hi Don,

    You asked if more reads would give more precision in calculating the velocity of the barbells passing by the sensors.

    You have 24 analog IR sensors mounted vertically spaced at about 2" apart.
    The new driver MCP3208_multi is designed to handle 24 ADC inputs.

    I think you're on the right track. Here are a few thoughts. Based on the figure you provided.

    1. You need to think of the time it takes for the weight to pass through a beam instead of through a cone.
    You can measure the time it takes to cross the beam of a particular sensor by recording the time it first sees it and
    also the time it leaves the beam. I'm not sure what you would use this for. See 3. below.

    2. I believe these IR sensors take about 15 ms between readings.
    (Someone correct me if I'm wrong, GP2Y0A700K.)
    If that's the case, then you won't gain much benefit from sampling the distance more than say
    once every ms. The MCP3208_multi code is capable of much faster sampling.
    Assuming it's being lifted around 3 ft/sec, then in 1 ms it moves about .003 ft.

    3. The velocity of the barbell would be calculated as the time between first seeing it at one sensor
    minus the time first seeing it at the previous sensor, all divided by the distance between the two sensors.
    You can average this several ways to get a better value: avarage each difference, moving average, or
    last sensor minus first sensor, which will all give better values than each individual reading. You might also want
    to record each individual velocity reading to plot a velocity profile as the weight is lifted.

    I hope that helps. Let me know if you have more questions.

    I expect to see pictures when you get it working[noparse]:)[/noparse]

    Jim
  • danthonydanthony Posts: 45
    edited 2008-12-29 17:53
    Thanks, that is sound logic.

    I am almost done setting it up for testing..........
  • danthonydanthony Posts: 45
    edited 2008-12-29 21:56
    Hey Jim!

    Attached is data plotted from mcp1 channels 1 and 2 at 2000kbits/sec (for all 24)



    Don
    1280 x 800 - 194K
  • mynet43mynet43 Posts: 644
    edited 2008-12-30 00:35
    Hi Don,

    I read your PM. It seems like you're not sure what you're getting. You asked for my thoughts on the pic.

    1. I'm not sure how to interpret the picture. You said it's for all 24 sensors.
    2. How do I tell which sensor is where on the charts?
    3. Is there anything in range that's being sensed during this scan?

    4. You said this was at 2000 kbps, the pic says 1000 kbps. My apologies, but I've never used ViewScope before.
    Not sure what this means... Do you have a regular scope?

    5. I'd like to see a scan from one sensor while the object is passing thru the beam.

    6. I'm not sure what the response curve of the sensor is. If it's a curve that peaks every 15 ms, then you
    may need to have the program check for local maximums. Why don't you capture an array of values while
    something is passing thru the beam and plot the values over time.

    7. Are you sure you're in the active range of the beam? I think the sensor may have a dead zone.

    8. How fast are you sampling the adc values? You can check this with the following code:


          cntsave := cnt
          val := adc.in(chan, chipnum)            ' get single adc value
    ' ... other code to sample 24 sensors and process in the loop
          cntdiff := cnt - cntsave
    
    



    I takes about 8,000 clock cycles for each adc sample call from a spin routine.
    So if you're sampling 24 sensors, it will take at least 24 X 8000 cycles or 2.4 ms.
    When I told you it could sample faster than once per ms, I was referring to 1 sensor,
    which can sample about 10 times per ms. So you're whole loop probably takes
    at least 5 ms to complete. I think this should still be fast enough to do what
    you want, but you have to take this into consideration. Use the code above to
    measure the actual execution time, then let me know.

    Whew! That's enough for now. Let me know what you find out.

    More pics[noparse]:)[/noparse]

    Jim
  • mynet43mynet43 Posts: 644
    edited 2008-12-30 03:10
    Don,

    I thought of a way you can speed up the processing.

    Instead of sampling all 24 sensors in a continuous loop, sample only the lower sensor until it senses something in its beam.

    Then move to the second sensor and track only it, until it senses something.

    Do the same thing for each sensor, one at a time.

    This should decrease the cpu time required to do the adc sensing by a factor of about 24.

    Be careful if you're using display or math routines in your sense loop, as these use up a lot of processing time.

    Better to save intermediate values and do all the math and display after all the sensors have been processed.

    Just some more thoughts.

    Jim
  • danthonydanthony Posts: 45
    edited 2008-12-30 13:28
    Jim,

    This is weird, technically speaking by implementing your routine we took it from 18 scans per second (old stamp) to 416 scans per second. Now the other factor that changed since the last generation was the number of sensors and their spacing.

    What has me puzzled the most is that it doesn't pick up the object like it used to. I am using the oscilloscope to validate it and looking for spikes. The last pic you saw had a spike on the blue channel. That was the object whirling past. Today I will strip it down to fewer sensors and retry. We are processing analog now faster than ever before and it seems to have lost performance characteristics.

    Stay Tuned....
  • danthonydanthony Posts: 45
    edited 2008-12-30 14:28
    Jim--here are some answers to your last post...

    1. Attachments are for only (1) sensor, the bottom one 12" from the floor.
    2/3. There are no obstructions
    4. now Connected at 2000kbytes/sec.· sorry
    5. see 2 reps slow attachment, you should see 4 blips on scope, each rep has an up and down but even going slow it only picked up half the activity


    Ex1 shows a natural data stream coming in with no BB activity

    EX2 shows what happens to signal 1 when I cover up the other sensors.

    Don
    1280 x 800 - 177K
    1280 x 800 - 172K
    1280 x 800 - 180K
  • mynet43mynet43 Posts: 644
    edited 2008-12-30 16:27
    Good morning,

    Regarding your answers.

    1. I was asking about the pic posted above, which referred to 24 sensors. Are you now talking only about the new pics?

    2/3. You said there are no obstructions. Is this referring to the original picture or to one/all of the ones here?
    Do you mean it's has nothing to sense in the path of the sensor beam?

    4. Sorry, I've never tried this 'scope' and I still don't understand how it works[noparse]:([/noparse]
    Does 2000 kbps mean that you are sampling the signal at 2 MHz or that it is communicating with the PC at 2 megabits/sec?
    It looks like you're showing a 10 sec scan. At a 2 MHz sampling rate, you would have 20,000,000 samples in this time.
    It doesn't look like you're using a trigger on the scope to sense an event, like voltage going above 2 V.
    If I were looking at this signal, I would set a trigger at 2V and sample about 50 ms of data to see the detail.
    You need to be able to see a much smaller time interval in great detail, based on a trigger.

    Interesting that you said you now get 416 scans per second. That's exactly what I predicted, 2.4 ms for 24 adc samples from spin code.

    5. OK, here's what you need to do.

    a. Get a voltmeter.
    b. Measure the voltage coming out of the lower sensor with no target in its path.
    c. Put the barbell on a bucket or something, so it's sitting directly in the beam of the lower sensor.
    d. With the voltmeter, measure the voltage from the sensor. It should match the predicted value based on the range.
    e. Change the program to only sense the lower sensor and display the adc count.
    Assuming you're using a 5 V reference voltage for the adc, the (count/4096) * 5 should equal the measured voltage.
    f. If anything doesn't doesn't look right when you're doing this, track it down until it does[noparse]:)[/noparse]

    You might also try it at a different distance from the sensor.

    I'll try to look up the sensor specs to see if I can figure out anything else.

    Keep me posted, I'm sure you can get this working.

    Jim
  • mynet43mynet43 Posts: 644
    edited 2008-12-30 16:58
    OK, I looked up the sensor you're using, the Sharp GP2Y0A700K.

    Take a look at the attached picture.

    It says the sensor works with a distance range of 1 meter (30.3") to 5.5 meters (215.5").

    If you're using it at a range less than 1 meter, that could be your problem.

    Let me know if this helps.
    1507 x 911 - 371K
  • danthonydanthony Posts: 45
    edited 2008-12-30 18:27
    Yeah, they don't work up close. We are mounting them inside power racks at about 30" from the activity.

    I am working on your last posted punch list items. I did the voltmeter activity before I bought the lot of them last year and they did work well.

    You might be onto something with the scope being set up wrong. I was also thinking of somehow latching a bit inside the propeller and counting the blips so to speak. There is no need to use the scope then. I assumed it would show us everything.

    Stay tuned...
  • mynet43mynet43 Posts: 644
    edited 2008-12-30 19:38
    30 inches is still less than a meter[noparse]:)[/noparse]

    I quoted the spec wrong, I typed 30.3", when it should have been 39.3"...

    How about 45-50 inches, just to be sure... At least for these tests.
Sign In or Register to comment.