1
0
forked from me/IronOS

Merge pull request #1043 from Ralim/ralim/pid

PID complete rewrite with kudos to @sandmanRO  and help from @discip
This commit is contained in:
Ben V. Brown
2021-09-17 18:53:38 +10:00
committed by GitHub
40 changed files with 688 additions and 496 deletions

View File

@@ -2,7 +2,7 @@
#include "BSP_Power.h" #include "BSP_Power.h"
#include "BSP_QC.h" #include "BSP_QC.h"
#include "Defines.h" #include "Defines.h"
#include "Model_Config.h" #include "configuration.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
/* /*
@@ -33,16 +33,13 @@ void BSPInit(void);
// Called to reset the hardware watchdog unit // Called to reset the hardware watchdog unit
void resetWatchdog(); void resetWatchdog();
// Accepts a output level of 0.. to use to control the tip output PWM // 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 // Returns the Handle temp in C, X10
uint16_t getHandleTemperature(uint8_t sample); uint16_t getHandleTemperature(uint8_t sample);
// Returns the Tip temperature ADC reading in raw units // Returns the Tip temperature ADC reading in raw units
uint16_t getTipRawTemp(uint8_t refresh); uint16_t getTipRawTemp(uint8_t refresh);
// Returns the main DC input voltage, using the adjustable divisor + sample flag // Returns the main DC input voltage, using the adjustable divisor + sample flag
uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample); 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 // Readers for the two buttons
// !! Returns 1 if held down, 0 if released // !! Returns 1 if held down, 0 if released

View File

@@ -2,7 +2,6 @@
#include "BSP.h" #include "BSP.h"
#include "I2C_Wrapper.hpp" #include "I2C_Wrapper.hpp"
#include "Model_Config.h"
#include "Pins.h" #include "Pins.h"
#include "Setup.h" #include "Setup.h"
#include "TipThermoModel.h" #include "TipThermoModel.h"
@@ -246,11 +245,7 @@ uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample) {
} }
return sum * 4 / divisor; return sum * 4 / divisor;
} }
bool tryBetterPWM(uint8_t pwm) { void setTipPWM(const uint8_t pulse, const bool shouldUseFastModePWM) {
// We dont need this for the MHP30
return false;
}
void setTipPWM(uint8_t pulse) {
// We can just set the timer directly // We can just set the timer directly
if (htim3.Instance->PSC > 20) { if (htim3.Instance->PSC > 20) {
htim3.Instance->CCR1 = 0; htim3.Instance->CCR1 = 0;

View File

@@ -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_ */

View File

@@ -7,7 +7,7 @@
#ifndef BSP_MINIWARE_PINS_H_ #ifndef BSP_MINIWARE_PINS_H_
#define BSP_MINIWARE_PINS_H_ #define BSP_MINIWARE_PINS_H_
#include "Model_Config.h" #include "configuration.h"
// MHP30 pin map // MHP30 pin map
#define KEY_B_Pin GPIO_PIN_0 #define KEY_B_Pin GPIO_PIN_0

View File

@@ -1,9 +1,9 @@
#include "BSP.h" #include "BSP.h"
#include "BSP_Power.h" #include "BSP_Power.h"
#include "Model_Config.h"
#include "Pins.h" #include "Pins.h"
#include "QC3.h" #include "QC3.h"
#include "Settings.h" #include "Settings.h"
#include "configuration.h"
#include "fusb_user.h" #include "fusb_user.h"
#include "fusbpd.h" #include "fusbpd.h"
#include "int_n.h" #include "int_n.h"

View File

@@ -5,11 +5,12 @@
* Author: Ralim * Author: Ralim
*/ */
#include "BSP.h" #include "BSP.h"
#include "Model_Config.h"
#include "Pins.h" #include "Pins.h"
#include "QC3.h" #include "QC3.h"
#include "Settings.h" #include "Settings.h"
#include "configuration.h"
#include "stm32f1xx_hal.h" #include "stm32f1xx_hal.h"
#ifdef POW_QC #ifdef POW_QC
void QC_DPlusZero_Six() { void QC_DPlusZero_Six() {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); // pull down D+ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); // pull down D+

View File

@@ -8,7 +8,7 @@
#ifndef BSP_MINIWARE_SOFTWARE_I2C_H_ #ifndef BSP_MINIWARE_SOFTWARE_I2C_H_
#define BSP_MINIWARE_SOFTWARE_I2C_H_ #define BSP_MINIWARE_SOFTWARE_I2C_H_
#include "BSP.h" #include "BSP.h"
#include "Model_Config.h" #include "configuration.h"
#include "stm32f1xx_hal.h" #include "stm32f1xx_hal.h"
#ifdef I2C_SOFT #ifdef I2C_SOFT

View File

@@ -0,0 +1,156 @@
#ifndef CONFIGURATION_H_
#define CONFIGURATION_H_
#include "Settings.h"
#include "configuration.h"
#include <stdint.h>
/**
* 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

View File

@@ -1,4 +1,4 @@
#include "Model_Config.h" #include "configuration.h"
#ifdef POW_PD #ifdef POW_PD
#include "BSP.h" #include "BSP.h"
#include "I2C_Wrapper.hpp" #include "I2C_Wrapper.hpp"

View File

@@ -7,11 +7,12 @@
#include "BSP.h" #include "BSP.h"
#include "I2CBB.hpp" #include "I2CBB.hpp"
#include "Model_Config.h"
#include "Pins.h" #include "Pins.h"
#include "Setup.h" #include "Setup.h"
#include "configuration.h"
#include "fusbpd.h" #include "fusbpd.h"
#include <I2C_Wrapper.hpp> #include <I2C_Wrapper.hpp>
void preRToSInit() { void preRToSInit() {
/* Reset of all peripherals, Initializes the Flash interface and the Systick. /* Reset of all peripherals, Initializes the Flash interface and the Systick.
*/ */

View File

@@ -2,7 +2,6 @@
#include "BSP.h" #include "BSP.h"
#include "I2C_Wrapper.hpp" #include "I2C_Wrapper.hpp"
#include "Model_Config.h"
#include "Pins.h" #include "Pins.h"
#include "Setup.h" #include "Setup.h"
#include "TipThermoModel.h" #include "TipThermoModel.h"
@@ -10,6 +9,7 @@
#include "history.hpp" #include "history.hpp"
#include "main.hpp" #include "main.hpp"
#include <IRQ.h> #include <IRQ.h>
volatile uint16_t PWMSafetyTimer = 0; volatile uint16_t PWMSafetyTimer = 0;
volatile uint8_t pendingPWM = 0; volatile uint8_t pendingPWM = 0;
@@ -21,9 +21,7 @@ uint16_t totalPWM; // htim2.Init.Period, the full PWM cycle
static bool fastPWM; static bool fastPWM;
// 2 second filter (ADC is PID_TIM_HZ Hz) void resetWatchdog() { HAL_IWDG_Refresh(&hiwdg); }
history<uint16_t, PID_TIM_HZ> rawTempFilter = {{0}, 0, 0};
void resetWatchdog() { HAL_IWDG_Refresh(&hiwdg); }
#ifdef TEMP_NTC #ifdef TEMP_NTC
// Lookup table for the NTC // Lookup table for the NTC
// Stored as ADCReading,Temp in degC // Stored as ADCReading,Temp in degC
@@ -135,48 +133,36 @@ uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample) {
return res; 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) { static void switchToFastPWM(void) {
fastPWM = true; // 10Hz
totalPWM = powerPWM + tempMeasureTicks * 2 + holdoffTicks; fastPWM = true;
htim2.Instance->ARR = totalPWM; totalPWM = powerPWM + tempMeasureTicks + holdoffTicks;
// ~3.5 Hz rate htim2.Instance->ARR = totalPWM;
htim2.Instance->CCR1 = powerPWM + holdoffTicks * 2; htim2.Instance->CCR1 = powerPWM + holdoffTicks;
// 2 MHz timer clock/2000 = 1 kHz tick rate htim2.Instance->PSC = 2690;
htim2.Instance->PSC = 2000;
} }
static void switchToSlowPWM(void) { static void switchToSlowPWM(void) {
fastPWM = false; // 5Hz
totalPWM = powerPWM + tempMeasureTicks + holdoffTicks; fastPWM = false;
htim2.Instance->ARR = totalPWM; totalPWM = powerPWM + tempMeasureTicks / 2 + holdoffTicks / 2;
// ~1.84 Hz rate htim2.Instance->ARR = totalPWM;
htim2.Instance->CCR1 = powerPWM + holdoffTicks; htim2.Instance->CCR1 = powerPWM + holdoffTicks / 2;
// 2 MHz timer clock/4000 = 500 Hz tick rate htim2.Instance->PSC = 2690 * 2;
htim2.Instance->PSC = 4000;
} }
bool tryBetterPWM(uint8_t pwm) { void setTipPWM(const uint8_t pulse, const bool shouldUseFastModePWM) {
if (fastPWM && pwm == powerPWM) { PWMSafetyTimer = 10; // This is decremented in the handler for PWM so that the tip pwm is
// maximum power for fast PWM reached, need to go slower to get more // disabled if the PID task is not scheduled often enough.
switchToSlowPWM(); pendingPWM = pulse;
return true; if (fastPWM != shouldUseFastModePWM) {
} else if (!fastPWM && pwm < 230) { if (shouldUseFastModePWM) {
// 254 in fast PWM mode gives the same power as 239 in slow switchToFastPWM();
// allow for some reasonable hysteresis by switching only when it goes } else {
// below 230 (equivalent to 245 in fast mode) switchToSlowPWM();
switchToFastPWM(); }
return true;
} }
return false;
} }
// These are called by the HAL after the corresponding events from the system // These are called by the HAL after the corresponding events from the system
// timers. // timers.

View File

@@ -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_ */

View File

@@ -7,7 +7,7 @@
#ifndef BSP_MINIWARE_PINS_H_ #ifndef BSP_MINIWARE_PINS_H_
#define BSP_MINIWARE_PINS_H_ #define BSP_MINIWARE_PINS_H_
#include "Model_Config.h" #include "configuration.h"
#ifdef MODEL_TS100 #ifdef MODEL_TS100

View File

@@ -1,9 +1,9 @@
#include "BSP.h" #include "BSP.h"
#include "BSP_Power.h" #include "BSP_Power.h"
#include "Model_Config.h"
#include "Pins.h" #include "Pins.h"
#include "QC3.h" #include "QC3.h"
#include "Settings.h" #include "Settings.h"
#include "configuration.h"
#include "fusb_user.h" #include "fusb_user.h"
#include "fusbpd.h" #include "fusbpd.h"
#include "int_n.h" #include "int_n.h"

View File

@@ -5,11 +5,12 @@
* Author: Ralim * Author: Ralim
*/ */
#include "BSP.h" #include "BSP.h"
#include "Model_Config.h"
#include "Pins.h" #include "Pins.h"
#include "QC3.h" #include "QC3.h"
#include "Settings.h" #include "Settings.h"
#include "configuration.h"
#include "stm32f1xx_hal.h" #include "stm32f1xx_hal.h"
#ifdef POW_QC #ifdef POW_QC
void QC_DPlusZero_Six() { void QC_DPlusZero_Six() {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); // pull down D+ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET); // pull down D+

View File

@@ -20,7 +20,7 @@ DMA_HandleTypeDef hdma_i2c1_tx;
IWDG_HandleTypeDef hiwdg; IWDG_HandleTypeDef hiwdg;
TIM_HandleTypeDef htim2; TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3; TIM_HandleTypeDef htim3;
#define ADC_FILTER_LEN 32 #define ADC_FILTER_LEN 4
#define ADC_SAMPLES 16 #define ADC_SAMPLES 16
uint16_t ADCReadings[ADC_SAMPLES]; // Used to store the adc readings for the handle cold junction temp 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" // 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; htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
// dummy value, will be reconfigured by BSPInit() // 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.ClockDivision = TIM_CLOCKDIVISION_DIV4; // 8 MHz (x2 APB1) before divide
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
htim2.Init.RepetitionCounter = 0; htim2.Init.RepetitionCounter = 0;
@@ -344,7 +345,7 @@ static void MX_TIM2_Init(void) {
sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.OCMode = TIM_OCMODE_PWM1;
// dummy value, will be reconfigured by BSPInit() in the BSP.cpp // 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 // 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. * It takes 4 milliseconds for output to be stable after PWM turns off.

View File

@@ -8,7 +8,7 @@
#ifndef BSP_MINIWARE_SOFTWARE_I2C_H_ #ifndef BSP_MINIWARE_SOFTWARE_I2C_H_
#define BSP_MINIWARE_SOFTWARE_I2C_H_ #define BSP_MINIWARE_SOFTWARE_I2C_H_
#include "BSP.h" #include "BSP.h"
#include "Model_Config.h" #include "configuration.h"
#include "stm32f1xx_hal.h" #include "stm32f1xx_hal.h"
#ifdef I2C_SOFT #ifdef I2C_SOFT

View File

@@ -1,10 +1,10 @@
#pragma once #ifndef CONFIGURATION_H_
#include "Model_Config.h" #define CONFIGURATION_H_
#include "Settings.h" #include "Settings.h"
#include <stdint.h> #include <stdint.h>
/** /**
* Configuration.h * 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 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 * 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_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: // Deriving the Voltage div:
// Vin_max = (3.3*(r1+r2))/(r2) // Vin_max = (3.3*(r1+r2))/(r2)
// vdiv = (32768*4)/(vin_max*10) // 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 #ifdef MODEL_TS100
#define SOLDERING_TEMP 320 // Default soldering temp is 320.0 °C #define SOLDERING_TEMP 320 // Default soldering temp is 320.0 °C
#define VOLTAGE_DIV 467 // 467 - Default divider from schematic #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_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_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 MIN_BOOST_TEMP_F 480 // The min settable temp for boost mode °F
#endif #define POW_DC
#define ACCEL_MMA
#ifdef MODEL_Pinecil #define ACCEL_LIS
#define SOLDERING_TEMP 320 // Default soldering temp is 320.0 °C #define TEMP_TMP36
#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
#endif #endif
#ifdef MODEL_TS80 #ifdef MODEL_TS80
@@ -180,6 +166,11 @@
#define MIN_TEMP_F 60 // Min soldering temp selectable °F #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_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 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 #endif
#ifdef MODEL_TS80P #ifdef MODEL_TS80P
@@ -200,59 +191,31 @@
#define MIN_TEMP_F 60 // Min soldering temp selectable °F #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_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 MIN_BOOST_TEMP_F 480 // The min settable temp for boost mode °F
#define ACCEL_LIS
#endif #define ACCEL_MSA
#define POW_PD
#ifdef MODEL_MHP30 #define POW_QC
#define SOLDERING_TEMP 200 // Default soldering temp is 200.0 °C #define TEMP_NTC
#define VOLTAGE_DIV 360 // Default for MHP30 #define I2C_SOFT
#define PID_POWER_LIMIT 65 // Sets the max pwm power limit #define LIS_ORI_FLIP
#define CALIBRATION_OFFSET 0 // the adc offset in uV - MHP compensates automagically #define OLED_FLIP
#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
#endif #endif
#ifdef MODEL_TS100 #ifdef MODEL_TS100
const int32_t tipMass = 65; // X10 watts to raise 1 deg C in 1 second #define HARDWARE_MAX_WATTAGE_X10 750
const uint8_t tipResistance = 75; // x10 ohms, 7.5 typical for ts100 tips #define TIP_THERMAL_MASS 65 // X10 watts to raise 1 deg C in 1 second
#endif #define tipResistance 75 // x10 ohms, 7.5 typical for ts100 tips
#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
#endif #endif
#ifdef MODEL_TS80 #ifdef MODEL_TS80
const uint32_t tipMass = 40; #define HARDWARE_MAX_WATTAGE_X10 180
const uint8_t tipResistance = 45; // x10 ohms, 4.5 typical for ts80 tips #define TIP_THERMAL_MASS 40
#define tipResistance 45 // x10 ohms, 4.5 typical for ts80 tips
#endif #endif
#ifdef MODEL_TS80P #ifdef MODEL_TS80P
const uint32_t tipMass = 40; #define HARDWARE_MAX_WATTAGE_X10 300
const uint8_t tipResistance = 45; // x10 ohms, 4.5 typical for ts80 tips #define TIP_THERMAL_MASS 40
#define tipResistance 45 // x10 ohms, 4.5 typical for ts80 tips
#endif #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 #endif

View File

@@ -1,4 +1,4 @@
#include "Model_Config.h" #include "configuration.h"
#ifdef POW_PD #ifdef POW_PD
#include "BSP.h" #include "BSP.h"
#include "I2CBB.hpp" #include "I2CBB.hpp"

View File

@@ -7,11 +7,12 @@
#include "BSP.h" #include "BSP.h"
#include "I2CBB.hpp" #include "I2CBB.hpp"
#include "Model_Config.h"
#include "Pins.h" #include "Pins.h"
#include "Setup.h" #include "Setup.h"
#include "configuration.h"
#include "fusbpd.h" #include "fusbpd.h"
#include <I2C_Wrapper.hpp> #include <I2C_Wrapper.hpp>
void preRToSInit() { void preRToSInit() {
/* Reset of all peripherals, Initializes the Flash interface and the Systick. /* Reset of all peripherals, Initializes the Flash interface and the Systick.
*/ */

View File

@@ -12,14 +12,12 @@
#include "main.hpp" #include "main.hpp"
const uint16_t powerPWM = 255; const uint16_t powerPWM = 255;
const uint8_t holdoffTicks = 25; // delay of 7 ms const uint8_t holdoffTicks = 10;
const uint8_t tempMeasureTicks = 25; 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) void resetWatchdog() { fwdgt_counter_reload(); }
history<uint16_t, PID_TIM_HZ> rawTempFilter = {{0}, 0, 0};
void resetWatchdog() { fwdgt_counter_reload(); }
uint16_t getHandleTemperature(uint8_t sample) { uint16_t getHandleTemperature(uint8_t sample) {
#ifdef TEMP_TMP36 #ifdef TEMP_TMP36

View File

@@ -18,6 +18,9 @@ volatile uint16_t i2c_nbytes;
volatile uint16_t i2c_write_dress; volatile uint16_t i2c_write_dress;
volatile uint16_t i2c_read_dress; volatile uint16_t i2c_read_dress;
volatile uint8_t i2c_process_flag = 0; volatile uint8_t i2c_process_flag = 0;
static bool fastPWM;
static void switchToSlowPWM(void);
static void switchToFastPWM(void);
void ADC0_1_IRQHandler(void) { void ADC0_1_IRQHandler(void) {
adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOIC); adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOIC);
@@ -34,74 +37,58 @@ void ADC0_1_IRQHandler(void) {
volatile uint16_t PWMSafetyTimer = 0; volatile uint16_t PWMSafetyTimer = 0;
volatile uint8_t pendingPWM = 0; volatile uint8_t pendingPWM = 0;
void TIMER1_IRQHandler(void) { void TIMER1_IRQHandler(void) {
static bool lastPeriodWasFast = false;
if (timer_interrupt_flag_get(TIMER1, TIMER_INT_UP) == SET) { if (timer_interrupt_flag_get(TIMER1, TIMER_INT_UP) == SET) {
timer_interrupt_flag_clear(TIMER1, TIMER_INT_UP); timer_interrupt_flag_clear(TIMER1, TIMER_INT_UP);
// rollover turn on output if required // rollover turn on output if required
if (PWMSafetyTimer && pendingPWM) {
timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 50);
}
if (PWMSafetyTimer) { if (PWMSafetyTimer) {
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) { if (timer_interrupt_flag_get(TIMER1, TIMER_INT_CH1) == SET) {
timer_interrupt_flag_clear(TIMER1, TIMER_INT_CH1); 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); 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 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. // disabled if the PID task is not scheduled often enough.
pendingPWM = pulse; pendingPWM = pulse;
} fastPWM = shouldUseFastModePWM;
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;
} }
void EXTI5_9_IRQHandler(void) { void EXTI5_9_IRQHandler(void) {

View File

@@ -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_ */

View File

@@ -1,9 +1,9 @@
#include "BSP.h" #include "BSP.h"
#include "BSP_Power.h" #include "BSP_Power.h"
#include "Model_Config.h"
#include "Pins.h" #include "Pins.h"
#include "QC3.h" #include "QC3.h"
#include "Settings.h" #include "Settings.h"
#include "configuration.h"
#include "fusb_user.h" #include "fusb_user.h"
#include "fusbpd.h" #include "fusbpd.h"
#include "int_n.h" #include "int_n.h"

View File

@@ -12,7 +12,7 @@
#include "history.hpp" #include "history.hpp"
#include <string.h> #include <string.h>
#define ADC_NORM_SAMPLES 16 #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 uint16_t ADCReadings[ADC_NORM_SAMPLES]; // room for 32 lots of the pair of readings
// Functions // Functions
@@ -264,17 +264,17 @@ void setup_timers() {
/* initialize TIMER init parameter struct */ /* initialize TIMER init parameter struct */
timer_struct_para_init(&timer_initpara); timer_struct_para_init(&timer_initpara);
/* TIMER1 configuration */ /* TIMER1 configuration */
timer_initpara.prescaler = 5000; timer_initpara.prescaler = 30000;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP; 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.clockdivision = TIMER_CKDIV_DIV4;
timer_initpara.repetitioncounter = 0; timer_initpara.repetitioncounter = 0;
timer_init(TIMER1, &timer_initpara); timer_init(TIMER1, &timer_initpara);
/* CH0 configured to implement the PWM irq's for the output control*/ /* CH0 configured to implement the PWM irq's for the output control*/
timer_channel_output_struct_para_init(&timer_ocintpara); 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_ocintpara.outputstate = TIMER_CCX_ENABLE;
timer_channel_output_config(TIMER1, TIMER_CH_0, &timer_ocintpara); timer_channel_output_config(TIMER1, TIMER_CH_0, &timer_ocintpara);

View File

@@ -118,7 +118,7 @@ static void system_clock_108m_hxtal(void) {
/* APB2 = AHB/1 */ /* APB2 = AHB/1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB/2 */ /* APB1 = AHB/2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; RCU_CFG0 |= RCU_APB1_CKAHB_DIV4;
/* CK_PLL = (CK_PREDIV0) * 27 = 108 MHz */ /* CK_PLL = (CK_PREDIV0) * 27 = 108 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4); RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);

View File

@@ -0,0 +1,151 @@
#ifndef CONFIGURATION_H_
#define CONFIGURATION_H_
#include "Settings.h"
#include <stdint.h>
/**
* 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

View File

@@ -1,4 +1,4 @@
#include "Model_Config.h" #include "configuration.h"
#ifdef POW_PD #ifdef POW_PD
#include "BSP.h" #include "BSP.h"
#include "I2C_Wrapper.hpp" #include "I2C_Wrapper.hpp"

View File

@@ -4,7 +4,7 @@
* Created on: 13 Jun 2020 * Created on: 13 Jun 2020
* Author: Ralim * Author: Ralim
*/ */
#include "Model_Config.h" #include "configuration.h"
#ifdef POW_PD #ifdef POW_PD
#include "BSP.h" #include "BSP.h"
#include "I2CBB.hpp" #include "I2CBB.hpp"

View File

@@ -4,7 +4,7 @@
* Created on: 12 Jun 2020 * Created on: 12 Jun 2020
* Author: Ralim * Author: Ralim
*/ */
#include "Model_Config.h" #include "configuration.h"
#ifdef I2C_SOFT #ifdef I2C_SOFT
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include <I2CBB.hpp> #include <I2CBB.hpp>

View File

@@ -7,7 +7,7 @@
#ifndef BSP_MINIWARE_I2CBB_HPP_ #ifndef BSP_MINIWARE_I2CBB_HPP_
#define BSP_MINIWARE_I2CBB_HPP_ #define BSP_MINIWARE_I2CBB_HPP_
#include "Model_Config.h" #include "configuration.h"
#ifdef I2C_SOFT #ifdef I2C_SOFT
#include "BSP.h" #include "BSP.h"
#include "FreeRTOS.h" #include "FreeRTOS.h"

View File

@@ -10,7 +10,7 @@
#ifndef OLED_HPP_ #ifndef OLED_HPP_
#define OLED_HPP_ #define OLED_HPP_
#include "Font.h" #include "Font.h"
#include "Model_Config.h" #include "configuration.h"
#include <BSP.h> #include <BSP.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>

View File

@@ -72,14 +72,11 @@ uint32_t TipThermoModel::convertFtoC(uint32_t degF) {
uint32_t TipThermoModel::getTipInC(bool sampleNow) { uint32_t TipThermoModel::getTipInC(bool sampleNow) {
int32_t currentTipTempInC = TipThermoModel::convertTipRawADCToDegC(getTipRawTemp(sampleNow)); int32_t currentTipTempInC = TipThermoModel::convertTipRawADCToDegC(getTipRawTemp(sampleNow));
currentTipTempInC += getHandleTemperature(sampleNow) / 10; // Add handle offset 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. // Power usage indicates that our tip temp is lower than our thermocouple temp.
// This could be tuned in concert with PID parameters... // I found a number that doesn't unbalance the existing PID, causing overshoot.
#ifdef THERMAL_MASS_OVERSHOOTS // This could be tuned in concert with PID parameters...
currentTipTempInC += x10WattHistory.average() / 25;
#else
currentTipTempInC -= x10WattHistory.average() / 25;
#endif
if (currentTipTempInC < 0) if (currentTipTempInC < 0)
return 0; return 0;
return currentTipTempInC; return currentTipTempInC;
@@ -92,7 +89,7 @@ uint32_t TipThermoModel::getTipInF(bool sampleNow) {
} }
uint32_t TipThermoModel::getTipMaxInC() { 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 maximumTipTemp += getHandleTemperature(0) / 10; // Add handle offset
return maximumTipTemp - 1; return maximumTipTemp - 1;
} }

View File

@@ -22,7 +22,8 @@
const uint8_t wattHistoryFilter = 24; // I term look back weighting const uint8_t wattHistoryFilter = 24; // I term look back weighting
extern expMovingAverage<uint32_t, wattHistoryFilter> x10WattHistory; extern expMovingAverage<uint32_t, wattHistoryFilter> x10WattHistory;
int32_t tempToX10Watts(int32_t rawTemp); uint32_t availableW10(uint8_t sample);
void setTipX10Watts(int32_t mw); int32_t tempToX10Watts(int32_t rawTemp);
uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample = 0); void setTipX10Watts(int32_t mw);
uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample = 0);
#endif /* POWER_HPP_ */ #endif /* POWER_HPP_ */

View File

@@ -7,9 +7,9 @@
// Quick charge 3.0 supporting functions // Quick charge 3.0 supporting functions
#include "QC3.h" #include "QC3.h"
#include "BSP.h" #include "BSP.h"
#include "cmsis_os.h" #include "cmsis_os.h"
#include "configuration.h"
#include "stdint.h" #include "stdint.h"
enum QCState { enum QCState {
NOT_STARTED = 0, // Have not checked 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 // try and step towards the wanted value
// 1. Measure current voltage // 1. Measure current voltage
int16_t vStart = getInputVoltageX10(divisor, 1); int16_t vStart = getInputVoltageX10(divisor, 0);
int difference = Vx10 - vStart; int difference = Vx10 - vStart;
// 2. calculate ideal steps (0.2V changes) // 2. calculate ideal steps (0.2V changes)
@@ -94,7 +94,7 @@ void seekQC(int16_t Vx10, uint16_t divisor) {
#ifdef ENABLE_QC2 #ifdef ENABLE_QC2
// Re-measure // Re-measure
/* Disabled due to nothing to test and code space of around 1k*/ /* 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) if (steps < 0)
steps = -steps; steps = -steps;
if (steps > 4) { if (steps > 4) {
@@ -118,7 +118,7 @@ void seekQC(int16_t Vx10, uint16_t divisor) {
void startQC(uint16_t divisor) { void startQC(uint16_t divisor) {
// Pre check that the input could be >5V already, and if so, dont both // Pre check that the input could be >5V already, and if so, dont both
// negotiating as someone is feeding in hv // negotiating as someone is feeding in hv
if (getInputVoltageX10(divisor, 1) > 80) { if (getInputVoltageX10(divisor, 0) > 80) {
QCTries = 11; QCTries = 11;
QCMode = QCState::NO_QC; QCMode = QCState::NO_QC;
return; return;
@@ -160,7 +160,7 @@ void startQC(uint16_t divisor) {
// Wait for frontend ADC to stabilise // Wait for frontend ADC to stabilise
QCMode = QCState::QC_2; QCMode = QCState::QC_2;
for (uint8_t i = 0; i < 10; i++) { 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 // yay we have at least QC2.0 or QC3.0
QCMode = QCState::QC_3; // We have at least QC2, pray for 3 QCMode = QCState::QC_3; // We have at least QC2, pray for 3
return; return;

View File

@@ -15,6 +15,12 @@
#include <string.h> // for memset #include <string.h> // for memset
bool sanitiseSettings(); 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 * This struct must be a multiple of 2 bytes as it is saved / restored from
* flash in uint16_t chunks * flash in uint16_t chunks
@@ -144,7 +150,7 @@ bool nextSettingValue(const enum SettingsOptions option) {
} else { } else {
systemSettings.settingsValues[(int)option] += constants.increment; 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) { bool prevSettingValue(const enum SettingsOptions option) {

View File

@@ -187,6 +187,8 @@ const menuitem UIMenu[] = {
* Display orientation * Display orientation
* Cooldown blink * Cooldown blink
* Reverse Temp change buttons + - * Reverse Temp change buttons + -
* Detailed IDLE
* Detailed Soldering
*/ */
{SETTINGS_DESC(SettingsItemIndex::TemperatureUnit), settings_setTempF, settings_displayTempF, {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() */ 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::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::ScrollingSpeed), nullptr, settings_displayScrollSpeed, SettingsOptions::DescriptionScrollSpeed}, /*Scroll Speed for descriptions*/
{SETTINGS_DESC(SettingsItemIndex::ReverseButtonTempChange), nullptr, settings_displayReverseButtonTempChangeEnabled, {SETTINGS_DESC(SettingsItemIndex::ReverseButtonTempChange), nullptr, settings_displayReverseButtonTempChangeEnabled,
SettingsOptions::ReverseButtonTempChangeEnabled}, /* Reverse Temp change buttons + - */ SettingsOptions::ReverseButtonTempChangeEnabled}, /* Reverse Temp change buttons + - */
{SETTINGS_DESC(SettingsItemIndex::AnimSpeed), nullptr, settings_displayAnimationSpeed, SettingsOptions::AnimationSpeed}, /*Animation Speed adjustment */ {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::AnimLoop), nullptr, settings_displayAnimationLoop, SettingsOptions::AnimationLoop}, /*Animation Loop switch */
{SETTINGS_DESC(SettingsItemIndex::Brightness), nullptr, settings_displayBrightnessLevel, SettingsOptions::OLEDBrightness}, /*Brightness Level*/ {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::ColourInversion), nullptr, settings_displayInvertColor, SettingsOptions::OLEDInversion}, /*Invert screen colour*/
{0, nullptr, nullptr, SettingsOptions::SettingsOptionsLength} // end of menu marker. DO NOT REMOVE {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[] = { const menuitem PowerSavingMenu[] = {
/* /*
@@ -225,8 +229,6 @@ const menuitem advancedMenu[] = {
/* /*
* Power limit * Power limit
* Detailed IDLE
* Detailed Soldering
* Calibrate Temperature * Calibrate Temperature
* Calibrate Input V * Calibrate Input V
* Reset Settings * Reset Settings
@@ -237,8 +239,6 @@ const menuitem advancedMenu[] = {
* Power Pulse Duration * Power Pulse Duration
*/ */
{SETTINGS_DESC(SettingsItemIndex::PowerLimit), nullptr, settings_displayPowerLimit, SettingsOptions::PowerLimit}, /*Power limit*/ {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::SettingsReset), settings_setResetSettings, settings_displayResetSettings, SettingsOptions::SettingsOptionsLength}, /*Resets settings*/
{SETTINGS_DESC(SettingsItemIndex::TemperatureCalibration), settings_setCalibrate, settings_displayCalibrate, SettingsOptions::SettingsOptionsLength}, /*Calibrate tip*/ {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*/ {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) { static bool settings_displayPDNegTimeout(void) {
printShortDescription(SettingsItemIndex::PDNegTimeout, 5); 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; return false;
} }
#endif #endif
@@ -409,7 +413,7 @@ static bool settings_displayShutdownTime(void) {
return false; return false;
} }
static bool settings_setTempF(void) { static bool settings_setTempF(void) {
nextSettingValue(SettingsOptions::TemperatureInF); bool res = nextSettingValue(SettingsOptions::TemperatureInF);
uint16_t BoostTemp = getSettingValue(SettingsOptions::BoostTemp); uint16_t BoostTemp = getSettingValue(SettingsOptions::BoostTemp);
uint16_t SolderingTemp = getSettingValue(SettingsOptions::SolderingTemp); uint16_t SolderingTemp = getSettingValue(SettingsOptions::SolderingTemp);
uint16_t SleepTemp = getSettingValue(SettingsOptions::SleepTemp); uint16_t SleepTemp = getSettingValue(SettingsOptions::SleepTemp);
@@ -438,7 +442,7 @@ static bool settings_setTempF(void) {
setSettingValue(SettingsOptions::SolderingTemp, SolderingTemp); setSettingValue(SettingsOptions::SolderingTemp, SolderingTemp);
setSettingValue(SettingsOptions::SleepTemp, SleepTemp); setSettingValue(SettingsOptions::SleepTemp, SleepTemp);
return false; return res;
} }
static bool settings_displayTempF(void) { static bool settings_displayTempF(void) {
@@ -641,7 +645,7 @@ static void setTipOffset() {
OLED::refresh(); OLED::refresh();
osDelay(100); osDelay(100);
} }
setoffset = TipThermoModel::convertTipRawADCTouV(offset / 16); setoffset = TipThermoModel::convertTipRawADCTouV(offset / 16, true);
} }
setSettingValue(SettingsOptions::CalibrationOffset, setoffset); setSettingValue(SettingsOptions::CalibrationOffset, setoffset);
OLED::clearScreen(); OLED::clearScreen();

View File

@@ -10,26 +10,41 @@
#include <power.hpp> #include <power.hpp>
static int32_t PWMToX10Watts(uint8_t pwm, uint8_t sample); static int32_t PWMToX10Watts(uint8_t pwm, uint8_t sample);
const int fastPWMChangeoverPoint = 128;
const int fastPWMChangeoverTolerance = 16;
expMovingAverage<uint32_t, wattHistoryFilter> x10WattHistory = {0}; expMovingAverage<uint32_t, wattHistoryFilter> 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) { int32_t tempToX10Watts(int32_t rawTemp) {
// mass is in milliJ/*C, rawC is raw per degree C // mass is in x10J/*C, rawC is raw per degree C
// returns milliWatts needed to raise/lower a mass by rawTemp // returns x10Watts needed to raise/lower a mass by rawTemp
// degrees in one cycle. // degrees in one cycle.
int32_t milliJoules = tipMass * rawTemp; int32_t x10Watts = TIP_THERMAL_MASS * rawTemp;
return milliJoules; return x10Watts;
} }
void setTipX10Watts(int32_t mw) { void setTipX10Watts(int32_t mw) {
int32_t output = X10WattsToPWM(mw, 1); int32_t outputPWMLevel = X10WattsToPWM(mw, 1);
setTipPWM(output); const bool shouldUseFastPWM = shouldBeUsingFastPWMMode(outputPWMLevel);
uint32_t actualMilliWatts = PWMToX10Watts(output, 0); setTipPWM(outputPWMLevel, shouldUseFastPWM);
uint32_t actualMilliWatts = PWMToX10Watts(outputPWMLevel, 0);
x10WattHistory.update(actualMilliWatts); 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 // P = V^2 / R, v*v = v^2 * 100
// R = R*10 // R = R*10
// P therefore is in V^2*100/R*10 = W*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 // availableMilliWattsX10 is now an accurate representation
return availableWattsX10; return availableWattsX10;
} }
uint8_t X10WattsToPWM(int32_t x10Watts, uint8_t sample) {
uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample) { // Scale input x10Watts to the pwm range available
// Scale input milliWatts to the pwm range available if (x10Watts < 0) {
if (milliWatts < 1) {
// keep the battery voltage updating the filter // keep the battery voltage updating the filter
getInputVoltageX10(getSettingValue(SettingsOptions::VoltageDiv), sample); getInputVoltageX10(getSettingValue(SettingsOptions::VoltageDiv), sample);
return 0; return 0;
} }
// Calculate desired milliwatts as a percentage of availableW10 // Calculate desired x10Watts as a percentage of availableW10
uint32_t pwm; uint32_t pwm;
do { pwm = (powerPWM * x10Watts) / availableW10(sample);
pwm = (powerPWM * milliWatts) / availableW10(sample); if (pwm > powerPWM) {
if (pwm > powerPWM) { // constrain to max PWM counter
// constrain to max PWM counter, shouldn't be possible, pwm = powerPWM;
// but small cost for safety to avoid wraps }
pwm = powerPWM;
}
} while (tryBetterPWM(pwm));
return pwm; return pwm;
} }

View File

@@ -12,17 +12,18 @@
#include "LIS2DH12.hpp" #include "LIS2DH12.hpp"
#include "MMA8652FC.hpp" #include "MMA8652FC.hpp"
#include "MSA301.h" #include "MSA301.h"
#include "Model_Config.h"
#include "QC3.h" #include "QC3.h"
#include "SC7A20.hpp" #include "SC7A20.hpp"
#include "Settings.h" #include "Settings.h"
#include "TipThermoModel.h" #include "TipThermoModel.h"
#include "cmsis_os.h" #include "cmsis_os.h"
#include "configuration.h"
#include "history.hpp" #include "history.hpp"
#include "main.hpp" #include "main.hpp"
#include "power.hpp" #include "power.hpp"
#include "stdlib.h" #include "stdlib.h"
#include "task.h" #include "task.h"
#define MOVFilter 8 #define MOVFilter 8
uint8_t accelInit = 0; uint8_t accelInit = 0;
TickType_t lastMovementTime = 0; TickType_t lastMovementTime = 0;

View File

@@ -20,36 +20,36 @@ TaskHandle_t pidTaskNotification = NULL;
uint32_t currentTempTargetDegC = 0; // Current temperature target in C uint32_t currentTempTargetDegC = 0; // Current temperature target in C
int32_t powerSupplyWattageLimit = 0; int32_t powerSupplyWattageLimit = 0;
bool heaterThermalRunaway = false; 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 */ /* StartPIDTask function */
void startPIDTask(void const *argument __unused) { void startPIDTask(void const *argument __unused) {
/* /*
* We take the current tip temperature & evaluate the next step for the tip * We take the current tip temperature & evaluate the next step for the tip
* control PWM. * control PWM.
*/ */
setTipX10Watts(0); // disable the output driver if the output is set to be off setTipX10Watts(0); // disable the output at startup
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
currentTempTargetDegC = 0; // Force start with no output (off). If in sleep / soldering this will // be over-ridden rapidly
// be over-ridden rapidly pidTaskNotification = xTaskGetCurrentTaskHandle();
pidTaskNotification = xTaskGetCurrentTaskHandle(); uint32_t PIDTempTarget = 0;
uint32_t PIDTempTarget = 0;
uint16_t tipTempCRunawayTemp = 0;
TickType_t runawaylastChangeTime = 0;
// Pre-seed the adc filters // Pre-seed the adc filters
for (int i = 0; i < 64; i++) { for (int i = 0; i < 128; i++) {
vTaskDelay(2); vTaskDelay(5);
TipThermoModel::getTipInC(true); TipThermoModel::getTipInC(true);
getInputVoltageX10(getSettingValue(SettingsOptions::VoltageDiv), 1);
} }
#ifdef SLEW_LIMIT int32_t x10WattsOut = 0;
int32_t x10WattsOutLast = 0;
#endif
for (;;) {
for (;;) {
x10WattsOut = 0;
// This is a call to block this thread until the ADC does its samples
if (ulTaskNotifyTake(pdTRUE, 2000)) { 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 // Do the reading here to keep the temp calculations churning along
uint32_t currentTipTempInC = TipThermoModel::getTipInC(true); uint32_t currentTipTempInC = TipThermoModel::getTipInC(true);
PIDTempTarget = currentTempTargetDegC; PIDTempTarget = currentTempTargetDegC;
@@ -63,116 +63,163 @@ void startPIDTask(void const *argument __unused) {
if (PIDTempTarget > TipThermoModel::getTipMaxInC()) { if (PIDTempTarget > TipThermoModel::getTipMaxInC()) {
PIDTempTarget = TipThermoModel::getTipMaxInC(); PIDTempTarget = TipThermoModel::getTipMaxInC();
} }
// Convert the current tip to degree's C int32_t tError = PIDTempTarget - currentTipTempInC;
// 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();
}
detectThermalRunaway(currentTipTempInC, tError);
x10WattsOut = getPIDResultX10Watts(tError);
} else { } else {
tipTempCRunawayTemp = currentTipTempInC; detectThermalRunaway(currentTipTempInC, 0);
runawaylastChangeTime = xTaskGetTickCount();
} }
setOutputx10WattsViaFilters(x10WattsOut);
// 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();
} else { } else {
// ADC interrupt timeout // ADC interrupt timeout
setTipPWM(0); setTipPWM(0, false);
} }
#ifdef DEBUG_UART_OUTPUT
log_system_state(x10WattsOut);
#endif
} }
} }
template <class T = int32_t> 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<int32_t> 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();
}