From 43cf277f2500c99d99bcafb79adef8d640d8773b Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Sun, 26 May 2024 17:20:37 +0900 Subject: [PATCH 1/2] Implement PWM Power ramping As reported in Eddddddddy/Songguo-PTS200#18 some PSUs could trigger high current protection on switch on. Here implement PWM Power ramping, heater will use LEDC's hw fader to gradualy increase PWM duty from 0 to max_duty. While the scaling itself is non-blocking and performed in hardware, ledc engine is blocked for the time of fading and no temperature measurments could be done. So heater controlling thread must be suspended for the period and awoken back with LEDC interrupt. --- ESPIron/common.hpp | 4 +- ESPIron/config.h | 14 +---- ESPIron/evtloop.cpp | 1 + ESPIron/evtloop.hpp | 14 +++-- ESPIron/heater.cpp | 118 +++++++++++++++++++++++++------------ ESPIron/heater.hpp | 23 +++++++- ESPIron/ironcontroller.cpp | 15 +++-- ESPIron/log.h | 6 ++ ESPIron/sensors.cpp | 2 +- 9 files changed, 134 insertions(+), 63 deletions(-) diff --git a/ESPIron/common.hpp b/ESPIron/common.hpp index bdafcb6..92534e4 100644 --- a/ESPIron/common.hpp +++ b/ESPIron/common.hpp @@ -25,8 +25,8 @@ enum class ironState_t { suspend, // hibernation mode boost, // heater temperature is increased for a short period of time setup, // iron is configuration mode, i.e. working with screen menu, heater switches off - notip // tip is missing or failed - + notip, // tip is missing or failed + PWRramp // controller is performing Power Ramping }; // working temperature values diff --git a/ESPIron/config.h b/ESPIron/config.h index 8dc53c4..2041dec 100644 --- a/ESPIron/config.h +++ b/ESPIron/config.h @@ -43,7 +43,7 @@ #define TEMP_MIN 150 // 最小温度 #define TEMP_MAX 450 // 最大温度 #define TEMP_STEP 5 // temperature change step / 旋转编码器温度变化步进 -#define TEMP_NOTIP 500 // virtual temperature threshold when tip is not installed +#define TEMP_NOTIP 500 // virtual temperature threshold when tip is not installed (op amp max ~650 C) #define TEMP_STANDBY 120 // 休眠温度 #define TEMP_STANDBY_MIN 100 #define TEMP_STANDBY_MAX 180 @@ -53,10 +53,6 @@ #define TEMP_BOOST_MAX 100 #define TEMP_BOOST_STEP 10 -#define POWER_LIMIT_15 170 // 功率限制 -#define POWER_LIMIT_20 255 // 功率限制 -#define POWER_LIMIT_20_2 127 // 功率限制 - // Default tip temperature calibration value / 默认的T12烙铁头温度校准值 #define TEMP200 200 // temperature at ADC = 200 #define TEMP280 280 // temperature at ADC = 280 @@ -89,12 +85,8 @@ #define WAKEUP_THRESHOLD 10 // Control values -#define TIME2SETTLE 5000 // The time in microseconds allowed for the OpAmp output to stabilize / 以微秒为单位的时间允许OpAmp输出稳定 -#define TIME2SETTLE_20V 2000 // The time in microseconds allowed for the OpAmp output to stabilize / 以微秒为单位的时间允许OpAmp输出稳定 -#define SMOOTHIE 0.2 // OpAmp output smoothing coefficient (1=no smoothing; default: 0.05) / OpAmp输出平滑系数 (1=无平滑; 默认:0.05) -//#define PID_ENABLE true // enable PID control -#define PID_ENGAGE_DIFF 30 // temperature difference when PID algo should be engaged -#define BEEP_ENABLE true // enable/disable buzzer +#define SMOOTHIE 0.2 // OpAmp output smoothing coefficient (1=no smoothing; default: 0.05) / OpAmp输出平滑系数 (1=无平滑; 默认:0.05) +#define BEEP_ENABLE true // enable/disable buzzer // MOSFET control definitions diff --git a/ESPIron/evtloop.cpp b/ESPIron/evtloop.cpp index e6a0368..24ccf68 100644 --- a/ESPIron/evtloop.cpp +++ b/ESPIron/evtloop.cpp @@ -29,6 +29,7 @@ ESP_EVENT_DEFINE_BASE(IRON_GET_EVT); ESP_EVENT_DEFINE_BASE(IRON_NOTIFY); ESP_EVENT_DEFINE_BASE(IRON_STATE); ESP_EVENT_DEFINE_BASE(IRON_VISET); +ESP_EVENT_DEFINE_BASE(IRON_HEATER); namespace evt { diff --git a/ESPIron/evtloop.hpp b/ESPIron/evtloop.hpp index b35ca24..0d714af 100644 --- a/ESPIron/evtloop.hpp +++ b/ESPIron/evtloop.hpp @@ -24,6 +24,7 @@ ESP_EVENT_DECLARE_BASE(IRON_GET_EVT); // ESPIron getter Commands events ba ESP_EVENT_DECLARE_BASE(IRON_NOTIFY); // ESPIron notification events base (those events are published when some state or mode changes due to any commands or component's logic) ESP_EVENT_DECLARE_BASE(IRON_STATE); // ESPIron State publishing events base (those events are published on IRON_GET_EVT requests on demand) ESP_EVENT_DECLARE_BASE(IRON_VISET); // ESPIron VisualSet HID events +ESP_EVENT_DECLARE_BASE(IRON_HEATER); // ESPIron heater control events // cast enum to int template @@ -48,20 +49,25 @@ enum class iron_t:int32_t { // Commands sensorsReload = 200, // reload configuration for any sensors available - heaterTargetT, // set heater target temperature, parameter int32_t workTemp, // set working temperature, parameter int32_t workModeToggle, // toggle working mode on/off boostModeToggle, // toggle boost mode on/off + // Heater Commands + heaterTargetT = 250, // set heater target temperature, parameter int32_t + heaterEnable, + heaterDisable, + heaterRampUp, // start PWM ramp heating, switch to enabled mode + reloadTemp, // reload temperature configuration reloadTimeouts, // reload timeouts configuration // Commands - power control pdVoltage, // switch PD trigger, arg uint32_t in V qcVoltage, // switch QC trigger, arg uint32_t in V - qc2enable, // activate QC trigger in QC2 mode - qc3enable, // activate QC trigger in QC3 mode - qcDisable, // disable QC trigger +// qc2enable, // activate QC trigger in QC2 mode +// qc3enable, // activate QC trigger in QC3 mode +// qcDisable, // disable QC trigger // State notifications stateWorking = 300, diff --git a/ESPIron/heater.cpp b/ESPIron/heater.cpp index aaa8c6c..cc6dcbb 100644 --- a/ESPIron/heater.cpp +++ b/ESPIron/heater.cpp @@ -13,15 +13,20 @@ #endif #define HEATER_TASK_NAME "HEATER" -#define LEDC_MODE LEDC_LOW_SPEED_MODE // S2 supports only low speed mode -#define LEDC_DUTY_RES HEATER_RES -#define LEDC_TIMER LEDC_TIMER_0 -#define LEDC_FREQUENCY HEATER_FREQ +#define HEATER_LEDC_SPEEDMODE LEDC_LOW_SPEED_MODE // S2 supports only low speed mode +#define HEATER_LEDC_DUTY_RES HEATER_RES +#define HEATER_LEDC_TIMER LEDC_TIMER_0 +#define HEATER_LEDC_FREQUENCY HEATER_FREQ +#define HEATER_LEDC_RAMPUP_TIME 3000 // time duration to fade in-for PWM ramping, ms #define HEATER_OPAMP_STABILIZE_MS 5 // how long to wait after disabling PWM to let OpAmp stabilize #define HEATER_ADC_SAMPLES 8 // number of ADC reads to averate tip tempearture +#define PID_ENGAGE_DIFF_LOW 30 // lower temperature difference when PID algo should be engaged +#define PID_ENGAGE_DIFF_HIGH 10 // higher temperature difference when PID algo should be engaged + + // normal interval between temp measurments constexpr TickType_t measure_delay_ticks = pdMS_TO_TICKS(1000 / HEATER_MEASURE_RATE); // long interval between temp measurments @@ -35,12 +40,12 @@ TipHeater::~TipHeater(){ esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_SET_EVT, ESP_EVENT_ANY_ID, _evt_cmd_handler); _evt_cmd_handler = nullptr; } - +/* if (_evt_ntf_handler){ esp_event_handler_instance_unregister_with(evt::get_hndlr(), IRON_NOTIFY, ESP_EVENT_ANY_ID, _evt_ntf_handler); _evt_ntf_handler = nullptr; } - +*/ _stop_runner(); } @@ -54,25 +59,28 @@ void TipHeater::init(){ if (!_evt_cmd_handler){ esp_event_handler_instance_register_with( evt::get_hndlr(), - IRON_SET_EVT, - ESP_EVENT_ANY_ID, // subscribe to 'sensorsReload command' + IRON_HEATER, + ESP_EVENT_ANY_ID, [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->_evt_picker(base, id, data); }, this, &_evt_cmd_handler ); } - +/* if (!_evt_ntf_handler){ esp_event_handler_instance_register_with( evt::get_hndlr(), IRON_NOTIFY, - ESP_EVENT_ANY_ID, // subscribe to 'sensorsReload command' + ESP_EVENT_ANY_ID, [](void* self, esp_event_base_t base, int32_t id, void* data) { static_cast(self)->_evt_picker(base, id, data); }, this, &_evt_ntf_handler ); } +*/ + // init HW fader + ledc_fade_func_install(0); // create RTOS task that controls heater PWM _start_runner(); } @@ -87,23 +95,32 @@ void TipHeater::_evt_picker(esp_event_base_t base, int32_t id, void* data){ return; } - case evt::iron_t::stateWorking : { + case evt::iron_t::heaterEnable : { // enable heater enable(); return; } - case evt::iron_t::stateIdle : { + case evt::iron_t::heaterDisable : { // disable heater in standby mode disable(); return; } + case evt::iron_t::heaterRampUp : { + // disable heater in standby mode + rampUp(); + return; + } + +/* + // obsolete, use deep-sleep in Suspend case evt::iron_t::stateSuspend : { // disable worker task in standby mode _stop_runner(); return; } +*/ } } @@ -111,10 +128,10 @@ void TipHeater::_evt_picker(esp_event_base_t base, int32_t id, void* data){ void TipHeater::_start_runner(){ // Prepare and then apply the LEDC PWM timer configuration ledc_timer_config_t ledc_timer = { - .speed_mode = LEDC_MODE, - .duty_resolution = LEDC_DUTY_RES, - .timer_num = LEDC_TIMER, - .freq_hz = LEDC_FREQUENCY, + .speed_mode = HEATER_LEDC_SPEEDMODE, + .duty_resolution = HEATER_LEDC_DUTY_RES, + .timer_num = HEATER_LEDC_TIMER, + .freq_hz = HEATER_LEDC_FREQUENCY, .clk_cfg = LEDC_AUTO_CLK }; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); @@ -122,10 +139,10 @@ void TipHeater::_start_runner(){ // Prepare and then apply the LEDC PWM channel configuration ledc_channel_config_t ledc_channel = { .gpio_num = _pwm.gpio, - .speed_mode = LEDC_MODE, + .speed_mode = HEATER_LEDC_SPEEDMODE, .channel = _pwm.channel, .intr_type = LEDC_INTR_DISABLE, - .timer_sel = LEDC_TIMER, + .timer_sel = HEATER_LEDC_TIMER, .duty = 0, .hpoint = 0, .flags = {.output_invert = _pwm.invert } @@ -140,18 +157,28 @@ void TipHeater::_start_runner(){ HEATER_TASK_PRIO, &_task_hndlr, tskNO_AFFINITY ) == pdPASS; + + // fader iterrupt handler, it will resume heaterControl task on fade end event + ledc_cbs_t fade_callback = { + .fade_cb = TipHeater::_cb_ledc_fade_end_event + }; + // register fader interrupt handler + ledc_cb_register(HEATER_LEDC_SPEEDMODE, _pwm.channel, &fade_callback, this); } void TipHeater::_stop_runner(){ _state = HeaterState_t::shutoff; - ledc_stop(LEDC_MODE, _pwm.channel, _pwm.invert); + ledc_stop(HEATER_LEDC_SPEEDMODE, _pwm.channel, _pwm.invert); if(_task_hndlr) vTaskDelete(_task_hndlr); _task_hndlr = nullptr; + + // unregister cb interrupt handler + ledc_fade_func_uninstall(); } void TipHeater::_heaterControl(){ - TickType_t xLastWakeTime = xTaskGetTickCount (); + TickType_t xLastWakeTime = xTaskGetTickCount(); TickType_t delay_time = measure_delay_ticks; for (;;){ // sleep to accomodate specified measuring rate @@ -165,12 +192,15 @@ void TipHeater::_heaterControl(){ delay_time = idle_delay_ticks; break; case HeaterState_t::active : { - if (ledc_get_duty(LEDC_MODE, _pwm.channel)){ + // use while to avoid concurency issues with hw fader interrupt + while (ledc_get_duty(HEATER_LEDC_SPEEDMODE, _pwm.channel)){ // shut off heater in order to measure temperature 关闭加热器以测量温度 - ledc_set_duty(LEDC_MODE, _pwm.channel, 0); - ledc_update_duty(LEDC_MODE, _pwm.channel); + ledc_set_duty(HEATER_LEDC_SPEEDMODE, _pwm.channel, 0); + ledc_update_duty(HEATER_LEDC_SPEEDMODE, _pwm.channel); // idle while OpAmp stabilizes vTaskDelay(pdMS_TO_TICKS(HEATER_OPAMP_STABILIZE_MS)); + // reset run time (need to avoid extra consecutive runs with xTaskDelayUntil if thread was suspended from the outside) + xLastWakeTime = xTaskGetTickCount(); } break; } @@ -189,10 +219,10 @@ void TipHeater::_heaterControl(){ if (_state != HeaterState_t::notip && t > TEMP_NOTIP){ // we have just lost connection with a tip sensor // disable PWM - ledc_set_duty(LEDC_MODE, _pwm.channel, 0); - ledc_update_duty(LEDC_MODE, _pwm.channel); + ledc_set_duty(HEATER_LEDC_SPEEDMODE, _pwm.channel, 0); + ledc_update_duty(HEATER_LEDC_SPEEDMODE, _pwm.channel); _state = HeaterState_t::notip; - LOGI(T_HEAT, printf, "Iron Tip ejected, T:%d\n", static_cast(t)); + LOGW(T_HEAT, printf, "Iron Tip ejected, T:%d\n", static_cast(t)); EVT_POST(SENSOR_DATA, e2int(evt::iron_t::tipEject)); continue; } @@ -201,7 +231,7 @@ void TipHeater::_heaterControl(){ if (_state == HeaterState_t::notip && t < TEMP_NOTIP){ _state = HeaterState_t::inactive; EVT_POST(SENSOR_DATA, e2int(evt::iron_t::tipInsert)); - LOGI(T_HEAT, println, "Iron Tip inserted"); + LOGW(T_HEAT, println, "Iron Tip inserted"); continue; } @@ -224,24 +254,23 @@ void TipHeater::_heaterControl(){ // note: this is ugly external function, I will rework it later //_t.calibrated = calculateTemp(_t.avg); _t.calibrated = _t.avg; - LOGV(T_HEAT, printf, "avg T: %5.1f, calibrated T: %d\n", _t.avg, _t.calibrated); + ADC_LOGD(printf, "avg T: %5.1f, cal T: %d, tgt T:%d\n", _t.avg, _t.calibrated, _t.target); EVT_POST_DATA(SENSOR_DATA, e2int(evt::iron_t::tiptemp), &_t.calibrated, sizeof(_t.calibrated)); - auto diff = abs(_t.target - _t.calibrated); // if PID algo should be engaged - if (diff < PID_ENGAGE_DIFF){ + if (_t.calibrated > (_t.target - PID_ENGAGE_DIFF_LOW) && _t.calibrated < (_t.target + PID_ENGAGE_DIFF_HIGH)){ _pwm.duty = _pid.step(_t.target, _t.calibrated); delay_time = measure_delay_ticks; } else { - // heater must be either turned on or off + // heater must be either turned full on or off _pwm.duty = _t.calibrated < _t.target ? 1<_heaterControl(); } - // events processing void _evt_picker(esp_event_base_t base, int32_t id, void* data); @@ -115,6 +112,14 @@ class TipHeater { */ void disable(); + /** + * @brief Enable the heater with PWM ramp-up + * i.e. it heater will use HW faider to ramp PWM duty from 0 to MAX + * to gradualy ramp power load + * + */ + void rampUp(); + /** * @brief Set target Tempearture in Celsius * @@ -136,4 +141,16 @@ class TipHeater { */ int32_t getCurrentTemp() const { return _t.calibrated; } +// other private methods +private: + +// static wrapper for _runner Task to call handling class member +static inline void _runner(void* pvParams){ ((TipHeater*)pvParams)->_heaterControl(); } + +static IRAM_ATTR bool _cb_ledc_fade_end_event(const ledc_cb_param_t *param, void *arg){ + // do not care what was the event, I need to unblock heater control anyway + // if (param->event == LEDC_FADE_END_EVT) + return xTaskResumeFromISR(static_cast(arg)->_task_hndlr) == pdTRUE; +} + }; diff --git a/ESPIron/ironcontroller.cpp b/ESPIron/ironcontroller.cpp index df0945c..08258cf 100644 --- a/ESPIron/ironcontroller.cpp +++ b/ESPIron/ironcontroller.cpp @@ -241,9 +241,12 @@ void IronController::_evt_commands(esp_event_base_t base, int32_t id, void* data _xTicks.motion = xTaskGetTickCount(); // notify other components LOGI(T_CTRL, println, "switch to working mode"); - EVT_POST(IRON_NOTIFY, e2int(iron_t::stateWorking)); - // set heater to working temperature - EVT_POST_DATA(IRON_SET_EVT, e2int(iron_t::heaterTargetT), &_temp.working, sizeof(_temp.working)); + // set heater to work temperature + EVT_POST_DATA(IRON_HEATER, e2int(iron_t::heaterTargetT), &_temp.working, sizeof(_temp.working)); + // enable heater + EVT_POST(IRON_HEATER, e2int(iron_t::heaterEnable)); + //EVT_POST(IRON_HEATER, e2int(iron_t::heaterRampUp)); + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateWorking)); // mode change notification break; case ironState_t::boost : @@ -254,7 +257,8 @@ void IronController::_evt_commands(esp_event_base_t base, int32_t id, void* data _xTicks.idle = xTaskGetTickCount(); // notify other components LOGI(T_CTRL, println, "switch to Idle mode"); - EVT_POST(IRON_NOTIFY, e2int(iron_t::stateIdle)); + EVT_POST(IRON_HEATER, e2int(iron_t::heaterDisable)); + EVT_POST(IRON_NOTIFY, e2int(iron_t::stateIdle)); // mode change notification break; /* // iron was suspended, wake up @@ -298,7 +302,7 @@ void IronController::_evt_commands(esp_event_base_t base, int32_t id, void* data break; } - // direction to switch to idle mode (from HUD menu selector) + // direction to switch to idle mode (from HID menu selector) case iron_t::stateIdle : { // switch to idle mode _state = ironState_t::idle; @@ -306,6 +310,7 @@ void IronController::_evt_commands(esp_event_base_t base, int32_t id, void* data _xTicks.idle = xTaskGetTickCount(); // notify other components LOGI(T_CTRL, println, "switch to Idle mode"); + EVT_POST(IRON_HEATER, e2int(iron_t::heaterDisable)); EVT_POST(IRON_NOTIFY, e2int(iron_t::stateIdle)); break; } diff --git a/ESPIron/log.h b/ESPIron/log.h index 104d107..d214b7e 100644 --- a/ESPIron/log.h +++ b/ESPIron/log.h @@ -68,6 +68,12 @@ static constexpr const char* S_E = "E: "; #endif // Per app macros +#if defined(ADC_DEBUG_LEVEL) && ADC_DEBUG_LEVEL > 3 + #define ADC_LOGD(func, ...) PTS200_DEBUG_PORT.print(S_D); PTS200_DEBUG_PORT.print(T_ADC); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) +#else + #define ADC_LOGD(...) +#endif + #if defined(ADC_DEBUG_LEVEL) && ADC_DEBUG_LEVEL == 5 #define ADC_LOGV(func, ...) PTS200_DEBUG_PORT.print(S_V); PTS200_DEBUG_PORT.print(T_ADC); PTS200_DEBUG_PORT.print((char)0x9); PTS200_DEBUG_PORT.func(__VA_ARGS__) #else diff --git a/ESPIron/sensors.cpp b/ESPIron/sensors.cpp index b12de9a..efb4ffa 100644 --- a/ESPIron/sensors.cpp +++ b/ESPIron/sensors.cpp @@ -281,7 +281,7 @@ void VinSensor::_runner(){ voltage = voltage / 4 * 31.3f; // log and publish Vin value - LOGD(T_ADC, printf, "Vin: %u mV\n", voltage); + ADC_LOGV(T_ADC, printf, "Vin: %u mV\n", voltage); EVT_POST_DATA(SENSOR_DATA, e2int(evt::iron_t::vin), &voltage, sizeof(voltage)); From 0580e9423551b59227d8dfe0efff00c21c268978 Mon Sep 17 00:00:00 2001 From: Emil Muratov Date: Sun, 26 May 2024 19:05:14 +0900 Subject: [PATCH 2/2] Implement UI Menu configuration for PWM Power ramping - add NVS variable to keep PWM ramping sate across reboots - add UI elents to confgigure it via "Power Supply" menu --- ESPIron/common.hpp | 2 +- ESPIron/const.h | 1 + ESPIron/evtloop.hpp | 5 ++++ ESPIron/heater.cpp | 12 +++++++- ESPIron/heater.hpp | 6 +--- ESPIron/hid.cpp | 56 ++++++++++++++++++++++++++++++++------ ESPIron/hid.hpp | 2 ++ ESPIron/ironcontroller.cpp | 19 ++++++++++--- ESPIron/ironcontroller.hpp | 3 ++ ESPIron/lang/i18n.h | 15 ++++++---- ESPIron/lang/lang_en_us.h | 25 ++++++++++++----- README.md | 1 + 12 files changed, 114 insertions(+), 33 deletions(-) diff --git a/ESPIron/common.hpp b/ESPIron/common.hpp index 92534e4..031d9d6 100644 --- a/ESPIron/common.hpp +++ b/ESPIron/common.hpp @@ -26,7 +26,7 @@ enum class ironState_t { boost, // heater temperature is increased for a short period of time setup, // iron is configuration mode, i.e. working with screen menu, heater switches off notip, // tip is missing or failed - PWRramp // controller is performing Power Ramping + ramping // power ramp in progress }; // working temperature values diff --git a/ESPIron/const.h b/ESPIron/const.h index 1ba04ff..00f8bf1 100644 --- a/ESPIron/const.h +++ b/ESPIron/const.h @@ -23,3 +23,4 @@ static constexpr const char* T_motionThr = "motionThr"; // motio static constexpr const char* T_pdVolts = "pdVolts"; // PD trigger voltage static constexpr const char* T_qcVolts = "qcVolts"; // QC trigger voltage static constexpr const char* T_qcMode = "qcMode"; // QC Mode +static constexpr const char* T_PWMRamp = "PWMRamp"; // PWM Power ramping diff --git a/ESPIron/evtloop.hpp b/ESPIron/evtloop.hpp index 0d714af..78cf43a 100644 --- a/ESPIron/evtloop.hpp +++ b/ESPIron/evtloop.hpp @@ -16,6 +16,7 @@ // helper macro to reduce typing #define EVT_POST(event_base, event_id) esp_event_post_to(evt::get_hndlr(), event_base, event_id, NULL, 0, portMAX_DELAY) #define EVT_POST_DATA(event_base, event_id, event_data, data_size) esp_event_post_to(evt::get_hndlr(), event_base, event_id, event_data, data_size, portMAX_DELAY) +#define EVT_POST_ISR(event_base, event_id, tsk_awoken) esp_event_isr_post_to(evt::get_hndlr(), event_base, event_id, NULL, 0, tsk_awoken) // ESP32 event loop defines ESP_EVENT_DECLARE_BASE(SENSOR_DATA); // events coming from different sensors, i.e. temperature, voltage, orientation, etc... @@ -61,6 +62,8 @@ enum class iron_t:int32_t { reloadTemp, // reload temperature configuration reloadTimeouts, // reload timeouts configuration + enablePWMRamp, // PWM ramping on + disablePWMRamp, // PWM ramping off // Commands - power control pdVoltage, // switch PD trigger, arg uint32_t in V @@ -78,6 +81,8 @@ enum class iron_t:int32_t { stateBoost, // iron controller switched to 'Boost' mode, parameter uint32_t - seconds left to disable boost mode stateSetup, // enter in menu confgiration mode stateNoTip, + statePWRRampStart, // Iron has started power ramping + statePWRRampCmplt, // Iron has completed power ramping tipEject, // sent by heater when it looses the tip sense tipInsert, // sent by heater when detect tip sensor diff --git a/ESPIron/heater.cpp b/ESPIron/heater.cpp index cc6dcbb..7db8f9b 100644 --- a/ESPIron/heater.cpp +++ b/ESPIron/heater.cpp @@ -2,7 +2,7 @@ #include "const.h" #include "evtloop.hpp" #include "heater.hpp" -#include "main.h" +//#include "main.h" #include "log.h" #define HEATER_TASK_PRIO tskIDLE_PRIORITY+1 // task priority @@ -322,6 +322,16 @@ void TipHeater::rampUp(){ _state = HeaterState_t::active; } +bool TipHeater::_cb_ledc_fade_end_event(const ledc_cb_param_t *param, void *arg){ + // do not care what was the event, I need to unblock heater control anyway + // if (param->event == LEDC_FADE_END_EVT) + + BaseType_t task_awoken; + // notify that rampUp has been complete + EVT_POST_ISR(IRON_NOTIFY, e2int(evt::iron_t::statePWRRampCmplt), &task_awoken); + task_awoken |= xTaskResumeFromISR(static_cast(arg)->_task_hndlr); + return task_awoken; +} // 对32个ADC读数进行平均以降噪 diff --git a/ESPIron/heater.hpp b/ESPIron/heater.hpp index 2c26540..7f047fc 100644 --- a/ESPIron/heater.hpp +++ b/ESPIron/heater.hpp @@ -147,10 +147,6 @@ class TipHeater { // static wrapper for _runner Task to call handling class member static inline void _runner(void* pvParams){ ((TipHeater*)pvParams)->_heaterControl(); } -static IRAM_ATTR bool _cb_ledc_fade_end_event(const ledc_cb_param_t *param, void *arg){ - // do not care what was the event, I need to unblock heater control anyway - // if (param->event == LEDC_FADE_END_EVT) - return xTaskResumeFromISR(static_cast(arg)->_task_hndlr) == pdTRUE; -} +static IRAM_ATTR bool _cb_ledc_fade_end_event(const ledc_cb_param_t *param, void *arg); }; diff --git a/ESPIron/hid.cpp b/ESPIron/hid.cpp index 2799d0e..610c383 100644 --- a/ESPIron/hid.cpp +++ b/ESPIron/hid.cpp @@ -356,16 +356,19 @@ void ViSet_MainScreen::drawScreen(){ u8g2.print(dictionary[D_idle]); break; case ironState_t::working : - u8g2.print(dictionary[D_heating]); + u8g2.print(dictionary[D_Heating]); break; case ironState_t::standby : - u8g2.print(dictionary[D_standby]); + u8g2.print(dictionary[D_Standby]); break; case ironState_t::boost : - u8g2.print(dictionary[D_boost]); + u8g2.print(dictionary[D_Boost]); break; case ironState_t::notip : - u8g2.print(dictionary[D_notip]); + u8g2.print(dictionary[D_NoTip]); + break; + case ironState_t::ramping : + u8g2.print(dictionary[D_Ramping]); break; default:; @@ -468,16 +471,22 @@ void ViSet_MainScreen::_evt_notify(int32_t id, void* data){ switch(static_cast(id)){ case evt::iron_t::stateIdle : _state = ironState_t::idle; - break; + break; case evt::iron_t::stateWorking : _state = ironState_t::working; - break; + break; case evt::iron_t::stateStandby : _state = ironState_t::standby; - break; + break; case evt::iron_t::stateBoost : _state = ironState_t::boost; - break; + break; + case evt::iron_t::statePWRRampStart : + _state = ironState_t::ramping; + break; + case evt::iron_t::statePWRRampCmplt : + _state = ironState_t::working; + break; } } @@ -982,13 +991,14 @@ ViSet_PwrSetup::ViSet_PwrSetup(GPIOButton &button, PseudoRotaryE // go back to prev viset on exit parentvs = viset_evt_t::goBack; - // load PD voltage values from NVS + // load settings values from NVS esp_err_t err; std::unique_ptr handle = nvs::open_nvs_handle(T_IRON, NVS_READONLY, &err); if (err == ESP_OK) { handle->get_item(T_pdVolts, _volts_pd); handle->get_item(T_qcVolts, _volts_qc); handle->get_item(T_qcMode, _qc_mode); + handle->get_item(T_PWMRamp, _pwm_ramp); } // set our iterator to selected voltage option @@ -1019,7 +1029,11 @@ ViSet_PwrSetup::~ViSet_PwrSetup(){ handle->set_item(T_pdVolts, _volts_pd); handle->set_item(T_qcVolts, _volts_qc); handle->set_item(T_qcMode, _qc_mode); + handle->set_item(T_PWMRamp, _pwm_ramp); } + + // command for PWM ramping + EVT_POST(IRON_SET_EVT, _pwm_ramp ? e2int(iron_t::enablePWMRamp) : e2int(iron_t::disablePWMRamp) ); } void ViSet_PwrSetup::_buildMenu(){ @@ -1153,6 +1167,30 @@ void ViSet_PwrSetup::_buildMenu(){ ); + // ************************************** + // ***** Page "Power Supply->PWM Ramp + muiItemId pwmramp_page = makePage(menu_PwrControlOpts.at(2), root_page); + + // Page Title + addItemToPage(ptitle_idx, pwmramp_page); + + // create checkbox item + addMuippItem( + new MuiItem_U8g2_CheckBox(u8g2, nextIndex(), dictionary[D_PwrRamp_label], _pwm_ramp, [&](size_t idx){ _pwm_ramp = idx; }, PAGE_TITLE_FONT, 0, SMALL_TEXT_FONT_Y_OFFSET+PAGE_TITLE_FONT_Y_OFFSET), + pwmramp_page + ); + + // create text hint + addMuippItem( + new MuiItem_U8g2_StaticText(u8g2, nextIndex(), dictionary[D_PwrRamp_hint], SMALL_TEXT_FONT, 0, 2*SMALL_TEXT_FONT_Y_OFFSET + PAGE_TITLE_FONT_Y_OFFSET), + pwmramp_page + ); + + // add back button on a warn page, it will lead to the root page + addItemToPage(bb_idx, pwmramp_page); + + + // start menu from root page menuStart(root_page); } diff --git a/ESPIron/hid.hpp b/ESPIron/hid.hpp index 1375285..9a5611e 100644 --- a/ESPIron/hid.hpp +++ b/ESPIron/hid.hpp @@ -326,6 +326,8 @@ class ViSet_PwrSetup : public MuiMenu { // selected QC mode (index for menu_QCFunctionOpts array) uint32_t _qc_mode{0}; + bool _pwm_ramp{false}; + // containters for string data that will be printed on-screen // selected PD/QC voltage std::string _pdv_s, _qcv_s; diff --git a/ESPIron/ironcontroller.cpp b/ESPIron/ironcontroller.cpp index 08258cf..d7527c6 100644 --- a/ESPIron/ironcontroller.cpp +++ b/ESPIron/ironcontroller.cpp @@ -116,6 +116,9 @@ void IronController::init(){ if (err != ESP_OK) return; + // restore PWM Ramping option + handle->get_item(T_PWMRamp, _pwm_ramp); + // init PD trigger handle->get_item(T_pdVolts, _voltage); _pd_trigger_init(); @@ -243,10 +246,9 @@ void IronController::_evt_commands(esp_event_base_t base, int32_t id, void* data LOGI(T_CTRL, println, "switch to working mode"); // set heater to work temperature EVT_POST_DATA(IRON_HEATER, e2int(iron_t::heaterTargetT), &_temp.working, sizeof(_temp.working)); - // enable heater - EVT_POST(IRON_HEATER, e2int(iron_t::heaterEnable)); - //EVT_POST(IRON_HEATER, e2int(iron_t::heaterRampUp)); - EVT_POST(IRON_NOTIFY, e2int(iron_t::stateWorking)); // mode change notification + // enable heater either with PWM ramping or plain + EVT_POST(IRON_HEATER, _pwm_ramp ? e2int(iron_t::heaterRampUp) : e2int(iron_t::heaterEnable)); + EVT_POST(IRON_NOTIFY, _pwm_ramp ? e2int(iron_t::statePWRRampStart) : e2int(iron_t::stateWorking)); // mode change notification break; case ironState_t::boost : @@ -358,6 +360,15 @@ void IronController::_evt_commands(esp_event_base_t base, int32_t id, void* data if(_qc) _qc->setQCV (*reinterpret_cast(data)); break; + // PWM ramp control + case evt::iron_t::enablePWMRamp : + _pwm_ramp = true; + break; + + case evt::iron_t::disablePWMRamp : + _pwm_ramp = false; + break; + /* // switch QC trigger modes case evt::iron_t::qcDisable : diff --git a/ESPIron/ironcontroller.hpp b/ESPIron/ironcontroller.hpp index 1f1558e..99dab42 100644 --- a/ESPIron/ironcontroller.hpp +++ b/ESPIron/ironcontroller.hpp @@ -44,6 +44,9 @@ class IronController { // working voltage, set default to 20v to let PD trigger select if no value set in NVS uint32_t _voltage{20}; + // Use PWM power ramping + bool _pwm_ramp{false}; + // Mode Switcher timer TimerHandle_t _tmr_mode = nullptr; diff --git a/ESPIron/lang/i18n.h b/ESPIron/lang/i18n.h index 9f3d1fa..95ccf66 100644 --- a/ESPIron/lang/i18n.h +++ b/ESPIron/lang/i18n.h @@ -16,7 +16,7 @@ #define MENU_MAIN_CFG_SIZE 6 #define MENU_TEMPERATURE_CFG_SIZE 5 #define MENU_TIMEOUTS_CFG_SIZE 5 -#define MENU_PWR_CONTROL_CFG_SIZE 3 +#define MENU_PWR_CONTROL_CFG_SIZE 4 /* // List of available translations @@ -34,15 +34,18 @@ enum lang_index : uint32_t { * the order of enums MUST match with elements in dictionary array */ enum dict_index { - D_boost = (0), - D_error, + D_Boost = (0), + D_Error, D_Disabled, - D_heating, + D_Heating, D_idle, D_min, D_none, - D_notip, + D_NoTip, D_OK, + D_Ramping, + D_PwrRamp_label, + D_PwrRamp_hint, D_return, D_PDVoltage, D_QCMode, @@ -52,7 +55,7 @@ enum dict_index { D_SaveLast_hint, D_Settings, D_set_t, - D_standby, + D_Standby, // Notes goes below D_Note_QCWarn, D_Temp_SaveLastWrkDescr, diff --git a/ESPIron/lang/lang_en_us.h b/ESPIron/lang/lang_en_us.h index e440211..6a199c4 100644 --- a/ESPIron/lang/lang_en_us.h +++ b/ESPIron/lang/lang_en_us.h @@ -30,6 +30,7 @@ // 11x15 nice bold font #define PAGE_TITLE_FONT u8g2_font_bauhaus2015_tr +#define PAGE_TITLE_FONT_Y_OFFSET 15 // 12x16 wrong? small gothic font #define PAGE_TITLE_FONT_SMALL u8g2_font_glasstown_nbp_t_all @@ -60,22 +61,25 @@ // EN-US namespace lang_en_us { -// EN US +// Iron Modes (On main screen) +static constexpr const char* T_Idle = "Idle"; // state display +static constexpr const char* T_Heating = "Heating"; // state display +static constexpr const char* T_Standby = "Standby"; // state display in 'Standby' static constexpr const char* T_Boost = "Boost"; // state display +static constexpr const char* T_Ramping = "Ramping!"; // state display power ramp +static constexpr const char* T_NoTip = "No tip!"; // state display when tip is missing + +// Others static constexpr const char* T_Disabled = "Disabled"; // disabled option static constexpr const char* T_Error = "Error"; -static constexpr const char* T_Heating = "Heating"; // state display -static constexpr const char* T_Idle = "Idle"; // state display static constexpr const char* T_min = "min."; // short for 'minutes' static constexpr const char* T_none = "none"; // none option -static constexpr const char* T_NoTip = "No tip!"; // state display when tip is missing static constexpr const char* T_OK = "OK"; // OK label/message static constexpr const char* T_PDVoltage = "PD Voltage:"; // PowerDelivery trigger voltage static constexpr const char* T_QCMode = "QC Mode:"; // QC trigger mode static constexpr const char* T_QCVoltage = "QC Voltage:"; // QC trigger voltage static constexpr const char* T_sec = "sec."; // short for 'seconds' static constexpr const char* T_setT = "Set:"; // Target temperature on main screen -static constexpr const char* T_standby = "Standby"; // state display in 'Standby' static constexpr const char* T_return = " dictionary = { lang_en_us::T_none, lang_en_us::T_NoTip, lang_en_us::T_OK, + lang_en_us::T_Ramping, + lang_en_us::T_PwrRamp_label, + lang_en_us::T_PwrRamp_hint, lang_en_us::T_return, lang_en_us::T_PDVoltage, lang_en_us::T_QCMode, @@ -145,7 +155,7 @@ static constexpr std::array dictionary = { lang_en_us::T_SaveLast_hint, lang_en_us::T_Settings, lang_en_us::T_setT, - lang_en_us::T_standby, + lang_en_us::T_Standby, // Notes goes below lang_en_us::T_Note_QCWarn, lang_en_us::T_Temp_SaveLastWrkDescr @@ -183,6 +193,7 @@ static constexpr std::array menu_TimeoutOp static constexpr std::array menu_PwrControlOpts = { lang_en_us::T_PwrPD, lang_en_us::T_PwrQC, + lang_en_us::T_PwrRamp, lang_en_us::T_return }; diff --git a/README.md b/README.md index 0bdacc5..41d9149 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Menu navigation demo capture. | :floppy_disk: (optionaly) save/restore last used temperature | :white_check_mark: done | | PD Configuration | :white_check_mark: done | | QC2/QC3 trigger | :white_check_mark: done (experimental) | +| :mountain_cableway: PWM Power ramping | :white_check_mark: done | | :straight_ruler: Tip calibration | :x: Planned | | :memo: Tip profiles | :x: Planned | | :wavy_dash: Power profile | :x: Planned |