diff --git a/source/Core/BSP/BSP.h b/source/Core/BSP/BSP.h index e0739c60..0caf21bb 100644 --- a/source/Core/BSP/BSP.h +++ b/source/Core/BSP/BSP.h @@ -2,7 +2,7 @@ #include "BSP_Power.h" #include "BSP_QC.h" #include "Defines.h" -#include "Model_Config.h" +#include "configuration.h" #include #include /* @@ -33,16 +33,13 @@ void BSPInit(void); // Called to reset the hardware watchdog unit void resetWatchdog(); // Accepts a output level of 0.. to use to control the tip output PWM -void setTipPWM(uint8_t pulse); +void setTipPWM(const uint8_t pulse, const bool shouldUseFastModePWM); // Returns the Handle temp in C, X10 uint16_t getHandleTemperature(uint8_t sample); // Returns the Tip temperature ADC reading in raw units uint16_t getTipRawTemp(uint8_t refresh); // Returns the main DC input voltage, using the adjustable divisor + sample flag uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample); -// Switch to the most suitable PWM freq given the desired period; -// returns true if the switch was performed and totalPWM changed -bool tryBetterPWM(uint8_t pwm); // Readers for the two buttons // !! Returns 1 if held down, 0 if released diff --git a/source/Core/BSP/MHP30/BSP.cpp b/source/Core/BSP/MHP30/BSP.cpp index 4757f3d3..12dac966 100644 --- a/source/Core/BSP/MHP30/BSP.cpp +++ b/source/Core/BSP/MHP30/BSP.cpp @@ -2,7 +2,6 @@ #include "BSP.h" #include "I2C_Wrapper.hpp" -#include "Model_Config.h" #include "Pins.h" #include "Setup.h" #include "TipThermoModel.h" @@ -246,11 +245,7 @@ uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample) { } return sum * 4 / divisor; } -bool tryBetterPWM(uint8_t pwm) { - // We dont need this for the MHP30 - return false; -} -void setTipPWM(uint8_t pulse) { +void setTipPWM(const uint8_t pulse, const bool shouldUseFastModePWM) { // We can just set the timer directly if (htim3.Instance->PSC > 20) { htim3.Instance->CCR1 = 0; diff --git a/source/Core/BSP/MHP30/Model_Config.h b/source/Core/BSP/MHP30/Model_Config.h deleted file mode 100644 index ecc0460c..00000000 --- a/source/Core/BSP/MHP30/Model_Config.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Model_Config.h - * - * Created on: 25 Jul 2020 - * Author: Ralim - */ - -#ifndef BSP_MINIWARE_MODEL_CONFIG_H_ -#define BSP_MINIWARE_MODEL_CONFIG_H_ -/* - * Lookup for mapping features <-> Models - */ - -#ifndef MODEL_MHP30 -#error "No model defined!" -#endif - -#ifdef MODEL_MHP30 -#define ACCEL_MSA -#define POW_PD -#define TEMP_NTC -#define I2C_SOFT -#define BATTFILTERDEPTH 8 -#define OLED_I2CBB -#define ACCEL_EXITS_ON_MOVEMENT -#endif -#ifdef ACCEL_EXITS_ON_MOVEMENT -#define NO_SLEEP_MODE -#endif -#endif /* BSP_MINIWARE_MODEL_CONFIG_H_ */ diff --git a/source/Core/BSP/MHP30/Pins.h b/source/Core/BSP/MHP30/Pins.h index d649c14b..610322ed 100644 --- a/source/Core/BSP/MHP30/Pins.h +++ b/source/Core/BSP/MHP30/Pins.h @@ -7,7 +7,7 @@ #ifndef BSP_MINIWARE_PINS_H_ #define BSP_MINIWARE_PINS_H_ -#include "Model_Config.h" +#include "configuration.h" // MHP30 pin map #define KEY_B_Pin GPIO_PIN_0 diff --git a/source/Core/BSP/MHP30/Power.cpp b/source/Core/BSP/MHP30/Power.cpp index 72a64017..80455d2b 100644 --- a/source/Core/BSP/MHP30/Power.cpp +++ b/source/Core/BSP/MHP30/Power.cpp @@ -1,9 +1,9 @@ #include "BSP.h" #include "BSP_Power.h" -#include "Model_Config.h" #include "Pins.h" #include "QC3.h" #include "Settings.h" +#include "configuration.h" #include "fusb_user.h" #include "fusbpd.h" #include "int_n.h" diff --git a/source/Core/BSP/MHP30/QC_GPIO.cpp b/source/Core/BSP/MHP30/QC_GPIO.cpp index b48ba1f3..8adc603b 100644 --- a/source/Core/BSP/MHP30/QC_GPIO.cpp +++ b/source/Core/BSP/MHP30/QC_GPIO.cpp @@ -5,11 +5,12 @@ * Author: Ralim */ #include "BSP.h" -#include "Model_Config.h" #include "Pins.h" #include "QC3.h" #include "Settings.h" +#include "configuration.h" #include "stm32f1xx_hal.h" + #ifdef POW_QC void QC_DPlusZero_Six() { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); // pull down D+ diff --git a/source/Core/BSP/MHP30/Software_I2C.h b/source/Core/BSP/MHP30/Software_I2C.h index 99f3a2e9..411362f5 100644 --- a/source/Core/BSP/MHP30/Software_I2C.h +++ b/source/Core/BSP/MHP30/Software_I2C.h @@ -8,7 +8,7 @@ #ifndef BSP_MINIWARE_SOFTWARE_I2C_H_ #define BSP_MINIWARE_SOFTWARE_I2C_H_ #include "BSP.h" -#include "Model_Config.h" +#include "configuration.h" #include "stm32f1xx_hal.h" #ifdef I2C_SOFT diff --git a/source/Core/BSP/MHP30/configuration.h b/source/Core/BSP/MHP30/configuration.h new file mode 100644 index 00000000..21e39fd9 --- /dev/null +++ b/source/Core/BSP/MHP30/configuration.h @@ -0,0 +1,156 @@ +#ifndef CONFIGURATION_H_ +#define CONFIGURATION_H_ +#include "Settings.h" +#include "configuration.h" +#include +/** + * Configuration.h + * Define here your default pre settings for TS80 or TS100 + * + */ + +//=========================================================================== +//============================= Default Settings ============================ +//=========================================================================== +/** + * Default soldering temp is 320.0 C + * Temperature the iron sleeps at - default 150.0 C + */ + +#define SLEEP_TEMP 150 // Default sleep temperature +#define BOOST_TEMP 420 // Default boost temp. +#define BOOST_MODE_ENABLED 1 // 0: Disable 1: Enable + +/** + * Blink the temperature on the cooling screen when its > 50C + */ +#define COOLING_TEMP_BLINK 0 // 0: Disable 1: Enable + +/** + * How many seconds/minutes we wait until going to sleep/shutdown. + * Values -> SLEEP_TIME * 10; i.e. 5*10 = 50 Seconds! + */ +#define SLEEP_TIME 5 // x10 Seconds +#define SHUTDOWN_TIME 10 // Minutes + +/** + * Auto start off for safety. + * Pissible values are: + * 0 - none + * 1 - Soldering Temperature + * 2 - Sleep Temperature + * 3 - Sleep Off Temperature + */ +#define AUTO_START_MODE 0 // Default to none + +/** + * Locking Mode + * When in soldering mode a long press on both keys toggle the lock of the buttons + * Possible values are: + * 0 - Desactivated + * 1 - Lock except boost + * 2 - Full lock + */ +#define LOCKING_MODE 0 // Default to desactivated for safety + +/** + * OLED Orientation + * + */ +#define ORIENTATION_MODE 0 // 0: Right 1:Left 2:Automatic - Default right +#define REVERSE_BUTTON_TEMP_CHANGE 0 // 0:Default 1:Reverse - Reverse the plus and minus button assigment for temperature change + +/** + * Temp change settings + */ +#define TEMP_CHANGE_SHORT_STEP 1 // Default temp change short step +1 +#define TEMP_CHANGE_LONG_STEP 10 // Default temp change long step +10 +#define TEMP_CHANGE_SHORT_STEP_MAX 50 // Temp change short step MAX value +#define TEMP_CHANGE_LONG_STEP_MAX 90 // Temp change long step MAX value + +/* Power pulse for keeping power banks awake*/ +#define POWER_PULSE_INCREMENT 1 +#define POWER_PULSE_MAX 100 // x10 max watts +#define POWER_PULSE_WAIT_MAX 9 // 9*2.5s = 22.5 seconds +#define POWER_PULSE_DURATION_MAX 9 // 9*250ms = 2.25 seconds + +#ifdef MODEL_TS100 +#define POWER_PULSE_DEFAULT 0 +#else +#define POWER_PULSE_DEFAULT 5 +#endif +#define POWER_PULSE_WAIT_DEFAULT 4 // Default rate of the power pulse: 4*2500 = 10000 ms = 10 s +#define POWER_PULSE_DURATION_DEFAULT 1 // Default duration of the power pulse: 1*250 = 250 ms + +/** + * OLED Orientation Sensitivity on Automatic mode! + * Motion Sensitivity <0=Off 1=Least Sensitive 9=Most Sensitive> + */ +#define SENSITIVITY 7 // Default 7 + +/** + * Detailed soldering screen + * Detailed idle screen (off for first time users) + */ +#define DETAILED_SOLDERING 0 // 0: Disable 1: Enable - Default 0 +#define DETAILED_IDLE 0 // 0: Disable 1: Enable - Default 0 + +#define THERMAL_RUNAWAY_TIME_SEC 20 +#define THERMAL_RUNAWAY_TEMP_C 20 + +#define CUT_OUT_SETTING 0 // default to no cut-off voltage +#define RECOM_VOL_CELL 33 // Minimum voltage per cell (Recommended 3.3V (33)) +#define TEMPERATURE_INF 0 // default to 0 +#define DESCRIPTION_SCROLL_SPEED 0 // 0: Slow 1: Fast - default to slow +#define ANIMATION_LOOP 1 // 0: off 1: on +#define ANIMATION_SPEED settingOffSpeed_t::MEDIUM + +#define OP_AMP_Rf_MHP30 268500 // 268.5 Kilo-ohms -> Measured +#define OP_AMP_Rin_MHP30 1600 // 1.6 Kilo-ohms -> Measured + +#define OP_AMP_GAIN_STAGE_MHP30 (1 + (OP_AMP_Rf_MHP30 / OP_AMP_Rin_MHP30)) +// Deriving the Voltage div: +// Vin_max = (3.3*(r1+r2))/(r2) +// vdiv = (32768*4)/(vin_max*10) + +#ifndef MODEL_MHP30 +#error "No model defined!" +#endif + +#ifdef MODEL_MHP30 +#define SOLDERING_TEMP 200 // Default soldering temp is 200.0 °C +#define VOLTAGE_DIV 360 // Default for MHP30 +#define PID_POWER_LIMIT 65 // Sets the max pwm power limit +#define CALIBRATION_OFFSET 0 // the adc offset in uV - MHP compensates automagically +#define POWER_LIMIT 65 // 65 watts default power limit +#define MAX_POWER_LIMIT 65 // +#define POWER_LIMIT_STEPS 1 // +#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_MHP30 // +#define USB_PD_VMAX 20 // Maximum voltage for PD to negotiate +#define MODEL_HAS_DCDC // Has inductor to current filter +#define PID_TIM_HZ (16) // +#define MAX_TEMP_C 300 // Max soldering temp selectable °C +#define MAX_TEMP_F 570 // Max soldering temp selectable °F +#define MIN_TEMP_C 10 // Min soldering temp selectable °C +#define MIN_TEMP_F 60 // Min soldering temp selectable °F +#define MIN_BOOST_TEMP_C 150 // The min settable temp for boost mode °C +#define MIN_BOOST_TEMP_F 300 // The min settable temp for boost mode °F +#define NO_DISPLAY_ROTATE // Disable OLED rotation by accel +#define SLEW_LIMIT 50 // Limit to 3.0 Watts per 64ms pid loop update rate slew rate +#define ACCEL_MSA +#define POW_PD +#define TEMP_NTC +#define I2C_SOFT +#define BATTFILTERDEPTH 8 +#define OLED_I2CBB +#define ACCEL_EXITS_ON_MOVEMENT + +#define HARDWARE_MAX_WATTAGE_X10 650 +#define TIP_THERMAL_MASS 65 // TODO, needs refinement +#define tipResistance 60 // x10 ohms, ~6 typical +#endif + +#ifdef ACCEL_EXITS_ON_MOVEMENT +#define NO_SLEEP_MODE +#endif +#endif diff --git a/source/Core/BSP/MHP30/fusb_user.cpp b/source/Core/BSP/MHP30/fusb_user.cpp index bddf5a72..e3dfa13c 100644 --- a/source/Core/BSP/MHP30/fusb_user.cpp +++ b/source/Core/BSP/MHP30/fusb_user.cpp @@ -1,4 +1,4 @@ -#include "Model_Config.h" +#include "configuration.h" #ifdef POW_PD #include "BSP.h" #include "I2C_Wrapper.hpp" diff --git a/source/Core/BSP/MHP30/preRTOS.cpp b/source/Core/BSP/MHP30/preRTOS.cpp index 44ac337f..7efd4da8 100644 --- a/source/Core/BSP/MHP30/preRTOS.cpp +++ b/source/Core/BSP/MHP30/preRTOS.cpp @@ -7,11 +7,12 @@ #include "BSP.h" #include "I2CBB.hpp" -#include "Model_Config.h" #include "Pins.h" #include "Setup.h" +#include "configuration.h" #include "fusbpd.h" #include + void preRToSInit() { /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ diff --git a/source/Core/BSP/Miniware/BSP.cpp b/source/Core/BSP/Miniware/BSP.cpp index 7a75d989..327cf05f 100644 --- a/source/Core/BSP/Miniware/BSP.cpp +++ b/source/Core/BSP/Miniware/BSP.cpp @@ -2,7 +2,6 @@ #include "BSP.h" #include "I2C_Wrapper.hpp" -#include "Model_Config.h" #include "Pins.h" #include "Setup.h" #include "TipThermoModel.h" @@ -10,6 +9,7 @@ #include "history.hpp" #include "main.hpp" #include + volatile uint16_t PWMSafetyTimer = 0; volatile uint8_t pendingPWM = 0; @@ -21,9 +21,7 @@ uint16_t totalPWM; // htim2.Init.Period, the full PWM cycle static bool fastPWM; -// 2 second filter (ADC is PID_TIM_HZ Hz) -history rawTempFilter = {{0}, 0, 0}; -void resetWatchdog() { HAL_IWDG_Refresh(&hiwdg); } +void resetWatchdog() { HAL_IWDG_Refresh(&hiwdg); } #ifdef TEMP_NTC // Lookup table for the NTC // Stored as ADCReading,Temp in degC @@ -135,48 +133,36 @@ uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample) { return res; } -void setTipPWM(uint8_t pulse) { - PWMSafetyTimer = 10; // This is decremented in the handler for PWM so that the tip pwm is - // disabled if the PID task is not scheduled often enough. - - pendingPWM = pulse; -} - static void switchToFastPWM(void) { - fastPWM = true; - totalPWM = powerPWM + tempMeasureTicks * 2 + holdoffTicks; - htim2.Instance->ARR = totalPWM; - // ~3.5 Hz rate - htim2.Instance->CCR1 = powerPWM + holdoffTicks * 2; - // 2 MHz timer clock/2000 = 1 kHz tick rate - htim2.Instance->PSC = 2000; + // 10Hz + fastPWM = true; + totalPWM = powerPWM + tempMeasureTicks + holdoffTicks; + htim2.Instance->ARR = totalPWM; + htim2.Instance->CCR1 = powerPWM + holdoffTicks; + htim2.Instance->PSC = 2690; } static void switchToSlowPWM(void) { - fastPWM = false; - totalPWM = powerPWM + tempMeasureTicks + holdoffTicks; - htim2.Instance->ARR = totalPWM; - // ~1.84 Hz rate - htim2.Instance->CCR1 = powerPWM + holdoffTicks; - // 2 MHz timer clock/4000 = 500 Hz tick rate - htim2.Instance->PSC = 4000; + // 5Hz + fastPWM = false; + totalPWM = powerPWM + tempMeasureTicks / 2 + holdoffTicks / 2; + htim2.Instance->ARR = totalPWM; + htim2.Instance->CCR1 = powerPWM + holdoffTicks / 2; + htim2.Instance->PSC = 2690 * 2; } -bool tryBetterPWM(uint8_t pwm) { - if (fastPWM && pwm == powerPWM) { - // maximum power for fast PWM reached, need to go slower to get more - switchToSlowPWM(); - return true; - } else if (!fastPWM && pwm < 230) { - // 254 in fast PWM mode gives the same power as 239 in slow - // allow for some reasonable hysteresis by switching only when it goes - // below 230 (equivalent to 245 in fast mode) - switchToFastPWM(); - return true; +void setTipPWM(const uint8_t pulse, const bool shouldUseFastModePWM) { + PWMSafetyTimer = 10; // This is decremented in the handler for PWM so that the tip pwm is + // disabled if the PID task is not scheduled often enough. + pendingPWM = pulse; + if (fastPWM != shouldUseFastModePWM) { + if (shouldUseFastModePWM) { + switchToFastPWM(); + } else { + switchToSlowPWM(); + } } - return false; } - // These are called by the HAL after the corresponding events from the system // timers. diff --git a/source/Core/BSP/Miniware/Model_Config.h b/source/Core/BSP/Miniware/Model_Config.h deleted file mode 100644 index 27ea0899..00000000 --- a/source/Core/BSP/Miniware/Model_Config.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Model_Config.h - * - * Created on: 25 Jul 2020 - * Author: Ralim - */ - -#ifndef BSP_MINIWARE_MODEL_CONFIG_H_ -#define BSP_MINIWARE_MODEL_CONFIG_H_ -/* - * Lookup for mapping features <-> Models - */ - -#if defined(MODEL_TS100) + defined(MODEL_TS80) + defined(MODEL_TS80P) > 1 -#error "Multiple models defined!" -#elif defined(MODEL_TS100) + defined(MODEL_TS80) + defined(MODEL_TS80P) == 0 -#error "No model defined!" -#endif - -#ifdef MODEL_TS100 -#define POW_DC -#define ACCEL_MMA -#define ACCEL_LIS -#define TEMP_TMP36 -#define BATTFILTERDEPTH 32 -#endif - -#ifdef MODEL_TS80 -#define ACCEL_LIS -#define POW_QC -#define TEMP_TMP36 -#define LIS_ORI_FLIP -#define OLED_FLIP -#define BATTFILTERDEPTH 8 -#endif - -#ifdef MODEL_TS80P -#define ACCEL_LIS -#define ACCEL_MSA -#define POW_PD -#define POW_QC -#define TEMP_NTC -#define I2C_SOFT -#define LIS_ORI_FLIP -#define OLED_FLIP -#define BATTFILTERDEPTH 8 -#endif - -#endif /* BSP_MINIWARE_MODEL_CONFIG_H_ */ diff --git a/source/Core/BSP/Miniware/Pins.h b/source/Core/BSP/Miniware/Pins.h index 4e4c9085..e8851d26 100644 --- a/source/Core/BSP/Miniware/Pins.h +++ b/source/Core/BSP/Miniware/Pins.h @@ -7,7 +7,7 @@ #ifndef BSP_MINIWARE_PINS_H_ #define BSP_MINIWARE_PINS_H_ -#include "Model_Config.h" +#include "configuration.h" #ifdef MODEL_TS100 diff --git a/source/Core/BSP/Miniware/Power.cpp b/source/Core/BSP/Miniware/Power.cpp index 2fcbdcb1..5aa42b42 100644 --- a/source/Core/BSP/Miniware/Power.cpp +++ b/source/Core/BSP/Miniware/Power.cpp @@ -1,9 +1,9 @@ #include "BSP.h" #include "BSP_Power.h" -#include "Model_Config.h" #include "Pins.h" #include "QC3.h" #include "Settings.h" +#include "configuration.h" #include "fusb_user.h" #include "fusbpd.h" #include "int_n.h" diff --git a/source/Core/BSP/Miniware/QC_GPIO.cpp b/source/Core/BSP/Miniware/QC_GPIO.cpp index 288e61ac..84edd842 100644 --- a/source/Core/BSP/Miniware/QC_GPIO.cpp +++ b/source/Core/BSP/Miniware/QC_GPIO.cpp @@ -5,11 +5,12 @@ * Author: Ralim */ #include "BSP.h" -#include "Model_Config.h" #include "Pins.h" #include "QC3.h" #include "Settings.h" +#include "configuration.h" #include "stm32f1xx_hal.h" + #ifdef POW_QC void QC_DPlusZero_Six() { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); // pull down D+ diff --git a/source/Core/BSP/Miniware/Setup.cpp b/source/Core/BSP/Miniware/Setup.cpp index 9532503e..b86c86ff 100644 --- a/source/Core/BSP/Miniware/Setup.cpp +++ b/source/Core/BSP/Miniware/Setup.cpp @@ -20,7 +20,7 @@ DMA_HandleTypeDef hdma_i2c1_tx; IWDG_HandleTypeDef hiwdg; TIM_HandleTypeDef htim2; TIM_HandleTypeDef htim3; -#define ADC_FILTER_LEN 32 +#define ADC_FILTER_LEN 4 #define ADC_SAMPLES 16 uint16_t ADCReadings[ADC_SAMPLES]; // Used to store the adc readings for the handle cold junction temp @@ -326,7 +326,8 @@ static void MX_TIM2_Init(void) { // These values give a rate of around 3.5 Hz for "fast" mode and 1.84 Hz for "slow" htim2.Init.CounterMode = TIM_COUNTERMODE_UP; // dummy value, will be reconfigured by BSPInit() - htim2.Init.Period = 255 + 17 * 2; + htim2.Init.Period = powerPWM + 14 * 2; + htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; // 8 MHz (x2 APB1) before divide htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; htim2.Init.RepetitionCounter = 0; @@ -344,7 +345,7 @@ static void MX_TIM2_Init(void) { sConfigOC.OCMode = TIM_OCMODE_PWM1; // dummy value, will be reconfigured by BSPInit() in the BSP.cpp - sConfigOC.Pulse = 255 + 13 * 2; // 13 -> Delay of 7 ms + sConfigOC.Pulse = powerPWM + 14; // 13 -> Delay of 7 ms // 255 is the largest time period of the drive signal, and then offset ADC sample to be a bit delayed after this /* * It takes 4 milliseconds for output to be stable after PWM turns off. diff --git a/source/Core/BSP/Miniware/Software_I2C.h b/source/Core/BSP/Miniware/Software_I2C.h index 214faa5a..ceca0131 100644 --- a/source/Core/BSP/Miniware/Software_I2C.h +++ b/source/Core/BSP/Miniware/Software_I2C.h @@ -8,7 +8,7 @@ #ifndef BSP_MINIWARE_SOFTWARE_I2C_H_ #define BSP_MINIWARE_SOFTWARE_I2C_H_ #include "BSP.h" -#include "Model_Config.h" +#include "configuration.h" #include "stm32f1xx_hal.h" #ifdef I2C_SOFT diff --git a/source/Core/Inc/configuration.h b/source/Core/BSP/Miniware/configuration.h similarity index 64% rename from source/Core/Inc/configuration.h rename to source/Core/BSP/Miniware/configuration.h index 0f85e170..6f5ab5a4 100644 --- a/source/Core/Inc/configuration.h +++ b/source/Core/BSP/Miniware/configuration.h @@ -1,10 +1,10 @@ -#pragma once -#include "Model_Config.h" +#ifndef CONFIGURATION_H_ +#define CONFIGURATION_H_ #include "Settings.h" #include /** * Configuration.h - * Define here your default pre settings for TS80 or TS100 + * Define here your default pre settings for TS80(P) or TS100 * */ @@ -57,7 +57,7 @@ * */ #define ORIENTATION_MODE 0 // 0: Right 1:Left 2:Automatic - Default right -#define REVERSE_BUTTON_TEMP_CHANGE 0 // 0:Default 1:Reverse - Reverse the plus and minus button assigment for temperatur change +#define REVERSE_BUTTON_TEMP_CHANGE 0 // 0:Default 1:Reverse - Reverse the plus and minus button assigment for temperature change /** * Temp change settings @@ -114,14 +114,16 @@ #define OP_AMP_GAIN_STAGE_TS80 (1 + (OP_AMP_Rf_TS80 / OP_AMP_Rin_TS80)) -#define OP_AMP_Rf_MHP30 268500 // 268.5 Kilo-ohms -> Measured -#define OP_AMP_Rin_MHP30 1600 // 1.6 Kilo-ohms -> Measured - -#define OP_AMP_GAIN_STAGE_MHP30 (1 + (OP_AMP_Rf_MHP30 / OP_AMP_Rin_MHP30)) // Deriving the Voltage div: // Vin_max = (3.3*(r1+r2))/(r2) // vdiv = (32768*4)/(vin_max*10) +#if defined(MODEL_TS100) + defined(MODEL_TS80) + defined(MODEL_TS80P) > 1 +#error "Multiple models defined!" +#elif defined(MODEL_TS100) + defined(MODEL_TS80) + defined(MODEL_TS80P) == 0 +#error "No model defined!" +#endif + #ifdef MODEL_TS100 #define SOLDERING_TEMP 320 // Default soldering temp is 320.0 °C #define VOLTAGE_DIV 467 // 467 - Default divider from schematic @@ -140,26 +142,10 @@ #define MIN_TEMP_F 60 // Min soldering temp selectable °F #define MIN_BOOST_TEMP_C 250 // The min settable temp for boost mode °C #define MIN_BOOST_TEMP_F 480 // The min settable temp for boost mode °F -#endif - -#ifdef MODEL_Pinecil -#define SOLDERING_TEMP 320 // Default soldering temp is 320.0 °C -#define VOLTAGE_DIV 467 // 467 - Default divider from schematic -#define CALIBRATION_OFFSET 900 // 900 - Default adc offset in uV -#define PID_POWER_LIMIT 70 // Sets the max pwm power limit -#define POWER_LIMIT 0 // 0 watts default limit -#define MAX_POWER_LIMIT 65 // -#define POWER_LIMIT_STEPS 5 // -#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_TS100 // Uses TS100 resistors -#define TEMP_uV_LOOKUP_HAKKO // Use Hakko lookup table -#define USB_PD_VMAX 20 // Maximum voltage for PD to negotiate -#define PID_TIM_HZ (8) // Tick rate of the PID loop -#define MAX_TEMP_C 450 // Max soldering temp selectable °C -#define MAX_TEMP_F 850 // Max soldering temp selectable °F -#define MIN_TEMP_C 10 // Min soldering temp selectable °C -#define MIN_TEMP_F 60 // Min soldering temp selectable °F -#define MIN_BOOST_TEMP_C 250 // The min settable temp for boost mode °C -#define MIN_BOOST_TEMP_F 480 // The min settable temp for boost mode °F +#define POW_DC +#define ACCEL_MMA +#define ACCEL_LIS +#define TEMP_TMP36 #endif #ifdef MODEL_TS80 @@ -180,6 +166,11 @@ #define MIN_TEMP_F 60 // Min soldering temp selectable °F #define MIN_BOOST_TEMP_C 250 // The min settable temp for boost mode °C #define MIN_BOOST_TEMP_F 480 // The min settable temp for boost mode °F +#define ACCEL_LIS +#define POW_QC +#define TEMP_TMP36 +#define LIS_ORI_FLIP +#define OLED_FLIP #endif #ifdef MODEL_TS80P @@ -200,59 +191,31 @@ #define MIN_TEMP_F 60 // Min soldering temp selectable °F #define MIN_BOOST_TEMP_C 250 // The min settable temp for boost mode °C #define MIN_BOOST_TEMP_F 480 // The min settable temp for boost mode °F - -#endif - -#ifdef MODEL_MHP30 -#define SOLDERING_TEMP 200 // Default soldering temp is 200.0 °C -#define VOLTAGE_DIV 360 // Default for MHP30 -#define PID_POWER_LIMIT 65 // Sets the max pwm power limit -#define CALIBRATION_OFFSET 0 // the adc offset in uV - MHP compensates automagically -#define POWER_LIMIT 65 // 65 watts default power limit -#define MAX_POWER_LIMIT 65 // -#define POWER_LIMIT_STEPS 1 // -#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_MHP30 // -#define USB_PD_VMAX 20 // Maximum voltage for PD to negotiate -#define MODEL_HAS_DCDC // Has inductor to current filter -#define PID_TIM_HZ (16) // -#define THERMAL_MASS_OVERSHOOTS // We have overshoot so reverse direction of compensation -#define MAX_TEMP_C 300 // Max soldering temp selectable °C -#define MAX_TEMP_F 570 // Max soldering temp selectable °F -#define MIN_TEMP_C 10 // Min soldering temp selectable °C -#define MIN_TEMP_F 60 // Min soldering temp selectable °F -#define MIN_BOOST_TEMP_C 150 // The min settable temp for boost mode °C -#define MIN_BOOST_TEMP_F 300 // The min settable temp for boost mode °F -#define NO_DISPLAY_ROTATE // Disable OLED rotation by accel -#define SLEW_LIMIT 50 // Limit to 3.0 Watts per 64ms pid loop update rate slew rate +#define ACCEL_LIS +#define ACCEL_MSA +#define POW_PD +#define POW_QC +#define TEMP_NTC +#define I2C_SOFT +#define LIS_ORI_FLIP +#define OLED_FLIP #endif #ifdef MODEL_TS100 -const int32_t tipMass = 65; // X10 watts to raise 1 deg C in 1 second -const uint8_t tipResistance = 75; // x10 ohms, 7.5 typical for ts100 tips -#endif - -#ifdef MODEL_Pinecil -const int32_t tipMass = 45; // X10 watts to raise 1 deg C in 1 second -const uint8_t tipResistance = 75; // x10 ohms, 7.5 typical for ts100 tips +#define HARDWARE_MAX_WATTAGE_X10 750 +#define TIP_THERMAL_MASS 65 // X10 watts to raise 1 deg C in 1 second +#define tipResistance 75 // x10 ohms, 7.5 typical for ts100 tips #endif #ifdef MODEL_TS80 -const uint32_t tipMass = 40; -const uint8_t tipResistance = 45; // x10 ohms, 4.5 typical for ts80 tips +#define HARDWARE_MAX_WATTAGE_X10 180 +#define TIP_THERMAL_MASS 40 +#define tipResistance 45 // x10 ohms, 4.5 typical for ts80 tips #endif #ifdef MODEL_TS80P -const uint32_t tipMass = 40; -const uint8_t tipResistance = 45; // x10 ohms, 4.5 typical for ts80 tips +#define HARDWARE_MAX_WATTAGE_X10 300 +#define TIP_THERMAL_MASS 40 +#define tipResistance 45 // x10 ohms, 4.5 typical for ts80 tips #endif - -#ifdef MODEL_MHP30 -const uint32_t tipMass = 45; // TODO -const uint8_t tipResistance = 60; // x10 ohms, ~6 typical #endif - -#ifdef POW_QC_20V -#define QC_SETTINGS_MAX 3 -#else -#define QC_SETTINGS_MAX 2 -#endif \ No newline at end of file diff --git a/source/Core/BSP/Miniware/fusb_user.cpp b/source/Core/BSP/Miniware/fusb_user.cpp index c37bb5ff..13454db7 100644 --- a/source/Core/BSP/Miniware/fusb_user.cpp +++ b/source/Core/BSP/Miniware/fusb_user.cpp @@ -1,4 +1,4 @@ -#include "Model_Config.h" +#include "configuration.h" #ifdef POW_PD #include "BSP.h" #include "I2CBB.hpp" diff --git a/source/Core/BSP/Miniware/preRTOS.cpp b/source/Core/BSP/Miniware/preRTOS.cpp index 5d9852b4..931df14e 100644 --- a/source/Core/BSP/Miniware/preRTOS.cpp +++ b/source/Core/BSP/Miniware/preRTOS.cpp @@ -7,11 +7,12 @@ #include "BSP.h" #include "I2CBB.hpp" -#include "Model_Config.h" #include "Pins.h" #include "Setup.h" +#include "configuration.h" #include "fusbpd.h" #include + void preRToSInit() { /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ diff --git a/source/Core/BSP/Pine64/BSP.cpp b/source/Core/BSP/Pine64/BSP.cpp index a1978bd3..4b06fae3 100644 --- a/source/Core/BSP/Pine64/BSP.cpp +++ b/source/Core/BSP/Pine64/BSP.cpp @@ -12,14 +12,12 @@ #include "main.hpp" const uint16_t powerPWM = 255; -const uint8_t holdoffTicks = 25; // delay of 7 ms -const uint8_t tempMeasureTicks = 25; +const uint8_t holdoffTicks = 10; +const uint8_t tempMeasureTicks = 14; -uint16_t totalPWM; // htim2.Init.Period, the full PWM cycle +uint16_t totalPWM; // Total length of the cycle's ticks -// 2 second filter (ADC is PID_TIM_HZ Hz) -history rawTempFilter = {{0}, 0, 0}; -void resetWatchdog() { fwdgt_counter_reload(); } +void resetWatchdog() { fwdgt_counter_reload(); } uint16_t getHandleTemperature(uint8_t sample) { #ifdef TEMP_TMP36 diff --git a/source/Core/BSP/Pine64/IRQ.cpp b/source/Core/BSP/Pine64/IRQ.cpp index 9285bb16..e0fff8f4 100644 --- a/source/Core/BSP/Pine64/IRQ.cpp +++ b/source/Core/BSP/Pine64/IRQ.cpp @@ -18,6 +18,9 @@ volatile uint16_t i2c_nbytes; volatile uint16_t i2c_write_dress; volatile uint16_t i2c_read_dress; volatile uint8_t i2c_process_flag = 0; +static bool fastPWM; +static void switchToSlowPWM(void); +static void switchToFastPWM(void); void ADC0_1_IRQHandler(void) { adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOIC); @@ -34,74 +37,58 @@ void ADC0_1_IRQHandler(void) { volatile uint16_t PWMSafetyTimer = 0; volatile uint8_t pendingPWM = 0; void TIMER1_IRQHandler(void) { + static bool lastPeriodWasFast = false; if (timer_interrupt_flag_get(TIMER1, TIMER_INT_UP) == SET) { timer_interrupt_flag_clear(TIMER1, TIMER_INT_UP); // rollover turn on output if required - if (PWMSafetyTimer && pendingPWM) { - timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 50); - } if (PWMSafetyTimer) { PWMSafetyTimer--; + if (lastPeriodWasFast != fastPWM) { + if (fastPWM) { + switchToFastPWM(); + } else { + switchToSlowPWM(); + } + } + if (pendingPWM) { + timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1, pendingPWM); + timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 50); + } else { + timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 0); + } } } if (timer_interrupt_flag_get(TIMER1, TIMER_INT_CH1) == SET) { timer_interrupt_flag_clear(TIMER1, TIMER_INT_CH1); - // This is triggered on pwm setpoint trigger; we want to copy the pending - // PWM value into the output control reg - timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 0); - if (pendingPWM) { - timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1, pendingPWM); - } } } -void setTipPWM(uint8_t pulse) { +void switchToFastPWM(void) { + fastPWM = true; + totalPWM = powerPWM + tempMeasureTicks + holdoffTicks; + TIMER_CAR(TIMER1) = (uint32_t)totalPWM; + + // ~10Hz + TIMER_CH0CV(TIMER1) = powerPWM + holdoffTicks; + // 1 kHz tick rate + TIMER_PSC(TIMER1) = 18000; +} + +void switchToSlowPWM(void) { + // 5Hz + fastPWM = false; + totalPWM = powerPWM + tempMeasureTicks / 2 + holdoffTicks / 2; + TIMER_CAR(TIMER1) = (uint32_t)totalPWM; + TIMER_CH0CV(TIMER1) = powerPWM + holdoffTicks / 2; + TIMER_PSC(TIMER1) = 36000; +} +void setTipPWM(const uint8_t pulse, const bool shouldUseFastModePWM) { PWMSafetyTimer = 10; // This is decremented in the handler for PWM so that the tip pwm is // disabled if the PID task is not scheduled often enough. pendingPWM = pulse; -} - -static bool fastPWM; -static void switchToFastPWM(void) { - fastPWM = true; - totalPWM = powerPWM + tempMeasureTicks * 2; - TIMER_CAR(TIMER1) = (uint32_t)totalPWM; - - // ~3.5 Hz rate - TIMER_CH0CV(TIMER1) = powerPWM + holdoffTicks * 2; - // 1 kHz tick rate - TIMER_PSC(TIMER1) = 12000; - /* generate an update event */ - TIMER_SWEVG(TIMER1) |= (uint32_t)TIMER_SWEVG_UPG; -} - -static void switchToSlowPWM(void) { - fastPWM = false; - totalPWM = powerPWM + tempMeasureTicks; - TIMER_CAR(TIMER1) = (uint32_t)totalPWM; - // ~1.84 Hz rate - TIMER_CH0CV(TIMER1) = powerPWM + holdoffTicks; - // 500 Hz tick rate - TIMER_PSC(TIMER1) = 24000; - /* generate an update event */ - TIMER_SWEVG(TIMER1) |= (uint32_t)TIMER_SWEVG_UPG; -} - -bool tryBetterPWM(uint8_t pwm) { - if (fastPWM && pwm == powerPWM) { - // maximum power for fast PWM reached, need to go slower to get more - switchToSlowPWM(); - return true; - } else if (!fastPWM && pwm < 230) { - // 254 in fast PWM mode gives the same power as 239 in slow - // allow for some reasonable hysteresis by switching only when it goes - // below 230 (equivalent to 245 in fast mode) - switchToFastPWM(); - return true; - } - return false; + fastPWM = shouldUseFastModePWM; } void EXTI5_9_IRQHandler(void) { diff --git a/source/Core/BSP/Pine64/Model_Config.h b/source/Core/BSP/Pine64/Model_Config.h deleted file mode 100644 index 1a63894e..00000000 --- a/source/Core/BSP/Pine64/Model_Config.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Model_Config.h - * - * Created on: 25 Jul 2020 - * Author: Ralim - */ - -#ifndef BSP_PINE64_MODEL_CONFIG_H_ -#define BSP_PINE64_MODEL_CONFIG_H_ -/* - * Lookup for mapping features <-> Models - */ - -#if defined(MODEL_Pinecil) == 0 -#error "No model defined!" -#endif - -#ifdef MODEL_Pinecil -#define POW_PD -#define POW_QC -#define POW_DC -#define POW_QC_20V -#define ENABLE_QC2 -#define TEMP_TMP36 -#define ACCEL_BMA -#define ACCEL_SC7 -#define HALL_SENSOR -#define HALL_SI7210 -#define BATTFILTERDEPTH 32 -#define DEBUG_UART_OUTPUT -#endif - -#endif /* BSP_PINE64_MODEL_CONFIG_H_ */ diff --git a/source/Core/BSP/Pine64/Power.cpp b/source/Core/BSP/Pine64/Power.cpp index 22fe53e0..5e60764e 100644 --- a/source/Core/BSP/Pine64/Power.cpp +++ b/source/Core/BSP/Pine64/Power.cpp @@ -1,9 +1,9 @@ #include "BSP.h" #include "BSP_Power.h" -#include "Model_Config.h" #include "Pins.h" #include "QC3.h" #include "Settings.h" +#include "configuration.h" #include "fusb_user.h" #include "fusbpd.h" #include "int_n.h" diff --git a/source/Core/BSP/Pine64/Setup.cpp b/source/Core/BSP/Pine64/Setup.cpp index 826dbdc4..96532b85 100644 --- a/source/Core/BSP/Pine64/Setup.cpp +++ b/source/Core/BSP/Pine64/Setup.cpp @@ -12,7 +12,7 @@ #include "history.hpp" #include #define ADC_NORM_SAMPLES 16 -#define ADC_FILTER_LEN 32 +#define ADC_FILTER_LEN 4 uint16_t ADCReadings[ADC_NORM_SAMPLES]; // room for 32 lots of the pair of readings // Functions @@ -264,17 +264,17 @@ void setup_timers() { /* initialize TIMER init parameter struct */ timer_struct_para_init(&timer_initpara); /* TIMER1 configuration */ - timer_initpara.prescaler = 5000; + timer_initpara.prescaler = 30000; timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_initpara.counterdirection = TIMER_COUNTER_UP; - timer_initpara.period = powerPWM + tempMeasureTicks; + timer_initpara.period = powerPWM + tempMeasureTicks + holdoffTicks; timer_initpara.clockdivision = TIMER_CKDIV_DIV4; timer_initpara.repetitioncounter = 0; timer_init(TIMER1, &timer_initpara); /* CH0 configured to implement the PWM irq's for the output control*/ timer_channel_output_struct_para_init(&timer_ocintpara); - timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH; + timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_LOW; timer_ocintpara.outputstate = TIMER_CCX_ENABLE; timer_channel_output_config(TIMER1, TIMER_CH_0, &timer_ocintpara); diff --git a/source/Core/BSP/Pine64/Vendor/SoC/gd32vf103/Common/Source/system_gd32vf103.c b/source/Core/BSP/Pine64/Vendor/SoC/gd32vf103/Common/Source/system_gd32vf103.c index fcb8786c..b34e55a4 100644 --- a/source/Core/BSP/Pine64/Vendor/SoC/gd32vf103/Common/Source/system_gd32vf103.c +++ b/source/Core/BSP/Pine64/Vendor/SoC/gd32vf103/Common/Source/system_gd32vf103.c @@ -118,7 +118,7 @@ static void system_clock_108m_hxtal(void) { /* APB2 = AHB/1 */ RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; /* APB1 = AHB/2 */ - RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; + RCU_CFG0 |= RCU_APB1_CKAHB_DIV4; /* CK_PLL = (CK_PREDIV0) * 27 = 108 MHz */ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); diff --git a/source/Core/BSP/Pine64/configuration.h b/source/Core/BSP/Pine64/configuration.h new file mode 100644 index 00000000..9d8c7f75 --- /dev/null +++ b/source/Core/BSP/Pine64/configuration.h @@ -0,0 +1,151 @@ +#ifndef CONFIGURATION_H_ +#define CONFIGURATION_H_ +#include "Settings.h" +#include +/** + * Configuration.h + * Define here your default pre settings for Pinecil + * + */ + +//=========================================================================== +//============================= Default Settings ============================ +//=========================================================================== +/** + * Default soldering temp is 320.0 C + * Temperature the iron sleeps at - default 150.0 C + */ + +#define SLEEP_TEMP 150 // Default sleep temperature +#define BOOST_TEMP 420 // Default boost temp. +#define BOOST_MODE_ENABLED 1 // 0: Disable 1: Enable + +/** + * Blink the temperature on the cooling screen when its > 50C + */ +#define COOLING_TEMP_BLINK 0 // 0: Disable 1: Enable + +/** + * How many seconds/minutes we wait until going to sleep/shutdown. + * Values -> SLEEP_TIME * 10; i.e. 5*10 = 50 Seconds! + */ +#define SLEEP_TIME 5 // x10 Seconds +#define SHUTDOWN_TIME 10 // Minutes + +/** + * Auto start off for safety. + * Pissible values are: + * 0 - none + * 1 - Soldering Temperature + * 2 - Sleep Temperature + * 3 - Sleep Off Temperature + */ +#define AUTO_START_MODE 0 // Default to none + +/** + * Locking Mode + * When in soldering mode a long press on both keys toggle the lock of the buttons + * Possible values are: + * 0 - Desactivated + * 1 - Lock except boost + * 2 - Full lock + */ +#define LOCKING_MODE 0 // Default to desactivated for safety + +/** + * OLED Orientation + * + */ +#define ORIENTATION_MODE 0 // 0: Right 1:Left 2:Automatic - Default right +#define REVERSE_BUTTON_TEMP_CHANGE 0 // 0:Default 1:Reverse - Reverse the plus and minus button assigment for temperature change + +/** + * Temp change settings + */ +#define TEMP_CHANGE_SHORT_STEP 1 // Default temp change short step +1 +#define TEMP_CHANGE_LONG_STEP 10 // Default temp change long step +10 +#define TEMP_CHANGE_SHORT_STEP_MAX 50 // Temp change short step MAX value +#define TEMP_CHANGE_LONG_STEP_MAX 90 // Temp change long step MAX value + +/* Power pulse for keeping power banks awake*/ +#define POWER_PULSE_INCREMENT 1 +#define POWER_PULSE_MAX 100 // x10 max watts +#define POWER_PULSE_WAIT_MAX 9 // 9*2.5s = 22.5 seconds +#define POWER_PULSE_DURATION_MAX 9 // 9*250ms = 2.25 seconds + +#ifdef MODEL_Pinecil +#define POWER_PULSE_DEFAULT 0 +#else +#define POWER_PULSE_DEFAULT 5 +#endif +#define POWER_PULSE_WAIT_DEFAULT 4 // Default rate of the power pulse: 4*2500 = 10000 ms = 10 s +#define POWER_PULSE_DURATION_DEFAULT 1 // Default duration of the power pulse: 1*250 = 250 ms + +/** + * OLED Orientation Sensitivity on Automatic mode! + * Motion Sensitivity <0=Off 1=Least Sensitive 9=Most Sensitive> + */ +#define SENSITIVITY 7 // Default 7 + +/** + * Detailed soldering screen + * Detailed idle screen (off for first time users) + */ +#define DETAILED_SOLDERING 0 // 0: Disable 1: Enable - Default 0 +#define DETAILED_IDLE 0 // 0: Disable 1: Enable - Default 0 + +#define THERMAL_RUNAWAY_TIME_SEC 20 +#define THERMAL_RUNAWAY_TEMP_C 20 + +#define CUT_OUT_SETTING 0 // default to no cut-off voltage +#define RECOM_VOL_CELL 33 // Minimum voltage per cell (Recommended 3.3V (33)) +#define TEMPERATURE_INF 0 // default to 0 +#define DESCRIPTION_SCROLL_SPEED 0 // 0: Slow 1: Fast - default to slow +#define ANIMATION_LOOP 1 // 0: off 1: on +#define ANIMATION_SPEED settingOffSpeed_t::MEDIUM + +#define OP_AMP_Rf_Pinecil 750 * 1000 // 750 Kilo-ohms -> From schematic, R1 +#define OP_AMP_Rin_Pinecil 2370 // 2.37 Kilo-ohms -> From schematic, R2 + +#define OP_AMP_GAIN_STAGE_PINECIL (1 + (OP_AMP_Rf_Pinecil / OP_AMP_Rin_Pinecil)) + +#if defined(MODEL_Pinecil) == 0 +#error "No model defined!" +#endif + +#ifdef MODEL_Pinecil +#define SOLDERING_TEMP 320 // Default soldering temp is 320.0 °C +#define VOLTAGE_DIV 467 // 467 - Default divider from schematic +#define CALIBRATION_OFFSET 900 // 900 - Default adc offset in uV +#define PID_POWER_LIMIT 70 // Sets the max pwm power limit +#define POWER_LIMIT 0 // 0 watts default limit +#define MAX_POWER_LIMIT 65 // +#define POWER_LIMIT_STEPS 5 // +#define OP_AMP_GAIN_STAGE OP_AMP_GAIN_STAGE_PINECIL // Uses Pinecil resistors +#define TEMP_uV_LOOKUP_HAKKO // Use Hakko lookup table +#define USB_PD_VMAX 20 // Maximum voltage for PD to negotiate +#define PID_TIM_HZ (8) // Tick rate of the PID loop +#define MAX_TEMP_C 450 // Max soldering temp selectable °C +#define MAX_TEMP_F 850 // Max soldering temp selectable °F +#define MIN_TEMP_C 10 // Min soldering temp selectable °C +#define MIN_TEMP_F 60 // Min soldering temp selectable °F +#define MIN_BOOST_TEMP_C 250 // The min settable temp for boost mode °C +#define MIN_BOOST_TEMP_F 480 // The min settable temp for boost mode °F + +#define POW_PD +#define POW_QC +#define POW_DC +#define POW_QC_20V +#define ENABLE_QC2 +#define TEMP_TMP36 +#define ACCEL_BMA +#define ACCEL_SC7 +#define HALL_SENSOR +#define HALL_SI7210 +#define DEBUG_UART_OUTPUT + +#define HARDWARE_MAX_WATTAGE_X10 750 +#define TIP_THERMAL_MASS 65 // X10 watts to raise 1 deg C in 1 second +#define tipResistance 75 // x10 ohms, 7.5 typical for Pinecil tips +#endif +#endif diff --git a/source/Core/BSP/Pine64/fusb_user.cpp b/source/Core/BSP/Pine64/fusb_user.cpp index 2d566399..5b9137ec 100644 --- a/source/Core/BSP/Pine64/fusb_user.cpp +++ b/source/Core/BSP/Pine64/fusb_user.cpp @@ -1,4 +1,4 @@ -#include "Model_Config.h" +#include "configuration.h" #ifdef POW_PD #include "BSP.h" #include "I2C_Wrapper.hpp" diff --git a/source/Core/Drivers/FUSB302/fusbpd.cpp b/source/Core/Drivers/FUSB302/fusbpd.cpp index f8a74155..18920c03 100644 --- a/source/Core/Drivers/FUSB302/fusbpd.cpp +++ b/source/Core/Drivers/FUSB302/fusbpd.cpp @@ -4,7 +4,7 @@ * Created on: 13 Jun 2020 * Author: Ralim */ -#include "Model_Config.h" +#include "configuration.h" #ifdef POW_PD #include "BSP.h" #include "I2CBB.hpp" diff --git a/source/Core/Drivers/I2CBB.cpp b/source/Core/Drivers/I2CBB.cpp index f90b09fc..b1fd6b7d 100644 --- a/source/Core/Drivers/I2CBB.cpp +++ b/source/Core/Drivers/I2CBB.cpp @@ -4,7 +4,7 @@ * Created on: 12 Jun 2020 * Author: Ralim */ -#include "Model_Config.h" +#include "configuration.h" #ifdef I2C_SOFT #include "FreeRTOS.h" #include diff --git a/source/Core/Drivers/I2CBB.hpp b/source/Core/Drivers/I2CBB.hpp index cf2e4df1..a66d5954 100644 --- a/source/Core/Drivers/I2CBB.hpp +++ b/source/Core/Drivers/I2CBB.hpp @@ -7,7 +7,7 @@ #ifndef BSP_MINIWARE_I2CBB_HPP_ #define BSP_MINIWARE_I2CBB_HPP_ -#include "Model_Config.h" +#include "configuration.h" #ifdef I2C_SOFT #include "BSP.h" #include "FreeRTOS.h" diff --git a/source/Core/Drivers/OLED.hpp b/source/Core/Drivers/OLED.hpp index f9d63a5a..a13e02a3 100644 --- a/source/Core/Drivers/OLED.hpp +++ b/source/Core/Drivers/OLED.hpp @@ -10,7 +10,7 @@ #ifndef OLED_HPP_ #define OLED_HPP_ #include "Font.h" -#include "Model_Config.h" +#include "configuration.h" #include #include #include diff --git a/source/Core/Drivers/TipThermoModel.cpp b/source/Core/Drivers/TipThermoModel.cpp index f283f3bf..4e708d54 100644 --- a/source/Core/Drivers/TipThermoModel.cpp +++ b/source/Core/Drivers/TipThermoModel.cpp @@ -72,14 +72,11 @@ uint32_t TipThermoModel::convertFtoC(uint32_t degF) { uint32_t TipThermoModel::getTipInC(bool sampleNow) { int32_t currentTipTempInC = TipThermoModel::convertTipRawADCToDegC(getTipRawTemp(sampleNow)); currentTipTempInC += getHandleTemperature(sampleNow) / 10; // Add handle offset - // Power usage indicates that our tip temp is lower than our thermocouple temp. - // I found a number that doesn't unbalance the existing PID, causing overshoot. - // This could be tuned in concert with PID parameters... -#ifdef THERMAL_MASS_OVERSHOOTS - currentTipTempInC += x10WattHistory.average() / 25; -#else - currentTipTempInC -= x10WattHistory.average() / 25; -#endif + + // Power usage indicates that our tip temp is lower than our thermocouple temp. + // I found a number that doesn't unbalance the existing PID, causing overshoot. + // This could be tuned in concert with PID parameters... + if (currentTipTempInC < 0) return 0; return currentTipTempInC; @@ -92,7 +89,7 @@ uint32_t TipThermoModel::getTipInF(bool sampleNow) { } uint32_t TipThermoModel::getTipMaxInC() { - uint32_t maximumTipTemp = TipThermoModel::convertTipRawADCToDegC(0x7FFF - (21 * 5)); // back off approx 5 deg c from ADC max + uint32_t maximumTipTemp = TipThermoModel::convertTipRawADCToDegC(0x7FFF - (21 * 3)); // back off approx 5 deg c from ADC max maximumTipTemp += getHandleTemperature(0) / 10; // Add handle offset return maximumTipTemp - 1; } diff --git a/source/Core/Inc/power.hpp b/source/Core/Inc/power.hpp index 786fb979..f8d35f67 100644 --- a/source/Core/Inc/power.hpp +++ b/source/Core/Inc/power.hpp @@ -22,7 +22,8 @@ const uint8_t wattHistoryFilter = 24; // I term look back weighting extern expMovingAverage x10WattHistory; -int32_t tempToX10Watts(int32_t rawTemp); -void setTipX10Watts(int32_t mw); -uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample = 0); +uint32_t availableW10(uint8_t sample); +int32_t tempToX10Watts(int32_t rawTemp); +void setTipX10Watts(int32_t mw); +uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample = 0); #endif /* POWER_HPP_ */ diff --git a/source/Core/Src/QC3.cpp b/source/Core/Src/QC3.cpp index 6a11c5de..311d1b73 100644 --- a/source/Core/Src/QC3.cpp +++ b/source/Core/Src/QC3.cpp @@ -7,9 +7,9 @@ // Quick charge 3.0 supporting functions #include "QC3.h" - #include "BSP.h" #include "cmsis_os.h" +#include "configuration.h" #include "stdint.h" enum QCState { NOT_STARTED = 0, // Have not checked @@ -70,7 +70,7 @@ void seekQC(int16_t Vx10, uint16_t divisor) { // try and step towards the wanted value // 1. Measure current voltage - int16_t vStart = getInputVoltageX10(divisor, 1); + int16_t vStart = getInputVoltageX10(divisor, 0); int difference = Vx10 - vStart; // 2. calculate ideal steps (0.2V changes) @@ -94,7 +94,7 @@ void seekQC(int16_t Vx10, uint16_t divisor) { #ifdef ENABLE_QC2 // Re-measure /* Disabled due to nothing to test and code space of around 1k*/ - steps = vStart - getInputVoltageX10(divisor, 1); + steps = vStart - getInputVoltageX10(divisor, 0); if (steps < 0) steps = -steps; if (steps > 4) { @@ -118,7 +118,7 @@ void seekQC(int16_t Vx10, uint16_t divisor) { void startQC(uint16_t divisor) { // Pre check that the input could be >5V already, and if so, dont both // negotiating as someone is feeding in hv - if (getInputVoltageX10(divisor, 1) > 80) { + if (getInputVoltageX10(divisor, 0) > 80) { QCTries = 11; QCMode = QCState::NO_QC; return; @@ -160,7 +160,7 @@ void startQC(uint16_t divisor) { // Wait for frontend ADC to stabilise QCMode = QCState::QC_2; for (uint8_t i = 0; i < 10; i++) { - if (getInputVoltageX10(divisor, 1) > 80) { + if (getInputVoltageX10(divisor, 0) > 80) { // yay we have at least QC2.0 or QC3.0 QCMode = QCState::QC_3; // We have at least QC2, pray for 3 return; diff --git a/source/Core/Src/Settings.cpp b/source/Core/Src/Settings.cpp index fddc3de6..bf4f4ef4 100644 --- a/source/Core/Src/Settings.cpp +++ b/source/Core/Src/Settings.cpp @@ -15,6 +15,12 @@ #include // for memset bool sanitiseSettings(); +#ifdef POW_QC_20V +#define QC_SETTINGS_MAX 3 +#else +#define QC_SETTINGS_MAX 2 +#endif + /* * This struct must be a multiple of 2 bytes as it is saved / restored from * flash in uint16_t chunks @@ -144,7 +150,7 @@ bool nextSettingValue(const enum SettingsOptions option) { } else { systemSettings.settingsValues[(int)option] += constants.increment; } - return (constants.max - systemSettings.settingsValues[(int)option]) < constants.increment; + return (constants.max - systemSettings.settingsValues[(int)option]) <= constants.increment; } bool prevSettingValue(const enum SettingsOptions option) { diff --git a/source/Core/Src/gui.cpp b/source/Core/Src/gui.cpp index 1d5c2821..3b8c9c10 100644 --- a/source/Core/Src/gui.cpp +++ b/source/Core/Src/gui.cpp @@ -187,6 +187,8 @@ const menuitem UIMenu[] = { * Display orientation * Cooldown blink * Reverse Temp change buttons + - + * Detailed IDLE + * Detailed Soldering */ {SETTINGS_DESC(SettingsItemIndex::TemperatureUnit), settings_setTempF, settings_displayTempF, SettingsOptions::SettingsOptionsLength}, /* Temperature units, this has to be the first element in the array to work with the logic in settings_enterUIMenu() */ @@ -196,12 +198,14 @@ const menuitem UIMenu[] = { {SETTINGS_DESC(SettingsItemIndex::CooldownBlink), nullptr, settings_displayCoolingBlinkEnabled, SettingsOptions::CoolingTempBlink}, /*Cooling blink warning*/ {SETTINGS_DESC(SettingsItemIndex::ScrollingSpeed), nullptr, settings_displayScrollSpeed, SettingsOptions::DescriptionScrollSpeed}, /*Scroll Speed for descriptions*/ {SETTINGS_DESC(SettingsItemIndex::ReverseButtonTempChange), nullptr, settings_displayReverseButtonTempChangeEnabled, - SettingsOptions::ReverseButtonTempChangeEnabled}, /* Reverse Temp change buttons + - */ - {SETTINGS_DESC(SettingsItemIndex::AnimSpeed), nullptr, settings_displayAnimationSpeed, SettingsOptions::AnimationSpeed}, /*Animation Speed adjustment */ - {SETTINGS_DESC(SettingsItemIndex::AnimLoop), nullptr, settings_displayAnimationLoop, SettingsOptions::AnimationLoop}, /*Animation Loop switch */ - {SETTINGS_DESC(SettingsItemIndex::Brightness), nullptr, settings_displayBrightnessLevel, SettingsOptions::OLEDBrightness}, /*Brightness Level*/ - {SETTINGS_DESC(SettingsItemIndex::ColourInversion), nullptr, settings_displayInvertColor, SettingsOptions::OLEDInversion}, /*Invert screen colour*/ - {0, nullptr, nullptr, SettingsOptions::SettingsOptionsLength} // end of menu marker. DO NOT REMOVE + SettingsOptions::ReverseButtonTempChangeEnabled}, /* Reverse Temp change buttons + - */ + {SETTINGS_DESC(SettingsItemIndex::AnimSpeed), nullptr, settings_displayAnimationSpeed, SettingsOptions::AnimationSpeed}, /*Animation Speed adjustment */ + {SETTINGS_DESC(SettingsItemIndex::AnimLoop), nullptr, settings_displayAnimationLoop, SettingsOptions::AnimationLoop}, /*Animation Loop switch */ + {SETTINGS_DESC(SettingsItemIndex::Brightness), nullptr, settings_displayBrightnessLevel, SettingsOptions::OLEDBrightness}, /*Brightness Level*/ + {SETTINGS_DESC(SettingsItemIndex::ColourInversion), nullptr, settings_displayInvertColor, SettingsOptions::OLEDInversion}, /*Invert screen colour*/ + {SETTINGS_DESC(SettingsItemIndex::AdvancedIdle), nullptr, settings_displayAdvancedIDLEScreens, SettingsOptions::DetailedIDLE}, /* Advanced idle screen*/ + {SETTINGS_DESC(SettingsItemIndex::AdvancedSoldering), nullptr, settings_displayAdvancedSolderingScreens, SettingsOptions::DetailedSoldering}, /* Advanced soldering screen*/ + {0, nullptr, nullptr, SettingsOptions::SettingsOptionsLength} // end of menu marker. DO NOT REMOVE }; const menuitem PowerSavingMenu[] = { /* @@ -225,8 +229,6 @@ const menuitem advancedMenu[] = { /* * Power limit - * Detailed IDLE - * Detailed Soldering * Calibrate Temperature * Calibrate Input V * Reset Settings @@ -237,8 +239,6 @@ const menuitem advancedMenu[] = { * Power Pulse Duration */ {SETTINGS_DESC(SettingsItemIndex::PowerLimit), nullptr, settings_displayPowerLimit, SettingsOptions::PowerLimit}, /*Power limit*/ - {SETTINGS_DESC(SettingsItemIndex::AdvancedIdle), nullptr, settings_displayAdvancedIDLEScreens, SettingsOptions::DetailedIDLE}, /* Advanced idle screen*/ - {SETTINGS_DESC(SettingsItemIndex::AdvancedSoldering), nullptr, settings_displayAdvancedSolderingScreens, SettingsOptions::DetailedSoldering}, /* Advanced soldering screen*/ {SETTINGS_DESC(SettingsItemIndex::SettingsReset), settings_setResetSettings, settings_displayResetSettings, SettingsOptions::SettingsOptionsLength}, /*Resets settings*/ {SETTINGS_DESC(SettingsItemIndex::TemperatureCalibration), settings_setCalibrate, settings_displayCalibrate, SettingsOptions::SettingsOptionsLength}, /*Calibrate tip*/ {SETTINGS_DESC(SettingsItemIndex::VoltageCalibration), settings_setCalibrateVIN, settings_displayCalibrateVIN, SettingsOptions::SettingsOptionsLength}, /*Voltage input cal*/ @@ -352,8 +352,12 @@ static bool settings_displayQCInputV(void) { static bool settings_displayPDNegTimeout(void) { printShortDescription(SettingsItemIndex::PDNegTimeout, 5); - OLED::printNumber(getSettingValue(SettingsOptions::PDNegTimeout), 2, FontStyle::LARGE); - + auto value = getSettingValue(SettingsOptions::PDNegTimeout); + if (value == 0) { + OLED::print(translatedString(Tr->OffString), FontStyle::LARGE); + } else { + OLED::printNumber(value, 2, FontStyle::LARGE); + } return false; } #endif @@ -409,7 +413,7 @@ static bool settings_displayShutdownTime(void) { return false; } static bool settings_setTempF(void) { - nextSettingValue(SettingsOptions::TemperatureInF); + bool res = nextSettingValue(SettingsOptions::TemperatureInF); uint16_t BoostTemp = getSettingValue(SettingsOptions::BoostTemp); uint16_t SolderingTemp = getSettingValue(SettingsOptions::SolderingTemp); uint16_t SleepTemp = getSettingValue(SettingsOptions::SleepTemp); @@ -438,7 +442,7 @@ static bool settings_setTempF(void) { setSettingValue(SettingsOptions::SolderingTemp, SolderingTemp); setSettingValue(SettingsOptions::SleepTemp, SleepTemp); - return false; + return res; } static bool settings_displayTempF(void) { @@ -641,7 +645,7 @@ static void setTipOffset() { OLED::refresh(); osDelay(100); } - setoffset = TipThermoModel::convertTipRawADCTouV(offset / 16); + setoffset = TipThermoModel::convertTipRawADCTouV(offset / 16, true); } setSettingValue(SettingsOptions::CalibrationOffset, setoffset); OLED::clearScreen(); diff --git a/source/Core/Src/power.cpp b/source/Core/Src/power.cpp index 97e4472a..d6665bd0 100644 --- a/source/Core/Src/power.cpp +++ b/source/Core/Src/power.cpp @@ -10,26 +10,41 @@ #include static int32_t PWMToX10Watts(uint8_t pwm, uint8_t sample); +const int fastPWMChangeoverPoint = 128; +const int fastPWMChangeoverTolerance = 16; expMovingAverage x10WattHistory = {0}; +bool shouldBeUsingFastPWMMode(const uint8_t pwmTicks) { + // Determine if we should use slow or fast PWM mode + // Crossover between modes set around the midpoint of the PWM control point + static bool lastPWMWasFast = true; + if (pwmTicks > (fastPWMChangeoverPoint + fastPWMChangeoverTolerance) && lastPWMWasFast) { + lastPWMWasFast = false; + } else if (pwmTicks < (fastPWMChangeoverPoint - fastPWMChangeoverTolerance) && !lastPWMWasFast) { + lastPWMWasFast = true; + } + return lastPWMWasFast; +} + 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 + // mass is in x10J/*C, rawC is raw per degree C + // returns x10Watts needed to raise/lower a mass by rawTemp // degrees in one cycle. - int32_t milliJoules = tipMass * rawTemp; - return milliJoules; + int32_t x10Watts = TIP_THERMAL_MASS * rawTemp; + return x10Watts; } void setTipX10Watts(int32_t mw) { - int32_t output = X10WattsToPWM(mw, 1); - setTipPWM(output); - uint32_t actualMilliWatts = PWMToX10Watts(output, 0); + int32_t outputPWMLevel = X10WattsToPWM(mw, 1); + const bool shouldUseFastPWM = shouldBeUsingFastPWMMode(outputPWMLevel); + setTipPWM(outputPWMLevel, shouldUseFastPWM); + uint32_t actualMilliWatts = PWMToX10Watts(outputPWMLevel, 0); x10WattHistory.update(actualMilliWatts); } -static uint32_t availableW10(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. @@ -45,26 +60,21 @@ static uint32_t availableW10(uint8_t sample) { // availableMilliWattsX10 is now an accurate representation return availableWattsX10; } - -uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample) { - // Scale input milliWatts to the pwm range available - if (milliWatts < 1) { +uint8_t X10WattsToPWM(int32_t x10Watts, uint8_t sample) { + // Scale input x10Watts to the pwm range available + if (x10Watts < 0) { // keep the battery voltage updating the filter getInputVoltageX10(getSettingValue(SettingsOptions::VoltageDiv), sample); return 0; } - // Calculate desired milliwatts as a percentage of availableW10 + // Calculate desired x10Watts as a percentage of availableW10 uint32_t pwm; - do { - pwm = (powerPWM * milliWatts) / availableW10(sample); - if (pwm > powerPWM) { - // constrain to max PWM counter, shouldn't be possible, - // but small cost for safety to avoid wraps - pwm = powerPWM; - } - } while (tryBetterPWM(pwm)); - + pwm = (powerPWM * x10Watts) / availableW10(sample); + if (pwm > powerPWM) { + // constrain to max PWM counter + pwm = powerPWM; + } return pwm; } diff --git a/source/Core/Threads/MOVThread.cpp b/source/Core/Threads/MOVThread.cpp index c929e8bd..e0e7c86e 100644 --- a/source/Core/Threads/MOVThread.cpp +++ b/source/Core/Threads/MOVThread.cpp @@ -12,17 +12,18 @@ #include "LIS2DH12.hpp" #include "MMA8652FC.hpp" #include "MSA301.h" -#include "Model_Config.h" #include "QC3.h" #include "SC7A20.hpp" #include "Settings.h" #include "TipThermoModel.h" #include "cmsis_os.h" +#include "configuration.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; diff --git a/source/Core/Threads/PIDThread.cpp b/source/Core/Threads/PIDThread.cpp index 3352e21c..b87a488e 100644 --- a/source/Core/Threads/PIDThread.cpp +++ b/source/Core/Threads/PIDThread.cpp @@ -20,36 +20,36 @@ TaskHandle_t pidTaskNotification = NULL; uint32_t currentTempTargetDegC = 0; // Current temperature target in C int32_t powerSupplyWattageLimit = 0; bool heaterThermalRunaway = false; + +static int32_t getPIDResultX10Watts(int32_t tError); +static void detectThermalRunaway(const int16_t currentTipTempInC, const int tError); +static void setOutputx10WattsViaFilters(int32_t x10Watts); +static int32_t getX10WattageLimits(); + /* 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; + setTipX10Watts(0); // disable the output at startup - history 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; - uint16_t tipTempCRunawayTemp = 0; - TickType_t runawaylastChangeTime = 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; // Pre-seed the adc filters - for (int i = 0; i < 64; i++) { - vTaskDelay(2); + for (int i = 0; i < 128; i++) { + vTaskDelay(5); TipThermoModel::getTipInC(true); + getInputVoltageX10(getSettingValue(SettingsOptions::VoltageDiv), 1); } -#ifdef SLEW_LIMIT - int32_t x10WattsOutLast = 0; -#endif - for (;;) { + int32_t x10WattsOut = 0; + for (;;) { + x10WattsOut = 0; + // This is a call to block this thread until the ADC does its samples 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; @@ -63,116 +63,163 @@ void startPIDTask(void const *argument __unused) { 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); - // 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. - - // Check for thermal runaway, where it has been x seconds with negligible (y) temp rise - // While trying to actively heat - if ((tError > THERMAL_RUNAWAY_TEMP_C)) { - // Temp error is high - int16_t delta = (int16_t)currentTipTempInC - (int16_t)tipTempCRunawayTemp; - if (delta < 0) { - delta = -delta; - } - if (delta > THERMAL_RUNAWAY_TEMP_C) { - // We have heated up more than the threshold, reset the timer - tipTempCRunawayTemp = currentTipTempInC; - runawaylastChangeTime = xTaskGetTickCount(); - } else { - if ((xTaskGetTickCount() - runawaylastChangeTime) > (THERMAL_RUNAWAY_TIME_SEC * TICKS_SECOND)) { - // It has taken too long to rise - heaterThermalRunaway = true; - } - } - } else { - tipTempCRunawayTemp = currentTipTempInC; - runawaylastChangeTime = xTaskGetTickCount(); - } + int32_t tError = PIDTempTarget - currentTipTempInC; + detectThermalRunaway(currentTipTempInC, tError); + x10WattsOut = getPIDResultX10Watts(tError); } else { - tipTempCRunawayTemp = currentTipTempInC; - runawaylastChangeTime = xTaskGetTickCount(); + detectThermalRunaway(currentTipTempInC, 0); } - - // If the user turns on the option of using an occasional pulse to keep the power bank on - if (getSettingValue(SettingsOptions::KeepAwakePulse)) { - const TickType_t powerPulseWait = powerPulseWaitUnit * getSettingValue(SettingsOptions::KeepAwakePulseWait); - if (xTaskGetTickCount() - lastPowerPulseStart > powerPulseWait) { - const TickType_t powerPulseDuration = powerPulseDurationUnit * getSettingValue(SettingsOptions::KeepAwakePulseDuration); - 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 < getSettingValue(SettingsOptions::KeepAwakePulse) && xTaskGetTickCount() < lastPowerPulseEnd) { - x10WattsOut = getSettingValue(SettingsOptions::KeepAwakePulse); - } - } - - // Secondary safety check to forcefully disable header when within ADC noise of top of ADC - if (getTipRawTemp(0) > (0x7FFF - 32)) { - x10WattsOut = 0; - } - if (heaterThermalRunaway) { - x10WattsOut = 0; - } - if (getSettingValue(SettingsOptions::PowerLimit) && x10WattsOut > (getSettingValue(SettingsOptions::PowerLimit) * 10)) { - x10WattsOut = getSettingValue(SettingsOptions::PowerLimit) * 10; - } - if (powerSupplyWattageLimit && x10WattsOut > powerSupplyWattageLimit * 10) { - x10WattsOut = powerSupplyWattageLimit * 10; - } -#ifdef SLEW_LIMIT - if (x10WattsOut - x10WattsOutLast > SLEW_LIMIT) { - x10WattsOut = x10WattsOutLast + SLEW_LIMIT; - } - if (x10WattsOut < 0) { - x10WattsOut = 0; - } - x10WattsOutLast = x10WattsOut; -#endif - setTipX10Watts(x10WattsOut); -#ifdef DEBUG_UART_OUTPUT - log_system_state(x10WattsOut); -#endif - resetWatchdog(); + setOutputx10WattsViaFilters(x10WattsOut); } else { // ADC interrupt timeout - setTipPWM(0); + setTipPWM(0, false); } +#ifdef DEBUG_UART_OUTPUT + log_system_state(x10WattsOut); +#endif } } + +template struct Integrator { + T sum; + + T update(const T val, const int32_t inertia, const int32_t gain, const int32_t rate, const int32_t limit) { + // Decay the old value. This is a simplified formula that still works with decent results + // Ideally we would have used an exponential decay but the computational effort required + // by exp function is just not justified here in respect to the outcome + sum = (sum * (100 - (inertia / rate))) / 100; + // Add the new value x integration interval ( 1 / rate) + sum += (gain * val) / rate; + + // limit the output + if (sum > limit) + sum = limit; + else if (sum < -limit) + sum = -limit; + + return sum; + } + + void set(T const val) { sum = val; } + + T get(bool positiveOnly = true) const { return (positiveOnly) ? ((sum > 0) ? sum : 0) : sum; } +}; +int32_t getPIDResultX10Watts(int32_t setpointDelta) { + static TickType_t lastCall = 0; + static Integrator powerStore = {0}; + + const int rate = 1000 / (xTaskGetTickCount() - lastCall); + lastCall = xTaskGetTickCount(); + // Sandman note: + // PID Challenge - we have a small thermal mass that we to want heat up as fast as possible but we don't + // want to overshot excessively (if at all) the setpoint temperature. In the same time we have 'imprecise' + // instant temperature measurements. The nature of temperature reading imprecision is not necessarily + // related to the sensor (thermocouple) or DAQ system, that otherwise are fairly decent. The real issue is + // the thermal inertia. We basically read the temperature in the window between two heating sessions when + // the output is off. However, the heater temperature does not dissipate instantly into the tip mass so + // at any moment right after heating, the thermocouple would sense a temperature significantly higher than + // moments later. We could use longer delays but that would slow the PID loop and that would lead to other + // negative side effects. As a result, we can only rely on the I term but with a twist. Instead of a simple + // integrator we are going to use a self decaying integrator that acts more like a dual I term / P term + // rather than a plain I term. Depending on the circumstances, like when the delta temperature is large, + // it acts more like a P term whereas on closing to setpoint it acts increasingly closer to a plain I term. + // So in a sense, we have a bit of both. + // So there we go... + + // P = (Thermal Mass) x (Delta Temperature ) / 1sec, where thermal mass is in X10 J / °C and + // delta temperature is in °C. The result is the power in X10 W needed to raise (or decrease!) the + // tip temperature with (Delta Temperature ) °C in 1 second. + // Note on powerStore. On update, if the value is provided in X10 (W) units then inertia shall be provided + // in X10 (J / °C) units as well. Also, powerStore is updated with a gain of 2. Where this comes from: The actual + // power CMOS is controlled by TIM3->CTR1 (that is software modulated - on/off - by TIM2-CTR4 interrupts). However, + // TIM3->CTR1 is configured with a duty cycle of 50% so, in real, we get only 50% of the presumed power output + // so we basically double the need (gain = 2) to get what we want. + return powerStore.update(TIP_THERMAL_MASS * setpointDelta, // the required power + TIP_THERMAL_MASS, // Inertia, smaller numbers increase dominance of the previous value + 2, // gain + rate, // PID cycle frequency + getX10WattageLimits()); +} + +void detectThermalRunaway(const int16_t currentTipTempInC, const int tError) { + static uint16_t tipTempCRunawayTemp = 0; + static TickType_t runawaylastChangeTime = 0; + + // Check for thermal runaway, where it has been x seconds with negligible (y) temp rise + // While trying to actively heat + if ((tError > THERMAL_RUNAWAY_TEMP_C)) { + // Temp error is high + int16_t delta = (int16_t)currentTipTempInC - (int16_t)tipTempCRunawayTemp; + if (delta < 0) { + delta = -delta; + } + if (delta > THERMAL_RUNAWAY_TEMP_C) { + // We have heated up more than the threshold, reset the timer + tipTempCRunawayTemp = currentTipTempInC; + runawaylastChangeTime = xTaskGetTickCount(); + } else { + if ((xTaskGetTickCount() - runawaylastChangeTime) > (THERMAL_RUNAWAY_TIME_SEC * TICKS_SECOND)) { + // It has taken too long to rise + heaterThermalRunaway = true; + } + } + } else { + tipTempCRunawayTemp = currentTipTempInC; + runawaylastChangeTime = xTaskGetTickCount(); + } +} + +int32_t getX10WattageLimits() { + int32_t limit = availableW10(0); + + if (getSettingValue(SettingsOptions::PowerLimit) && limit > (getSettingValue(SettingsOptions::PowerLimit) * 10)) { + limit = getSettingValue(SettingsOptions::PowerLimit) * 10; + } + if (powerSupplyWattageLimit && limit > powerSupplyWattageLimit * 10) { + limit = powerSupplyWattageLimit * 10; + } + return limit; +} + +void setOutputx10WattsViaFilters(int32_t x10WattsOut) { + static TickType_t lastPowerPulseStart = 0; + static TickType_t lastPowerPulseEnd = 0; +#ifdef SLEW_LIMIT + static int32_t x10WattsOutLast = 0; +#endif + + // If the user turns on the option of using an occasional pulse to keep the power bank on + if (getSettingValue(SettingsOptions::KeepAwakePulse)) { + const TickType_t powerPulseWait = powerPulseWaitUnit * getSettingValue(SettingsOptions::KeepAwakePulseWait); + if (xTaskGetTickCount() - lastPowerPulseStart > powerPulseWait) { + const TickType_t powerPulseDuration = powerPulseDurationUnit * getSettingValue(SettingsOptions::KeepAwakePulseDuration); + 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 < getSettingValue(SettingsOptions::KeepAwakePulse) && xTaskGetTickCount() < lastPowerPulseEnd) { + x10WattsOut = getSettingValue(SettingsOptions::KeepAwakePulse); + } + } + + // Secondary safety check to forcefully disable header when within ADC noise of top of ADC + if (getTipRawTemp(0) > (0x7FFF - 32)) { + x10WattsOut = 0; + } + if (heaterThermalRunaway) { + x10WattsOut = 0; + } +#ifdef SLEW_LIMIT + if (x10WattsOut - x10WattsOutLast > SLEW_LIMIT) { + x10WattsOut = x10WattsOutLast + SLEW_LIMIT; + } + if (x10WattsOut < 0) { + x10WattsOut = 0; + } + x10WattsOutLast = x10WattsOut; +#endif + setTipX10Watts(x10WattsOut); + resetWatchdog(); +} \ No newline at end of file