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.
This commit is contained in:
Markus Hitter 2015-08-07 15:34:18 +02:00
parent 8f24fbaad4
commit ee43a86474
3 changed files with 311 additions and 258 deletions

217
heater-avr.c Normal file
View File

@ -0,0 +1,217 @@
/** \file
\brief Manage heaters, including PID and PWM, AVR specific part.
*/
#if defined TEACUP_C_INCLUDE && defined __AVR__
#include <stdlib.h>
#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__ */

299
heater.c
View File

@ -1,39 +1,25 @@
#include "heater.h"
/** \file
\brief Manage heaters
\brief Manage heaters, including PID and PWM.
*/
#include <stdlib.h>
#include <avr/eeprom.h>
#include "heater.h"
#define TEACUP_C_INCLUDE
#include "heater-avr.c"
//#include "heater-arm.c"
#undef TEACUP_C_INCLUDE
#include <stdlib.h>
#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 <avr/eeprom.h>
#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() {

View File

@ -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);