temp.c: assemble cooperative state machine.

Heater PID loops must be called every 250ms, and temperature
probes do not need to be called any more often than that. Some
probes require some asynchronous operations to complete before
they're ready.  Handle these in a state machine that first begins
the conversion and finally completes it on some future tick.
Signal it is complete by setting the new state variable to IDLE.

Kick off the heater PID loop by simply beginning the temperature
conversion on all the temperature probes.  When each completes,
it will finish the process by calling its PID routine.

Remove the "next_read_time" concept altogether and just run each
temp conversion at fixed 250ms intervals.
This commit is contained in:
Phil Hord 2016-04-26 19:31:18 -04:00 committed by Markus Hitter
parent 335437022c
commit 9d53c13ebd
1 changed files with 104 additions and 107 deletions

211
temp.c
View File

@ -78,7 +78,7 @@ static const temp_sensor_definition_t temp_sensors[NUM_TEMP_SENSORS] =
#undef DEFINE_TEMP_SENSOR #undef DEFINE_TEMP_SENSOR
/// this struct holds the runtime sensor data- read temperatures, targets, etc /// this struct holds the runtime sensor data- read temperatures, targets, etc
struct { static struct {
//temp_flags_enum temp_flags; ///< flags //temp_flags_enum temp_flags; ///< flags
uint16_t last_read_temp; ///< last received reading uint16_t last_read_temp; ///< last received reading
@ -86,7 +86,7 @@ struct {
uint16_t temp_residency; ///< how long have we been close to target temperature in temp ticks? uint16_t temp_residency; ///< how long have we been close to target temperature in temp ticks?
uint16_t next_read_time; ///< how long until we can read this sensor again? uint8_t active; ///< State machine tracker for readers that need it.
} temp_sensors_runtime[NUM_TEMP_SENSORS]; } temp_sensors_runtime[NUM_TEMP_SENSORS];
/** \def TEMP_EWMA /** \def TEMP_EWMA
@ -101,14 +101,6 @@ struct {
#define TEMP_EWMA 1.0 #define TEMP_EWMA 1.0
#endif #endif
/**
Interval between analog_read() calls. 10ms if EWMA is enabled, because EWMA
needs frequent updates, otherwise 250ms so that we don't waste time on
unnecessary reads.
*/
#define ANALOG_READ_INTERVAL ((TEMP_EWMA == 1.0) ? 25 : 1)
/// Set up temp sensors. /// Set up temp sensors.
void temp_init() { void temp_init() {
temp_sensor_t i; temp_sensor_t i;
@ -129,24 +121,6 @@ void temp_init() {
break; break;
#endif #endif
#ifdef TEMP_THERMISTOR
// Mostly handled by analog_init().
case TT_THERMISTOR:
// Schedule first read. Decrement by 1, because clock_counter_250ms
// is 1 tick ahead.
temp_sensors_runtime[i].next_read_time = ANALOG_READ_INTERVAL - 1;
break;
#endif
#ifdef TEMP_AD595
// Mostly handled by analog_init().
case TT_AD595:
// Schedule first read. Decrement by 1, because clock_counter_250ms
// is 1 tick ahead.
temp_sensors_runtime[i].next_read_time = ANALOG_READ_INTERVAL - 1;
break;
#endif
#ifdef TEMP_INTERCOM #ifdef TEMP_INTERCOM
case TT_INTERCOM: case TT_INTERCOM:
// Enable the RS485 transceiver // Enable the RS485 transceiver
@ -287,6 +261,7 @@ static uint16_t temp_table_lookup(uint16_t temp, uint8_t sensor) {
static uint16_t temp_read_max6675(temp_sensor_t i) { static uint16_t temp_read_max6675(temp_sensor_t i) {
// Note: value reading in this section was rewritten without // Note: value reading in this section was rewritten without
// testing when spi.c/.h was introduced. --Traumflug // testing when spi.c/.h was introduced. --Traumflug
// Note: MAX6675 can give a reading every 0.22s
spi_select_max6675(); spi_select_max6675();
// No delay required, see // No delay required, see
// https://github.com/Traumflug/Teacup_Firmware/issues/22 // https://github.com/Traumflug/Teacup_Firmware/issues/22
@ -311,8 +286,6 @@ static uint16_t temp_read_max6675(temp_sensor_t i) {
temp = temp >> 3; temp = temp >> 3;
} }
} }
// MAX6675 can give a reading every 0.22s, so set this to about 250ms.
temp_sensors_runtime[i].next_read_time = 25;
return temp; return temp;
} }
@ -320,34 +293,60 @@ static uint16_t temp_read_max6675(temp_sensor_t i) {
#ifdef TEMP_THERMISTOR #ifdef TEMP_THERMISTOR
static uint16_t temp_read_thermistor(temp_sensor_t i) { static uint16_t temp_read_thermistor(temp_sensor_t i) {
temp_sensors_runtime[i].next_read_time = ANALOG_READ_INTERVAL; switch (temp_sensors_runtime[i].active++) {
return temp_table_lookup(analog_read(i), i); case 1: // Start ADC conversion.
#ifdef NEEDS_START_ADC
start_adc();
return 0;
#endif
// else fall through to conversion
case 2: // Convert temperature values.
temp_sensors_runtime[i].active = 0;
return temp_table_lookup(analog_read(i), i);
case 0: // IDLE
default:
temp_sensors_runtime[i].active = 0;
return 0;
}
} }
#endif /* TEMP_THERMISTOR */ #endif /* TEMP_THERMISTOR */
#ifdef TEMP_MCP3008 #ifdef TEMP_MCP3008
static uint16_t temp_read_mcp3008(temp_sensor_t i) { static uint16_t temp_read_mcp3008(temp_sensor_t i) {
// This is an SPI read so it is not as fast as on-chip ADC. A read temp_sensors_runtime[i].active = 0;
// every 100ms should be sufficient.
temp_sensors_runtime[i].next_read_time = 10;
return temp_table_lookup(mcp3008_read(temp_sensors[i].temp_pin), i); return temp_table_lookup(mcp3008_read(temp_sensors[i].temp_pin), i);
} }
#endif /* TEMP_MCP3008 */ #endif /* TEMP_MCP3008 */
#ifdef TEMP_AD595 #ifdef TEMP_AD595
static uint16_t temp_read_ad595(temp_sensor_t i) { static uint16_t temp_read_ad595(temp_sensor_t i) {
temp_sensors_runtime[i].next_read_time = ANALOG_READ_INTERVAL; switch (temp_sensors_runtime[i].active++) {
case 1: // Start ADC conversion.
#ifdef NEEDS_START_ADC
start_adc();
return 0;
#endif
// else fall through to conversion
// Convert >> 8 instead of >> 10 because internal temp is stored as case 2: // Convert temperature values.
// 14.2 fixed point. temp_sensors_runtime[i].active = 0;
return (analog_read(i) * 500L) >> 8; // Convert >> 8 instead of >> 10 because internal temp is stored as
// 14.2 fixed point.
return (analog_read(i) * 500L) >> 8;
case 0: // IDLE
default:
temp_sensors_runtime[i].active = 0;
return 0;
}
} }
#endif /* TEMP_AD595 */ #endif /* TEMP_AD595 */
#ifdef TEMP_INTERCOM #ifdef TEMP_INTERCOM
static uint16_t temp_read_intercom(temp_sensor_t i) { static uint16_t temp_read_intercom(temp_sensor_t i) {
temp_sensors_runtime[i].next_read_time = 25; temp_sensors_runtime[i].active = 0;
return read_temperature(temp_sensors[i].temp_pin); return read_temperature(temp_sensors[i].temp_pin);
} }
#endif /* TEMP_INTERCOM */ #endif /* TEMP_INTERCOM */
@ -356,7 +355,7 @@ static uint16_t temp_read_intercom(temp_sensor_t i) {
static uint16_t temp_read_dummy(temp_sensor_t i) { static uint16_t temp_read_dummy(temp_sensor_t i) {
uint16_t temp = temp_sensors_runtime[i].last_read_temp; uint16_t temp = temp_sensors_runtime[i].last_read_temp;
temp_sensors_runtime[i].next_read_time = 1; temp_sensors_runtime[i].active = 0;
if (temp_sensors_runtime[i].target_temp > temp) if (temp_sensors_runtime[i].target_temp > temp)
temp++; temp++;
@ -367,61 +366,63 @@ static uint16_t temp_read_dummy(temp_sensor_t i) {
} }
#endif /* TEMP_DUMMY */ #endif /* TEMP_DUMMY */
/// called every 10ms from clock.c - check all temp sensors that are ready for checking static uint16_t read_temp_sensor(temp_sensor_t i) {
switch (temp_sensors[i].temp_type) {
#ifdef TEMP_MAX6675
case TT_MAX6675:
return temp_read_max6675(i);
#endif
#ifdef TEMP_THERMISTOR
case TT_THERMISTOR:
return temp_read_thermistor(i);
#endif
#ifdef TEMP_MCP3008
case TT_MCP3008:
return temp_read_mcp3008(i);
#endif
#ifdef TEMP_AD595
case TT_AD595:
return temp_read_ad595(i);
#endif
#ifdef TEMP_PT100
case TT_PT100:
#warning TODO: PT100 code
break;
#endif
#ifdef TEMP_INTERCOM
case TT_INTERCOM:
return temp_read_intercom(i);
#endif
#ifdef TEMP_DUMMY
case TT_DUMMY:
return temp_read_dummy(i);
#endif
default: /* Prevent compiler warning. */
return 0;
}
}
/**
Called every 10ms from clock.c. Check all temp sensors that are ready for
checking. When complete, update the PID loop for sensors tied to heaters.
*/
void temp_sensor_tick() { void temp_sensor_tick() {
temp_sensor_t i = 0; temp_sensor_t i = 0;
for (; i < NUM_TEMP_SENSORS; i++) { for (; i < NUM_TEMP_SENSORS; i++) {
temp_sensors_runtime[i].next_read_time--; if (temp_sensors_runtime[i].active) {
if (temp_sensors_runtime[i].next_read_time == 0) { uint16_t temp = read_temp_sensor(i);
uint16_t temp = 0;
//time to deal with this temp sensor
switch(temp_sensors[i].temp_type) {
#ifdef TEMP_MAX6675
case TT_MAX6675:
temp = temp_read_max6675(i);
break;
#endif /* TEMP_MAX6675 */
#ifdef TEMP_THERMISTOR // Ignore temperature value if sensor read is still active.
case TT_THERMISTOR: if (temp_sensors_runtime[i].active)
temp = temp_read_thermistor(i); continue;
break;
#endif /* TEMP_THERMISTOR */
#ifdef TEMP_MCP3008
case TT_MCP3008:
temp = temp_read_mcp3008(i);
break;
#endif /* TEMP_MCP3008 */
#ifdef TEMP_AD595
case TT_AD595:
temp = temp_read_ad595(i);
break;
#endif /* TEMP_AD595 */
#ifdef TEMP_PT100
case TT_PT100:
#warning TODO: PT100 code
break;
#endif /* TEMP_PT100 */
#ifdef TEMP_INTERCOM
case TT_INTERCOM:
temp = temp_read_intercom(i);
break;
#endif /* TEMP_INTERCOM */
#ifdef TEMP_DUMMY
case TT_DUMMY:
temp = temp_read_dummy(i);
break;
#endif /* TEMP_DUMMY */
default: /* prevent compiler warning */
break;
}
/* Exponentially Weighted Moving Average alpha constant for smoothing /* Exponentially Weighted Moving Average alpha constant for smoothing
noisy sensors. Instrument Engineer's Handbook, 4th ed, Vol 2 p126 noisy sensors. Instrument Engineer's Handbook, 4th ed, Vol 2 p126
@ -431,16 +432,16 @@ void temp_sensor_tick() {
temp_sensors_runtime[i].last_read_temp = (uint16_t) ((EWMA_ALPHA * temp + temp_sensors_runtime[i].last_read_temp = (uint16_t) ((EWMA_ALPHA * temp +
(EWMA_SCALE-EWMA_ALPHA) * temp_sensors_runtime[i].last_read_temp (EWMA_SCALE-EWMA_ALPHA) * temp_sensors_runtime[i].last_read_temp
) / EWMA_SCALE); ) / EWMA_SCALE);
}
#ifdef NEEDS_START_ADC // Finished reading this temperature probe. Update heater PID loop.
// Start the ADC one tick (10ms) before it's needed, if it'll be needed. // This must only be done four times per second to keep the PID values
if (temp_sensors_runtime[i].next_read_time == 1) { // sane.
if (temp_sensors[i].temp_type == TT_THERMISTOR || if (temp_sensors[i].heater < NUM_HEATERS) {
temp_sensors[i].temp_type == TT_AD595) heater_tick(temp_sensors[i].heater, temp_sensors[i].temp_type,
start_adc(); temp_sensors_runtime[i].last_read_temp,
temp_sensors_runtime[i].target_temp);
} }
#endif }
} }
} }
@ -450,13 +451,9 @@ void temp_sensor_tick() {
void temp_heater_tick() { void temp_heater_tick() {
temp_sensor_t i; temp_sensor_t i;
for (i = 0; i < NUM_TEMP_SENSORS; i++) { // Signal all the temperature probes to begin reading.
if (temp_sensors[i].heater < NUM_HEATERS) { for (i = 0; i < NUM_TEMP_SENSORS; i++)
heater_tick(temp_sensors[i].heater, temp_sensors[i].temp_type, temp_sensors_runtime[i].active = 1;
temp_sensors_runtime[i].last_read_temp,
temp_sensors_runtime[i].target_temp);
}
}
} }
/** /**