From d80a1d2a2e01143970ffc13b971859a98ce9cb34 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 30 Nov 2023 13:50:35 -0700 Subject: added initial code for emisar-d3aa torch --- hw/hank/emisar-d3aa/hwdef.c | 118 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 hw/hank/emisar-d3aa/hwdef.c (limited to 'hw/hank/emisar-d3aa/hwdef.c') diff --git a/hw/hank/emisar-d3aa/hwdef.c b/hw/hank/emisar-d3aa/hwdef.c new file mode 100644 index 0000000..853e84d --- /dev/null +++ b/hw/hank/emisar-d3aa/hwdef.c @@ -0,0 +1,118 @@ +// Emisar D3AA helper functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "fsm/chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + DAC_LVL = 0; // DAC off + DAC_VREF = V10; // low Vref + HDR_ENABLE_PORT &= ~(1 << HDR_ENABLE_PIN); // HDR off + + // prevent post-off flash + IN_NFET_ENABLE_PORT |= (1 << IN_NFET_ENABLE_PIN); + delay_4ms(IN_NFET_DELAY_TIME/4); + IN_NFET_ENABLE_PORT &= ~(1 << IN_NFET_ENABLE_PIN); + + // turn off boost last + BST_ENABLE_PORT &= ~(1 << BST_ENABLE_PIN); // BST off +} + +// single set of LEDs with 1 regulated power channel +// and low/high HDR plus low/high Vref as different "gears" +void set_level_main(uint8_t level) { + uint8_t noflash = 0; + + // when turning on from off, use IN_NFET to prevent a flash + if ((! actual_level) && (level < HDR_ENABLE_LEVEL_MIN)) { + noflash = 1; + IN_NFET_ENABLE_PORT |= (1 << IN_NFET_ENABLE_PIN); + } + + // BST on first, to give it a few extra microseconds to spin up + BST_ENABLE_PORT |= (1 << BST_ENABLE_PIN); + + // pre-load ramp data so it can be assigned faster later + // DAC level register is left-aligned + PWM1_DATATYPE dac_lvl = PWM1_GET(level) << 6; + PWM2_DATATYPE dac_vref = PWM2_GET(level); + + // enable HDR on top half of ramp + if (level >= (HDR_ENABLE_LEVEL_MIN-1)) + HDR_ENABLE_PORT |= (1 << HDR_ENABLE_PIN); + else + HDR_ENABLE_PORT &= ~(1 << HDR_ENABLE_PIN); + + // set these in successive clock cycles to avoid getting out of sync + // (minimizes ramp bumps when changing gears) + DAC_LVL = dac_lvl; + DAC_VREF = dac_vref; + + if (noflash) { + // wait for flash prevention to finish + delay_4ms(IN_NFET_DELAY_TIME/4); + IN_NFET_ENABLE_PORT &= ~(1 << IN_NFET_ENABLE_PIN); + } +} + +bool gradual_tick_main(uint8_t gt) { + // if HDR and Vref "engine gear" is the same, do a small adjustment... + // otherwise, simply jump to the next ramp level + // and let set_level() handle any gear changes + + // different gear = full adjustment + PWM2_DATATYPE vref_next = PWM2_GET(gt); + if (vref_next != DAC_VREF) return true; // let parent set_level() for us + + // same gear = small adjustment + PWM1_DATATYPE dac_now = DAC_LVL >> 6; // register is left-aligned + PWM1_DATATYPE dac_next = PWM1_GET(gt); + + // adjust multiple times based on how far until the next level + // (so it adjusts faster/coarser for big steps) + + int16_t diff = (dac_next - dac_now); + if (diff < 0) diff = -diff; + + // ~70 max DAC levels per ramp step, 1 + (70 >> 3) = max 10 + uint8_t steps; + steps = 1 + (diff >> 3); + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(dac_next, dac_now); + + DAC_LVL = dac_now << 6; + + if (dac_next == dac_now) return true; // done + + return false; // not done yet +} + + +uint8_t voltage_raw2cooked(uint16_t measurement) { + // In : 65535 * BATTLVL / 1.024V + // Out: uint8_t: Vbat * 40 + // BATTLVL = Vbat * (100.0/(330+100)) = Vbat / 4.3 + // So, Out = In * 4.3 / 1600 + // (plus a bit of fudging to fix the slope and offset, + // based on measuring actual hardware) + uint8_t result = (uint32_t)(measurement + (65535 * 4 / 1024)) + * 43 / 16128; + return result; +} + -- cgit v1.2.3 From 843dc0ba1c06e4a9b1bf034fff0e6ec5f8c12c84 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Tue, 5 Dec 2023 16:47:24 -0700 Subject: d3aa: made it easy to switch between vddio2 and external voltage divider --- hw/hank/emisar-d3aa/hwdef.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'hw/hank/emisar-d3aa/hwdef.c') diff --git a/hw/hank/emisar-d3aa/hwdef.c b/hw/hank/emisar-d3aa/hwdef.c index 853e84d..5f41d9d 100644 --- a/hw/hank/emisar-d3aa/hwdef.c +++ b/hw/hank/emisar-d3aa/hwdef.c @@ -104,6 +104,7 @@ bool gradual_tick_main(uint8_t gt) { } +#ifdef USE_VOLTAGE_DIVIDER uint8_t voltage_raw2cooked(uint16_t measurement) { // In : 65535 * BATTLVL / 1.024V // Out: uint8_t: Vbat * 40 @@ -115,4 +116,5 @@ uint8_t voltage_raw2cooked(uint16_t measurement) { * 43 / 16128; return result; } +#endif -- cgit v1.2.3 From d623fe5a1bbe02d680094eae5c16cdc83732a984 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 4 Mar 2024 06:18:26 -0700 Subject: d3aa: fixed voltage measurement --- hw/hank/emisar-d3aa/hwdef.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw/hank/emisar-d3aa/hwdef.c') diff --git a/hw/hank/emisar-d3aa/hwdef.c b/hw/hank/emisar-d3aa/hwdef.c index 5f41d9d..a963bc2 100644 --- a/hw/hank/emisar-d3aa/hwdef.c +++ b/hw/hank/emisar-d3aa/hwdef.c @@ -113,7 +113,7 @@ uint8_t voltage_raw2cooked(uint16_t measurement) { // (plus a bit of fudging to fix the slope and offset, // based on measuring actual hardware) uint8_t result = (uint32_t)(measurement + (65535 * 4 / 1024)) - * 43 / 16128; + * 43 / 16000; return result; } #endif -- cgit v1.2.3 From 412df484ddf0650c07da56193bfa906a8647d1dc Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 11 Mar 2024 17:51:14 -0600 Subject: d3aa fine-tuning: - new ramp - production style config defaults (simple mode, Hank config) - candle tuning - fixed way-too-fast thermal regulation (might still be a bit fast, but it's a lot better) --- hw/hank/emisar-d3aa/hwdef.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'hw/hank/emisar-d3aa/hwdef.c') diff --git a/hw/hank/emisar-d3aa/hwdef.c b/hw/hank/emisar-d3aa/hwdef.c index a963bc2..e2fd315 100644 --- a/hw/hank/emisar-d3aa/hwdef.c +++ b/hw/hank/emisar-d3aa/hwdef.c @@ -84,17 +84,9 @@ bool gradual_tick_main(uint8_t gt) { PWM1_DATATYPE dac_now = DAC_LVL >> 6; // register is left-aligned PWM1_DATATYPE dac_next = PWM1_GET(gt); - // adjust multiple times based on how far until the next level - // (so it adjusts faster/coarser for big steps) - - int16_t diff = (dac_next - dac_now); - if (diff < 0) diff = -diff; - - // ~70 max DAC levels per ramp step, 1 + (70 >> 3) = max 10 - uint8_t steps; - steps = 1 + (diff >> 3); - for (uint8_t i=0; i<=steps; i++) - GRADUAL_ADJUST_SIMPLE(dac_next, dac_now); + // only adjust 1 dac level, max is 1023 + // (but speed it up with "#define GRADUAL_ADJUST_SPEED 4" elsewhere) + GRADUAL_ADJUST_SIMPLE(dac_next, dac_now); DAC_LVL = dac_now << 6; -- cgit v1.2.3 From a87812f436e08b14a7cede83e30306d779774872 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Mon, 25 Mar 2024 04:25:43 -0600 Subject: dammit, got alkaline detection half working and then my flashing adapter died (saving progress here so I can work on a different branch) --- hw/hank/emisar-d3aa/hwdef.c | 64 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'hw/hank/emisar-d3aa/hwdef.c') diff --git a/hw/hank/emisar-d3aa/hwdef.c b/hw/hank/emisar-d3aa/hwdef.c index e2fd315..f6cf94e 100644 --- a/hw/hank/emisar-d3aa/hwdef.c +++ b/hw/hank/emisar-d3aa/hwdef.c @@ -4,6 +4,8 @@ #pragma once #include "fsm/chan-rgbaux.c" +#include "fsm/ramping.h" +#include "ui/anduril/misc.h" void set_level_zero(); @@ -110,3 +112,65 @@ uint8_t voltage_raw2cooked(uint16_t measurement) { } #endif +#ifdef USE_WEAK_BATTERY_PROTECTION +uint8_t quick_volt_measurement() { + // take the average of a few samples + // (assumes the ADC is in voltage mode and running continuously) + uint16_t total = 0; + for (uint8_t i=0; i<8; i++) { + uint16_t m = adc_raw[0]; + total += voltage_raw2cooked(m); + delay_zero(); + } + uint8_t v = total / 8; + return v; +} + +void detect_weak_battery() { + // guess at the cell strength with a load test... + // - measure voltage while LEDs off + // - measure again with LEDs on + // - determine how much to limit power + // - blink to indicate weak battery mode, if active + + uint16_t resting, loaded; + + set_level(0); + resting = quick_volt_measurement(); + + set_level(WEAK_BATTERY_CHECK_LEVEL); + loaded = quick_volt_measurement(); + set_level(0); + + int16_t diff = resting - loaded; + uint8_t extra_blinks = 0; + + if (loaded <= DUAL_VOLTAGE_LOW_LOW) { + // weak or empty AA battery has a low limit + ramp_level_hard_limit = WEAK_BATTERY_LOWEST_LIMIT; + extra_blinks = 2; + } else if (loaded >= VOLTAGE_RED) { + // reasonably strong li-ion battery + ramp_level_hard_limit = WEAK_BATTERY_HIGHEST_LIMIT; + } else if (diff <= (-5 * 4)) { + // marginal battery, dropped a lot under mild load + ramp_level_hard_limit = WEAK_BATTERY_MEDIUM_LIMIT; + extra_blinks = 1; + } + + for (uint8_t i=0; i WEAK_BATTERY_SAG_THRESHOLD) || + // Li-ion critical voltage + ((resting > VOLTAGE_LOW) && (loaded < VOLTAGE_LOW)) + ) { + ramp_level_hard_limit = l; + break; + } + } set_level(0); - int16_t diff = resting - loaded; - uint8_t extra_blinks = 0; + // TODO? limit NiMH to ~half power, even if it passed the sag test? + // (and if so, blink 2X for NiMH, 3X for alkaline) - if (loaded <= DUAL_VOLTAGE_LOW_LOW) { - // weak or empty AA battery has a low limit - ramp_level_hard_limit = WEAK_BATTERY_LOWEST_LIMIT; - extra_blinks = 2; - } else if (loaded >= VOLTAGE_RED) { - // reasonably strong li-ion battery - ramp_level_hard_limit = WEAK_BATTERY_HIGHEST_LIMIT; - } else if (diff <= (-5 * 4)) { - // marginal battery, dropped a lot under mild load - ramp_level_hard_limit = WEAK_BATTERY_MEDIUM_LIMIT; - extra_blinks = 1; - } + uint8_t extra_blinks = 0; + if (ramp_level_hard_limit) extra_blinks ++; for (uint8_t i=0; i DUAL_VOLTAGE_FLOOR) { + sag_limit = WEAK_BATTERY_SAG_THRESHOLD_LIION; + crit_voltage = VOLTAGE_LOW; + } else { + sag_limit = WEAK_BATTERY_SAG_THRESHOLD_AA; + crit_voltage = DUAL_VOLTAGE_LOW_LOW; + } + // progressively turn up the power until sag threshold is hit, // or critical voltage, or max testing level is reached for (uint8_t l=1; l WEAK_BATTERY_SAG_THRESHOLD) || - // Li-ion critical voltage - ((resting > VOLTAGE_LOW) && (loaded < VOLTAGE_LOW)) - ) { - ramp_level_hard_limit = l; - break; + if ( (loaded <= crit_voltage) || (sag > sag_limit) ) { + // battery empty or weak + ramp_level_hard_limit = l; + break; } } set_level(0); -- cgit v1.2.3 From 3cafb86c00a95a70e58144230f641c2c94208e7b Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 29 Mar 2024 08:21:07 -0600 Subject: d3aa weak battery test: blink 3x instead of 2x, and omit number readout --- hw/hank/emisar-d3aa/hwdef.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'hw/hank/emisar-d3aa/hwdef.c') diff --git a/hw/hank/emisar-d3aa/hwdef.c b/hw/hank/emisar-d3aa/hwdef.c index 8aaa893..36c6fed 100644 --- a/hw/hank/emisar-d3aa/hwdef.c +++ b/hw/hank/emisar-d3aa/hwdef.c @@ -123,9 +123,10 @@ uint8_t quick_volt_measurement() { void detect_weak_battery() { // guess at the cell strength with a load test... - // - measure voltage while LEDs off + // - measure voltage with LEDs off // - measure again with LEDs on // - determine how much to limit power + // (ramp up until battery becomes unstable) // - blink to indicate weak battery mode, if active ramp_level_hard_limit = 0; @@ -162,17 +163,23 @@ void detect_weak_battery() { } set_level(0); - // TODO? limit NiMH to ~half power, even if it passed the sag test? - // (and if so, blink 2X for NiMH, 3X for alkaline) + // Blink again if not in full-power mode: + // - 1 blink total: Strong Li-ion cell, full power enabled + // - 2 blinks: Strong AA cell, max AA power enabled + // (not used on this driver, strong AA uses mode 1) + // - 3 blinks: Weak battery, power severely limited uint8_t extra_blinks = 0; - if (ramp_level_hard_limit) extra_blinks ++; + if (ramp_level_hard_limit) extra_blinks += 2; for (uint8_t i=0; i