From ee43a86474d26feceddbbec5784bb58d8ad77c44 Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Fri, 7 Aug 2015 15:34:18 +0200 Subject: [PATCH] ARM: split out heater-avr.c from heater.c. Also move #defines from heater.c to heater.h This operation became bigger than expected, because PID handling hat to be separated from heater handling. Code and strategy wasn't changed, but some chunks of code had to be moved. --- heater-avr.c | 217 +++++++++++++++++++++++++++++++++++++ heater.c | 299 +++++++-------------------------------------------- heater.h | 53 +++++++++ 3 files changed, 311 insertions(+), 258 deletions(-) create mode 100644 heater-avr.c diff --git a/heater-avr.c b/heater-avr.c new file mode 100644 index 0000000..663f5ac --- /dev/null +++ b/heater-avr.c @@ -0,0 +1,217 @@ + +/** \file + \brief Manage heaters, including PID and PWM, AVR specific part. +*/ + +#if defined TEACUP_C_INCLUDE && defined __AVR__ + +#include +#include "pinio.h" +#include "crc.h" +#include "sersendf.h" +#include "debug.h" + +/// \struct heater_definition_t +/// \brief simply holds pinout data- port, pin, pwm channel if used +typedef struct { + volatile uint8_t *heater_port; ///< pointer to port. DDR is inferred from this pointer too + uint8_t heater_pin; ///< heater pin, not masked. eg for PB3 enter '3' here, or PB3_PIN or similar + volatile uint8_t *heater_pwm; ///< pointer to 8-bit PWM register, eg OCR0A (8-bit) or ORC3L (low byte, 16-bit) +} heater_definition_t; + +#undef DEFINE_HEATER +/// \brief helper macro to fill heater definition struct from config.h +#define DEFINE_HEATER(name, pin, pwm) { &(pin ## _WPORT), pin ## _PIN, \ + pwm ? (pin ## _PWM) : NULL}, +static const heater_definition_t heaters[NUM_HEATERS] = +{ + #include "config_wrapper.h" +}; +#undef DEFINE_HEATER + + +/// \brief initialise heater subsystem +/// Set directions, initialise PWM timers, read PID factors from eeprom, etc +void heater_init() { + heater_t i; + + // setup PWM timers: fast PWM + // Warning 2012-01-11: these are not consistent across all AVRs + TCCR0A = MASK(WGM01) | MASK(WGM00); + // PWM frequencies in TCCR0B, see page 108 of the ATmega644 reference. + TCCR0B = MASK(CS00); // F_CPU / 256 (about 78(62.5) kHz on a 20(16) MHz chip) + #ifndef FAST_PWM + TCCR0B = MASK(CS00) | MASK(CS02); // F_CPU / 256 / 1024 (about 76(61) Hz) + #endif + TIMSK0 = 0; + OCR0A = 0; + OCR0B = 0; + + // timer 1 is used for stepping + + #ifdef TCCR2A + TCCR2A = MASK(WGM21) | MASK(WGM20); + // PWM frequencies in TCCR2B, see page 156 of the ATmega644 reference. + TCCR2B = MASK(CS20); // F_CPU / 256 (about 78(62.5) kHz on a 20(16) MHz chip) + #ifndef FAST_PWM + TCCR2B = MASK(CS20) | MASK(CS21) | MASK(CS22); // F_CPU / 256 / 1024 + #endif + TIMSK2 = 0; + OCR2A = 0; + OCR2B = 0; + #endif + + #ifdef TCCR3A + TCCR3A = MASK(WGM30); + TCCR3B = MASK(WGM32) | MASK(CS30); + TIMSK3 = 0; + OCR3A = 0; + OCR3B = 0; + #endif + + #ifdef TCCR4A + #ifdef TIMER4_IS_10_BIT + // ATmega16/32U4 fourth timer is a special 10 bit timer + TCCR4A = MASK(PWM4A) | MASK(PWM4B) ; // enable A and B + TCCR4C = MASK(PWM4D); // and D + TCCR4D = MASK(WGM40); // Phase correct + TCCR4B = MASK(CS40); // no prescaler + #ifndef FAST_PWM + TCCR4B = MASK(CS40) | MASK(CS42) | MASK(CS43); // 16 MHz / 1024 / 256 + //TCCR4B = MASK(CS40) | MASK(CS41) | MASK(CS43); // 16 MHz / 4096 / 256 + #endif + TC4H = 0; // clear high bits + OCR4C = 0xff; // 8 bit max count at top before reset + #else + TCCR4A = MASK(WGM40); + TCCR4B = MASK(WGM42) | MASK(CS40); + #endif + TIMSK4 = 0; + OCR4A = 0; + OCR4B = 0; + #ifdef OCR4D + OCR4D = 0; + #endif + #endif + + #ifdef TCCR5A + TCCR5A = MASK(WGM50); + TCCR5B = MASK(WGM52) | MASK(CS50); + TIMSK5 = 0; + OCR5A = 0; + OCR5B = 0; + #endif + + // setup pins + for (i = 0; i < NUM_HEATERS; i++) { + if (heaters[i].heater_pwm) { + *heaters[i].heater_pwm = 0; + // this is somewhat ugly too, but switch() won't accept pointers for reasons unknown + switch((uint16_t) heaters[i].heater_pwm) { + case (uint16_t) &OCR0A: + TCCR0A |= MASK(COM0A1); + break; + case (uint16_t) &OCR0B: + TCCR0A |= MASK(COM0B1); + break; + #ifdef TCCR2A + case (uint16_t) &OCR2A: + TCCR2A |= MASK(COM2A1); + break; + case (uint16_t) &OCR2B: + TCCR2A |= MASK(COM2B1); + break; + #endif + #ifdef TCCR3A + case (uint16_t) &OCR3AL: + TCCR3A |= MASK(COM3A1); + break; + case (uint16_t) &OCR3BL: + TCCR3A |= MASK(COM3B1); + break; + #ifdef COM3C1 + case (uint16_t) &OCR3CL: + TCCR3A |= MASK(COM3C1); + break; + #endif + #endif + #ifdef TCCR4A + #if defined (OCR4AL) + case (uint16_t) &OCR4AL: + TCCR4A |= MASK(COM4A1); + break; + case (uint16_t) &OCR4BL: + TCCR4A |= MASK(COM4B1); + break; + case (uint16_t) &OCR4CL: + TCCR4A |= MASK(COM4C1); + break; + #else + // 10 bit timer + case (uint16_t) &OCR4A: + TCCR4A |= MASK(COM4A1); + break; + case (uint16_t) &OCR4B: + TCCR4A |= MASK(COM4B1); + break; + #ifdef OCR4D + case (uint16_t) &OCR4D: + TCCR4C |= MASK(COM4D1); + break; + #endif + #endif + #endif + #ifdef TCCR5A + case (uint16_t) &OCR5AL: + TCCR5A |= MASK(COM5A1); + break; + case (uint16_t) &OCR5BL: + TCCR5A |= MASK(COM5B1); + break; + case (uint16_t) &OCR5CL: + TCCR5A |= MASK(COM5C1); + break; + #endif + } + } + + pid_init(i); + } + + // set all heater pins to output + #undef DEFINE_HEATER + #define DEFINE_HEATER(name, pin, pwm) SET_OUTPUT(pin); WRITE(pin, 0); + #include "config_wrapper.h" + #undef DEFINE_HEATER +} + +/** \brief manually set PWM output + \param index the heater we're setting the output for + \param value the PWM value to write + + anything done by this function is overwritten by heater_tick above if the heater has an associated temp sensor +*/ +void heater_set(heater_t index, uint8_t value) { + if (index >= NUM_HEATERS) + return; + + heaters_runtime[index].heater_output = value; + + if (heaters[index].heater_pwm) { + *(heaters[index].heater_pwm) = value; + + if (DEBUG_PID && (debug_flags & DEBUG_PID)) + sersendf_P(PSTR("PWM{%u = %u}\n"), index, *heaters[index].heater_pwm); + } + else { + if (value >= HEATER_THRESHOLD) + *(heaters[index].heater_port) |= MASK(heaters[index].heater_pin); + else + *(heaters[index].heater_port) &= ~MASK(heaters[index].heater_pin); + } + + if (value) + power_on(); +} + +#endif /* defined TEACUP_C_INCLUDE && defined __AVR__ */ diff --git a/heater.c b/heater.c index 45af37e..70c685b 100644 --- a/heater.c +++ b/heater.c @@ -1,39 +1,25 @@ -#include "heater.h" /** \file - \brief Manage heaters + \brief Manage heaters, including PID and PWM. */ -#include -#include +#include "heater.h" +#define TEACUP_C_INCLUDE +#include "heater-avr.c" +//#include "heater-arm.c" +#undef TEACUP_C_INCLUDE + +#include #include "arduino.h" #include "debug.h" -#include "temp.h" -#include "pinio.h" #include "crc.h" - #ifndef EXTRUDER #include "sersendf.h" #endif - -/// \struct heater_definition_t -/// \brief simply holds pinout data- port, pin, pwm channel if used -typedef struct { - volatile uint8_t *heater_port; ///< pointer to port. DDR is inferred from this pointer too - uint8_t heater_pin; ///< heater pin, not masked. eg for PB3 enter '3' here, or PB3_PIN or similar - volatile uint8_t *heater_pwm; ///< pointer to 8-bit PWM register, eg OCR0A (8-bit) or ORC3L (low byte, 16-bit) -} heater_definition_t; - -#undef DEFINE_HEATER -/// \brief helper macro to fill heater definition struct from config.h -#define DEFINE_HEATER(name, pin, pwm) { &(pin ## _WPORT), pin ## _PIN, \ - pwm ? (pin ## _PWM) : NULL}, -static const heater_definition_t heaters[NUM_HEATERS] = -{ - #include "config_wrapper.h" -}; -#undef DEFINE_HEATER +#ifdef EECONFIG + #include +#endif /** \var heaters_pid @@ -54,36 +40,6 @@ struct { int16_t i_limit; ///< scaled I limit, such that \f$-i_{limit} < i_{factor} < i_{limit}\f$ } heaters_pid[NUM_HEATERS]; -/// \brief this struct holds the runtime heater data- PID integrator history, temperature history, sanity checker -struct { - int16_t heater_i; ///< integrator, \f$-i_{limit} < \sum{4*eC*\Delta t} < i_{limit}\f$ - - uint16_t temp_history[TH_COUNT]; ///< store last TH_COUNT readings in a ring, so we can smooth out our differentiator - uint8_t temp_history_pointer; ///< pointer to last entry in ring - - #ifdef HEATER_SANITY_CHECK - uint16_t sanity_counter; ///< how long things haven't seemed sane - uint16_t sane_temperature; ///< a temperature we consider sane given the heater settings - #endif - - uint8_t heater_output; ///< this is the PID value we eventually send to the heater -} heaters_runtime[NUM_HEATERS]; - -#ifdef BANG_BANG - #define HEATER_THRESHOLD ((BANG_BANG_ON + BANG_BANG_OFF) / 2) -#else - #define HEATER_THRESHOLD 8 -#endif - -/// default scaled P factor, equivalent to 8.0 counts/qC or 32 counts/C -#define DEFAULT_P 8192 -/// default scaled I factor, equivalent to 0.5 counts/(qC*qs) or 8 counts/C*s -#define DEFAULT_I 512 -/// default scaled D factor, equivalent to 24 counts/(qc/(TH_COUNT*qs)) or 192 counts/(C/s) -#define DEFAULT_D 24576 -/// default scaled I limit, equivalent to 384 qC*qs, or 24 C*s -#define DEFAULT_I_LIMIT 384 - #ifdef EECONFIG /// this lives in the eeprom so we can save our PID settings for each heater typedef struct { @@ -97,186 +53,42 @@ typedef struct { EE_factor EEMEM EE_factors[NUM_HEATERS]; #endif /* EECONFIG */ -/// \brief initialise heater subsystem -/// Set directions, initialise PWM timers, read PID factors from eeprom, etc -void heater_init() { - heater_t i; - // setup PWM timers: fast PWM - // Warning 2012-01-11: these are not consistent across all AVRs - TCCR0A = MASK(WGM01) | MASK(WGM00); - // PWM frequencies in TCCR0B, see page 108 of the ATmega644 reference. - TCCR0B = MASK(CS00); // F_CPU / 256 (about 78(62.5) kHz on a 20(16) MHz chip) - #ifndef FAST_PWM - TCCR0B = MASK(CS00) | MASK(CS02); // F_CPU / 256 / 1024 (about 76(61) Hz) - #endif - TIMSK0 = 0; - OCR0A = 0; - OCR0B = 0; +heater_runtime_t heaters_runtime[NUM_HEATERS]; - // timer 1 is used for stepping +/** Inititalise PID data structures. - #ifdef TCCR2A - TCCR2A = MASK(WGM21) | MASK(WGM20); - // PWM frequencies in TCCR2B, see page 156 of the ATmega644 reference. - TCCR2B = MASK(CS20); // F_CPU / 256 (about 78(62.5) kHz on a 20(16) MHz chip) - #ifndef FAST_PWM - TCCR2B = MASK(CS20) | MASK(CS21) | MASK(CS22); // F_CPU / 256 / 1024 - #endif - TIMSK2 = 0; - OCR2A = 0; - OCR2B = 0; + \param i Index of the heater to initialise by Teacup numbering. +*/ +void pid_init(heater_t i) { + + #ifdef HEATER_SANITY_CHECK + // 0 is a "sane" temperature when we're trying to cool down. + heaters_runtime[i].sane_temperature = 0; #endif - #ifdef TCCR3A - TCCR3A = MASK(WGM30); - TCCR3B = MASK(WGM32) | MASK(CS30); - TIMSK3 = 0; - OCR3A = 0; - OCR3B = 0; - #endif + #ifndef BANG_BANG + #ifdef EECONFIG + // Read factors from EEPROM. + heaters_pid[i].p_factor = + eeprom_read_dword((uint32_t *)&EE_factors[i].EE_p_factor); + heaters_pid[i].i_factor = + eeprom_read_dword((uint32_t *)&EE_factors[i].EE_i_factor); + heaters_pid[i].d_factor = + eeprom_read_dword((uint32_t *)&EE_factors[i].EE_d_factor); + heaters_pid[i].i_limit = + eeprom_read_word((uint16_t *)&EE_factors[i].EE_i_limit); - #ifdef TCCR4A - #ifdef TIMER4_IS_10_BIT - // ATmega16/32U4 fourth timer is a special 10 bit timer - TCCR4A = MASK(PWM4A) | MASK(PWM4B) ; // enable A and B - TCCR4C = MASK(PWM4D); // and D - TCCR4D = MASK(WGM40); // Phase correct - TCCR4B = MASK(CS40); // no prescaler - #ifndef FAST_PWM - TCCR4B = MASK(CS40) | MASK(CS42) | MASK(CS43); // 16 MHz / 1024 / 256 - //TCCR4B = MASK(CS40) | MASK(CS41) | MASK(CS43); // 16 MHz / 4096 / 256 - #endif - TC4H = 0; // clear high bits - OCR4C = 0xff; // 8 bit max count at top before reset - #else - TCCR4A = MASK(WGM40); - TCCR4B = MASK(WGM42) | MASK(CS40); - #endif - TIMSK4 = 0; - OCR4A = 0; - OCR4B = 0; - #ifdef OCR4D - OCR4D = 0; - #endif - #endif - - #ifdef TCCR5A - TCCR5A = MASK(WGM50); - TCCR5B = MASK(WGM52) | MASK(CS50); - TIMSK5 = 0; - OCR5A = 0; - OCR5B = 0; - #endif - - // setup pins - for (i = 0; i < NUM_HEATERS; i++) { - if (heaters[i].heater_pwm) { - *heaters[i].heater_pwm = 0; - // this is somewhat ugly too, but switch() won't accept pointers for reasons unknown - switch((uint16_t) heaters[i].heater_pwm) { - case (uint16_t) &OCR0A: - TCCR0A |= MASK(COM0A1); - break; - case (uint16_t) &OCR0B: - TCCR0A |= MASK(COM0B1); - break; - #ifdef TCCR2A - case (uint16_t) &OCR2A: - TCCR2A |= MASK(COM2A1); - break; - case (uint16_t) &OCR2B: - TCCR2A |= MASK(COM2B1); - break; - #endif - #ifdef TCCR3A - case (uint16_t) &OCR3AL: - TCCR3A |= MASK(COM3A1); - break; - case (uint16_t) &OCR3BL: - TCCR3A |= MASK(COM3B1); - break; - #ifdef COM3C1 - case (uint16_t) &OCR3CL: - TCCR3A |= MASK(COM3C1); - break; - #endif - #endif - #ifdef TCCR4A - #if defined (OCR4AL) - case (uint16_t) &OCR4AL: - TCCR4A |= MASK(COM4A1); - break; - case (uint16_t) &OCR4BL: - TCCR4A |= MASK(COM4B1); - break; - case (uint16_t) &OCR4CL: - TCCR4A |= MASK(COM4C1); - break; - #else - // 10 bit timer - case (uint16_t) &OCR4A: - TCCR4A |= MASK(COM4A1); - break; - case (uint16_t) &OCR4B: - TCCR4A |= MASK(COM4B1); - break; - #ifdef OCR4D - case (uint16_t) &OCR4D: - TCCR4C |= MASK(COM4D1); - break; - #endif - #endif - #endif - #ifdef TCCR5A - case (uint16_t) &OCR5AL: - TCCR5A |= MASK(COM5A1); - break; - case (uint16_t) &OCR5BL: - TCCR5A |= MASK(COM5B1); - break; - case (uint16_t) &OCR5CL: - TCCR5A |= MASK(COM5C1); - break; - #endif - } - } - - #ifdef HEATER_SANITY_CHECK - // 0 is a "sane" temperature when we're trying to cool down - heaters_runtime[i].sane_temperature = 0; - #endif - - #ifndef BANG_BANG - #ifdef EECONFIG - // read factors from eeprom - heaters_pid[i].p_factor = - eeprom_read_dword((uint32_t *) &EE_factors[i].EE_p_factor); - heaters_pid[i].i_factor = - eeprom_read_dword((uint32_t *) &EE_factors[i].EE_i_factor); - heaters_pid[i].d_factor = - eeprom_read_dword((uint32_t *) &EE_factors[i].EE_d_factor); - heaters_pid[i].i_limit = - eeprom_read_word((uint16_t *) &EE_factors[i].EE_i_limit); - - if (crc_block(&heaters_pid[i].p_factor, 14) != eeprom_read_word((uint16_t *) &EE_factors[i].crc)) - #endif /* EECONFIG */ - { - heaters_pid[i].p_factor = DEFAULT_P; - heaters_pid[i].i_factor = DEFAULT_I; - heaters_pid[i].d_factor = DEFAULT_D; - heaters_pid[i].i_limit = DEFAULT_I_LIMIT; - } - #endif /* BANG_BANG */ - } - - // set all heater pins to output - do { - #undef DEFINE_HEATER - #define DEFINE_HEATER(name, pin, pwm) SET_OUTPUT(pin); WRITE(pin, 0); - #include "config_wrapper.h" - #undef DEFINE_HEATER - } while (0); + if (crc_block(&heaters_pid[i].p_factor, 14) != + eeprom_read_word((uint16_t *)&EE_factors[i].crc)) + #endif /* EECONFIG */ + { + heaters_pid[i].p_factor = DEFAULT_P; + heaters_pid[i].i_factor = DEFAULT_I; + heaters_pid[i].d_factor = DEFAULT_D; + heaters_pid[i].i_limit = DEFAULT_I_LIMIT; + } + #endif /* BANG_BANG */ } /** \brief run heater PID algorithm @@ -415,35 +227,6 @@ void heater_tick(heater_t h, temp_type_t type, uint16_t current_temp, uint16_t t heater_set(h, pid_output); } -/** \brief manually set PWM output - \param index the heater we're setting the output for - \param value the PWM value to write - - anything done by this function is overwritten by heater_tick above if the heater has an associated temp sensor -*/ -void heater_set(heater_t index, uint8_t value) { - if (index >= NUM_HEATERS) - return; - - heaters_runtime[index].heater_output = value; - - if (heaters[index].heater_pwm) { - *(heaters[index].heater_pwm) = value; - - if (DEBUG_PID && (debug_flags & DEBUG_PID)) - sersendf_P(PSTR("PWM{%u = %u}\n"), index, *heaters[index].heater_pwm); - } - else { - if (value >= HEATER_THRESHOLD) - *(heaters[index].heater_port) |= MASK(heaters[index].heater_pin); - else - *(heaters[index].heater_port) &= ~MASK(heaters[index].heater_pin); - } - - if (value) - power_on(); -} - /** \brief check whether all heaters are off */ uint8_t heaters_all_zero() { diff --git a/heater.h b/heater.h index c16e6e3..73a41ba 100644 --- a/heater.h +++ b/heater.h @@ -6,6 +6,30 @@ #include "simulator.h" #include "temp.h" +/// Default scaled P factor, equivalent to 8.0 counts/qC or 32 counts/C. +#define DEFAULT_P 8192 +/// Default scaled I factor, equivalent to 0.5 counts/(qC*qs) or 8 counts/C*s. +#define DEFAULT_I 512 +/// Default scaled D factor, equivalent to 24 counts/(qc/(TH_COUNT*qs)) or +/// 192 counts/(C/s). +#define DEFAULT_D 24576 +/// Default scaled I limit, equivalent to 384 qC*qs, or 24 C*s. +#define DEFAULT_I_LIMIT 384 + +/** \def HEATER_THRESHOLD + + Defines the threshold when to turn a non-PWM heater on and when to turn it + off. Applies only to heaters which have only two states, on and off. + Opposed to those heaters which allow to turn them on gradually as needed, + usually by using PWM. +*/ +#ifdef BANG_BANG + #define HEATER_THRESHOLD ((BANG_BANG_ON + BANG_BANG_OFF) / 2) +#else + #define HEATER_THRESHOLD 8 +#endif + + #undef DEFINE_HEATER #define DEFINE_HEATER(name, pin, pwm) HEATER_ ## name, typedef enum @@ -16,7 +40,36 @@ typedef enum } heater_t; #undef DEFINE_HEATER +/** This struct holds the runtime heater data. + + PID integrator history, temperature history, sanity checker. +*/ +typedef struct { + /// Integrator, \f$-i_{limit} < \sum{4*eC*\Delta t} < i_{limit}\f$ + int16_t heater_i; + + /// Store last TH_COUNT readings in a ring, so we can smooth out our + /// differentiator. + uint16_t temp_history[TH_COUNT]; + /// Pointer to last entry in ring. + uint8_t temp_history_pointer; + + #ifdef HEATER_SANITY_CHECK + /// How long things haven't seemed sane. + uint16_t sanity_counter; + /// A temperature we consider sane given the heater settings. + uint16_t sane_temperature; + #endif + + /// This is the PID value we eventually send to the heater. + uint8_t heater_output; +} heater_runtime_t; + + +extern heater_runtime_t heaters_runtime[]; + void heater_init(void); +void pid_init(heater_t index); void heater_set(heater_t index, uint8_t value); void heater_tick(heater_t h, temp_type_t type, uint16_t current_temp, uint16_t target_temp);