./workspace/TS100 -> ./source/
This commit is contained in:
964
source/Core/Threads/GUIThread.cpp
Normal file
964
source/Core/Threads/GUIThread.cpp
Normal file
@@ -0,0 +1,964 @@
|
||||
/*
|
||||
* GUIThread.cpp
|
||||
*
|
||||
* Created on: 19 Aug 2019
|
||||
* Author: ralim
|
||||
*/
|
||||
extern "C" {
|
||||
#include "FreeRTOSConfig.h"
|
||||
}
|
||||
#include "../../configuration.h"
|
||||
#include "Buttons.hpp"
|
||||
#include "I2CBB.hpp"
|
||||
#include "LIS2DH12.hpp"
|
||||
#include "Settings.h"
|
||||
#include "TipThermoModel.h"
|
||||
#include "Translation.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "main.hpp"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include "unit.h"
|
||||
#include <MMA8652FC.hpp>
|
||||
#include <gui.hpp>
|
||||
#include <history.hpp>
|
||||
#include <power.hpp>
|
||||
#ifdef POW_PD
|
||||
#include "policy_engine.h"
|
||||
#endif
|
||||
// File local variables
|
||||
extern uint32_t currentTempTargetDegC;
|
||||
extern TickType_t lastMovementTime;
|
||||
extern osThreadId GUITaskHandle;
|
||||
extern osThreadId MOVTaskHandle;
|
||||
extern osThreadId PIDTaskHandle;
|
||||
static bool shouldBeSleeping(bool inAutoStart = false);
|
||||
static bool shouldShutdown();
|
||||
void showWarnings();
|
||||
#define MOVEMENT_INACTIVITY_TIME (60 * configTICK_RATE_HZ)
|
||||
#define BUTTON_INACTIVITY_TIME (60 * configTICK_RATE_HZ)
|
||||
static TickType_t lastHallEffectSleepStart = 0;
|
||||
static uint16_t min(uint16_t a, uint16_t b) {
|
||||
if (a > b)
|
||||
return b;
|
||||
else
|
||||
return a;
|
||||
}
|
||||
void warnUser(const char *warning, const int font, const int timeout) {
|
||||
OLED::setFont(font);
|
||||
OLED::clearScreen();
|
||||
OLED::setCursor(0, 0);
|
||||
OLED::print(warning);
|
||||
OLED::refresh();
|
||||
waitForButtonPressOrTimeout(timeout);
|
||||
}
|
||||
|
||||
void printVoltage() {
|
||||
uint32_t volt = getInputVoltageX10(systemSettings.voltageDiv, 0);
|
||||
OLED::printNumber(volt / 10, 2);
|
||||
OLED::print(SymbolDot);
|
||||
OLED::printNumber(volt % 10, 1);
|
||||
}
|
||||
void GUIDelay() {
|
||||
// Called in all UI looping tasks,
|
||||
// This limits the re-draw rate to the LCD and also lets the DMA run
|
||||
// As the gui task can very easily fill this bus with transactions, which will
|
||||
// prevent the movement detection from running
|
||||
osDelay(50);
|
||||
}
|
||||
void gui_drawTipTemp(bool symbol) {
|
||||
// Draw tip temp handling unit conversion & tolerance near setpoint
|
||||
uint32_t Temp = 0;
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF) {
|
||||
Temp = TipThermoModel::getTipInF();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
Temp = TipThermoModel::getTipInC();
|
||||
}
|
||||
|
||||
OLED::printNumber(Temp, 3); // Draw the tip temp out
|
||||
if (symbol) {
|
||||
if (OLED::getFont() == 0) {
|
||||
// Big font, can draw nice symbols
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF)
|
||||
OLED::drawSymbol(0);
|
||||
else
|
||||
#endif
|
||||
OLED::drawSymbol(1);
|
||||
} else {
|
||||
// Otherwise fall back to chars
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF)
|
||||
OLED::print(SymbolDegF);
|
||||
else
|
||||
#endif
|
||||
OLED::print(SymbolDegC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef POW_DC
|
||||
// returns true if undervoltage has occured
|
||||
static bool checkVoltageForExit() {
|
||||
if (!getIsPoweredByDCIN()) {
|
||||
return false;
|
||||
}
|
||||
uint16_t v = getInputVoltageX10(systemSettings.voltageDiv, 0);
|
||||
|
||||
// Dont check for first 2 seconds while the ADC stabilizes and the DMA fills
|
||||
// the buffer
|
||||
if (xTaskGetTickCount() > (TICKS_SECOND * 2)) {
|
||||
if ((v < lookupVoltageLevel())) {
|
||||
currentTempTargetDegC = 0;
|
||||
OLED::clearScreen();
|
||||
OLED::setCursor(0, 0);
|
||||
if (systemSettings.detailedSoldering) {
|
||||
OLED::setFont(1);
|
||||
OLED::print(UndervoltageString);
|
||||
OLED::setCursor(0, 8);
|
||||
OLED::print(InputVoltageString);
|
||||
printVoltage();
|
||||
OLED::print(SymbolVolts);
|
||||
} else {
|
||||
OLED::setFont(0);
|
||||
OLED::print(UVLOWarningString);
|
||||
}
|
||||
|
||||
OLED::refresh();
|
||||
GUIDelay();
|
||||
waitForButtonPress();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
static void gui_drawBatteryIcon() {
|
||||
#if defined(POW_PD) || defined(POW_QC)
|
||||
if (!getIsPoweredByDCIN()) {
|
||||
// On TS80 we replace this symbol with the voltage we are operating on
|
||||
// If <9V then show single digit, if not show dual small ones vertically stacked
|
||||
uint8_t V = getInputVoltageX10(systemSettings.voltageDiv, 0);
|
||||
if (V % 10 >= 5)
|
||||
V = V / 10 + 1; // round up
|
||||
else
|
||||
V = V / 10;
|
||||
if (V >= 10) {
|
||||
int16_t xPos = OLED::getCursorX();
|
||||
OLED::setFont(1);
|
||||
OLED::printNumber(V / 10, 1);
|
||||
OLED::setCursor(xPos, 8);
|
||||
OLED::printNumber(V % 10, 1);
|
||||
OLED::setFont(0);
|
||||
OLED::setCursor(xPos + 12, 0); // need to reset this as if we drew a wide char
|
||||
} else {
|
||||
OLED::printNumber(V, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef POW_DC
|
||||
if (systemSettings.minDCVoltageCells) {
|
||||
// User is on a lithium battery
|
||||
// we need to calculate which of the 10 levels they are on
|
||||
uint8_t cellCount = systemSettings.minDCVoltageCells + 2;
|
||||
uint32_t cellV = getInputVoltageX10(systemSettings.voltageDiv, 0) / cellCount;
|
||||
// Should give us approx cell voltage X10
|
||||
// Range is 42 -> 33 = 9 steps therefore we will use battery 0-9
|
||||
if (cellV < 33)
|
||||
cellV = 33;
|
||||
cellV -= 33; // Should leave us a number of 0-9
|
||||
if (cellV > 9)
|
||||
cellV = 9;
|
||||
OLED::drawBattery(cellV + 1);
|
||||
} else {
|
||||
OLED::drawSymbol(15); // Draw the DC Logo
|
||||
}
|
||||
#endif
|
||||
}
|
||||
static void gui_solderingTempAdjust() {
|
||||
uint32_t lastChange = xTaskGetTickCount();
|
||||
currentTempTargetDegC = 0;
|
||||
uint32_t autoRepeatTimer = 0;
|
||||
uint8_t autoRepeatAcceleration = 0;
|
||||
for (;;) {
|
||||
OLED::setCursor(0, 0);
|
||||
OLED::clearScreen();
|
||||
OLED::setFont(0);
|
||||
ButtonState buttons = getButtonState();
|
||||
if (buttons)
|
||||
lastChange = xTaskGetTickCount();
|
||||
switch (buttons) {
|
||||
case BUTTON_NONE:
|
||||
// stay
|
||||
break;
|
||||
case BUTTON_BOTH:
|
||||
// exit
|
||||
return;
|
||||
break;
|
||||
case BUTTON_B_LONG:
|
||||
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration >
|
||||
PRESS_ACCEL_INTERVAL_MAX) {
|
||||
if (systemSettings.ReverseButtonTempChangeEnabled) {
|
||||
systemSettings.SolderingTemp += systemSettings.TempChangeLongStep;
|
||||
} else
|
||||
systemSettings.SolderingTemp -= systemSettings.TempChangeLongStep;
|
||||
|
||||
autoRepeatTimer = xTaskGetTickCount();
|
||||
autoRepeatAcceleration += PRESS_ACCEL_STEP;
|
||||
}
|
||||
break;
|
||||
case BUTTON_B_SHORT:
|
||||
if (systemSettings.ReverseButtonTempChangeEnabled) {
|
||||
systemSettings.SolderingTemp += systemSettings.TempChangeShortStep;
|
||||
} else
|
||||
systemSettings.SolderingTemp -= systemSettings.TempChangeShortStep;
|
||||
break;
|
||||
case BUTTON_F_LONG:
|
||||
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration >
|
||||
PRESS_ACCEL_INTERVAL_MAX) {
|
||||
if (systemSettings.ReverseButtonTempChangeEnabled) {
|
||||
systemSettings.SolderingTemp -= systemSettings.TempChangeLongStep;
|
||||
} else
|
||||
systemSettings.SolderingTemp += systemSettings.TempChangeLongStep;
|
||||
autoRepeatTimer = xTaskGetTickCount();
|
||||
autoRepeatAcceleration += PRESS_ACCEL_STEP;
|
||||
}
|
||||
break;
|
||||
case BUTTON_F_SHORT:
|
||||
if (systemSettings.ReverseButtonTempChangeEnabled) {
|
||||
systemSettings.SolderingTemp -= systemSettings.TempChangeShortStep; // add 10
|
||||
} else
|
||||
systemSettings.SolderingTemp += systemSettings.TempChangeShortStep; // add 10
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if ((PRESS_ACCEL_INTERVAL_MAX - autoRepeatAcceleration) <
|
||||
PRESS_ACCEL_INTERVAL_MIN) {
|
||||
autoRepeatAcceleration =
|
||||
PRESS_ACCEL_INTERVAL_MAX - PRESS_ACCEL_INTERVAL_MIN;
|
||||
}
|
||||
// constrain between 10-450 C
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF) {
|
||||
if (systemSettings.SolderingTemp > 850)
|
||||
systemSettings.SolderingTemp = 850;
|
||||
if (systemSettings.SolderingTemp < 60)
|
||||
systemSettings.SolderingTemp = 60;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (systemSettings.SolderingTemp > 450)
|
||||
systemSettings.SolderingTemp = 450;
|
||||
if (systemSettings.SolderingTemp < 10)
|
||||
systemSettings.SolderingTemp = 10;
|
||||
}
|
||||
|
||||
if (xTaskGetTickCount() - lastChange > 2000)
|
||||
return; // exit if user just doesn't press anything for a bit
|
||||
|
||||
#ifdef OLED_FLIP
|
||||
if (!OLED::getRotation()) {
|
||||
#else
|
||||
if (OLED::getRotation()) {
|
||||
#endif
|
||||
OLED::print(systemSettings.ReverseButtonTempChangeEnabled ? SymbolPlus : SymbolMinus);
|
||||
} else {
|
||||
OLED::print(systemSettings.ReverseButtonTempChangeEnabled ? SymbolMinus : SymbolPlus);
|
||||
}
|
||||
|
||||
OLED::print(SymbolSpace);
|
||||
OLED::printNumber(systemSettings.SolderingTemp, 3);
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF)
|
||||
OLED::drawSymbol(0);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
OLED::drawSymbol(1);
|
||||
}
|
||||
OLED::print(SymbolSpace);
|
||||
#ifdef OLED_FLIP
|
||||
if (!OLED::getRotation()) {
|
||||
#else
|
||||
if (OLED::getRotation()) {
|
||||
#endif
|
||||
OLED::print(systemSettings.ReverseButtonTempChangeEnabled ? SymbolMinus : SymbolPlus);
|
||||
} else {
|
||||
OLED::print(systemSettings.ReverseButtonTempChangeEnabled ? SymbolPlus : SymbolMinus);
|
||||
}
|
||||
OLED::refresh();
|
||||
GUIDelay();
|
||||
}
|
||||
}
|
||||
static bool shouldShutdown() {
|
||||
if (systemSettings.ShutdownTime) { // only allow shutdown exit if time > 0
|
||||
if (lastMovementTime) {
|
||||
if (((TickType_t) (xTaskGetTickCount() - lastMovementTime)) > (TickType_t) (systemSettings.ShutdownTime * TICKS_MIN)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (lastHallEffectSleepStart) {
|
||||
if (((TickType_t) (xTaskGetTickCount() - lastHallEffectSleepStart)) > (TickType_t) (systemSettings.ShutdownTime * TICKS_MIN)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static int gui_SolderingSleepingMode(bool stayOff, bool autoStarted) {
|
||||
// Drop to sleep temperature and display until movement or button press
|
||||
|
||||
for (;;) {
|
||||
// user moved or pressed a button, go back to soldering
|
||||
//If in the first two seconds we disable this to let accelerometer warm up
|
||||
|
||||
#ifdef POW_DC
|
||||
if (checkVoltageForExit())
|
||||
return 1; // return non-zero on error
|
||||
#endif
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF) {
|
||||
currentTempTargetDegC = stayOff ? 0 : TipThermoModel::convertFtoC(min(systemSettings.SleepTemp, systemSettings.SolderingTemp));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
currentTempTargetDegC = stayOff ? 0 : min(systemSettings.SleepTemp, systemSettings.SolderingTemp);
|
||||
}
|
||||
// draw the lcd
|
||||
uint16_t tipTemp;
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF)
|
||||
tipTemp = TipThermoModel::getTipInF();
|
||||
else
|
||||
#endif
|
||||
{
|
||||
tipTemp = TipThermoModel::getTipInC();
|
||||
}
|
||||
|
||||
OLED::clearScreen();
|
||||
OLED::setCursor(0, 0);
|
||||
if (systemSettings.detailedSoldering) {
|
||||
OLED::setFont(1);
|
||||
OLED::print(SleepingAdvancedString);
|
||||
OLED::setCursor(0, 8);
|
||||
OLED::print(SleepingTipAdvancedString);
|
||||
OLED::printNumber(tipTemp, 3);
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF)
|
||||
OLED::print(SymbolDegF);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
OLED::print(SymbolDegC);
|
||||
}
|
||||
|
||||
OLED::print(SymbolSpace);
|
||||
printVoltage();
|
||||
OLED::print(SymbolVolts);
|
||||
} else {
|
||||
OLED::setFont(0);
|
||||
OLED::print(SleepingSimpleString);
|
||||
OLED::printNumber(tipTemp, 3);
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF)
|
||||
OLED::drawSymbol(0);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
OLED::drawSymbol(1);
|
||||
}
|
||||
}
|
||||
|
||||
OLED::refresh();
|
||||
GUIDelay();
|
||||
if (!shouldBeSleeping(autoStarted)) {
|
||||
return 0;
|
||||
}
|
||||
if (shouldShutdown()) {
|
||||
// shutdown
|
||||
currentTempTargetDegC = 0;
|
||||
return 1; // we want to exit soldering mode
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void display_countdown(int sleepThres) {
|
||||
/*
|
||||
* Print seconds or minutes (if > 99 seconds) until sleep
|
||||
* mode is triggered.
|
||||
*/
|
||||
int lastEventTime = lastButtonTime < lastMovementTime ? lastMovementTime : lastButtonTime;
|
||||
TickType_t downCount = sleepThres - xTaskGetTickCount() + lastEventTime;
|
||||
if (downCount > (99 * TICKS_SECOND)) {
|
||||
OLED::printNumber(downCount / 60000 + 1, 2);
|
||||
OLED::print(SymbolMinutes);
|
||||
} else {
|
||||
OLED::printNumber(downCount / 1000 + 1, 2);
|
||||
OLED::print(SymbolSeconds);
|
||||
}
|
||||
}
|
||||
static uint32_t getSleepTimeout() {
|
||||
if (systemSettings.sensitivity && systemSettings.SleepTime) {
|
||||
|
||||
uint32_t sleepThres = 0;
|
||||
if (systemSettings.SleepTime < 6)
|
||||
sleepThres = systemSettings.SleepTime * 10 * 1000;
|
||||
else
|
||||
sleepThres = (systemSettings.SleepTime - 5) * 60 * 1000;
|
||||
return sleepThres;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static bool shouldBeSleeping(bool inAutoStart) {
|
||||
// Return true if the iron should be in sleep mode
|
||||
if (systemSettings.sensitivity && systemSettings.SleepTime) {
|
||||
if (inAutoStart) {
|
||||
//In auto start we are asleep until movement
|
||||
if (lastMovementTime == 0 && lastButtonTime == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (lastMovementTime > 0 || lastButtonTime > 0) {
|
||||
if ((xTaskGetTickCount() - lastMovementTime) > getSleepTimeout() && (xTaskGetTickCount() - lastButtonTime) > getSleepTimeout()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HALL_SENSOR
|
||||
// If the hall effect sensor is enabled in the build, check if its over
|
||||
// threshold, and if so then we force sleep
|
||||
if (lookupHallEffectThreshold()) {
|
||||
int16_t hallEffectStrength = getRawHallEffect();
|
||||
if (hallEffectStrength < 0)
|
||||
hallEffectStrength = -hallEffectStrength;
|
||||
// Have absolute value of measure of magnetic field strength
|
||||
if (hallEffectStrength > lookupHallEffectThreshold()) {
|
||||
if (lastHallEffectSleepStart == 0) {
|
||||
lastHallEffectSleepStart = xTaskGetTickCount();
|
||||
}
|
||||
if ((xTaskGetTickCount() - lastHallEffectSleepStart) > TICKS_SECOND) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
lastHallEffectSleepStart = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
static void gui_solderingMode(uint8_t jumpToSleep) {
|
||||
/*
|
||||
* * Soldering (gui_solderingMode)
|
||||
* -> Main loop where we draw temp, and animations
|
||||
* --> User presses buttons and they goto the temperature adjust screen
|
||||
* ---> Display the current setpoint temperature
|
||||
* ---> Use buttons to change forward and back on temperature
|
||||
* ---> Both buttons or timeout for exiting
|
||||
* --> Long hold front button to enter boost mode
|
||||
* ---> Just temporarily sets the system into the alternate temperature for
|
||||
* PID control
|
||||
* --> Long hold back button to exit
|
||||
* --> Double button to exit
|
||||
* --> Long hold double button to toggle key lock
|
||||
*/
|
||||
bool boostModeOn = false;
|
||||
bool buttonsLocked = false;
|
||||
|
||||
if (jumpToSleep) {
|
||||
if (gui_SolderingSleepingMode(jumpToSleep == 2, true) == 1) {
|
||||
lastButtonTime = xTaskGetTickCount();
|
||||
return; // If the function returns non-0 then exit
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
ButtonState buttons = getButtonState();
|
||||
if (buttonsLocked && (systemSettings.lockingMode != 0)) { // If buttons locked
|
||||
switch (buttons) {
|
||||
case BUTTON_NONE:
|
||||
boostModeOn = false;
|
||||
break;
|
||||
case BUTTON_BOTH_LONG:
|
||||
// Unlock buttons
|
||||
buttonsLocked = false;
|
||||
warnUser(UnlockingKeysString, 0, TICKS_SECOND);
|
||||
break;
|
||||
case BUTTON_F_LONG:
|
||||
// if boost mode is enabled turn it on
|
||||
if (systemSettings.BoostTemp && (systemSettings.lockingMode == 1)) {
|
||||
boostModeOn = true;
|
||||
}
|
||||
break;
|
||||
// fall through
|
||||
case BUTTON_BOTH:
|
||||
case BUTTON_B_LONG:
|
||||
case BUTTON_F_SHORT:
|
||||
case BUTTON_B_SHORT:
|
||||
// Do nothing and display a lock warming
|
||||
warnUser(WarningKeysLockedString, 0, TICKS_SECOND / 2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else { // Button not locked
|
||||
switch (buttons) {
|
||||
case BUTTON_NONE:
|
||||
// stay
|
||||
boostModeOn = false;
|
||||
break;
|
||||
case BUTTON_BOTH:
|
||||
// exit
|
||||
return;
|
||||
break;
|
||||
case BUTTON_B_LONG:
|
||||
return; // exit on back long hold
|
||||
break;
|
||||
case BUTTON_F_LONG:
|
||||
// if boost mode is enabled turn it on
|
||||
if (systemSettings.BoostTemp)
|
||||
boostModeOn = true;
|
||||
break;
|
||||
case BUTTON_F_SHORT:
|
||||
case BUTTON_B_SHORT: {
|
||||
uint16_t oldTemp = systemSettings.SolderingTemp;
|
||||
gui_solderingTempAdjust(); // goto adjust temp mode
|
||||
if (oldTemp != systemSettings.SolderingTemp) {
|
||||
saveSettings(); // only save on change
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BUTTON_BOTH_LONG:
|
||||
if (systemSettings.lockingMode != 0) {
|
||||
// Lock buttons
|
||||
buttonsLocked = true;
|
||||
warnUser(LockingKeysString, 0, TICKS_SECOND);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// else we update the screen information
|
||||
OLED::setCursor(0, 0);
|
||||
OLED::clearScreen();
|
||||
OLED::setFont(0);
|
||||
// Draw in the screen details
|
||||
if (systemSettings.detailedSoldering) {
|
||||
OLED::setFont(1);
|
||||
OLED::print(SolderingAdvancedPowerPrompt); // Power:
|
||||
OLED::printNumber(x10WattHistory.average() / 10, 2);
|
||||
OLED::print(SymbolDot);
|
||||
OLED::printNumber(x10WattHistory.average() % 10, 1);
|
||||
OLED::print(SymbolWatts);
|
||||
|
||||
if (systemSettings.sensitivity && systemSettings.SleepTime) {
|
||||
OLED::print(SymbolSpace);
|
||||
display_countdown(getSleepTimeout());
|
||||
}
|
||||
|
||||
OLED::setCursor(0, 8);
|
||||
OLED::print(SleepingTipAdvancedString);
|
||||
gui_drawTipTemp(true);
|
||||
OLED::print(SymbolSpace);
|
||||
printVoltage();
|
||||
OLED::print(SymbolVolts);
|
||||
} else {
|
||||
// We switch the layout direction depending on the orientation of the oled
|
||||
if (OLED::getRotation()) {
|
||||
// battery
|
||||
gui_drawBatteryIcon();
|
||||
OLED::print(SymbolSpace); // Space out gap between battery <-> temp
|
||||
gui_drawTipTemp(true); // Draw current tip temp
|
||||
|
||||
// We draw boost arrow if boosting, or else gap temp <-> heat
|
||||
// indicator
|
||||
if (boostModeOn)
|
||||
OLED::drawSymbol(2);
|
||||
else
|
||||
OLED::print(SymbolSpace);
|
||||
|
||||
// Draw heating/cooling symbols
|
||||
OLED::drawHeatSymbol(X10WattsToPWM(x10WattHistory.average()));
|
||||
} else {
|
||||
// Draw heating/cooling symbols
|
||||
OLED::drawHeatSymbol(X10WattsToPWM(x10WattHistory.average()));
|
||||
// We draw boost arrow if boosting, or else gap temp <-> heat
|
||||
// indicator
|
||||
if (boostModeOn)
|
||||
OLED::drawSymbol(2);
|
||||
else
|
||||
OLED::print(SymbolSpace);
|
||||
gui_drawTipTemp(true); // Draw current tip temp
|
||||
|
||||
OLED::print(SymbolSpace); // Space out gap between battery <-> temp
|
||||
|
||||
gui_drawBatteryIcon();
|
||||
}
|
||||
}
|
||||
OLED::refresh();
|
||||
|
||||
// Update the setpoints for the temperature
|
||||
if (boostModeOn) {
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF)
|
||||
currentTempTargetDegC = TipThermoModel::convertFtoC(systemSettings.BoostTemp);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
currentTempTargetDegC = (systemSettings.BoostTemp);
|
||||
}
|
||||
} else {
|
||||
#ifdef ENABLED_FAHRENHEIT_SUPPORT
|
||||
if (systemSettings.temperatureInF)
|
||||
currentTempTargetDegC = TipThermoModel::convertFtoC(systemSettings.SolderingTemp);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
currentTempTargetDegC = (systemSettings.SolderingTemp);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef POW_DC
|
||||
// Undervoltage test
|
||||
if (checkVoltageForExit()) {
|
||||
lastButtonTime = xTaskGetTickCount();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (shouldBeSleeping()) {
|
||||
if (gui_SolderingSleepingMode(false, false)) {
|
||||
return; // If the function returns non-0 then exit
|
||||
}
|
||||
}
|
||||
// slow down ui update rate
|
||||
GUIDelay();
|
||||
}
|
||||
}
|
||||
|
||||
void showDebugMenu(void) {
|
||||
uint8_t screen = 0;
|
||||
ButtonState b;
|
||||
OLED::setFont(1); // small font
|
||||
for (;;) {
|
||||
OLED::clearScreen(); // Ensure the buffer starts clean
|
||||
OLED::setCursor(0, 0); // Position the cursor at the 0,0 (top left)
|
||||
OLED::print(SymbolVersionNumber); // Print version number
|
||||
OLED::setCursor(0, 8); // second line
|
||||
OLED::print(DebugMenu[screen]);
|
||||
switch (screen) {
|
||||
case 0: // Just prints date
|
||||
break;
|
||||
case 1:
|
||||
// High water mark for GUI
|
||||
OLED::printNumber(uxTaskGetStackHighWaterMark(GUITaskHandle), 5);
|
||||
break;
|
||||
case 2:
|
||||
// High water mark for the Movement task
|
||||
OLED::printNumber(uxTaskGetStackHighWaterMark(MOVTaskHandle), 5);
|
||||
break;
|
||||
case 3:
|
||||
// High water mark for the PID task
|
||||
OLED::printNumber(uxTaskGetStackHighWaterMark(PIDTaskHandle), 5);
|
||||
break;
|
||||
case 4:
|
||||
// system up time stamp
|
||||
OLED::printNumber(xTaskGetTickCount() / 100, 5);
|
||||
break;
|
||||
case 5:
|
||||
// Movement time stamp
|
||||
OLED::printNumber(lastMovementTime / 100, 5);
|
||||
break;
|
||||
case 6:
|
||||
// Raw Tip
|
||||
{
|
||||
uint32_t temp = systemSettings.CalibrationOffset;
|
||||
systemSettings.CalibrationOffset = 0;
|
||||
OLED::printNumber(TipThermoModel::convertTipRawADCTouV(getTipRawTemp(0)), 6);
|
||||
systemSettings.CalibrationOffset = temp;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
// Temp in C
|
||||
OLED::printNumber(TipThermoModel::getTipInC(), 5);
|
||||
break;
|
||||
case 8:
|
||||
// Handle Temp
|
||||
OLED::printNumber(getHandleTemperature(), 3);
|
||||
break;
|
||||
case 9:
|
||||
// Voltage input
|
||||
printVoltage();
|
||||
break;
|
||||
case 10:
|
||||
// Print PCB ID number
|
||||
OLED::printNumber(DetectedAccelerometerVersion, 2);
|
||||
break;
|
||||
case 11:
|
||||
// Power negotiation status
|
||||
if (getIsPoweredByDCIN()) {
|
||||
OLED::printNumber(0, 1);
|
||||
} else {
|
||||
//We are not powered via DC, so want to display the appropriate state for PD or QC
|
||||
bool poweredbyPD = false;
|
||||
#ifdef POW_PD
|
||||
if (usb_pd_detect()) {
|
||||
//We are PD capable
|
||||
if (PolicyEngine::pdHasNegotiated()) {
|
||||
//We are powered via PD
|
||||
poweredbyPD = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (poweredbyPD) {
|
||||
OLED::printNumber(2, 1);
|
||||
} else {
|
||||
|
||||
OLED::printNumber(1, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
//Max deg C limit
|
||||
OLED::printNumber(TipThermoModel::getTipMaxInC(), 3);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
OLED::refresh();
|
||||
b = getButtonState();
|
||||
if (b == BUTTON_B_SHORT)
|
||||
return;
|
||||
else if (b == BUTTON_F_SHORT) {
|
||||
screen++;
|
||||
screen = screen % 13;
|
||||
}
|
||||
GUIDelay();
|
||||
}
|
||||
}
|
||||
|
||||
void showWarnings() {
|
||||
// Display alert if settings were reset
|
||||
if (settingsWereReset) {
|
||||
warnUser(SettingsResetMessage, 1, 10 * TICKS_SECOND);
|
||||
}
|
||||
#ifndef NO_WARN_MISSING
|
||||
//We also want to alert if accel or pd is not detected / not responding
|
||||
// In this case though, we dont want to nag the user _too_ much
|
||||
// So only show first 2 times
|
||||
while (DetectedAccelerometerVersion == ACCELEROMETERS_SCANNING) {
|
||||
osDelay(1);
|
||||
}
|
||||
// Display alert if accelerometer is not detected
|
||||
if (DetectedAccelerometerVersion == NO_DETECTED_ACCELEROMETER) {
|
||||
if (systemSettings.accelMissingWarningCounter < 2) {
|
||||
systemSettings.accelMissingWarningCounter++;
|
||||
saveSettings();
|
||||
warnUser(NoAccelerometerMessage, 1, 10 * TICKS_SECOND);
|
||||
}
|
||||
}
|
||||
#ifdef POW_PD
|
||||
//We expect pd to be present
|
||||
if (!usb_pd_detect()) {
|
||||
if (systemSettings.pdMissingWarningCounter < 2) {
|
||||
systemSettings.pdMissingWarningCounter++;
|
||||
saveSettings();
|
||||
warnUser(NoPowerDeliveryMessage, 1, 10 * TICKS_SECOND);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t idleScreenBGF[sizeof(idleScreenBG)];
|
||||
/* StartGUITask function */
|
||||
void startGUITask(void const *argument __unused) {
|
||||
OLED::initialize(); // start up the LCD
|
||||
|
||||
uint8_t tempWarningState = 0;
|
||||
bool buttonLockout = false;
|
||||
bool tempOnDisplay = false;
|
||||
bool tipDisconnectedDisplay = false;
|
||||
{
|
||||
// Generate the flipped screen into ram for later use
|
||||
// flipped is generated by flipping each row
|
||||
for (int row = 0; row < 2; row++) {
|
||||
for (int x = 0; x < 84; x++) {
|
||||
idleScreenBGF[(row * 84) + x] = idleScreenBG[(row * 84) + (83 - x)];
|
||||
}
|
||||
}
|
||||
}
|
||||
getTipRawTemp(1); // reset filter
|
||||
OLED::setRotation(systemSettings.OrientationMode & 1);
|
||||
uint32_t ticks = xTaskGetTickCount();
|
||||
ticks += 4000; // 4 seconds from now
|
||||
while (xTaskGetTickCount() < ticks) {
|
||||
if (showBootLogoIfavailable() == false)
|
||||
ticks = xTaskGetTickCount();
|
||||
ButtonState buttons = getButtonState();
|
||||
if (buttons)
|
||||
ticks = xTaskGetTickCount(); // make timeout now so we will exit
|
||||
OLED::refresh();
|
||||
GUIDelay();
|
||||
}
|
||||
|
||||
showWarnings();
|
||||
|
||||
if (systemSettings.autoStartMode) {
|
||||
// jump directly to the autostart mode
|
||||
gui_solderingMode(systemSettings.autoStartMode - 1);
|
||||
buttonLockout = true;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ButtonState buttons = getButtonState();
|
||||
if (buttons != BUTTON_NONE) {
|
||||
OLED::setDisplayState(OLED::DisplayState::ON);
|
||||
OLED::setFont(0);
|
||||
}
|
||||
if (tempWarningState == 2)
|
||||
buttons = BUTTON_F_SHORT;
|
||||
if (buttons != BUTTON_NONE && buttonLockout)
|
||||
buttons = BUTTON_NONE;
|
||||
else
|
||||
buttonLockout = false;
|
||||
|
||||
switch (buttons) {
|
||||
case BUTTON_NONE:
|
||||
// Do nothing
|
||||
break;
|
||||
case BUTTON_BOTH:
|
||||
// Not used yet
|
||||
// In multi-language this might be used to reset language on a long hold
|
||||
// or some such
|
||||
break;
|
||||
|
||||
case BUTTON_B_LONG:
|
||||
// Show the version information
|
||||
showDebugMenu();
|
||||
break;
|
||||
case BUTTON_F_LONG:
|
||||
gui_solderingTempAdjust();
|
||||
saveSettings();
|
||||
break;
|
||||
case BUTTON_F_SHORT:
|
||||
gui_solderingMode(0); // enter soldering mode
|
||||
buttonLockout = true;
|
||||
break;
|
||||
case BUTTON_B_SHORT:
|
||||
enterSettingsMenu(); // enter the settings menu
|
||||
buttonLockout = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
currentTempTargetDegC = 0; // ensure tip is off
|
||||
getInputVoltageX10(systemSettings.voltageDiv, 0);
|
||||
uint32_t tipTemp = TipThermoModel::getTipInC();
|
||||
|
||||
// Preemptively turn the display on. Turn it off if and only if
|
||||
// the tip temperature is below 50 degrees C *and* motion sleep
|
||||
// detection is enabled *and* there has been no activity (movement or
|
||||
// button presses) in a while.
|
||||
// This is zero cost really as state is only changed on display updates
|
||||
OLED::setDisplayState(OLED::DisplayState::ON);
|
||||
|
||||
if ((tipTemp < 50) && systemSettings.sensitivity && (((xTaskGetTickCount() - lastMovementTime) >
|
||||
MOVEMENT_INACTIVITY_TIME) && ((xTaskGetTickCount() - lastButtonTime) > BUTTON_INACTIVITY_TIME))) {
|
||||
OLED::setDisplayState(OLED::DisplayState::OFF);
|
||||
}
|
||||
uint16_t tipDisconnectedThres = TipThermoModel::getTipMaxInC() - 5;
|
||||
// Clear the lcd buffer
|
||||
OLED::clearScreen();
|
||||
OLED::setCursor(0, 0);
|
||||
if (systemSettings.detailedIDLE) {
|
||||
OLED::setFont(1);
|
||||
if (tipTemp > tipDisconnectedThres) {
|
||||
OLED::print(TipDisconnectedString);
|
||||
} else {
|
||||
OLED::print(IdleTipString);
|
||||
gui_drawTipTemp(false);
|
||||
OLED::print(IdleSetString);
|
||||
OLED::printNumber(systemSettings.SolderingTemp, 3);
|
||||
}
|
||||
OLED::setCursor(0, 8);
|
||||
|
||||
OLED::print(InputVoltageString);
|
||||
printVoltage();
|
||||
|
||||
} else {
|
||||
OLED::setFont(0);
|
||||
#ifdef OLED_FLIP
|
||||
if (!OLED::getRotation()) {
|
||||
#else
|
||||
if (OLED::getRotation()) {
|
||||
#endif
|
||||
OLED::drawArea(12, 0, 84, 16, idleScreenBG);
|
||||
OLED::setCursor(0, 0);
|
||||
gui_drawBatteryIcon();
|
||||
} else {
|
||||
OLED::drawArea(0, 0, 84, 16, idleScreenBGF); // Needs to be flipped so button ends up
|
||||
// on right side of screen
|
||||
OLED::setCursor(84, 0);
|
||||
gui_drawBatteryIcon();
|
||||
}
|
||||
tipDisconnectedDisplay = false;
|
||||
if (tipTemp > 55)
|
||||
tempOnDisplay = true;
|
||||
else if (tipTemp < 45)
|
||||
tempOnDisplay = false;
|
||||
if (tipTemp > tipDisconnectedThres) {
|
||||
tempOnDisplay = false;
|
||||
tipDisconnectedDisplay = true;
|
||||
}
|
||||
if (tempOnDisplay || tipDisconnectedDisplay) {
|
||||
// draw temp over the start soldering button
|
||||
// Location changes on screen rotation
|
||||
#ifdef OLED_FLIP
|
||||
if (!OLED::getRotation()) {
|
||||
#else
|
||||
if (OLED::getRotation()) {
|
||||
#endif
|
||||
// in right handed mode we want to draw over the first part
|
||||
OLED::fillArea(55, 0, 41, 16, 0); // clear the area for the temp
|
||||
OLED::setCursor(56, 0);
|
||||
|
||||
} else {
|
||||
OLED::fillArea(0, 0, 41, 16, 0); // clear the area
|
||||
OLED::setCursor(0, 0);
|
||||
}
|
||||
//If we have a tip connected draw the temp, if not we leave it blank
|
||||
if (!tipDisconnectedDisplay) {
|
||||
// draw in the temp
|
||||
if (!(systemSettings.coolingTempBlink && (xTaskGetTickCount() % 26 < 16)))
|
||||
gui_drawTipTemp(false); // draw in the temp
|
||||
} else {
|
||||
//Draw in missing tip symbol
|
||||
|
||||
#ifdef OLED_FLIP
|
||||
if (!OLED::getRotation()) {
|
||||
#else
|
||||
if (OLED::getRotation()) {
|
||||
#endif
|
||||
// in right handed mode we want to draw over the first part
|
||||
OLED::drawArea(55, 0, 41, 16, disconnectedTipIcon);
|
||||
|
||||
} else {
|
||||
OLED::drawArea(0, 0, 41, 16, disconnectedTipIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OLED::refresh();
|
||||
GUIDelay();
|
||||
}
|
||||
}
|
||||
171
source/Core/Threads/MOVThread.cpp
Normal file
171
source/Core/Threads/MOVThread.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* MOVThread.cpp
|
||||
*
|
||||
* Created on: 29 May 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
|
||||
#include "BMA223.hpp"
|
||||
#include "SC7A20.hpp"
|
||||
#include "BSP.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "I2C_Wrapper.hpp"
|
||||
#include "LIS2DH12.hpp"
|
||||
#include "MMA8652FC.hpp"
|
||||
#include "MSA301.h"
|
||||
#include "QC3.h"
|
||||
#include "Settings.h"
|
||||
#include "TipThermoModel.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "history.hpp"
|
||||
#include "main.hpp"
|
||||
#include "power.hpp"
|
||||
#include "stdlib.h"
|
||||
#include "task.h"
|
||||
#define MOVFilter 8
|
||||
uint8_t accelInit = 0;
|
||||
TickType_t lastMovementTime = 0;
|
||||
void detectAccelerometerVersion() {
|
||||
DetectedAccelerometerVersion = 99;
|
||||
#ifdef ACCEL_MMA
|
||||
if (MMA8652FC::detect()) {
|
||||
if (MMA8652FC::initalize()) {
|
||||
DetectedAccelerometerVersion = 1;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
#ifdef ACCEL_LIS
|
||||
if (LIS2DH12::detect()) {
|
||||
// Setup the ST Accelerometer
|
||||
if (LIS2DH12::initalize()) {
|
||||
DetectedAccelerometerVersion = 2;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
#ifdef ACCEL_BMA
|
||||
if (BMA223::detect()) {
|
||||
// Setup the ST Accelerometer
|
||||
if (BMA223::initalize()) {
|
||||
DetectedAccelerometerVersion = 3;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
#ifdef ACCEL_MSA
|
||||
if (MSA301::detect()) {
|
||||
// Setup the MSA301 Accelerometer
|
||||
if (MSA301::initalize()) {
|
||||
DetectedAccelerometerVersion = 4;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
#ifdef ACCEL_SC7
|
||||
if (SC7A20::detect()) {
|
||||
// Setup the SC7A20 Accelerometer
|
||||
if (SC7A20::initalize()) {
|
||||
DetectedAccelerometerVersion = 5;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// disable imu sensitivity
|
||||
systemSettings.sensitivity = 0;
|
||||
}
|
||||
}
|
||||
inline void readAccelerometer(int16_t &tx, int16_t &ty, int16_t &tz, Orientation &rotation) {
|
||||
#ifdef ACCEL_LIS
|
||||
if (DetectedAccelerometerVersion == 2) {
|
||||
LIS2DH12::getAxisReadings(tx, ty, tz);
|
||||
rotation = LIS2DH12::getOrientation();
|
||||
} else
|
||||
#endif
|
||||
#ifdef ACCEL_MMA
|
||||
if (DetectedAccelerometerVersion == 1) {
|
||||
MMA8652FC::getAxisReadings(tx, ty, tz);
|
||||
rotation = MMA8652FC::getOrientation();
|
||||
} else
|
||||
#endif
|
||||
#ifdef ACCEL_BMA
|
||||
if (DetectedAccelerometerVersion == 3) {
|
||||
BMA223::getAxisReadings(tx, ty, tz);
|
||||
rotation = BMA223::getOrientation();
|
||||
} else
|
||||
#endif
|
||||
#ifdef ACCEL_MSA
|
||||
if (DetectedAccelerometerVersion == 4) {
|
||||
MSA301::getAxisReadings(tx, ty, tz);
|
||||
rotation = MSA301::getOrientation();
|
||||
} else
|
||||
#endif
|
||||
#ifdef ACCEL_SC7
|
||||
if (DetectedAccelerometerVersion == 5) {
|
||||
SC7A20::getAxisReadings(tx, ty, tz);
|
||||
rotation = SC7A20::getOrientation();
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// do nothing :(
|
||||
}
|
||||
}
|
||||
void startMOVTask(void const *argument __unused) {
|
||||
detectAccelerometerVersion();
|
||||
osDelay(TICKS_100MS / 2); // wait ~50ms for setup of accel to finalise
|
||||
lastMovementTime = 0;
|
||||
// Mask 2 seconds if we are in autostart so that if user is plugging in and
|
||||
// then putting in stand it doesnt wake instantly
|
||||
if (systemSettings.autoStartMode)
|
||||
osDelay(2 * TICKS_SECOND);
|
||||
|
||||
int16_t datax[MOVFilter] = { 0 };
|
||||
int16_t datay[MOVFilter] = { 0 };
|
||||
int16_t dataz[MOVFilter] = { 0 };
|
||||
uint8_t currentPointer = 0;
|
||||
int16_t tx = 0, ty = 0, tz = 0;
|
||||
int32_t avgx, avgy, avgz;
|
||||
if (systemSettings.sensitivity > 9)
|
||||
systemSettings.sensitivity = 9;
|
||||
Orientation rotation = ORIENTATION_FLAT;
|
||||
for (;;) {
|
||||
int32_t threshold = 1500 + (9 * 200);
|
||||
threshold -= systemSettings.sensitivity * 200; // 200 is the step size
|
||||
readAccelerometer(tx, ty, tz, rotation);
|
||||
if (systemSettings.OrientationMode == 2) {
|
||||
if (rotation != ORIENTATION_FLAT) {
|
||||
OLED::setRotation(rotation == ORIENTATION_LEFT_HAND); // link the data through
|
||||
}
|
||||
}
|
||||
datax[currentPointer] = (int32_t) tx;
|
||||
datay[currentPointer] = (int32_t) ty;
|
||||
dataz[currentPointer] = (int32_t) tz;
|
||||
if (!accelInit) {
|
||||
for (uint8_t i = currentPointer + 1; i < MOVFilter; i++) {
|
||||
datax[i] = (int32_t) tx;
|
||||
datay[i] = (int32_t) ty;
|
||||
dataz[i] = (int32_t) tz;
|
||||
}
|
||||
accelInit = 1;
|
||||
}
|
||||
currentPointer = (currentPointer + 1) % MOVFilter;
|
||||
avgx = avgy = avgz = 0;
|
||||
// calculate averages
|
||||
for (uint8_t i = 0; i < MOVFilter; i++) {
|
||||
avgx += datax[i];
|
||||
avgy += datay[i];
|
||||
avgz += dataz[i];
|
||||
}
|
||||
avgx /= MOVFilter;
|
||||
avgy /= MOVFilter;
|
||||
avgz /= MOVFilter;
|
||||
|
||||
// Sum the deltas
|
||||
int32_t error = (abs(avgx - tx) + abs(avgy - ty) + abs(avgz - tz));
|
||||
// So now we have averages, we want to look if these are different by more
|
||||
// than the threshold
|
||||
|
||||
// If movement has occurred then we update the tick timer
|
||||
if (error > threshold) {
|
||||
lastMovementTime = xTaskGetTickCount();
|
||||
}
|
||||
|
||||
osDelay(TICKS_100MS); // Slow down update rate
|
||||
}
|
||||
}
|
||||
128
source/Core/Threads/PIDThread.cpp
Normal file
128
source/Core/Threads/PIDThread.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* PIDThread.cpp
|
||||
*
|
||||
* Created on: 29 May 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
|
||||
#include "main.hpp"
|
||||
#include "BSP.h"
|
||||
#include "power.hpp"
|
||||
#include "history.hpp"
|
||||
#include "TipThermoModel.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "Settings.h"
|
||||
static TickType_t powerPulseRate = 10000;
|
||||
static TickType_t powerPulseDuration = 250;
|
||||
TaskHandle_t pidTaskNotification = NULL;
|
||||
uint32_t currentTempTargetDegC = 0; // Current temperature target in C
|
||||
|
||||
/* StartPIDTask function */
|
||||
void startPIDTask(void const *argument __unused) {
|
||||
/*
|
||||
* We take the current tip temperature & evaluate the next step for the tip
|
||||
* control PWM.
|
||||
*/
|
||||
setTipX10Watts(0); // disable the output driver if the output is set to be off
|
||||
TickType_t lastPowerPulseStart = 0;
|
||||
TickType_t lastPowerPulseEnd = 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();
|
||||
uint32_t PIDTempTarget = 0;
|
||||
for (;;) {
|
||||
|
||||
if (ulTaskNotifyTake(pdTRUE, 2000)) {
|
||||
// This is a call to block this thread until the ADC does its samples
|
||||
int32_t x10WattsOut = 0;
|
||||
// Do the reading here to keep the temp calculations churning along
|
||||
uint32_t currentTipTempInC = TipThermoModel::getTipInC(true);
|
||||
PIDTempTarget = currentTempTargetDegC;
|
||||
if (PIDTempTarget) {
|
||||
// Cap the max set point to 450C
|
||||
if (PIDTempTarget > (450)) {
|
||||
//Maximum allowed output
|
||||
PIDTempTarget = (450);
|
||||
}
|
||||
//Safety check that not aiming higher than current tip can measure
|
||||
if (PIDTempTarget > TipThermoModel::getTipMaxInC()) {
|
||||
PIDTempTarget = TipThermoModel::getTipMaxInC();
|
||||
}
|
||||
// Convert the current tip to degree's C
|
||||
|
||||
// As we get close to our target, temp noise causes the system
|
||||
// to be unstable. Use a rolling average to dampen it.
|
||||
// We overshoot by roughly 1 degree C.
|
||||
// This helps stabilize the display.
|
||||
int32_t tError = PIDTempTarget - currentTipTempInC + 1;
|
||||
tError = tError > INT16_MAX ? INT16_MAX : tError;
|
||||
tError = tError < INT16_MIN ? INT16_MIN : tError;
|
||||
tempError.update(tError);
|
||||
|
||||
// Now for the PID!
|
||||
|
||||
// P term - total power needed to hit target temp next cycle.
|
||||
// thermal mass = 1690 milliJ/*C for my tip.
|
||||
// = Watts*Seconds to raise Temp from room temp to +100*C, divided by 100*C.
|
||||
// we divide milliWattsNeeded by 20 to let the I term dominate near the set point.
|
||||
// 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 x10WattsNeeded = tempToX10Watts(tError);
|
||||
// tempError.average());
|
||||
// note that milliWattsNeeded is sometimes negative, this counters overshoot
|
||||
// from I term's inertia.
|
||||
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).
|
||||
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
|
||||
// and counters extra power if the iron is no longer losing temp.
|
||||
// basically: temp - lastTemp
|
||||
// Unfortunately, our temp signal is too noisy to really help.
|
||||
|
||||
}
|
||||
//If the user turns on the option of using an occasional pulse to keep the power bank on
|
||||
if (systemSettings.KeepAwakePulse) {
|
||||
|
||||
if (xTaskGetTickCount() - lastPowerPulseStart
|
||||
> powerPulseRate) {
|
||||
lastPowerPulseStart = xTaskGetTickCount();
|
||||
lastPowerPulseEnd = lastPowerPulseStart
|
||||
+ powerPulseDuration;
|
||||
}
|
||||
|
||||
//If current PID is less than the pulse level, check if we want to constrain to the pulse as the floor
|
||||
if (x10WattsOut < systemSettings.KeepAwakePulse
|
||||
&& xTaskGetTickCount() < lastPowerPulseEnd) {
|
||||
x10WattsOut = systemSettings.KeepAwakePulse;
|
||||
}
|
||||
}
|
||||
|
||||
//Secondary safety check to forcefully disable header when within ADC noise of top of ADC
|
||||
if (getTipRawTemp(0) > (0x7FFF - 150)) {
|
||||
x10WattsOut = 0;
|
||||
}
|
||||
if (systemSettings.powerLimit
|
||||
&& x10WattsOut > (systemSettings.powerLimit * 10)) {
|
||||
setTipX10Watts(systemSettings.powerLimit * 10);
|
||||
} else {
|
||||
setTipX10Watts(x10WattsOut);
|
||||
}
|
||||
|
||||
resetWatchdog();
|
||||
} else {
|
||||
//ADC interrupt timeout
|
||||
setTipPWM(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
source/Core/Threads/POWThread.cpp
Normal file
25
source/Core/Threads/POWThread.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* POWThread.cpp
|
||||
*
|
||||
* Created on: 16 Jan 2021
|
||||
* Author: Ralim
|
||||
*/
|
||||
|
||||
#include "BSP.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "QC3.h"
|
||||
#include "Settings.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "main.hpp"
|
||||
#include "stdlib.h"
|
||||
#include "task.h"
|
||||
|
||||
// Small worker thread to handle power (mostly QC) related steps
|
||||
|
||||
void startPOWTask(void const *argument __unused) {
|
||||
postRToSInit();
|
||||
for (;;) {
|
||||
osDelay(TICKS_100MS); // Slow down update rate
|
||||
power_check();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user