yarf 0.1
Yet Another RepRap Firmware
|
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