NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.
Microcontroller Programming » ADC HELP!!!
December 29, 2010 by jasongillikin |
I don't know where I went wrong. basically I am trying to make a differential temp controller, but I can't get the ADC to read properly... I am alternating between MUX0 and MUX1 being on and storing the values as 8 bit (ADLAR=1).. I tried running my motor on program by itself, and it makes an LED blink, so I know that part works... does anyone know where I went wrong with the code?
|
---|---|
December 30, 2010 by Ralphxyz |
Hi jasongillikin, I posted my multisensor code in this thread http://www.nerdkits.com/forum/thread/6/. There is also a good discussion about multi sensors there. Hope this helps. Ralph |
December 30, 2010 by hevans (NerdKits Staff) |
Hi jasongillikin, I think you the way you are setting ADMUX isn't quite right. The 3 bits MUX2-0 are used to select between the 6 ADC inputs. You need to use the table on page 257 of the datasheet if you want to set it bit by bit. However you can quickly note that MUX2-0 for ADC0=0, and for ADC1 it equals one. So to select ADC0 just set ADMUX = 0, or ADMUX = 1 for the ADC0 pin. You probably need to OR something else in there to make sure your left adjusting is still right. One other tip I I have for you is to make your init function such that you only need to call it once. The speed at which you need to read is probably slow enough that you can afford to set the mux channgel, start the conversion, and wait for it to finish inside your "read" function, the init function should really just be called once to set up the ADC as a whole. Humberto |
December 31, 2010 by jasongillikin |
thanks for everyones help, I am confused on setting the ADC bitwise. I think its the 8 bit ADC thats throwing me off. I guess i just don't see the need to do 10 bit for this. am I using the ADLAR bit correctly? i mean if I set ADMUX=1 thats 00000001 and I really want 00100001 for 8 bit right? I had another question about the LM34, over what temperature range does it follow Ohm's law. I know its 1 degree F per 10mV, but does that start @ 0 Degrees? and if so will a (-)10degree F result in (-)10 mV. how do ADC's react to (-) Voltage? do they have (-) registers one more question, I noticed when I debugged that I had to declare variables in both my sub functions and in the main function. is this because I didn't declare them as universal variables? if so, is it better to declare all Vars as universal? thanks again for all the help, I just started learning C by reading "C++ For Dummies" trying to make some sense of it all. Jason |
January 01, 2011 by Rick_S |
I think you're getting things a bit mixed up. When you said: "I guess i just don't see the need to do 10 bit for this. " You don't have a choice, the ADC is a 10 bit converter on these micro's. That simply means that the result of a read will be 10 bits long or from 0 to 1023 decimal. The converter is 8 channel which means you can read from 8 different inputs. The ADMUX register does not control how many bits are returned, it is simply there to:
The ATMega168 has 8 independant inputs for the ACD but can only read from them one at a time. If you look at the tempsensor code, you will see in the adc_init() function, the ADMUX=0; line. Notice the two comment lines above. They tell you what that line does. If you look at the datasheet, you'll see that the ADMUX register is broken down like this
If you look at the tables below that in the datasheet, you'll see that if all those are zero as set in the tempsensor program then: REFS1 and REFS2 both zero = AREF, Internal Vref turned off -- In other words, it uses the reference voltage at the AREF pin of the microcontroller for its reference and shuts off it's internal reference. ADLAR = 0 is the default This places the upper two bits of the 10 bit result in register ADCH and the lower 8 bits in register ADCL This allows us to get our 10 bit result by simply adding the upper bits shifted 8 places to our lower bits as seen in the adc_read() function. The lower 4 bits are the MUX0 thru MUX3 bits and the represent the port we are reading. With them set at 0, we are reading from ADC0. The other register you see reference in the adc_init() function is ADCSRA or the ADC Control and Status Register A. You really don't have to worry a whole lot about that one for basic operation other than to know the way it is set in the adc_init() function is perfectly fine for normal operation and that setting bit 6 (ADSC) in this register will trigger a conversion. I hope this helped some without totally overwhelming you :D Rick |
January 01, 2011 by Rick_S |
One other thing, The ADC on the micro will not accept negative voltage without bad things happening. But as wired, you don't have to worry about that. The LM34 as it is setup will only read from 5 deg F to 300 deg F. (from it's datasheet) That would be an output voltage of 50mv to 3000mv or .05 to 3V. While the LM34 is capable of reading temperatures below that, it has to be wired differently to swing to the negative side of things. Rick |
January 04, 2011 by jasongillikin |
thanks for your help again Rick,
I misread the datasheet and thought I saw "8 Bit ADC" and now I see its "8 channel ADC" in regard Reference voltage, if both REFS1 and REFS0 are set to 0 then the reference voltage is 5V from VOC. and since the full scale deflection of the LM34 is 3V, you should never get a ADC reading more than 615 steps (3V/5V*1024 steps) correct? if you needed better resolution would you put a voltage divider for 3V on the internal Reference giving you the ability to use all 1024 steps? thanks again, jason |
January 04, 2011 by Ralphxyz |
I get 1024 steps. I think you are complicating a easy operation. From the Specsheet: 23.5.2 ADC Voltage Reference The reference voltage for the ADC (VREF) indicates the conversion range for the ADC. Single ended channels that exceed VREF will result in codes close to 0x3FF. VREF can be selected as either AVCC, internal 1.1V reference, or external AREF pin (Pin21). If you followed the Nerdkit User Guide AREF is 5v (VCC). I really do not think you need to concern your self with REFS1 and REFS0, at least I do not see any benifit just to get the ADC working. Just my 2 cents worth. maybe I am completely missing what you are trying to do. Ralph |
January 04, 2011 by mrobbins (NerdKits Staff) |
Hi Jason, If you really wanted a higher resolution temperature reading, you're correct that you could use a lower AREF voltage and that would make the volts/step number smaller. That would mean that each degree of temperature change would appear as more ADC steps, effectively giving you greater resolution. At most, this might give you "effectively" 1 more bit of resolution if you used a 2.5V reference, or "effectively" 2 more bits of resolution if you used a 1.25V reference, when compared to the normal 5V reference configuration. I should also mention that what we recommend with sampling the temperature reading many times and averaging also produces more effective resolution. This is formally known as "oversampling and decimation". Basically, you can take thousands or tens of thousands of ADC measurements per second with your microcontroller, but for your end purpose (display on a screen, or control a device) you may only need 1 to 10 readings per second. By averaging over lots of ADC readings, and relying on the idea that the signal is noisy but that the noise is centered around the "true" value, you can get a result that has much more effective resolution than the ADC increment itself. Please see this paper from Atmel which describes this technique in a bit more detail. Because of how the statistical averages work out (and after making a bunch of assumptions about how the noise is distributed), you basically need to average 4 samples to get 1 extra bit of effective resolution, 16 samples for 2 bits, 64 samples for 3 bits, 256 samples for 4 bits, and so on. Mike |
January 04, 2011 by Ralphxyz |
And of course if accuracy is what you are looking for you need a more precise temperature sensor than the LM34 that comes with the Nerdkit. Ralph |
January 04, 2011 by mongo |
A little sensor I have been using for years is the AD590. It delivers 1uA per degree K. If you offset the -273.15 for zero, you have a very accurate input. I do it with a 250 ohm resistor between the negative terminal and ground. If you measure the voltage, it comes out to 1 mV per degree C. Connect the positive to about +8V to +12V. If you have a reading of 273 mV across the resistor, then you are reading zero degrees C or 32 deg F. |
January 18, 2011 by jasongillikin |
So, the issue remains... I still have an unworking Differential controller. For a while I thought it was a ATMEGA... so i got a new one (and new LM34's) and it still isn't working. if I run Mikes's multisensor program (http://www.nerdkits.com/forum/thread/6/)and put delay before the main() loop starts again, so I can read it. the numbers are comming out all messed up. and even the difference doesn't add up. for instance (just two examples b/c I am running it right now): A=130, B=132, A-B= -20 A=133, B=132, A-B= 110 also when I remove either of the LM34's or change the ADMUX to an empty input, the thing still reads! I have no idea what is going on makes no sense to me here is the code I am running...
HOWEVER...... I was just using that code to trouble shoot my setup. my code I want to run is //Motor Stuff void motorOn(){ //Pin =PB5 // DDRB|= (1<<DDB5); PORTB|= (1<<PB5); //DELAY 1 MINUTE (60000 MICROSECONDS)
tempDiff=(collectorResult-storageResult); to tempDiff=(collectorResult-storageResult)+1; which leads me to believe that tempDiff<0 all the time everytime. which is contradictory to the result from the troubleshooting code I mentioned first. a solution to my code working would be great (that way I can learn what I did wrong) |
January 18, 2011 by bretm |
It's probably working just fine. With A=130 and B=132 it's displaying -2. The "0" is left over from some previous info that you wrote out. When A=133 and B=132 it's displaying 1. The "10" is also left over from some previous output.
Probably not--it's just still showing whatever was left over from before. Write some spaces after your numbers. |
January 19, 2011 by jasongillikin |
oh I also forgot to mention that when I apply heat to one of the sensors(doesn't matter which one) both of the numbers change. As far as the values being left over from something else it should stay the same after I remove the sensors, but it keeps behaving just like it did when the sensors were in. |
January 19, 2011 by bretm |
Both of those programs still have some problems. Add the spaces after the display and see what's still broken. In the first program, you're doing this:
This won't do what you expect. The adc_read function waits for the conversion to complete and then immediately starts another conversion. If you do this in a loop, what happens is that the first adc_read starts the next conversion using ADC0, so when that conversion is complete the ADC0 value gets stored in sensor2. Setting ADMUX=5 during the middle of the conversion doesn't change it. Then, the second time you call adc_read while ADMUX=5, adc_read starts a new conversion after sampling ADC5 (the current ADMUX) so sensor1 will get the result of that conversion the next time through the loop. You need to change adc_read to start the conversion at the start of the function and then wait for it to complete. Don't start a new conversion before returning. The second program does the ADMUX stuff in the right order, but it has a different problem:
What is the value of (5000 / 1024 / 10)? Working from left to right we have 5000 / 1024 which is 4, and then we have 4 / 10 which is 0. So this is equivalent to
What you might want is
but this won't work either. collectorResult is a uint16_t which means the range is 0 to 65535. If you multiply 1023 by 5000 you get 5115000 which overflows that by quite a bit. You should do this instead, which should cause it to use 32-bit math:
Another problem is this:
That doesn't actually call the function and store the result in collectorResult. It's actually not even a function call at all, it's a function declaration. I'm a little surprised it compiles. It should say
The variable called "collectorResult" inside the collectorAdc_Read function is local to that function. It's unrelated to the other collectorResult variable you declared in main(). Same issue with storageResult and storageAdc_Read and storageResult * (5000 / 1024 / 10). |
January 31, 2011 by jasongillikin |
Okay, almost got it... I can see the light... just a few more bugs to iron out maybe you guys can help me... Progress so far 1) got my motor (led light) to turn on (when heat applied via touching sensor) 2) got my motor to turn off... 3) got an accumulator to work to average readings Existing problems I think are related to noise in the ADC... or some type of number overflow... 1) sometimes LED turns on without heat being applied to collector sensor 2) even after heat is removed from collector sensor motor comes on again (I figured it was residual heat, until I turned the motor On delay up to 1 minute... and it still happened) 3) when I apply heat to storage sensor it still makes motor come on... Not supposed to happen since tempDiff=(storageResult-collectorResult)... motor on if tempDiff>10 3)even with collector sensor removed... motor still comes on solutions I have tried 1)adding 1 sec delay before each reading... 2)bumping up the number of readings in the accumulator average to 500 or even 1000. 3)changing uint_16's to int_16's Glad for the breakthroughs, but still don't have a working product... as I mentioned before all hardware is new. and here is my code... void motorOn(){
|
Please log in to post a reply.
Did you know that LEDs (light emitting diodes) only conduct current in one direction, like normal diodes? Learn more...
|