NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Sensors, Actuators, and Robotics » Sensor interdependence

February 11, 2012
by lsoltmann
lsoltmann's Avatar

I am trying to build a temperature controller and am having some trouble with what seems to be a coupling between two ADC pins. I have a 10K thermistor connected to PC0 using a voltage divider, an LED connected to PC1, and a 5K potentiometer connected to PC2. The code is as follows

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

// PIN DEFINITIONS:
//
// PC0 -- thermistor input
// PC1 -- relay output
// PC2 -- potentiometer input

// ******** Initialize Variables ********
uint16_t adc0;
uint16_t adc2;
uint16_t adc_val;
uint8_t i;
double ctemp;
double ctemp_avg;
double sysv;
double pottemp;
double setpoint;
int dev;

void adc_init() {
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
while(ADCSRA & (1<<ADSC)) {
}
uint16_t value1 = ADCL;
uint16_t value2 = ADCH;
adc_val = value1 + (value2<<8);
ADCSRA |= (1<<ADSC);
return adc_val;
}

double datatotempf(adc0) {
//***** Equation for thermisotr *********
ctemp= -0.494*(((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0))+6.8725*(((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0))-39.559*(((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0))+117.59*(((adc0/1024.0)*5.0)*((adc0/1024.0)*5.0))-212.09*(((adc0/1024.0)*5.0))+270.24;  
return ctemp;
//Note: equation only valid for temperatures between 5 and 104 degF
}

double datatostp(adc2) {
//Convert potentiometer voltage to a temperature deviation
pottemp=((adc2/1024.0)*5.0)*10.0+30.0;
return pottemp;
}

int main() {
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_home();

adc_init();

sysv=5.0;
dev=1;

DDRC |= (1<<PC1);

//*****************************************************************

while(1) {

    // take 100 samples and average them
    ctemp_avg = 0.0;
    for(i=0; i<100; i++) {
        ADMUX=0;
        adc0=0;
        adc_val=0;
        adc0 = adc_read();
        ctemp = datatotempf(adc0);
        ctemp_avg = ctemp_avg + ctemp/100.0;
    }

        ADMUX=2;
        adc2=0;
        adc_val=0;
        adc2=adc_read();
        setpoint=datatostp(adc2);

        // Display data to LCD
    lcd_home();
    lcd_line_one();
    fprintf_P(&lcd_stream, PSTR("Temp: %3.1f degF"),ctemp_avg);
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Setpoint: %3.1f degF"),stp_avg);
    lcd_line_three();
    fprintf_P(&lcd_stream, PSTR("Temp Dev: %d degF"),dev);
    lcd_line_four();
    fprintf_P(&lcd_stream, PSTR("Status: Off"));
}

return 0;
}

I have taken out some of the code (stuff to do with the LED) to home in on the problem. If I run the code as is, the temperature reads a reasonable value. When I start to turn the potentiometer the value of "setpoint" does not change and the temperature goes up by about 3 degreesF. If I turn the pot the other way the temperature goes down by about 3 degreesF. If I apply heat to the thermistor the temperature goes up as expect but the setpoint value also goes up by about 2 degreesF. Rotating the pot seems to only affect the temperature value not the setpoint. I hooked up a voltmeter to the voltage regulator to ensure the voltage was not dropping as I was turning the pot and it remained steady.

I also noticed that if I add a averaging loop (as was done with the temperature) the setpoint value reads correctly and changes with the rotation of the pot. However, the temperature still undergoes the odd increase or decrease as mentioned above.

My questions are... why does the temperature change when I rotate the pot? why must I average the potentiometer readings for it to work instead of just taking one sample?

February 12, 2012
by Rick_S
Rick_S's Avatar

Try commenting out line 35 in the above program. Then move line 44 beteween lines 38 & 39.

What you are seeing could be caused by the "pre-read" configuration the NerdKit guys used in their original tempsensor program. In that program, they start the ADC read at the end of the initialization and at the end of each call to adc_read. The problem when running two is that the reading you get is from the prior call to adc_read, not the current call. Thus if you set ADMUX to 0, then call adc_read, you get the result from the prior call then it starts a new conversion for ADMUX=0. If you then call it with ADMUX=2, you get the results from the earlier ADMUX=0 and fire a conversion for ADMUX=2.

See if what I suggested helps,

Rick

February 12, 2012
by lsoltmann
lsoltmann's Avatar

That fixed it! I'm still new to microcontrollers and programming so I would never have caught that. Thanks for the help.

February 12, 2012
by Rick_S
Rick_S's Avatar

No problem, glad it worked out for you.

Rick

July 02, 2012
by wisc4ever
wisc4ever's Avatar

I am glad that you got your code working. I am wondering if someone could explain line 50 to me? I have 30+ years of programming experience (and am probably a code snob, but I do know that my way is not always the best). I am curious about the constants and why it was coded that way. I have been trying to get a thermistor working for a couple of weeks now and am too pigheaded to ask for help, but I enjoy seeing what other people have done and learning from my mistakes.

July 02, 2012
by lsoltmann
lsoltmann's Avatar

Line 50 exemplifies my lack of coding knowledge. I wanted to apply a fifth order polynomial fit that I obtained from the data sheet but was not able to figure out how to raise a value to another value. I googled exponents in C but the examples given for some reason would not work for me. The compiler kept giving me an error on that line. So instead of asking for help, I brute forced it. Not very efficient but it gets the job done. If anybody knows how to do exponents in C that would be very helpful.

Lars

July 02, 2012
by pcbolt
pcbolt's Avatar

Lars -

Did you try the 'pow(x, y)' function in the <math.h> library? Both variables need to be "doubles" and it is x raised to the y power. One problem that can come up is a 'double' in AVR is only good for 7 significant digits.

July 02, 2012
by lsoltmann
lsoltmann's Avatar

I did try the pow function ,but now that you mention it I did forget to add the math.h library. Just ran a test case and it did work. Guess I'm too used to MATLAB where all these function are built in ...

July 02, 2012
by wisc4ever
wisc4ever's Avatar

Lars - As you found out pcbolt is correct with the Pow function in the math library. There is nothing wrong with your code, you are trying to learn as I am. One thing you might want to consider is store ((adc0/1024.0)*5.0) in a variable. For example:

volts = ((adc0/1024.0)*5.0);
ctemp = -0.494*pow(volts,5)
        +6.8723*pow(volts,4)
        -39.559*pow(volts,3)
        +117.59*pow(volts,2)
        -212.09*volts
        +270.24;

The reason for this is because it takes quite a bit of time to multiply and divide doubles. A lot of my experience was from writing drivers for SCSI and USB controller boards, so performance was always a big issue. Some compilers will optimize it, but I never take that for granted. What are the constant values you are using. i.e. -0.494, 6.8723, -39.559, etc?

July 03, 2012
by lsoltmann
lsoltmann's Avatar

That does make sense and makes it look better. I'll definitely go back and change that in the code. I appreciate the help.

The constant values in the equation are just the coefficients for each the terms to shape the polynomial. I obtained them from the calibration curve that I generated from the data sheet.

ThermistorCalibration

The 5th order fit is overkill but it does provide a perfect fit, at least within the region I am interested.

If anyone is interested, I got the thermistors from DigiKey

http://www.digikey.com/product-detail/en/B57861S0103F040

July 11, 2012
by lsoltmann
lsoltmann's Avatar

wisc4ever - I tried the block of code you suggested,

volts = ((adc0/1024.0)*5.0);
ctemp = -0.494*pow(volts,5)
        +6.8723*pow(volts,4)
        -39.559*pow(volts,3)
        +117.59*pow(volts,2)
        -212.09*volts
        +270.24;

but I keep getting errors when I compile. I ran some tests and it appears that I can not use an exponent higher than 2 if my base is not an integer. Is this a limitation of the pow function or am I just doing something wrong?

July 11, 2012
by wisc4ever
wisc4ever's Avatar

I will try a couple of tests, but <math.h> pow function should handle a double in both parameters and should return a double. Send a code snippet when you get the chance, I will try the test right away.

July 11, 2012
by lsoltmann
lsoltmann's Avatar

This compiles...

volts = ((adc0/1024.0)*5.0);
ctemp = -0.494*pow(volts,2);

This does not...

volts = ((adc0/1024.0)*5.0);
ctemp = -0.494*pow(volts,3);

I can string them together and it works as long as the exponent is less than or equal to 2. The error i'm getting is this

nom27883d:rims_controller lsoltmann$ make

make -C ../libnerdkits

make[1]: Nothing to be done for `all'.

avr-gcc -g -Os -Wall -mmcu=atmega168 -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm -o erims.o erims.c ../libnerdkits/delay.o ../libnerdkits/lcd.o ../libnerdkits/uart.o

/usr/local/AVRMacPack-20080721/lib/gcc/avr/4.3.0/../../../../avr/lib/avr5/libc.a(inverse.o):../../../libm/fplib/inverse.S:50: relocation truncated to fit: R_AVR_13_PCREL against symbol `__divsf3' defined in .text section in /usr/local/AVRMacPack-20080721/lib/gcc/avr/4.3.0/avr5/libgcc.a(_div_sf.o)

/usr/local/AVRMacPack-20080721/lib/gcc/avr/4.3.0/../../../../avr/lib/avr5/libc.a(log.o):../../../libm/fplib/log.S:94: relocation truncated to fit: R_AVR_13_PCREL against symbol `__addsf3' defined in .text section in /usr/local/AVRMacPack-20080721/lib/gcc/avr/4.3.0/avr5/libgcc.a(_addsub_sf.o)

/usr/local/AVRMacPack-20080721/lib/gcc/avr/4.3.0/../../../../avr/lib/avr5/libc.a(log.o):../../../libm/fplib/log.S:98: relocation truncated to fit: R_AVR_13_PCREL against symbol `__addsf3' defined in .text section in /usr/local/AVRMacPack-20080721/lib/gcc/avr/4.3.0/avr5/libgcc.a(_addsub_sf.o)

/usr/local/AVRMacPack-20080721/lib/gcc/avr/4.3.0/../../../../avr/lib/avr5/libc.a(modf.o):../../../libm/fplib/modf.S:87: relocation truncated to fit: R_AVR_13_PCREL against symbol `__subsf3' defined in .text section in /usr/local/AVRMacPack-20080721/lib/gcc/avr/4.3.0/avr5/libgcc.a(_addsub_sf.o)

make: *** [erims.hex] Error 1

July 11, 2012
by wisc4ever
wisc4ever's Avatar

Your pow function call should not be causing these errors. From what I see (I will continue looking in case I am wrong) is being caused in the make command and not your code, but it appears your code is making it happen. What development system are you using (computer/OS)? Check your include for math.h is it: #include <math.h> I did a cut and paste of the original snippet I posted and it works fine, but my dev system is a Mac running OS 10.4.

July 11, 2012
by lsoltmann
lsoltmann's Avatar

I am running the code on a MacBook Pro with OS 10.6. I checked to make sure that I had the #include <math.h> in the code. I assuming that the make file did not need any modifications when I added the math.h library. I appreciate the help.

July 11, 2012
by wisc4ever
wisc4ever's Avatar

For what you are doing you should not have to change your make file. Your C compiler and libraries should be the same as mine, so that should not be a problem. Try the following: 1) Get your code to compile (even if you comment out the offending code). 2) Copy and paste the following snippet. Including the '{' and '}'. 3) Try to compile it. This should isolate the pow function into a chunk of code with everything defined locally and no external resources being used with the exception of the pow function. 4) Let me know what happens.

{ uint16_t aTemp; double vTemp; double Temp;

aTemp = 1; vTemp = ((aTemp/1024.0)*5.0);

Temp = -0.494*pow(vTemp,5); }

July 11, 2012
by wisc4ever
wisc4ever's Avatar
{
   uint16_t aTemp;
   double vTemp;
   double Temp;

   aTemp = 1;
   vTemp = ((aTemp/1024.0)*5.0);

   Temp = -0.494*pow(vTemp,5);
 }
July 12, 2012
by lsoltmann
lsoltmann's Avatar

That did compile.

July 12, 2012
by wisc4ever
wisc4ever's Avatar

Since the code you just tried used the power of 5, we can assume the pow function is correct. The next step is to check your declarations of adc0, volts, and ctemp. adc0 should be a uint16_t. volts and ctemp should be double.

double ctemp;
double volts;
uint16_t adc0;

Post a Reply

Please log in to post a reply.

Did you know that you can see each keypress on a computer keyboard on an oscilloscope? Learn more...