1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
/*
* Pid.h
*
* Created on: 21 Jul 2016
* Author: David
*/
#ifndef SRC_PID_H_
#define SRC_PID_H_
/**
* This class implements a PID controller for the heaters
*/
#include "RepRapFirmware.h"
#include "FOPDT.h"
#include "TemperatureError.h"
class HeaterProtection;
class PID
{
enum class HeaterMode : uint8_t
{
// The order of these is important because we test "mode > HeatingMode::off" to determine whether the heater is active
fault,
off,
heating,
cooling,
stable,
// All states from here onwards must be PID tuning states because function IsTuning assumes that
tuning0,
tuning1,
tuning2,
tuning3,
lastTuningMode = tuning3
};
static const size_t NumPreviousTemperatures = 4; // How many samples we average the temperature derivative over
public:
PID(Platform& p, int8_t h);
void Init(float pGain, float pTc, float pTd, bool usePid, bool inverted); // (Re)Set everything to start
void Reset();
void Spin(); // Called in a tight loop to keep things running
void SetActiveTemperature(float t);
float GetActiveTemperature() const;
void SetStandbyTemperature(float t);
float GetStandbyTemperature() const;
void SetHeaterProtection(HeaterProtection *h);
float GetHighestTemperatureLimit() const; // Get the highest temperature limit
float GetLowestTemperatureLimit() const; // Get the lowest temperature limit
void Activate(); // Switch from idle to active
void Standby(); // Switch from active to idle
bool Active() const; // Are we active?
void SwitchOff(); // Not even standby - all heater power off
bool SwitchedOff() const; // Are we switched off?
bool CheckProtection() const; // Check heater protection elements and return true if everything is good
bool FaultOccurred() const; // Has a heater fault occurred?
void ResetFault(); // Reset a fault condition - only call this if you know what you are doing
float GetTemperature() const; // Get the current temperature
float GetAveragePWM() const; // Return the running average PWM to the heater. Answer is a fraction in [0, 1].
uint32_t GetLastSampleTime() const; // Return when the temp sensor was last sampled
float GetAccumulator() const; // Return the integral accumulator
void StartAutoTune(float targetTemp, float maxPwm, StringRef& reply); // Start an auto tune cycle for this PID
bool IsTuning() const;
void GetAutoTuneStatus(StringRef& reply); // Get the auto tune status or last result
const FopDt& GetModel() const // Get the process model
{ return model; }
bool SetModel(float gain, float tc, float td, float maxPwm, float voltage, bool usePid, bool inverted, PwmFrequency pwmFreq); // Set the process model
bool IsHeaterSignalInverted() const // Is the PWM output signal inverted?
{ return invertPwmSignal; }
void SetHeaterSignalInverted(bool inverted) // Set PWM output signal inversion
{ invertPwmSignal = inverted; }
bool IsHeaterEnabled() const // Is this heater enabled?
{ return model.IsEnabled(); }
void GetFaultDetectionParameters(float& pMaxTempExcursion, float& pMaxFaultTime) const
{ pMaxTempExcursion = maxTempExcursion; pMaxFaultTime = maxHeatingFaultTime; }
void SetFaultDetectionParameters(float pMaxTempExcursion, float pMaxFaultTime)
{ maxTempExcursion = pMaxTempExcursion; maxHeatingFaultTime = pMaxFaultTime; }
void SetM301PidParameters(const M301PidParameters& params)
{ model.SetM301PidParameters(params); }
#if HAS_VOLTAGE_MONITOR
void Suspend(bool sus); // Suspend the heater to conserve power
#endif
private:
void SwitchOn(); // Turn the heater on and set the mode
void SetHeater(float power) const; // Power is a fraction in [0,1]
TemperatureError ReadTemperature(); // Read and store the temperature of this heater
void DoTuningStep(); // Called on each temperature sample when auto tuning
static bool ReadingsStable(size_t numReadings, float maxDiff)
pre(numReadings >= 2; numReadings <= MaxTuningTempReadings);
static int GetPeakTempIndex(); // Auto tune helper function
static int IdentifyPeak(size_t numToAverage); // Auto tune helper function
void CalculateModel(); // Calculate G, td and tc from the accumulated readings
void DisplayBuffer(const char *intro); // Debug helper
float GetExpectedHeatingRate() const; // Get the minimum heating rate we expect
Platform& platform; // The instance of the class that is the RepRap hardware
HeaterProtection *heaterProtection; // The first element of assigned heater protection items
float activeTemperature; // The required active temperature
float standbyTemperature; // The required standby temperature
float maxTempExcursion; // The maximum temperature excursion permitted while maintaining the setpoint
float maxHeatingFaultTime; // How long a heater fault is permitted to persist before a heater fault is raised
float temperature; // The current temperature
float previousTemperatures[NumPreviousTemperatures]; // The temperatures of the previous NumDerivativeSamples measurements, used for calculating the derivative
size_t previousTemperatureIndex; // Which slot in previousTemperature we fill in next
FopDt model; // The process model and PID parameters
float iAccumulator; // The integral PID component
float lastPwm; // The last PWM value we output, before scaling by kS
float averagePWM; // The running average of the PWM, after scaling.
uint32_t timeSetHeating; // When we turned on the heater
uint32_t lastSampleTime; // Time when the temperature was last sampled by Spin()
uint16_t heatingFaultCount; // Count of questionable heating behaviours
int8_t heater; // The index of our heater
uint8_t previousTemperaturesGood; // Bitmap indicating which previous temperature were good readings
HeaterMode mode; // Current state of the heater
bool invertPwmSignal; // Invert the final PWM output signal (same behaviour as with HEAT_ON in earlier firmware versions)
bool active; // Are we active or standby?
bool tuned; // True if tuning was successful
#if HAS_VOLTAGE_MONITOR
bool suspended; // True if suspended to save power
#endif
uint8_t badTemperatureCount; // Count of sequential dud readings
static_assert(sizeof(previousTemperaturesGood) * 8 >= NumPreviousTemperatures, "too few bits in previousTemperaturesGood");
// Variables used during heater tuning
static const size_t MaxTuningTempReadings = 128; // The maximum number of readings we keep. Must be an even number.
static float *tuningTempReadings; // the readings from the heater being tuned
static float tuningStartTemp; // the temperature when we turned on the heater
static float tuningPwm; // the PWM to use, 0..1
static float tuningTargetTemp; // the maximum temperature we are allowed to reach
static uint32_t tuningBeginTime; // when we started the tuning process
static uint32_t tuningPhaseStartTime; // when we started the current tuning phase
static uint32_t tuningReadingInterval; // how often we are sampling, in milliseconds
static size_t tuningReadingsTaken; // how many temperature samples we have taken
static float tuningHeaterOffTemp; // the temperature when we turned the heater off
static float tuningPeakTemperature; // the peak temperature reached, averaged over 3 readings (so slightly less than the true peak)
static uint32_t tuningHeatingTime; // how long we had the heating on for
static uint32_t tuningPeakDelay; // how many milliseconds the temperature continues to rise after turning the heater off
};
inline bool PID::Active() const
{
return active;
}
inline float PID::GetActiveTemperature() const
{
return activeTemperature;
}
inline float PID::GetStandbyTemperature() const
{
return standbyTemperature;
}
inline float PID::GetTemperature() const
{
return temperature;
}
inline bool PID::FaultOccurred() const
{
return mode == HeaterMode::fault;
}
inline bool PID::SwitchedOff() const
{
return mode == HeaterMode::off;
}
inline uint32_t PID::GetLastSampleTime() const
{
return lastSampleTime;
}
inline float PID::GetAccumulator() const
{
return iAccumulator;
}
inline bool PID::IsTuning() const
{
return mode >= HeaterMode::tuning0;
}
#endif /* SRC_PID_H_ */
|