What am I doing wrong? (mcp3208)
turbosupra
Posts: 1,088
I am trying to interface the mcp3208 ADC with my propeller. I have taken a picture of the setup, and I know it is hard to make sense of a 3d setup on a 2d picture, so I will describe it as well. I am using object 488 in the object exchange (Brandon Nimons), http://obex.parallax.com/objects/488/ .
As you can see in the picture (see picture below), the pressure sensor I am using as my analog voltage item is outputting 0.41vDC and that output is going to adc pin 1 of the 3208. Adc pins 2-8 are empty. Adc pin 9 goes to ground, Adc pin 10 goes to pin 14 in my prop (which is configured as the CS pin my object), Adc pins 11/12 are tied together and go to pin 13 on my prop (which is configured as the data in/out pin my object), Adc pin 13 goes to pin 15 on my prop (which is configured as the CLK pin my object). Adc pin 14 is grounded, Adc pin 15/16 go to 5v supply voltage.
ADC pin 1 = analog voltage from sensor
ADC pins 2-8 = not connected
ADC pin 9 = grounded
ADC pin 10 = prop pin 14
ADC pin 11/12 = prop pin 13
ADC pin 13 = prop pin 15
ADC pin 14 = grounded
ADC pin 15/16 = supply +5vDC
I hooked up my prop scope and I'm seeing voltage spikes (see picture below), but the output to the terminal is empty (see picture below). Since I'm getting > 2v digital signals to pin 13 on my prop, can anyone tell me what I'm doing wrong here? It appears to be the way I'm using the driver, and since this is my first time using it, it is something I am overlooking or doing wrong.
Here is the code I'm using
As you can see in the picture (see picture below), the pressure sensor I am using as my analog voltage item is outputting 0.41vDC and that output is going to adc pin 1 of the 3208. Adc pins 2-8 are empty. Adc pin 9 goes to ground, Adc pin 10 goes to pin 14 in my prop (which is configured as the CS pin my object), Adc pins 11/12 are tied together and go to pin 13 on my prop (which is configured as the data in/out pin my object), Adc pin 13 goes to pin 15 on my prop (which is configured as the CLK pin my object). Adc pin 14 is grounded, Adc pin 15/16 go to 5v supply voltage.
ADC pin 1 = analog voltage from sensor
ADC pins 2-8 = not connected
ADC pin 9 = grounded
ADC pin 10 = prop pin 14
ADC pin 11/12 = prop pin 13
ADC pin 13 = prop pin 15
ADC pin 14 = grounded
ADC pin 15/16 = supply +5vDC
I hooked up my prop scope and I'm seeing voltage spikes (see picture below), but the output to the terminal is empty (see picture below). Since I'm getting > 2v digital signals to pin 13 on my prop, can anyone tell me what I'm doing wrong here? It appears to be the way I'm using the driver, and since this is my first time using it, it is something I am overlooking or doing wrong.
Here is the code I'm using
''**************************************************************** ''* Read documentation at top of ADC_INPUT_DRIVER for complete * ''* information on copyright and usage. * ''* Each method also has it's own explaination on functionality. * ''**************************************************************** CON { ==[ CLOCK SET ]== } _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 ' 5MHz Crystal {Vclk_p = 12 ' clock pin Vn_p = 13 ' data in Vo_p = 14 ' data out Vcs_p = 15 ' CS pin } Vclk_p = 15 ' clock pin Vn_p = 13 ' data in Vo_p = 13 ' data out Vcs_p = 14 ' CS pin OBJ DEBUG : "FullDuplexSerial" ADC : "ADC_INPUT_DRIVER" VAR '===[ ONLY USED FOR start_pointed ]=== LONG chanstate[8] LONG chanval[8] LONG chanmax[8] LONG chanmin[8] PUB Main '' Select which type of driver start and value accessing you want Standard_example ' Uses long blocks already in driver's object 'Pointed_example ' Requires the use of the above long blocks PUB Standard_example | i '' Start driver in normal mode and run some commands DEBUG.start(31, 30, 0, 115_200) waitcnt(clkfreq + cnt) DEBUG.tx($D) ADC.start(Vo_p, Vn_p, Vclk_p, Vcs_p, 8, 8, 12, 1, false) ' startup the ADC driver (Vo_p and Vn_p can be the same IO pin), 8 channel ADC, ' scan all 8 channels, 12-bit ADC, mode 1 (single-ended), and using slow communication ADC.setthreshold(120, 200) ' not necisary if already setup in the ADC.start settings (but ' these values can be altered at any time with this method), it ' affects all of the chennels on the ADC DEBUG.str(string("Enable Standby")) ADC.standby_enable(6000) ' enable standby mode (put ADC and driver cog into wait mode to save power ' it checks for the standby_disable every 6000 cycles (higher number could ' save more power, but increase possible wait time during standby_disable waitcnt(clkfreq * 2 + cnt) DEBUG.str(string(" -- Disable Standby")) ADC.standby_disable ' disable standby (any command will pull the driver out of standby, but ' this one is specifically designed to wait for a complete exit and take ' the least amount of time doing it DEBUG.tx($D) i~ REPEAT IF (i // 10 == 9) DEBUG.str(string("Wait Hi... ")) DEBUG.dec(ADC.waithigh(2, 2500, 0)) ' wait a maximum of 2.5 seconds or until channel 2's value is less than the ' above threshold, it then displays the current value of the channel ' (incase of watchdog timeout) a watchdog value of 0 would disable it, thus ' it will wait indefinately DEBUG.tx($D) DEBUG.str(string("Wait Lo... ")) DEBUG.dec(ADC.waitlow(2, 4000, 1)) ' wait a maximum of 4 seconds or until channel 2's value is below or equal ' to the above threshold, it then displays the current value of the channel ' (incase of watchdog timeout), If current state is low then there is no ' wait. DEBUG.tx($D) DEBUG.str(string("Reset Max/Min ")) ADC.resetmaxminall ' reset all channel's maximum and minimum values to their defaults DEBUG.tx($D) DEBUG.str(string("Frequency: ")) DEBUG.dec(ADC.getfreq(2, 1000, 3, 5, false)) ' attempt to determine a frequency on channel 2, but do not excede 1 second ' get 8 sampless of the frequency before averaging the result, do not count ' frequency clocks unless the channel is held high for at least 5 cycles DEBUG.str(string("Hz done in ")) DEBUG.dec(ADC.getsamples) ' display number of ADC samples it took to determine the [above] frequency DEBUG.str(string(" samples")) DEBUG.tx($D) DEBUG.str(string("Average: ")) DEBUG.dec(ADC.average_time(2, 500)) ' gather the value of channel 2 for 500ms and calculate the average DEBUG.str(string(" average tested with ")) DEBUG.dec(ADC.getsamples) ' display number of ADC samples it took to determine the [above] average DEBUG.str(string(" samples")) DEBUG.tx($D) DEBUG.str(string("State: ")) DEBUG.dec(ADC.getstate(2)) ' is channel 2 high (-1) or low (0) right now DEBUG.tx($D) DEBUG.str(string("Cur Value: ")) DEBUG.dec(ADC.getval(2)) ' what is the ADC value of channel 2 right now DEBUG.tx($D) DEBUG.str(string("Max Value: ")) DEBUG.dec(ADC.getmax(2)) ' what is channel 2's maximum value since the driver was started or since ' it was last reset with ADC.resetmaxminall or ADC.resetmax DEBUG.tx($D) DEBUG.str(string("Min Value: ")) DEBUG.dec(ADC.getmin(2)) ' what is channel 2's minimum value since the driver was started or since ' it was last reset with ADC.resetmaxminall or ADC.resetmin DEBUG.tx($D) DEBUG.tx($D) waitcnt(clkfreq + cnt) i++ PUB Pointed_example | i '' Start driver with supplied variables and run some commands DEBUG.start(31, 30, 0, 115_200) waitcnt(clkfreq + cnt) DEBUG.tx($D) ADC.start_pointed(Vo_p, Vn_p, Vclk_p, Vcs_p, 8, 8, 12, 1, false, @chanstate, @chanval, @chanmax, @chanmin) ' startup the ADC driver (Vo_p and Vn_p can be the same IO pin), 8 channel ADC, ' scan all 8 channels, 12-bit ADC, and mode 1 (single-ended), and using slow communication. ' Supplied are the addresses to 4 8-long blocks ADC.setthreshold(120, 200) ' not necisary if already setup in the ADC.start settings (but ' these values can be altered at any time with this method), it ' affects all of the chennels on the ADC DEBUG.str(string("Enable Standby")) ADC.standby_enable(6000) ' enable standby mode (put ADC and driver cog into wait mode to save power ' it checks for the standby_disable every 6000 cycles (higher number could ' save more power, but increase possible wait time during standby_disable waitcnt(clkfreq * 2 + cnt) DEBUG.str(string(" -- Disable Standby")) ADC.standby_disable ' disable standby (any command will pull the driver out of standby, but ' this one is specifically designed to wait for a complete exit and take ' the least amount of time doing it DEBUG.tx($D) i~ REPEAT IF (i // 10 == 9) DEBUG.str(string("Wait Hi... ")) DEBUG.dec(ADC.waithigh(2, 2500, 0)) ' wait a maximum of 2.5 seconds or until channel 2's value is above 20, it ' then displays the current value of the channel (incase of watchdog timeout) ' a watchdog value of 0 would disable it, thus it will wait indefinately DEBUG.tx($D) DEBUG.str(string("Wait Lo... ")) DEBUG.dec(ADC.waitlow(2, 4000, 1)) ' wait a maximum of 4 seconds or until channel 2's value is below or equal ' to 12, it then displays the current value of the channel (incase of ' watchdog timeout). If current state is low then there is no wait. DEBUG.tx($D) DEBUG.str(string("Reset Max/Min ")) ADC.resetmaxminall ' reset all channel's maximum and minimum values to their defaults DEBUG.tx($D) DEBUG.str(string("Frequency: ")) DEBUG.dec(ADC.getfreq(2, 1000, 3, 5, false)) ' attempt to determine a frequency on channel 2, but do not excede 1 second ' get 8 sampless of the frequency before averaging the result, do not count ' frequency clocks unless the channel is held high for at least 5 cycles DEBUG.str(string("Hz done in ")) DEBUG.dec(ADC.getsamples) ' display number of ADC samples it took to determine the [above] frequency DEBUG.str(string(" samples")) DEBUG.tx($D) DEBUG.str(string("Average: ")) DEBUG.dec(ADC.average_time(2, 500)) ' gather the value of channel 2 for 500ms and calculate the average DEBUG.str(string(" average tested with ")) DEBUG.dec(ADC.getsamples) ' display number of ADC samples it took to determine the [above] average DEBUG.str(string(" samples")) DEBUG.tx($D) DEBUG.str(string("State: ")) DEBUG.dec(chanstate[2]) ' is channel 2 high (-1) or low (0) right now DEBUG.tx($D) DEBUG.str(string("Cur Value: ")) DEBUG.dec(chanval[2]) ' what is the ADC value of channel 2 right now DEBUG.tx($D) DEBUG.str(string("Max Value: ")) DEBUG.dec(chanmax[2]) ' what is channel 2's maximum value since the driver was started or since ' it was last reset with ADC.resetmaxminall or ADC.resetmax DEBUG.tx($D) DEBUG.str(string("Min Value: ")) DEBUG.dec(chanmin[2]) ' what is channel 2's minimum value since the driver was started or since ' it was last reset with ADC.resetmaxminall or ADC.resetmin DEBUG.tx($D) DEBUG.tx($D) waitcnt(clkfreq + cnt) i++
Comments
Since you're powering the chip with 5V, you'll want a 3.3K or more resistor between the ADC's output and the Prop. You could put the resistor between the chip's input and output pins and connect the Prop directly to the input pin.
I'm not sure about any other problems you may be having; I just wanted to make sure you don't ruin a Prop while you figure this out.
Thanks for the reply. The object creator (Brandon) told me the voltage divider circuit was optional? Are you saying there should be a single resistor (bare minimum) between the DO/DI ADC pins and the prop pin?
I did connect pin 13 to +3.3vDC and got the following output on the terminal, so something is going on? Maybe the adc output pin needs a pull down?
What is the voltage range that you expect to measure?
You may also want to try out this code, just to see if you get something different. It's very simple to use:
http://obex.parallax.com/objects/224/
Note also that your VREF can't be higher than your VDD... It's not a problem, but it's caught me unawares before.
It's not to divide the voltage, it's to protect the IO pins as SRLM said. Though, the CLK, CS, and ADC input pins wont have 5V on them. You only need the resistor on the ADC's output pin since it will be outputting 5V with its logic signal.
Just tie the ADC's pins 12&13 together with the resistor and connect pin 12 (if that's the ADC's input pin) to the Prop with a wire.
To test new circuits you can do a quick try with the whole thing (as you did) if this does not work
go back to the most basic system that's possible. In this case the most basic system is a simple voltage-divider formed by two resistors between +5V and ground.
GND
Resistor1
x
Resistor2
+5V
Feed the voltage taken from "x" into the MCP3208. Now you should read the voltage.
The measured 12-bit-value should correspond to the voltage if you measure it directly with your DMM.
MCP3208-value * 5000 / 4095 should result in the voltage in millivolts.
Do this check with a MCP3208-demo-code that is well known to work properly
best regards
Stefan
Hi, I have placed a 3.9k resistor inline now, the prop seems to be ok when I did the old LED on, LED off test with that pin
3.9k resistor inserted
Did you mean pins 11/12? The data input/output pins?
HI Stefan!
I did this with 3.3v, but I will do it with a voltage divider circuit as well. And I will report back with a follow up post. Everything has a common ground.
I can add this, but when I spoke to Brandon Nimon, he said this was not necessary if you did not think your analog voltage would ever eclipse your ADC Vdd voltage, so I did not do this at first. I now have a 3.9k resistor inline, inbetween pins 11/12 (which are tied together) and the propellers input pin.
It showed 4095 with 3.3v and it is showing 2047 with 2.33v, with (2047 * 5000)/4095 = 2499.4, which I believe should be 2.499vDC ... which is close to 2.33? What do you think?
Yes, 11 and 12.
The bypass caps don't have anything to do with the voltage you're measuring. When the chip sends it's output is pulls a little more current than normal which can create a problem for the chip (when there's not a cap to keep the voltage constant at the Vdd pin). It's good use them on all ICs and sometimes it will make a difference between the IC working correctly or not.
This will probably work. In general, the resistor would go between the input and output pins with the input pin connected to the Prop with a just a wire.
Try adding a 0.1uf cap on the ADC Vdd pin. You might want a larger cap there too (1uf or 10uf).
Edit: One lead goes to Vdd of the ADC chip the other lead to ground.
Another thing is that when I hook up my meter to measure the voltage of the resistor divider, the ADC does from 255 to 127 or something? I'm pretty confused.
Can anyone tell me what the fool proof way of connecting these two are?
I now have a 1uF polarized capacitor in between Vdd/Vref and ground.
Do I need three 3.9k resistors in between ADC pin 10 and the prop, ADC pins 11/12 and the prop and ADC pin 13 and the prop? Or do I need a resistor ladder in between each ADC pin and the prop?
I see the following on the scope, can anyone verify this as good data or not? I'd like to verify what is going on here and narrow this down, right now it looks like the prop is doing its thing, but not receiving any data from the 3208 ... agreed?
CLK
CS
DATAIN
DATAOUT
It originally said resistor values were 1K which I changed to 3.3K which is about as low as one should go with 5V. I personally use 10K resistors with 5V signals because I have a lot of them and they always seem to work fine.
-- http://obex.parallax.com/objects/625/
I'd second that - some of those 'scope traces suggest you are trying to drive it too fast.
Also there is no guarantee that you can drive a 5V powered 3208 with 3V3 logic, its just outside the specs (input logic high is 3.5V min in the specs). Certainly this will affect the speed you can drive it at even if it does work since the settling time will be higher. You could use 10k pull-ups to 5V on the CLK, MOSI and CS lines to improve the drive voltage.
In SPI speak MOSI = DI, MISO = DO on the 3208.
If you can spare another pin and not couple the DI / DO pins via a resistor you might find better behaviour - currently the prop pin can fight the DO pin via the resistor, further dropping the DI high voltage below 3V3 (which is already below spec)...
Is there a specific reason you are using 5v for the 3208? I have been using two 3208's on 3.3v for some time now with wonderful results. I am also using a 1k resistor between datain and dataout on the 3208. I also use JonnyMac's jm_mcp3208_se_fast.spin object.
train nut
Here is how I will hook it up when I get home tonight, if you concur that this is correct? I'm sorry this schematic is so crude, I believe it is understandable though.
Here is a template if that is wrong
I love KISS stuff and your other driver worked great, so I can't wait to try this when I get home. Thank you.
Hi,
I have a 0-5vDC analog sensor, which is why I'm driving it with 5v as opposed to 3.3v. Are you saying I cannot use that analog sensor realistically, or that the prop would have no problem sinking the 1/2 mA if I used the pull up resistors and pulled the 3.3v to 5v? Also, do I need to worry about DI, to the 3208?
Hi,
I am using 5v because my analog in, is 0-5v and I'd like to have the entire voltage range for measuring with (it is a pressure sensor).
I will try JonnyMacs program and hopefully have success tonight.
As JonnyMac suggests, the other resistors could interfere with the ADC chip seeing the 3.3V logic.
The Dout pin on the ADC is the only one that will be connected to the Prop that will have 5V on it.
I've personally use MCP3208 at 5V many times with a Prop. I use a 10K ohm resistor on the Dout pin.
Thanks for the reply, I know this is granular, but just to confirm I have changed my rough schematic to reflect what I think you are saying for confirmation. There has been mention of using a resistor at the analog input channel for the 3208, I take it that is not necessary? I will not try the pull up resistors at this point, since you are using this IC in conjunction with the prop, without them.
The i/o pin is a bit confusing though, why am I able to use the same pin on the prop to connect to the datain/dataout pin on the 3208? Does the propeller object make this duplex, so that it receives on that pin at the correct time and transmits on that pin during non receiving times? What data do I need to send to the 3208 from the prop?
I'd think a resistor on the analog input would cause trouble.
Yes, the object takes care of switching between sending and receiving data. The Prop needs to tell the ADC what channels to read. There is at least one 3208 object that uses separate input and output lines which is desirable if you have multiple SPI devices since they can then share the input and output lines among the different devices and just use the CS line to indicate which device is to be active. I'm pretty sure the C3 has its ADC chip using separate input and output lines.
I think your schematic looks fine now.
The datasheet for the 3208 and 3204 says that for input the guaranteed logic HIGH is 0.7Vdd or greater, logic LOW is 0.3Vdd or lower. For 5V supply thats 3.5V and 1.5V as the critical values. Those are the worst case figures, so in practice most devices will be much more like 0.5Vdd as the threshold, and things will likely work, but the manufacturer is not guaranteeing it...
I'd suggest that in practice this could mean that input responses to rising edges will be slower than you might expect as the signal will take a while to get up to 3V3 and the ADC might be wanting close to that voltage to respond. Or if you are unlucky it might fail (usually devices fare quite a lot better than worst case, especially at normal temperatures and when new - temperature and aging affect various parameters and the worst case values are taking this into account)
My idea for weak pull-ups is to help raise the 3V3 a bit and improve matters - it could help if things are borderline, but on reflection I don't think it will make very much difference.
Mainly I'm just giving a heads-up that the voltage difference is taking things just a bit outside the "safe" limits, which might cause unreliable behaviour, possibly affecting your circuit (or possibly not).
Wow, this could have saved me many hours yesterday!
It reads
atmosphere (.20v) = 170
1v = 820
2v = 1638
Mark, maybe I am seeing a certain behavior because of your points?
When the ADC is reading analog voltage, say .2v, it displays the proper binary value of ~170, but when I have the ADC not powered it is displaying ~319. I checked just the dataout pin (they were not tied together for the scope check) on the ADC and there is some noise, even with the pull down resistor, any suggestions? Here is a screen capture of the noise scope finds once every second or two, it's quick and then goes to flatline again for another second or two.
With my 100psi analog pressure sensor ...
40.95 = 1psi
81.90 = 2psi
and so on
or do you do [binary value as a number] * 0.001221 to get a psi value and truncate after the tenth of a decimal point if that is what you wanted?
To me the math seems easier, but there might be a reason not to do it that way, which is why I ask.
When the ADC takes a sample, it has an internal capacitor (part of the sample and hold circuit) that needs to charge quickly. For that reason ADCs like to be driven from a low impedance source, and its not clear whether your pressure sensor is low impedance or not (have a link? does its datasheet specify a source impedance?) If the pressure doesn't change suddenly you could put a capacitor to ground on the analog input, so the ADC gets a more steady voltage into its internal sampling capacitor, and see if that reduces the noise.
I _am_ the man. And you're welcome.
Do you know how many volts the sensor puts out per psi? Knowing the range of the ADC it might be simple math.