From c5409f4f1b62e64426c06a6017db9ef6ca3bdccb Mon Sep 17 00:00:00 2001 From: "Ben V. Brown" Date: Sat, 13 Jul 2019 22:54:07 +1000 Subject: [PATCH] Ts80 Tuned a bit better, Ts100 WiP --- workspace/TS100/inc/hardware.h | 2 +- workspace/TS100/inc/power.hpp | 15 +- workspace/TS100/src/Setup.c | 26 ++-- .../TS100/src/{hardware.c => hardware.cpp} | 49 ++++--- workspace/TS100/src/main.cpp | 129 ++++++++---------- workspace/TS100/src/power.cpp | 78 +++++++---- workspace/TS100A/.cproject | 4 +- workspace/TS100A/.project | 4 +- 8 files changed, 165 insertions(+), 142 deletions(-) rename workspace/TS100/src/{hardware.c => hardware.cpp} (91%) diff --git a/workspace/TS100/inc/hardware.h b/workspace/TS100/inc/hardware.h index ecc142f2b..561f12293 100644 --- a/workspace/TS100/inc/hardware.h +++ b/workspace/TS100/inc/hardware.h @@ -16,7 +16,7 @@ extern "C" { enum Orientation { ORIENTATION_LEFT_HAND = 0, ORIENTATION_RIGHT_HAND = 1, ORIENTATION_FLAT = 3 }; - +#define PID_TIM_HZ (16) #if defined(MODEL_TS100) + defined(MODEL_TS80) > 1 #error "Multiple models defined!" #elif defined(MODEL_TS100) + defined(MODEL_TS80) == 0 diff --git a/workspace/TS100/inc/power.hpp b/workspace/TS100/inc/power.hpp index c08e4cbcd..87df47f2a 100644 --- a/workspace/TS100/inc/power.hpp +++ b/workspace/TS100/inc/power.hpp @@ -2,23 +2,22 @@ * Power.hpp * * Created on: 28 Oct, 2018 - * Authors: Ben V. Brown, David Hilton + * Authors: Ben V. Brown, David Hilton (David's Idea) */ #include "stdint.h" #include - +#include "hardware.h" #ifndef POWER_HPP_ #define POWER_HPP_ -const uint8_t hz = 32;//PID loop rate -const uint8_t oscillationPeriod = 3.5 * hz; // dampening look back tuning +const uint8_t oscillationPeriod = 4 * PID_TIM_HZ; // I term look back value extern history milliWattHistory; -void setupPower(uint8_t resistance); -int32_t tempToMilliWatts(int32_t rawTemp, uint16_t mass, uint8_t rawC); +int32_t tempToMilliWatts(int32_t rawTemp, uint8_t rawC); void setTipMilliWatts(int32_t mw); -uint8_t milliWattsToPWM(int32_t milliWatts, uint8_t divisor,uint8_t sample=0); -int32_t PWMToMilliWatts(uint8_t pwm, uint8_t divisor); +uint8_t milliWattsToPWM(int32_t milliWatts, uint8_t divisor, + uint8_t sample = 0); +int32_t PWMToMilliWatts(uint8_t pwm, uint8_t divisor, uint8_t sample = 0); #endif /* POWER_HPP_ */ diff --git a/workspace/TS100/src/Setup.c b/workspace/TS100/src/Setup.c index 8108d1a5e..50f742a3f 100644 --- a/workspace/TS100/src/Setup.c +++ b/workspace/TS100/src/Setup.c @@ -262,9 +262,9 @@ static void MX_TIM3_Init(void) { htim3.Instance = TIM3; htim3.Init.Prescaler = 8; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; - htim3.Init.Period = 100; // 10 Khz PWM freq + htim3.Init.Period = 400; // 5 Khz PWM freq htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; // 4mhz before div - htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; + htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;//Preload the ARR register (though we dont use this) HAL_TIM_Base_Init(&htim3); sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; @@ -279,7 +279,7 @@ static void MX_TIM3_Init(void) { HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig); sConfigOC.OCMode = TIM_OCMODE_PWM1; - sConfigOC.Pulse = 50; + sConfigOC.Pulse = 80;//80% duty cycle, that is AC coupled through the cap sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_ENABLE; HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, PWM_Out_CHANNEL); @@ -291,10 +291,10 @@ static void MX_TIM3_Init(void) { */ GPIO_InitStruct.Pin = PWM_Out_Pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;//We would like sharp rising edges HAL_GPIO_Init(PWM_Out_GPIO_Port, &GPIO_InitStruct); #ifdef MODEL_TS100 - // Remap TIM3_CH1 to be on pB4 + // Remap TIM3_CH1 to be on PB4 __HAL_AFIO_REMAP_TIM3_PARTIAL(); #else // No re-map required @@ -314,14 +314,17 @@ static void MX_TIM2_Init(void) { // Timer 2 is fairly slow as its being used to run the PWM and trigger the ADC // in the PWM off time. htim2.Instance = TIM2; - htim2.Init.Prescaler = 785; // pwm out is 10k from tim3, we want to run our PWM at around 10hz or slower on the output stage + htim2.Init.Prescaler = 2000; //1mhz tick rate/800 = 1.25 KHz tick rate + + // pwm out is 10k from tim3, we want to run our PWM at around 10hz or slower on the output stage // The input is 1mhz after the div/4, so divide this by 785 to give around 4Hz output change rate //Trade off is the slower the PWM output the slower we can respond and we gain temperature accuracy in settling time, //But it increases the time delay between the heat cycle and the measurement and calculate cycle htim2.Init.CounterMode = TIM_COUNTERMODE_UP; - htim2.Init.Period = 255 + 60; + htim2.Init.Period = 255 + 20; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; // 4mhz before divide htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; + htim2.Init.RepetitionCounter=0; HAL_TIM_Base_Init(&htim2); sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; @@ -335,7 +338,8 @@ static void MX_TIM2_Init(void) { HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig); sConfigOC.OCMode = TIM_OCMODE_PWM1; - sConfigOC.Pulse = 255 + 50; //255 is the largest time period of the drive signal, and the 50 offsets this around 5ms afterwards + sConfigOC.Pulse = 255 + 10; + //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. * Assume ADC samples in 0.5ms @@ -344,11 +348,7 @@ static void MX_TIM2_Init(void) { sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_ENABLE; HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1); - - sConfigOC.OCMode = TIM_OCMODE_PWM1; - sConfigOC.Pulse = 0; - sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; - sConfigOC.OCFastMode = TIM_OCFAST_ENABLE; + sConfigOC.Pulse = 0;//default to entirely off HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4); HAL_TIM_Base_Start_IT(&htim2); diff --git a/workspace/TS100/src/hardware.c b/workspace/TS100/src/hardware.cpp similarity index 91% rename from workspace/TS100/src/hardware.c rename to workspace/TS100/src/hardware.cpp index 63fb07039..daf0659e1 100644 --- a/workspace/TS100/src/hardware.c +++ b/workspace/TS100/src/hardware.cpp @@ -10,6 +10,7 @@ #include "FreeRTOS.h" #include "stm32f1xx_hal.h" #include "cmsis_os.h" +#include "history.hpp" volatile uint16_t PWMSafetyTimer = 0; volatile int16_t CalibrationTempOffset = 0; uint16_t tipGainCalValue = 0; @@ -68,15 +69,31 @@ uint16_t ftoTipMeasurement(uint16_t temp) { } uint16_t getTipInstantTemperature() { - uint16_t sum; - sum = hadc1.Instance->JDR1; - sum += hadc1.Instance->JDR2; - sum += hadc1.Instance->JDR3; - sum += hadc1.Instance->JDR4; - sum += hadc2.Instance->JDR1; - sum += hadc2.Instance->JDR2; - sum += hadc2.Instance->JDR3; - sum += hadc2.Instance->JDR4; + uint16_t sum = 0; // 12 bit readings * 8 -> 15 bits + uint16_t readings[8]; + //Looking to reject the highest outlier readings. + //As on some hardware these samples can run into the op-amp recovery time + //Once this time is up the signal stabilises quickly, so no need to reject minimums + readings[0] = hadc1.Instance->JDR1; + readings[1] = hadc1.Instance->JDR2; + readings[2] = hadc1.Instance->JDR3; + readings[3] = hadc1.Instance->JDR4; + readings[4] = hadc2.Instance->JDR1; + readings[5] = hadc2.Instance->JDR2; + readings[6] = hadc2.Instance->JDR3; + readings[7] = hadc2.Instance->JDR4; + uint8_t minID = 0, maxID = 0; + for (int i = 0; i < 8; i++) { + if (readings[i] < readings[minID]) + minID = i; + else if (readings[i] > readings[maxID]) + maxID = i; + } + for (int i = 0; i < 8; i++) { + if (i != maxID) + sum += readings[i]; + } + sum += readings[minID]; //Duplicate the min to make up for the missing max value return sum; // 8x over sample } /* @@ -117,15 +134,17 @@ uint16_t lookupTipDefaultCalValue(enum TipType tipID) { } #endif } +//2 second filter (ADC is PID_TIM_HZ Hz) +history rawTempFilter = { { 0 }, 0, 0 }; uint16_t getTipRawTemp(uint8_t refresh) { - static uint16_t lastSample = 0; - if (refresh) { - lastSample = getTipInstantTemperature(); + uint16_t lastSample = getTipInstantTemperature(); + rawTempFilter.update(lastSample); + return lastSample; + } else { + return rawTempFilter.average(); } - - return lastSample; } uint16_t getInputVoltageX10(uint16_t divisor, uint8_t sample) { @@ -237,8 +256,6 @@ 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 uint16_t vin = getInputVoltageX10(divisor, 1); - if (vin > 150) - return; // Over voltage if (vin > 100) { QCMode = 1; // ALready at ~12V return; diff --git a/workspace/TS100/src/main.cpp b/workspace/TS100/src/main.cpp index 43cf11282..cc970019e 100644 --- a/workspace/TS100/src/main.cpp +++ b/workspace/TS100/src/main.cpp @@ -271,36 +271,36 @@ static void gui_drawBatteryIcon() { // we need to calculate which of the 10 levels they are on uint8_t cellCount = systemSettings.cutoutSetting + 2; uint32_t cellV = getInputVoltageX10(systemSettings.voltageDiv, 0) - / cellCount; + / cellCount; // Should give us approx cell voltage X10 // Range is 42 -> 33 = 9 steps therefore we will use battery 1-10 if (cellV < 33) - cellV = 33; - cellV -= 33;// Should leave us a number of 0-9 + cellV = 33; + cellV -= 33; // Should leave us a number of 0-9 if (cellV > 9) - cellV = 9; + cellV = 9; OLED::drawBattery(cellV + 1); } else - OLED::drawSymbol(15); // Draw the DC Logo + OLED::drawSymbol(15); // Draw the DC Logo #else - // On TS80 we replace this symbol with the voltage we are operating on - // If <9V then show single digit, if not show duals - uint8_t V = getInputVoltageX10(systemSettings.voltageDiv, 0); - if (V % 10 >= 5) - V = V / 10 + 1; // round up - else - V = V / 10; - if (V >= 10) { - int16_t xPos = OLED::getCursorX(); - OLED::setFont(1); - OLED::printNumber(1, 1); - OLED::setCursor(xPos, 8); - OLED::printNumber(V % 10, 1); - OLED::setFont(0); - OLED::setCursor(xPos + 12, 0); // need to reset this as if we drew a wide char - } else { - OLED::printNumber(V, 1); - } + // On TS80 we replace this symbol with the voltage we are operating on + // If <9V then show single digit, if not show duals + uint8_t V = getInputVoltageX10(systemSettings.voltageDiv, 0); + if (V % 10 >= 5) + V = V / 10 + 1;// round up + else + V = V / 10; + if (V >= 10) { + int16_t xPos = OLED::getCursorX(); + OLED::setFont(1); + OLED::printNumber(1, 1); + OLED::setCursor(xPos, 8); + OLED::printNumber(V % 10, 1); + OLED::setFont(0); + OLED::setCursor(xPos + 12, 0); // need to reset this as if we drew a wide char + } else { + OLED::printNumber(V, 1); + } #endif } static void gui_solderingTempAdjust() { @@ -372,7 +372,7 @@ static void gui_solderingTempAdjust() { #ifdef MODEL_TS80 if (!OLED::getRotation()) #else - if (OLED::getRotation()) + if (OLED::getRotation()) #endif OLED::print(SymbolMinus); else @@ -388,7 +388,7 @@ static void gui_solderingTempAdjust() { #ifdef MODEL_TS80 if (!OLED::getRotation()) #else - if (OLED::getRotation()) + if (OLED::getRotation()) #endif OLED::print(SymbolPlus); else @@ -415,7 +415,7 @@ static int gui_SolderingSleepingMode() { || (xTaskGetTickCount() - lastButtonTime < 100)) return 0; // user moved or pressed a button, go back to soldering #ifdef MODEL_TS100 - if (checkVoltageForExit()) + if (checkVoltageForExit()) return 1; // return non-zero on error #endif if (systemSettings.temperatureInF) { @@ -682,9 +682,9 @@ void showVersion(void) { OLED::setCursor(0, 0); // Position the cursor at the 0,0 (top left) OLED::setFont(1); // small font #ifdef MODEL_TS100 - OLED::print(SymbolVersionNumber); // Print version number -#else OLED::print(SymbolVersionNumber); // Print version number +#else + OLED::print(SymbolVersionNumber); // Print version number #endif OLED::setCursor(0, 8); // second line OLED::print(DebugMenu[screen]); @@ -733,7 +733,7 @@ void showVersion(void) { #ifdef MODEL_TS80 OLED::printNumber(calculateTipR(), 5); #else - OLED::printNumber(8500,5)); + OLED::printNumber(8500, 5); #endif break; default: @@ -871,7 +871,7 @@ void startGUITask(void const *argument __unused) { #ifdef MODEL_TS80 if (!OLED::getRotation()) { #else - if (OLED::getRotation()) { + if (OLED::getRotation()) { #endif OLED::drawArea(12, 0, 84, 16, idleScreenBG); OLED::setCursor(0, 0); @@ -892,7 +892,7 @@ void startGUITask(void const *argument __unused) { #ifdef MODEL_TS80 if (!OLED::getRotation()) { #else - if (OLED::getRotation()) { + if (OLED::getRotation()) { #endif // in right handed mode we want to draw over the first part OLED::fillArea(55, 0, 41, 16, 0); // clear the area for the temp @@ -921,19 +921,16 @@ void startPIDTask(void const *argument __unused) { */ setTipMilliWatts(0); // disable the output driver if the output is set to be off #ifdef MODEL_TS80 - idealQCVoltage = calculateMaxVoltage(systemSettings.cutoutSetting); + idealQCVoltage = calculateMaxVoltage(systemSettings.cutoutSetting); #endif uint8_t rawC = ctoTipMeasurement(101) - ctoTipMeasurement(100); // 1*C change in raw. #ifdef MODEL_TS80 - //Set power management code to the tip resistance in ohms * 10 - setupPower(calculateTipR() / 100); - TickType_t lastPowerPulse = 0; -#else - setupPower(85); - + //Set power management code to the tip resistance in ohms * 10 + TickType_t lastPowerPulse = 0; #endif - history tempError = { { 0 }, 0, 0 }; + // Tip temp reading filter + history tempError = { { 0 }, 0, 0 }; currentlyActiveTemperatureTarget = 0; // Force start with no output (off). If in sleep / soldering this will // be over-ridden rapidly pidTaskNotification = xTaskGetCurrentTaskHandle(); @@ -947,9 +944,9 @@ void startPIDTask(void const *argument __unused) { if (currentlyActiveTemperatureTarget > ctoTipMeasurement(450)) { //Maximum allowed output currentlyActiveTemperatureTarget = ctoTipMeasurement(450); - } else if (currentlyActiveTemperatureTarget > 32400) { - //Cap to max adc reading - currentlyActiveTemperatureTarget = 32400; + } else if (currentlyActiveTemperatureTarget > 32700) { + //Cap to max adc reading (32768) + currentlyActiveTemperatureTarget = 32700; } // As we get close to our target, temp noise causes the system @@ -963,27 +960,15 @@ void startPIDTask(void const *argument __unused) { tempError.update(tError); // Now for the PID! - int32_t milliWattsOut = 0; // 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. -#ifdef MODEL_TS100 - const uint16_t mass = 2020 / 20; // divide here so division is compile-time. -#endif -#ifdef MODEL_TS80 - const uint16_t mass = 2020 / 50; -#endif - - int32_t milliWattsNeeded = tempToMilliWatts(tempError.average(), - mass, rawC); + int32_t milliWattsOut = tempToMilliWatts(tempError.average(), + rawC); // note that milliWattsNeeded is sometimes negative, this counters overshoot // from I term's inertia. - milliWattsOut += milliWattsNeeded; // I term - energy needed to compensate for heat loss. // We track energy put into the system over some window. @@ -991,11 +976,12 @@ void startPIDTask(void const *argument __unused) { // (If it isn't, P will dominate). milliWattsOut += milliWattHistory.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. + // Not Used: + // 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. setTipMilliWatts(milliWattsOut); } else { @@ -1005,9 +991,10 @@ void startPIDTask(void const *argument __unused) { // This is purely guesswork :'( as everyone implements stuff differently if (xTaskGetTickCount() - lastPowerPulse < 10) { // for the first 100mS turn on for a bit - setTipMilliWatts(5000); // typically its around 5W to hold the current temp, so this wont raise temp much - } else + setTipMilliWatts(2000);// typically its around 5W to hold the current temp, so this wont raise temp much + } else { setTipMilliWatts(0); + } //Then wait until the next 0.5 seconds if (xTaskGetTickCount() - lastPowerPulse > 50) { lastPowerPulse = xTaskGetTickCount(); @@ -1019,7 +1006,6 @@ void startPIDTask(void const *argument __unused) { HAL_IWDG_Refresh(&hiwdg); } else { - asm("bkpt"); //ADC interrupt timeout setTipMilliWatts(0); @@ -1035,9 +1021,9 @@ void startMOVTask(void const *argument __unused) { #ifdef MODEL_TS80 startQC(systemSettings.voltageDiv); while (pidTaskNotification == 0) - osDelay(30); // To ensure we return after idealQCVoltage/tip resistance + osDelay(30); // To ensure we return after idealQCVoltage/tip resistance - seekQC(idealQCVoltage, systemSettings.voltageDiv); // this will move the QC output to the preferred voltage to start with + seekQC(idealQCVoltage, systemSettings.voltageDiv);// this will move the QC output to the preferred voltage to start with #else osDelay(250); // wait for accelerometer to stabilize @@ -1096,16 +1082,11 @@ void startMOVTask(void const *argument __unused) { bool showBootLogoIfavailable() { // check if the header is there (0xAA,0x55,0xF0,0x0D) // If so display logo - // TODO REDUCE STACK ON THIS ONE, USE DRAWING IN THE READ LOOP - uint16_t temp[98]; - - for (uint8_t i = 0; i < (98); i++) { - temp[i] = *(uint16_t *) (FLASH_LOGOADDR + (i * 2)); - } uint8_t temp8[98 * 2]; for (uint8_t i = 0; i < 98; i++) { - temp8[i * 2] = temp[i] >> 8; - temp8[i * 2 + 1] = temp[i] & 0xFF; + uint16_t temp = *(uint16_t *) (FLASH_LOGOADDR + (i * 2)); + temp8[i * 2] = temp >> 8; + temp8[i * 2 + 1] = temp & 0xFF; } if (temp8[0] != 0xAA) diff --git a/workspace/TS100/src/power.cpp b/workspace/TS100/src/power.cpp index d8bd0118f..1182bf452 100644 --- a/workspace/TS100/src/power.cpp +++ b/workspace/TS100/src/power.cpp @@ -9,58 +9,84 @@ #include #include -uint8_t tipResistance = 85; //x10 ohms, 8.5 typical for ts100, 4.5 typical for ts80 const uint16_t powerPWM = 255; -const uint16_t totalPWM = 255 + 60; //htim2.Init.Period, the full PWM cycle +const uint16_t totalPWM = 255 + 30; //htim2.Init.Period, the full PWM cycle -history milliWattHistory = { { 0 }, 0, 0 }; +// thermal mass = 1690 milliJ/*C for my tip. +// -> Wattsx10*Seconds to raise Temp from room temp to +100*C, divided by 100*C. +// we divide mass 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. -void setupPower(uint8_t res) { - tipResistance = res; -} +#ifdef MODEL_TS100 +const uint16_t tipMass = 2020 ; // divide here so division is compile-time. +const uint8_t tipResistance = 85;//x10 ohms, 8.5 typical for ts100, 4.5 typical for ts80 + +#endif +#ifdef MODEL_TS80 +const uint16_t tipMass = 1000/4; +const uint8_t tipResistance = 46; //x10 ohms, 8.5 typical for ts100, 4.5 typical for ts80 + +#endif +history milliWattHistory = { { 0 }, 0, 0 }; -int32_t tempToMilliWatts(int32_t rawTemp, uint16_t mass, uint8_t rawC) { +int32_t tempToMilliWatts(int32_t rawTemp, uint8_t rawC) { // mass is in milliJ/*C, rawC is raw per degree C // returns milliWatts needed to raise/lower a mass by rawTemp // degrees in one cycle. - int32_t milliJoules = mass * rawTemp / rawC; - return milliJoules * hz; + int32_t milliJoules = tipMass * (rawTemp / rawC); + return milliJoules; } void setTipMilliWatts(int32_t mw) { //Enforce Max Watts Limiter # TODO - int32_t output = milliWattsToPWM(mw, systemSettings.voltageDiv / 10,1); + int32_t output = milliWattsToPWM(mw, systemSettings.voltageDiv / 10, 1); setTipPWM(output); - uint16_t actualMilliWatts = PWMToMilliWatts(output, - systemSettings.voltageDiv / 10); + uint32_t actualMilliWatts = PWMToMilliWatts(output, + systemSettings.voltageDiv / 10, 0); milliWattHistory.update(actualMilliWatts); } -uint8_t milliWattsToPWM(int32_t milliWatts, uint8_t divisor, uint8_t sample) { +int32_t availableW10(uint8_t divisor, uint8_t sample) { //P = V^2 / R, v*v = v^2 * 100 // R = R*10 - // P therefore is in V^2*10/R = W*10. + // P therefore is in V^2*100/R*10 = W*10. + int32_t v = getInputVoltageX10(divisor, sample); // 100 = 10v + int32_t availableWattsX10 = (v * v) / tipResistance; + //However, 100% duty cycle is not possible as there is a dead time while the ADC takes a reading + //Therefore need to scale available milliwats by this + + // avMw=(AvMw*powerPWM)/totalPWM. + availableWattsX10 = availableWattsX10 * powerPWM; + availableWattsX10 /= totalPWM; + + //availableMilliWattsX10 is now an accurate representation + return availableWattsX10; +} + +uint8_t milliWattsToPWM(int32_t milliWatts, uint8_t divisor, uint8_t sample) { + // Scale input milliWatts to the pwm rate - if (milliWatts == 0) + if (milliWatts < 10) // no pint driving tip return 0; - int32_t v = getInputVoltageX10(divisor, sample); // 100 = 10v - int32_t availableMilliWatts = v * v / tipResistance; - //int32_t pwm = ((powerPWM * totalPWM / powerPWM) * milliWatts) / availableMilliWatts; - int32_t pwm = (totalPWM * milliWatts) / availableMilliWatts; + //Calculate desired milliwatts as a percentage of availableW10 + int32_t pwm = (powerPWM * milliWatts) / availableW10(divisor, sample); if (pwm > powerPWM) { - pwm = powerPWM; - } else if (pwm < 0) { + pwm = powerPWM; //constrain to max PWM counter, shouldnt be possible, but small cost for safety to avoid wraps + } else if (pwm < 0) { //cannot go negative pwm = 0; } - - return pwm; } -int32_t PWMToMilliWatts(uint8_t pwm, uint8_t divisor) { - int32_t v = getInputVoltageX10(divisor, 0); - return pwm * (v * v / tipResistance) / (powerPWM * totalPWM / powerPWM); +int32_t PWMToMilliWatts(uint8_t pwm, uint8_t divisor, uint8_t sample) { + int32_t maxMW = availableW10(divisor, sample); //Get the milliwatts for the max pwm period + //Then convert pwm into percentage of powerPWM to get the percentage of the max mw + int32_t res = (pwm * maxMW) / powerPWM; + if (res < 0) + res = 0; + return res; } diff --git a/workspace/TS100A/.cproject b/workspace/TS100A/.cproject index 01717ce00..75bd21699 100644 --- a/workspace/TS100A/.cproject +++ b/workspace/TS100A/.cproject @@ -52,7 +52,7 @@