yarf 0.1
Yet Another RepRap Firmware
src/temperature/temperature.c
Go to the documentation of this file.
00001 /*
00002  * temperature.c
00003  *
00004  * Copyright 2011 Pieter Agten
00005  *
00006  * This file is part of Yarf.
00007  *
00008  * Yarf is free software: you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation, either version 3 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * Yarf is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with Yarf.  If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 
00030 #include "temperature.h"
00031 
00032 #include "yarf.h"
00033 #include "thermistor_table.h"
00034 #include "scheduling/periodic.h"
00035 #include "util/math.h"
00036 #include "pid.h"
00037 
00038 #include <stdint.h>
00039 #include <avr/interrupt.h> 
00040 #include <util/atomic.h> 
00041 
00046 #define INITIAL_TARGET_TEMP_C 0
00047 
00048 
00052 struct heater {
00053   pid_t pid;                     
00054   float max_temp;                
00055   void (*set_power)(uint8_t dc); 
00056   volatile uint16_t current_adc; 
00057   volatile float current_temp;   
00058   uint8_t current_power;         
00059 };
00060 
00064 static struct heater nozzle;
00065 
00069 static struct heater printbed;
00070 
00074 static volatile enum {
00075   IDLE,              
00076   READING_NOZZLE,    
00077   READING_PRINTBED   
00078 } adc_state;
00079 
00080 
00085 static periodic_task_t temperature_task;
00086 
00087 
00097 static void
00098 set_nozzle_duty_cycle(uint8_t dc)
00099 {
00100   NOZZLE_HEATER_PWM_REG = MIN(dc,NOZZLE_HEATER_MAX_DUTY_CYCLE);
00101 }
00102 
00112 static inline void
00113 set_printbed_duty_cycle(uint8_t dc)
00114 {
00115   PRINTBED_HEATER_PWM_REG = MIN(dc,PRINTBED_HEATER_MAX_DUTY_CYCLE);
00116 }
00117 
00121 static inline void
00122 adc_init(void)
00123 {
00124   /* Disable digital inputs on ADC pins for reducing power consumption */
00125   DIDR0 = _BV(TEMP1_PIN) | _BV(TEMP2_PIN);
00126   
00127   /* Set voltage reference */
00128   ADMUX = (1 << REFS0);
00129 
00130   /*@brief  The temperature component manages the temperature of the nozzle 
00131  *         and the printbed. Set ADC prescaler and enable ADC */
00132   ADCSRA = _BV(ADEN) | _BV(ADIE) | (7 << ADPS0);
00133 }
00134 
00138 static inline void
00139 pwm_init(void)
00140 {
00141   /* Disable all timer 0 interrupts */
00142   TIMSK0 = 0;
00143 
00144   /* Set duty cycle to 0 */
00145   OCR0A = 0;
00146   OCR0B = 0;
00147 
00148   /* Select Compare Output Mode, 1024 prescaler, phase correct PWM */
00149   TCCR0A = (2 << COM0A0) | (2 << COM0B0) | (1 << WGM00);
00150   TCCR0B = (5 << CS00);
00151   // PWM freq = F_CPU/1024/255/2
00152 }
00153 
00161 static void
00162 manage_heater_power(struct heater *heater)
00163 {
00164   uint8_t power;
00165   if (heater->current_temp < heater->max_temp) {
00166     power = pid_next(&(heater->pid), heater->current_temp);
00167   } else {
00168     power = 0;
00169   }
00170   heater->set_power(power);
00171   heater->current_power = power;
00172 }
00173 
00180 static inline float
00181 nozzle_temperature(uint16_t adc)
00182 {
00183   return pgm_read_float(&(NOZZLE_THERMISTOR_TABLE[adc]));
00184 }
00185 
00192 static inline float
00193 printbed_temperature(uint16_t adc)
00194 {
00195   return pgm_read_float(&(PRINTBED_THERMISTOR_TABLE[adc]));
00196 }
00197 
00205 ISR(ADC_vect,ISR_NOBLOCK)
00206 {
00207   uint16_t adc_value;
00208   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
00209     adc_value = ADCL;
00210     adc_value |= (ADCH << 8);
00211   }
00212   switch (adc_state) {
00213     case READING_NOZZLE:
00214       nozzle.current_adc = adc_value;
00215       nozzle.current_temp = nozzle_temperature(adc_value);
00216       manage_heater_power(&nozzle);
00217 
00218       /* Select the printbed thermistor pin */
00219       ADMUX = (ADMUX & ~(0x1F << MUX0)) | (PRINTBED_THERMISTOR_PIN << MUX0);
00220 
00221       /* Start the ADC conversion (asynchronously) */
00222       adc_state = READING_PRINTBED;
00223       ADCSRA |= _BV(ADSC);
00224       break;
00225     case READING_PRINTBED:
00226       printbed.current_adc = adc_value;
00227       printbed.current_temp = printbed_temperature(adc_value);
00228       manage_heater_power(&printbed);
00229 
00230       adc_state = IDLE;
00231       break;
00232     default:
00233       break;
00234   }
00235 }
00236 
00243 static void
00244 temp_periodic(void)
00245 {
00246   /* Make sure the previous adc conversion has been completed */
00247   if (adc_state != IDLE) {
00248     /* ADC can't keep up! */
00249     return;
00250   }
00251 
00252   /* Select the nozzle thermistor pin */
00253   ADMUX = (ADMUX & ~(0x1F << MUX0)) | (NOZZLE_THERMISTOR_PIN << MUX0);
00254 
00255   /* Start the ADC conversion (asynchronously) */
00256   adc_state = READING_NOZZLE;
00257   ADCSRA |= _BV(ADSC);
00258 }
00259 
00260 
00271 //TODO: improve this method
00272 static inline bool
00273 temperature_reached(struct heater *h)
00274 {
00275   return h->pid.target_temp == 0 ||
00276          fabs(h->current_temp - h->pid.target_temp) < 2;
00277 }
00278 
00279 void
00280 temp_init(void)
00281 {
00282   adc_init();
00283   pwm_init();
00284 
00285   nozzle.max_temp = NOZZLE_HEATER_MAX_TEMP;
00286   nozzle.set_power = set_nozzle_duty_cycle;
00287   pid_init(&nozzle.pid, NOZZLE_PID_ENVELOPE, NOZZLE_PID_P_GAIN, NOZZLE_PID_I_GAIN, NOZZLE_PID_D_GAIN);
00288   pid_set_target(&nozzle.pid, INITIAL_TARGET_TEMP_C);
00289   
00290   printbed.max_temp = PRINTBED_HEATER_MAX_TEMP;
00291   printbed.set_power = set_printbed_duty_cycle;
00292   pid_init(&printbed.pid, PRINTBED_PID_ENVELOPE, PRINTBED_PID_P_GAIN, PRINTBED_PID_I_GAIN, PRINTBED_PID_D_GAIN);
00293   pid_set_target(&printbed.pid, INITIAL_TARGET_TEMP_C);
00294 
00295   periodic_task_set(&temperature_task, temp_periodic, (int)(1000.0/HEATER_MANAGEMENT_FREQUENCY_HZ));
00296   periodic_add(&temperature_task);
00297 }
00298 
00299 
00300 /* ********** Nozzle ********** */
00301 void
00302 temp_set_nozzle_target(float c)
00303 {
00304   pid_set_target(&nozzle.pid, c);
00305 }
00306 
00307 
00308 float
00309 temp_get_nozzle_temp(void)
00310 {
00311   return nozzle.current_temp;
00312 }
00313 
00314 
00315 uint16_t
00316 temp_get_nozzle_adc(void)
00317 {
00318   return nozzle.current_adc;
00319 }
00320 
00321 
00322 uint8_t
00323 temp_get_nozzle_power(void)
00324 {
00325   return nozzle.current_power;
00326 }
00327 
00328 bool
00329 temp_nozzle_temperature_reached(void)
00330 {
00331   return temperature_reached(&nozzle);
00332 }
00333 
00334 void
00335 temp_nozzle_temperature_wait(void)
00336 {
00337   while (! temp_nozzle_temperature_reached()) {
00338   }
00339 }
00340 
00341 /* ********** Printbed ********** */
00342 void
00343 temp_set_printbed_target(float c)
00344 {
00345   pid_set_target(&printbed.pid, c);
00346 }
00347 
00348 
00349 float
00350 temp_get_printbed_temp(void)
00351 {
00352   return printbed.current_temp;
00353 }
00354 
00355 
00356 uint8_t
00357 temp_get_printbed_power(void)
00358 {
00359   return printbed.current_power;
00360 }
00361 
00362 bool
00363 temp_printbed_temperature_reached(void)
00364 {
00365   return temperature_reached(&printbed);
00366 }
00367 
00368 void
00369 temp_printbed_temperature_wait(void)
00370 {
00371   while (! temp_printbed_temperature_reached()) {
00372   }
00373 }
00374 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines