MCP3208 Analog Chips
danthony
Posts: 45
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.
}
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.
}
Comments
*Peter*
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
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.
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
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
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
Merry Christmas
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
Let me know what you think
Thanks,
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
Thanks for encouragement
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.
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
Attached is pic you asked for...
Let me know your thoughts
Thanks,
Don
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
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:
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
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
Why is 80us the divisor in the program timer? Is that what it takes for one channel?
Don
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
Attached is the sketch of the MCP3208 update rate question I had for you.
Thanks,
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
I am almost done setting it up for testing..........
Attached is data plotted from mcp1 channels 1 and 2 at 2000kbits/sec (for all 24)
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:
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
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
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....
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
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
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.
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...
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.