April 29, 2012
by gsmoove
|
Hello,
I'm a complete newbie with this stuff. I successfully completed the meat thermometer tutorial but thought I'd give myself the extra challenge of lighting an LED when the raw temp was greater then 100. This actually worked, except that once the light turns on the temperature value immediately rises about 20 degrees and the LCD turns off. Once the temp goes back below 100 and the LED turns off the temperature value drops 20 degrees and the LCD turns back on.
I'm not sure if this is because of some basic programming concept that I haven't grasped or variation in the voltage once the LED starts drawing energy. Anyone have any idea? I've attached the code I patched together.
// meat_thermometer.c
// for NerdKits with ATmega168
// Author: Humberto Evans
// based on temp sensor code by Mike Robbins
// support@nerdkits.com
#define F_CPU 14745600
#include <stdio.h>
#include <math.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"
// PIN DEFINITIONS:
//
// PC0 -- temperature sensor analog input
void adc_init() {
// set analog to digital converter
// for external reference (5v), single ended input ADC0
ADMUX = 0;
// set analog to digital converter
// to be enabled, with a clock prescale of 1/128
// so that the ADC clock runs at 115.2kHz.
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
// fire a conversion just to get the ADC warmed up
ADCSRA |= (1<<ADSC);
}
uint16_t adc_read() {
// read from ADC, waiting for conversion to finish
// (assumes someone else asked for a conversion.)
// wait for it to be cleared
while(ADCSRA & (1<<ADSC)) {
// do nothing... just hold your breath.
}
// bit is cleared, so we have a result.
// read from the ADCL/ADCH registers, and combine the result
// Note: ADCL must be read first (datasheet pp. 259)
uint16_t result = ADCL;
uint16_t temp = ADCH;
result = result + (temp<<8);
// set ADSC bit to get the *next* conversion started
ADCSRA |= (1<<ADSC);
return result;
}
double sampleToFahrenheit(uint16_t sample) {
// conversion ratio in DEGREES/STEP:
// (5000 mV / 1024 steps) * (1 degree / 10mV)
// ^^^^^^^^^^^ ^^^^^^^^^^
// from ADC from LM34
return sample * (5000.0 / 1024.0 / 10.0);
}
//scale used to calibrate the sensor
#define th 176.5 //174.87 //high value read in boiling water
#define tl 37.7 //37.35 //low value read in freezing water
double sa;
double sb;
double scale(double sample){
return sa*sample+sb;
}
double lastTrans = 0;
double a;
//Implements the inverse transfer functions
// (x[n] - a*x[n-1])
// y[n]= -----------------
// (1-a)
double transFunc(double sample){
double answer = (sample - a*lastTrans)/(1-a);
lastTrans = sample;
return answer;
}
double lastLow = 0;
double filterTau;
//Implments the lowpass filter to supress noise
// y[n] = tau*x[n] + (1-tau)*y[n-1]
double lowPass(double sample){
double answer = filterTau*sample + (1-filterTau)*lastLow;
lastLow = answer;
return answer;
}
int main() {
// set LED pin
DDRC |= (1<<PC4);
// start up the LCD
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_clear_and_home();
lcd_line_four();
fprintf_P(&lcd_stream, PSTR(" www.NerdKits.com "));
lcd_line_four();
fprintf_P(&lcd_stream, PSTR(" www.NerdKits.com "));
// start up the Analog to Digital Converter
adc_init();
// start up the serial port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;
// holder variables for temperature data
uint16_t last_sample = 0;
double this_temp;
double temp_avg;
uint8_t i;
// must initialize global constants before they are used.
// filterTau and a are tuned experimentally to get the right responce.
filterTau = 0.15;
a = 0.99;
sa = (212.0-32.0)/(th-tl);
sb = 212.0-sa*th;
lastTrans = lastLow = adc_read(); // get an initial reading to initialize the filters
while(1) {
// take 100 samples and average them!
temp_avg = 0.0;
for(i=0; i<100; i++) {
last_sample = adc_read();
this_temp = sampleToFahrenheit(last_sample);
// add this contribution to the average
temp_avg = temp_avg + this_temp/100.0;
}
temp_avg = scale(temp_avg); //get the scaled raw value
double trans = transFunc(temp_avg); //apply the predictive transfer function
double low = lowPass(trans); //low pass filter
// write message to LCD
lcd_home();
fprintf_P(&lcd_stream, PSTR("Raw: %7.2f"), temp_avg);
lcd_line_two();
fprintf_P(&lcd_stream, PSTR("Transfer: %7.2f "), trans);
lcd_line_three();
fprintf_P(&lcd_stream, PSTR("Low Pass: %7.2f"), low);
// write message to serial port
printf_P(PSTR("%.2f %.2f %.2f %.2f %.2f\r\n"), temp_avg,trans,low,a,filterTau);
// LED control
if(temp_avg>100){
PORTC |= (1<<PC4);
}
else if(temp_avg<100){
PORTC &= ~(1<<PC4);
}
}
return 0;
}
|
April 30, 2012
by Rick_S
|
Two guesses on my part.
- You are running off battery
- You did not use a current limiting resistor on the LED
Your analysis is most likely correct in that when you turn on the LED the current draw is such that there is no longer adequate power to fully operate the LCD.
If you aren't currently using one, place a resistor between pin PC4 and your LED (220 Ohm to 1K Ohm should be fine). Secondly, make sure you use a fresh battery.
Rick |