Work in progress
Working, but has temp offset issue slightly. Could have slightly wrong gain values
This commit is contained in:
@@ -44,9 +44,7 @@ typedef struct {
|
||||
|
||||
uint8_t customTipGain; // Tip gain value if custom tuned, or 0 if using a
|
||||
// tipType param
|
||||
#ifdef MODEL_TS80
|
||||
uint8_t pidPowerLimit;
|
||||
#endif
|
||||
uint8_t version; // Used to track if a reset is needed on firmware upgrade
|
||||
uint32_t padding; // This is here for in case we are not an even divisor so
|
||||
// that nothing gets cut off
|
||||
|
||||
@@ -18,22 +18,21 @@
|
||||
// Once we have feed-forward temp estimation we should be able to better tune this.
|
||||
|
||||
#ifdef MODEL_TS100
|
||||
const uint16_t tipMass = 450; // divide here so division is compile-time.
|
||||
const int32_t tipMass = 3500; // divide here so division is compile-time.
|
||||
const uint8_t tipResistance = 85; //x10 ohms, 8.5 typical for ts100, 4.5 typical for ts80
|
||||
|
||||
#endif
|
||||
#ifdef MODEL_TS80
|
||||
const uint16_t tipMass = 450;
|
||||
const uint32_t tipMass = 4500;
|
||||
const uint8_t tipResistance = 45; //x10 ohms, 8.5 typical for ts100, 4.5 typical for ts80
|
||||
|
||||
#endif
|
||||
const uint8_t oscillationPeriod = 6 * PID_TIM_HZ; // I term look back value
|
||||
extern history<uint32_t, oscillationPeriod> milliWattHistory;
|
||||
|
||||
int32_t tempToMilliWatts(int32_t rawTemp);
|
||||
void setTipMilliWatts(int32_t mw);
|
||||
uint8_t milliWattsToPWM(int32_t milliWatts, uint8_t divisor,
|
||||
uint8_t sample = 0);
|
||||
int32_t PWMToMilliWatts(uint8_t pwm, uint8_t divisor, uint8_t sample = 0);
|
||||
const uint8_t oscillationPeriod = 8*PID_TIM_HZ; // I term look back value
|
||||
extern history<uint32_t, oscillationPeriod> x10WattHistory;
|
||||
|
||||
int32_t tempToX10Watts(int32_t rawTemp);
|
||||
void setTipX10Watts(int32_t mw);
|
||||
uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample = 0);
|
||||
int32_t PWMToX10Watts(uint8_t pwm, uint8_t sample = 0);
|
||||
uint32_t availableW10(uint8_t sample) ;
|
||||
#endif /* POWER_HPP_ */
|
||||
|
||||
@@ -502,9 +502,9 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
|
||||
if (systemSettings.detailedSoldering) {
|
||||
OLED::setFont(1);
|
||||
OLED::print(SolderingAdvancedPowerPrompt); // Power:
|
||||
OLED::printNumber(milliWattHistory[0] / 1000, 2);
|
||||
OLED::printNumber(x10WattHistory[0] / 10, 2);
|
||||
OLED::print(SymbolDot);
|
||||
OLED::printNumber(milliWattHistory[0] / 100 % 10, 1);
|
||||
OLED::printNumber(x10WattHistory[0] % 10, 1);
|
||||
OLED::print(SymbolWatts);
|
||||
|
||||
if (systemSettings.sensitivity && systemSettings.SleepTime) {
|
||||
@@ -514,6 +514,9 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
|
||||
|
||||
OLED::setCursor(0, 8);
|
||||
OLED::print(SleepingTipAdvancedString);
|
||||
//OLED::printNumber(
|
||||
// TipThermoModel::convertTipRawADCTouV(getTipRawTemp(0)), 5); // Draw the tip temp out finally
|
||||
|
||||
gui_drawTipTemp(true);
|
||||
OLED::print(SymbolSpace);
|
||||
printVoltage();
|
||||
@@ -535,14 +538,10 @@ static void gui_solderingMode(uint8_t jumpToSleep) {
|
||||
OLED::print(SymbolSpace);
|
||||
|
||||
// Draw heating/cooling symbols
|
||||
OLED::drawHeatSymbol(
|
||||
milliWattsToPWM(milliWattHistory[0],
|
||||
systemSettings.voltageDiv));
|
||||
OLED::drawHeatSymbol(X10WattsToPWM(x10WattHistory[0]));
|
||||
} else {
|
||||
// Draw heating/cooling symbols
|
||||
OLED::drawHeatSymbol(
|
||||
milliWattsToPWM(milliWattHistory[0],
|
||||
systemSettings.voltageDiv));
|
||||
OLED::drawHeatSymbol(X10WattsToPWM(x10WattHistory[0]));
|
||||
// We draw boost arrow if boosting, or else gap temp <-> heat
|
||||
// indicator
|
||||
if (boostModeOn)
|
||||
@@ -645,13 +644,17 @@ void showDebugMenu(void) {
|
||||
break;
|
||||
case 6:
|
||||
//Raw Tip
|
||||
OLED::printNumber(TipThermoModel::convertTipRawADCTouV(getTipRawTemp(0)), 6);
|
||||
{
|
||||
uint32_t temp = systemSettings.CalibrationOffset;
|
||||
systemSettings.CalibrationOffset = 0;
|
||||
OLED::printNumber(
|
||||
TipThermoModel::convertTipRawADCTouV(getTipRawTemp(1)), 6);
|
||||
systemSettings.CalibrationOffset = temp;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
//Temp in C
|
||||
OLED::printNumber(
|
||||
TipThermoModel::convertTipRawADCToDegC(getTipRawTemp(0)),
|
||||
5);
|
||||
OLED::printNumber(TipThermoModel::getTipInC(1), 5);
|
||||
break;
|
||||
case 8:
|
||||
//Handle Temp
|
||||
|
||||
@@ -106,13 +106,14 @@ void resetSettings() {
|
||||
systemSettings.descriptionScrollSpeed = 0; // default to slow
|
||||
|
||||
#ifdef MODEL_TS100
|
||||
|
||||
systemSettings.CalibrationOffset = 650; // the adc offset in uV
|
||||
systemSettings.CalibrationOffset = 300; // the adc offset in uV
|
||||
systemSettings.pidPowerLimit=70; // Sets the max pwm power limit
|
||||
|
||||
#endif
|
||||
#ifdef MODEL_TS80
|
||||
systemSettings.pidPowerLimit=24; // Sets the max pwm power limit
|
||||
systemSettings.CalibrationOffset = 650; // the adc offset in uV
|
||||
|
||||
systemSettings.CalibrationOffset = 300; // the adc offset in uV
|
||||
#endif
|
||||
saveSettings(); // Save defaults
|
||||
}
|
||||
|
||||
@@ -256,6 +256,6 @@ uint32_t TipThermoModel::getTipInC(bool sampleNow) {
|
||||
uint32_t TipThermoModel::getTipInF(bool sampleNow) {
|
||||
uint32_t currentTipTempInF = TipThermoModel::convertTipRawADCToDegF(
|
||||
getTipRawTemp(sampleNow));
|
||||
currentTipTempInF += convertCtoF(getHandleTemperature() / 10); //Add handle offset
|
||||
return currentTipTempInF;
|
||||
currentTipTempInF += convertCtoF(getHandleTemperature() / 10); //Add handle offset
|
||||
return currentTipTempInF;
|
||||
}
|
||||
|
||||
@@ -581,8 +581,9 @@ static void setTipOffset() {
|
||||
OLED::clearScreen();
|
||||
OLED::setCursor(0, 0);
|
||||
OLED::drawCheckbox(true);
|
||||
OLED::printNumber(systemSettings.CalibrationOffset,4);
|
||||
OLED::refresh();
|
||||
osDelay(1000);
|
||||
osDelay(1200);
|
||||
}
|
||||
|
||||
//Provide the user the option to tune their own tip if custom is selected
|
||||
|
||||
@@ -27,7 +27,6 @@ uint16_t getHandleTemperature() {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
uint16_t getTipInstantTemperature() {
|
||||
uint16_t sum = 0; // 12 bit readings * 8 -> 15 bits
|
||||
uint16_t readings[8];
|
||||
@@ -42,23 +41,15 @@ uint16_t getTipInstantTemperature() {
|
||||
readings[5] = hadc2.Instance->JDR2;
|
||||
readings[6] = hadc2.Instance->JDR3;
|
||||
readings[7] = hadc2.Instance->JDR4;
|
||||
uint8_t minID = 0, maxID = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (readings[i] < readings[minID])
|
||||
minID = i;
|
||||
else if (readings[i] > readings[maxID])
|
||||
maxID = i;
|
||||
sum += readings[i];
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (i != maxID)
|
||||
sum += readings[i];
|
||||
}
|
||||
sum += readings[minID]; //Duplicate the min to make up for the missing max value
|
||||
return sum; // 8x over sample
|
||||
}
|
||||
|
||||
//2 second filter (ADC is PID_TIM_HZ Hz)
|
||||
history<uint16_t, PID_TIM_HZ*4> rawTempFilter = { { 0 }, 0, 0 };
|
||||
history<uint16_t, PID_TIM_HZ > rawTempFilter = { { 0 }, 0, 0 };
|
||||
|
||||
uint16_t getTipRawTemp(uint8_t refresh) {
|
||||
if (refresh) {
|
||||
@@ -354,7 +345,6 @@ void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void vApplicationIdleHook(void) {
|
||||
HAL_IWDG_Refresh(&hiwdg);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ int main(void) {
|
||||
HAL_Init();
|
||||
Setup_HAL(); // Setup all the HAL objects
|
||||
HAL_IWDG_Refresh(&hiwdg);
|
||||
setTipMilliWatts(0); // force tip off
|
||||
setTipX10Watts(0); // force tip off
|
||||
FRToSI2C::init(&hi2c1);
|
||||
OLED::initialize(); // start up the LCD
|
||||
OLED::setFont(0); // default to bigger font
|
||||
@@ -106,7 +106,7 @@ void startPIDTask(void const *argument __unused) {
|
||||
* We take the current tip temperature & evaluate the next step for the tip
|
||||
* control PWM.
|
||||
*/
|
||||
setTipMilliWatts(0); // disable the output driver if the output is set to be off
|
||||
setTipX10Watts(0); // disable the output driver if the output is set to be off
|
||||
#ifdef MODEL_TS80
|
||||
idealQCVoltage = calculateMaxVoltage(systemSettings.cutoutSetting);
|
||||
#endif
|
||||
@@ -118,7 +118,7 @@ void startPIDTask(void const *argument __unused) {
|
||||
#else
|
||||
|
||||
#endif
|
||||
history<int32_t, 16> tempError = { { 0 }, 0, 0 };
|
||||
history<int32_t, PID_TIM_HZ > tempError = { { 0 }, 0, 0 };
|
||||
currentTempTargetDegC = 0; // Force start with no output (off). If in sleep / soldering this will
|
||||
// be over-ridden rapidly
|
||||
pidTaskNotification = xTaskGetCurrentTaskHandle();
|
||||
@@ -145,7 +145,7 @@ void startPIDTask(void const *argument __unused) {
|
||||
tempError.update(tError);
|
||||
|
||||
// Now for the PID!
|
||||
int32_t milliWattsOut = 0;
|
||||
int32_t x10WattsOut = 0;
|
||||
|
||||
// P term - total power needed to hit target temp next cycle.
|
||||
// thermal mass = 1690 milliJ/*C for my tip.
|
||||
@@ -154,17 +154,17 @@ void startPIDTask(void const *argument __unused) {
|
||||
// This is necessary because of the temp noise and thermal lag in the system.
|
||||
// Once we have feed-forward temp estimation we should be able to better tune this.
|
||||
|
||||
int32_t milliWattsNeeded = tempToMilliWatts(
|
||||
tempError.average());
|
||||
int32_t x10WattsNeeded = tempToX10Watts(tError);
|
||||
// tempError.average());
|
||||
// note that milliWattsNeeded is sometimes negative, this counters overshoot
|
||||
// from I term's inertia.
|
||||
milliWattsOut += milliWattsNeeded;
|
||||
x10WattsOut += x10WattsNeeded;
|
||||
|
||||
// I term - energy needed to compensate for heat loss.
|
||||
// We track energy put into the system over some window.
|
||||
// Assuming the temp is stable, energy in = energy transfered.
|
||||
// (If it isn't, P will dominate).
|
||||
milliWattsOut += milliWattHistory.average();
|
||||
x10WattsOut += x10WattHistory.average();
|
||||
|
||||
// D term - use sudden temp change to counter fast cooling/heating.
|
||||
// In practice, this provides an early boost if temp is dropping
|
||||
@@ -172,7 +172,7 @@ void startPIDTask(void const *argument __unused) {
|
||||
// basically: temp - lastTemp
|
||||
// Unfortunately, our temp signal is too noisy to really help.
|
||||
|
||||
setTipMilliWatts(milliWattsOut);
|
||||
setTipX10Watts(x10WattsOut);
|
||||
} else {
|
||||
|
||||
#ifdef MODEL_TS80
|
||||
@@ -180,15 +180,15 @@ void startPIDTask(void const *argument __unused) {
|
||||
// This is purely guesswork :'( as everyone implements stuff differently
|
||||
if (xTaskGetTickCount() - lastPowerPulse < 10) {
|
||||
// for the first 100mS turn on for a bit
|
||||
setTipMilliWatts(2500); // typically its around 5W to hold the current temp, so this wont raise temp much
|
||||
setTipX10Watts(25); // typically its around 5W to hold the current temp, so this wont raise temp much
|
||||
} else
|
||||
setTipMilliWatts(0);
|
||||
setTipX10Watts(0);
|
||||
//Then wait until the next 0.5 seconds
|
||||
if (xTaskGetTickCount() - lastPowerPulse > 50) {
|
||||
lastPowerPulse = xTaskGetTickCount();
|
||||
}
|
||||
#else
|
||||
setTipMilliWatts(0);
|
||||
setTipX10Watts(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -197,7 +197,6 @@ void startPIDTask(void const *argument __unused) {
|
||||
asm("bkpt");
|
||||
|
||||
//ADC interrupt timeout
|
||||
setTipMilliWatts(0);
|
||||
setTipPWM(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,33 +12,30 @@
|
||||
const uint16_t powerPWM = 255;
|
||||
const uint16_t totalPWM = 255 + 17; //htim2.Init.Period, the full PWM cycle
|
||||
|
||||
history<uint32_t, oscillationPeriod> milliWattHistory = { { 0 }, 0, 0 };
|
||||
history<uint32_t, oscillationPeriod> x10WattHistory = { { 0 }, 0, 0 };
|
||||
|
||||
int32_t tempToMilliWatts(int32_t rawTemp) {
|
||||
int32_t tempToX10Watts(int32_t rawTemp) {
|
||||
// mass is in milliJ/*C, rawC is raw per degree C
|
||||
// returns milliWatts needed to raise/lower a mass by rawTemp
|
||||
// degrees in one cycle.
|
||||
int32_t milliJoules = tipMass*10 * rawTemp;
|
||||
int32_t milliJoules = tipMass * rawTemp;
|
||||
return milliJoules;
|
||||
}
|
||||
|
||||
void setTipMilliWatts(int32_t mw) {
|
||||
//Enforce Max Watts Limiter # TODO
|
||||
|
||||
int32_t output = milliWattsToPWM(mw, systemSettings.voltageDiv , 1);
|
||||
void setTipX10Watts(int32_t mw) {
|
||||
int32_t output = X10WattsToPWM(mw, 1);
|
||||
setTipPWM(output);
|
||||
uint32_t actualMilliWatts = PWMToMilliWatts(output,
|
||||
systemSettings.voltageDiv , 0);
|
||||
uint32_t actualMilliWatts = PWMToX10Watts(output, 0);
|
||||
|
||||
milliWattHistory.update(actualMilliWatts);
|
||||
x10WattHistory.update(actualMilliWatts);
|
||||
}
|
||||
|
||||
int32_t availableW10(uint8_t divisor, uint8_t sample) {
|
||||
uint32_t availableW10(uint8_t sample) {
|
||||
//P = V^2 / R, v*v = v^2 * 100
|
||||
// R = R*10
|
||||
// P therefore is in V^2*100/R*10 = W*10.
|
||||
int32_t v = getInputVoltageX10(divisor, sample); // 100 = 10v
|
||||
int32_t availableWattsX10 = (v * v) / tipResistance;
|
||||
uint32_t v = getInputVoltageX10(systemSettings.voltageDiv, sample); // 100 = 10v
|
||||
uint32_t availableWattsX10 = (v * v) / tipResistance;
|
||||
//However, 100% duty cycle is not possible as there is a dead time while the ADC takes a reading
|
||||
//Therefore need to scale available milliwats by this
|
||||
|
||||
@@ -50,27 +47,23 @@ int32_t availableW10(uint8_t divisor, uint8_t sample) {
|
||||
return availableWattsX10;
|
||||
}
|
||||
|
||||
uint8_t milliWattsToPWM(int32_t milliWatts, uint8_t divisor, uint8_t sample) {
|
||||
|
||||
// Scale input milliWatts to the pwm rate
|
||||
if (milliWatts < 10) // no pint driving tip
|
||||
uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample) {
|
||||
// Scale input milliWatts to the pwm range available
|
||||
if (milliWatts < 1)
|
||||
return 0;
|
||||
|
||||
// if (milliWatts > (int(systemSettings.pidPowerLimit) * 10))
|
||||
// milliWatts = (int(systemSettings.pidPowerLimit) * 10);
|
||||
//Calculate desired milliwatts as a percentage of availableW10
|
||||
int32_t pwm = (powerPWM * milliWatts) / availableW10(divisor, sample);
|
||||
uint32_t pwm = (powerPWM * milliWatts) / availableW10(sample);
|
||||
if (pwm > powerPWM) {
|
||||
pwm = powerPWM; //constrain to max PWM counter, shouldnt be possible, but small cost for safety to avoid wraps
|
||||
} else if (pwm < 0) { //cannot go negative
|
||||
pwm = 0;
|
||||
}
|
||||
return pwm;
|
||||
}
|
||||
|
||||
int32_t PWMToMilliWatts(uint8_t pwm, uint8_t divisor, uint8_t sample) {
|
||||
int32_t maxMW = availableW10(divisor, sample); //Get the milliwatts for the max pwm period
|
||||
int32_t PWMToX10Watts(uint8_t pwm, uint8_t sample) {
|
||||
uint32_t maxMW = availableW10(sample); //Get the milliwatts for the max pwm period
|
||||
//Then convert pwm into percentage of powerPWM to get the percentage of the max mw
|
||||
int32_t res = (pwm * maxMW) / powerPWM;
|
||||
if (res < 0)
|
||||
res = 0;
|
||||
return res;
|
||||
return (((uint32_t) pwm) * maxMW) / powerPWM;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user