1
0
forked from me/IronOS

Merge pull request #697 from paulfertser/poc-limit-power

Limit maximum power more efficiently using dual speed PWM
This commit is contained in:
Ben V. Brown
2020-10-11 19:51:28 +11:00
committed by GitHub
7 changed files with 84 additions and 17 deletions

View File

@@ -1,9 +1,10 @@
#include <stdint.h>
#include <stdbool.h>
#include "BSP_Flash.h" #include "BSP_Flash.h"
#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 "Model_Config.h"
#include "stdint.h"
/* /*
* BSP.h -- Board Support * BSP.h -- Board Support
* *
@@ -16,11 +17,19 @@
extern "C" { extern "C" {
#endif #endif
// maximum htim2 PWM value
extern const uint16_t powerPWM;
// htim2.Init.Period, the full PWM cycle
extern uint16_t totalPWM;
// Called first thing in main() to init the hardware // Called first thing in main() to init the hardware
void preRToSInit(); void preRToSInit();
// Called once the RToS has started for any extra work // Called once the RToS has started for any extra work
void postRToSInit(); void postRToSInit();
// Called once from preRToSInit()
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
@@ -31,6 +40,9 @@ uint16_t getHandleTemperature();
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

@@ -12,6 +12,14 @@
volatile uint16_t PWMSafetyTimer = 0; volatile uint16_t PWMSafetyTimer = 0;
volatile uint8_t pendingPWM = 0; volatile uint8_t pendingPWM = 0;
const uint16_t powerPWM = 255;
static const uint8_t holdoffTicks = 13; // delay of 7 ms
static const uint8_t tempMeasureTicks = 17;
uint16_t totalPWM; //htim2.Init.Period, the full PWM cycle
static bool fastPWM;
//2 second filter (ADC is PID_TIM_HZ Hz) //2 second filter (ADC is PID_TIM_HZ Hz)
history<uint16_t, PID_TIM_HZ> rawTempFilter = { { 0 }, 0, 0 }; history<uint16_t, PID_TIM_HZ> rawTempFilter = { { 0 }, 0, 0 };
void resetWatchdog() { void resetWatchdog() {
@@ -190,6 +198,41 @@ void setTipPWM(uint8_t pulse) {
pendingPWM = pulse; pendingPWM = pulse;
} }
static void switchToFastPWM(void) {
fastPWM = true;
totalPWM = powerPWM + tempMeasureTicks * 2;
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;
}
static void switchToSlowPWM(void) {
fastPWM = false;
totalPWM = powerPWM + tempMeasureTicks;
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;
}
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;
}
// 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.
@@ -299,6 +342,10 @@ uint8_t getButtonB() {
1 : 0; 1 : 0;
} }
void BSPInit(void) {
switchToFastPWM();
}
void reboot() { void reboot() {
NVIC_SystemReset(); NVIC_SystemReset();

View File

@@ -315,13 +315,15 @@ static void MX_TIM2_Init(void) {
// Timer 2 is fairly slow as its being used to run the PWM and trigger the ADC // Timer 2 is fairly slow as its being used to run the PWM and trigger the ADC
// in the PWM off time. // in the PWM off time.
htim2.Instance = TIM2; htim2.Instance = TIM2;
htim2.Init.Prescaler = 4000; //1mhz tick rate/800 = 1.25 KHz tick rate // dummy value, will be reconfigured by BSPInit()
htim2.Init.Prescaler = 2000; // 2 MHz timer clock/2000 = 1 kHz tick rate
// pwm out is 10k from tim3, we want to run our PWM at around 10hz or slower on the output stage // pwm out is 10k from tim3, we want to run our PWM at around 10hz or slower on the output stage
// These values give a rate of around 8Hz // 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;
htim2.Init.Period = 255 + 17; // dummy value, will be reconfigured by BSPInit()
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; // 4mhz before divide htim2.Init.Period = 255 + 17 * 2;
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;
HAL_TIM_Base_Init(&htim2); HAL_TIM_Base_Init(&htim2);
@@ -337,7 +339,8 @@ static void MX_TIM2_Init(void) {
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig); HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 255 + 13; //13 -> Delay of 5ms // dummy value, will be reconfigured by BSPInit()
sConfigOC.Pulse = 255 + 13 * 2; // 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

@@ -17,6 +17,7 @@ void preRToSInit() {
*/ */
HAL_Init(); HAL_Init();
Setup_HAL(); // Setup all the HAL objects Setup_HAL(); // Setup all the HAL objects
BSPInit();
#ifdef I2C_SOFT #ifdef I2C_SOFT
I2CBB::init(); I2CBB::init();
#endif #endif

View File

@@ -25,6 +25,4 @@ extern expMovingAverage<uint32_t, wattHistoryFilter> x10WattHistory;
int32_t tempToX10Watts(int32_t rawTemp); int32_t tempToX10Watts(int32_t rawTemp);
void setTipX10Watts(int32_t mw); void setTipX10Watts(int32_t mw);
uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample = 0); uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample = 0);
int32_t PWMToX10Watts(uint8_t pwm, uint8_t sample = 0);
uint32_t availableW10(uint8_t sample) ;
#endif /* POWER_HPP_ */ #endif /* POWER_HPP_ */

View File

@@ -9,8 +9,7 @@
#include <Settings.h> #include <Settings.h>
#include <BSP.h> #include <BSP.h>
const uint16_t powerPWM = 255; static int32_t PWMToX10Watts(uint8_t pwm, uint8_t sample);
const uint16_t totalPWM = 255 + 17; //htim2.Init.Period, the full PWM cycle
expMovingAverage<uint32_t, wattHistoryFilter> x10WattHistory = { 0 }; expMovingAverage<uint32_t, wattHistoryFilter> x10WattHistory = { 0 };
@@ -30,7 +29,7 @@ void setTipX10Watts(int32_t mw) {
x10WattHistory.update(actualMilliWatts); x10WattHistory.update(actualMilliWatts);
} }
uint32_t availableW10(uint8_t sample) { static 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.
@@ -56,15 +55,22 @@ uint8_t X10WattsToPWM(int32_t milliWatts, uint8_t sample) {
} }
// if (milliWatts > (int(systemSettings.pidPowerLimit) * 10)) // if (milliWatts > (int(systemSettings.pidPowerLimit) * 10))
// milliWatts = (int(systemSettings.pidPowerLimit) * 10); // milliWatts = (int(systemSettings.pidPowerLimit) * 10);
//Calculate desired milliwatts as a percentage of availableW10 //Calculate desired milliwatts as a percentage of availableW10
uint32_t pwm = (powerPWM * milliWatts) / availableW10(sample); uint32_t pwm;
do {
pwm = (powerPWM * milliWatts) / availableW10(sample);
if (pwm > powerPWM) { if (pwm > powerPWM) {
pwm = powerPWM; //constrain to max PWM counter, shouldnt be possible, but small cost for safety to avoid wraps // constrain to max PWM counter, shouldn't be possible,
// but small cost for safety to avoid wraps
pwm = powerPWM;
} }
} while (tryBetterPWM(pwm));
return pwm; return pwm;
} }
int32_t PWMToX10Watts(uint8_t pwm, uint8_t sample) { static int32_t PWMToX10Watts(uint8_t pwm, uint8_t sample) {
uint32_t maxMW = availableW10(sample); //Get the milliwatts for the max pwm period uint32_t maxMW = availableW10(sample); //Get the milliwatts for the max pwm period
//Then convert pwm into percentage of powerPWM to get the percentage of the max mw //Then convert pwm into percentage of powerPWM to get the percentage of the max mw
return (((uint32_t) pwm) * maxMW) / powerPWM; return (((uint32_t) pwm) * maxMW) / powerPWM;

View File

@@ -131,7 +131,7 @@
#ifdef MODEL_TS100 #ifdef MODEL_TS100
const int32_t tipMass = 45; // X10 watts to raise 1 deg C in 1 second const int32_t tipMass = 45; // X10 watts to raise 1 deg C in 1 second
const uint8_t tipResistance = 85; //x10 ohms, 8.5 typical for ts100, 4.5 typical for ts80 const uint8_t tipResistance = 75; //x10 ohms, 7.5 typical for ts100 tips
#endif #endif
#ifdef MODEL_TS80 #ifdef MODEL_TS80