diff options
31 files changed, 686 insertions, 105 deletions
diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 2ba771f..c77ac85 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -2,7 +2,7 @@ name: build all on: # all branches: - [ push, pull_request ] + [ push, pull_request, workflow_dispatch ] # trunk only: #push: # branches: [ "trunk" ] @@ -1,4 +1,4 @@ -Models: 72 +Models: 75 Model MCU Name ----- --- ---- @@ -23,6 +23,7 @@ Model MCU Name 0142 attiny85 hank-emisar-d18-219 0143 attiny1634 hank-noctigon-m44 0151 attiny1634 hank-emisar-d4k-3ch +0161 avr32dd20 hank-emisar-d3aa 0211 attiny1634 hank-noctigon-kr4 0212 attiny1634 hank-noctigon-kr4-nofet 0213 attiny1634 hank-noctigon-kr4-219 diff --git a/arch/attiny1616.c b/arch/attiny1616.c index c5499dd..02e64b1 100644 --- a/arch/attiny1616.c +++ b/arch/attiny1616.c @@ -104,18 +104,18 @@ inline uint16_t mcu_adc_result_volts() { inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) { // In : 65535 * 1.5 / Vbat - // Out: uint8_t: Vbat * 40 + // Out: uint8_t: Vbat * 50 // 1.5 = ADC Vref #if 0 // 1024 = how much ADC resolution we're using (10 bits) // (12 bits available, but it costs an extra 84 bytes of ROM to calculate) - uint8_t vbat40 = (uint16_t)(40 * 1.5 * 1024) / (measurement >> 6); + uint8_t vbat = (uint16_t)(10 * dV * 1.5 * 1024) / (measurement >> 6); #else // ... spend the extra 84 bytes of ROM for better precision // 4096 = how much ADC resolution we're using (12 bits) - uint8_t vbat40 = (uint32_t)(40 * 1.5 * 4096) / (measurement >> 4); + uint8_t vbat = (uint32_t)(10 * dV * 1.5 * 4096) / (measurement >> 4); #endif - return vbat40; + return vbat; } #if 0 // fine voltage, 0 to 10.24V in 1/6400th V steps @@ -130,12 +130,12 @@ inline uint16_t mcu_vdd_raw2fine(uint16_t measurement) { #ifdef USE_VOLTAGE_DIVIDER inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement) { // In : 4095 * Vdiv / 1.1V - // Out: uint8_t: Vbat * 40 + // Out: uint8_t: Vbat * 50 // Vdiv = Vbat / 4.3 (typically) // 1.1 = ADC Vref const uint16_t adc_per_volt = (((uint16_t)ADC_44 << 4) - ((uint16_t)ADC_22 << 4)) - / (4 * (44-22)); + / (dV * (44-22)); uint8_t result = measurement / adc_per_volt; return result; } diff --git a/arch/attiny1616.h b/arch/attiny1616.h index 940973e..711452d 100644 --- a/arch/attiny1616.h +++ b/arch/attiny1616.h @@ -85,7 +85,7 @@ inline void mcu_adc_vect_clear(); inline uint16_t mcu_adc_result_temp(); inline uint16_t mcu_adc_result_volts(); -// return Volts * 40, range 0 to 6.375V +// return Volts * 50, range 0 to 5.10V #define voltage_raw2cooked mcu_vdd_raw2cooked inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement); inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement); diff --git a/arch/attiny1634.c b/arch/attiny1634.c index e29d1c3..314ca52 100644 --- a/arch/attiny1634.c +++ b/arch/attiny1634.c @@ -97,30 +97,30 @@ inline uint16_t mcu_adc_result() { inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) { // In : 65535 * 1.1 / Vbat - // Out: uint8_t: Vbat * 40 + // Out: uint8_t: Vbat * 50 // 1.1 = ADC Vref #if 0 // 1024 = how much ADC resolution we're using (10 bits) // (12 bits available, but it costs an extra 84 bytes of ROM to calculate) - uint8_t vbat40 = (uint16_t)(40 * 1.1 * 1024) / (measurement >> 6); + uint8_t vbat = (uint16_t)(10 * dV * 1.1 * 1024) / (measurement >> 6); #else // ... spend the extra 84 bytes of ROM for better precision // 4096 = how much ADC resolution we're using (12 bits) - uint8_t vbat40 = (uint32_t)(40 * 1.1 * 4096) / (measurement >> 4); + uint8_t vbat = (uint32_t)(10 * dV * 1.1 * 4096) / (measurement >> 4); #endif - return vbat40; + return vbat; } #ifdef USE_VOLTAGE_DIVIDER inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement) { // In : 4095 * Vdiv / 1.1V - // Out: uint8_t: Vbat * 40 + // Out: uint8_t: Vbat * 50 // Vdiv = Vbat / 4.3 (typically) // 1.1 = ADC Vref const uint16_t adc_per_volt = (((uint16_t)ADC_44 << 4) - ((uint16_t)ADC_22 << 4)) - / (4 * (44-22)); + / (dV * (44-22)); uint8_t result = measurement / adc_per_volt; return result; } diff --git a/arch/attiny1634.h b/arch/attiny1634.h index 559d04e..19920fe 100644 --- a/arch/attiny1634.h +++ b/arch/attiny1634.h @@ -66,7 +66,7 @@ inline void mcu_adc_off(); inline uint16_t mcu_adc_result(); -// return Volts * 40, range 0 to 6.375V +// return Volts * 50, range 0 to 5.10V #define voltage_raw2cooked mcu_vdd_raw2cooked inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement); inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement); diff --git a/arch/attiny85.c b/arch/attiny85.c index 9e298cc..4ca4b87 100644 --- a/arch/attiny85.c +++ b/arch/attiny85.c @@ -103,24 +103,24 @@ inline uint16_t mcu_adc_result() { return ADC; } inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) { // In : 65535 * 1.1 / Vbat - // Out: uint8_t: Vbat * 40 + // Out: uint8_t: Vbat * 50 // 1.1 = ADC Vref // 1024 = how much ADC resolution we're using (10 bits) // (12 bits available, but it costs an extra 84 bytes of ROM to calculate) - uint8_t vbat40 = (uint16_t)(40 * 1.1 * 1024) / (measurement >> 6); - return vbat40; + uint8_t vbat = (uint16_t)(10 * dV * 1.1 * 1024) / (measurement >> 6); + return vbat; } #ifdef USE_VOLTAGE_DIVIDER inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement) { // In : 4095 * Vdiv / 1.1V - // Out: uint8_t: Vbat * 40 + // Out: uint8_t: Vbat * 50 // Vdiv = Vbat / 4.3 (typically) // 1.1 = ADC Vref const uint16_t adc_per_volt = (((uint16_t)ADC_44 << 4) - ((uint16_t)ADC_22 << 4)) - / (4 * (44-22)); + / (dV * (44-22)); uint8_t result = measurement / adc_per_volt; return result; } diff --git a/arch/attiny85.h b/arch/attiny85.h index 3f6ffcb..06a1061 100644 --- a/arch/attiny85.h +++ b/arch/attiny85.h @@ -53,7 +53,7 @@ inline void mcu_adc_off(); inline uint16_t mcu_adc_result(); -// return Volts * 40, range 0 to 6.375V +// return Volts * 50, range 0 to 5.10V #define voltage_raw2cooked mcu_vdd_raw2cooked inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement); inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement); diff --git a/arch/avr32dd20.c b/arch/avr32dd20.c index 2ac3526..3ada2ee 100644 --- a/arch/avr32dd20.c +++ b/arch/avr32dd20.c @@ -141,10 +141,10 @@ inline uint16_t mcu_adc_result() { inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) { // In : 65535 * (Vbat / 10) / 1.024V - // Out: uint8_t: Vbat * 40 + // Out: uint8_t: Vbat * 50 // (add 80 to round up near a boundary) - uint8_t vbat40 = (uint16_t)(measurement + 80) / 160; - return vbat40; + uint8_t vbat50 = (uint16_t)(measurement + 64) / 128; + return vbat50; } #if 0 diff --git a/arch/avr32dd20.h b/arch/avr32dd20.h index 09b4096..7d06863 100644 --- a/arch/avr32dd20.h +++ b/arch/avr32dd20.h @@ -71,7 +71,7 @@ inline uint16_t mcu_adc_result(); //inline uint16_t mcu_adc_result_temp(); //inline uint16_t mcu_adc_result_volts(); -// return Volts * 40, range 0 to 6.375V +// return Volts * 50, range 0 to 5.10V #define voltage_raw2cooked mcu_vdd_raw2cooked inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement); diff --git a/bin/make-release.sh b/bin/make-release.sh index da985d1..66216b5 100755 --- a/bin/make-release.sh +++ b/bin/make-release.sh @@ -18,9 +18,7 @@ cd "$REPODIR" #make # release name -#REV=$(date +'%Y-%m-%d') -REV=$(git describe --tags --dirty --match='r2*') -REV="${REV:1}" # convert 'r2023-...' to '2023-...' +REV=$(bin/version-string.sh git) # allow manually specifying a release name [[ -n "$1" ]] && REV="$1" @@ -209,8 +209,8 @@ static void ADC_voltage_handler() { #endif else measurement = adc_smooth[0]; - // convert raw ADC value to FSM voltage units: Volts * 40 - // 0 .. 200 = 0.0V .. 5.0V + // convert raw ADC value to FSM voltage units: Volts * 50 + // 0 .. 250 = 0.0V .. 5.0V voltage = voltage_raw2cooked(measurement) + (VOLTAGE_FUDGE_FACTOR << 1) #ifdef USE_VOLTAGE_CORRECTION @@ -392,46 +392,46 @@ static void ADC_temperature_handler() { #ifdef USE_BATTCHECK #ifdef BATTCHECK_4bars PROGMEM const uint8_t voltage_blinks[] = { - 4*30, - 4*35, - 4*38, - 4*40, - 4*42, - 255, + 30*dV, + 35*dV, + 38*dV, + 40*dV, + 42*dV, + 255, }; #endif #ifdef BATTCHECK_6bars PROGMEM const uint8_t voltage_blinks[] = { - 4*30, - 4*34, - 4*36, - 4*38, - 4*40, - 4*41, - 4*43, - 255, + 30*dV, + 34*dV, + 36*dV, + 38*dV, + 40*dV, + 41*dV, + 43*dV, + 255, }; #endif #ifdef BATTCHECK_8bars PROGMEM const uint8_t voltage_blinks[] = { - 4*30, - 4*33, - 4*35, - 4*37, - 4*38, - 4*39, - 4*40, - 4*41, - 4*42, - 255, + 30*dV, + 33*dV, + 35*dV, + 37*dV, + 38*dV, + 39*dV, + 40*dV, + 41*dV, + 42*dV, + 255, }; #endif void battcheck() { #ifdef BATTCHECK_VpT - blink_num(voltage / 4); + blink_num(voltage / dV); #ifdef USE_EXTRA_BATTCHECK_DIGIT - // 0 1 2 3 --> 0 2 5 7, representing x.x00 x.x25 x.x50 x.x75 - blink_num(((voltage % 4)<<1) + ((voltage % 4)>>1)); + // 0.02V precision, 0 1 2 3 4 remainder -> .00 .02 .04 .06 .08V + blink_num((voltage % dV) * (10/dV)); #endif #else uint8_t i; @@ -4,6 +4,10 @@ #pragma once +// voltage is 0.00V to 5.10V in 0.02V steps, from 0 to 255 +// so one deci-Volt is 5 steps +#define dV 5 + #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) // use raw value instead of lowpassed value for the next N measurements // (2 = 1 for voltage + 1 for temperature) @@ -15,13 +19,13 @@ volatile uint8_t adc_reset = 2; #ifndef VOLTAGE_WARNING_SECONDS #define VOLTAGE_WARNING_SECONDS 5 #endif -// low-battery threshold in volts * 10 +// low-battery threshold in volts * 50 #ifndef VOLTAGE_LOW -#define VOLTAGE_LOW (4*29) +#define VOLTAGE_LOW (29*dV) #endif // battery is low but not critical #ifndef VOLTAGE_RED -#define VOLTAGE_RED (4*33) +#define VOLTAGE_RED (33*dV) #endif // MCU sees voltage 0.X volts lower than actual, add X/2 to readings #ifndef VOLTAGE_FUDGE_FACTOR diff --git a/fsm/eeprom.c b/fsm/eeprom.c index 8f8bd67..436a736 100644 --- a/fsm/eeprom.c +++ b/fsm/eeprom.c @@ -14,7 +14,7 @@ uint8_t eeprom[EEPROM_BYTES]; #endif uint8_t load_eeprom() { - #if defined(LED_ENABLE_PIN) || defined(LED2_ENABLE_PIN) + #ifdef USE_EEP_DELAY delay_4ms(2); // wait for power to stabilize #endif @@ -32,7 +32,7 @@ uint8_t load_eeprom() { } void save_eeprom() { - #if defined(LED_ENABLE_PIN) || defined(LED2_ENABLE_PIN) + #ifdef USE_EEP_DELAY delay_4ms(2); // wait for power to stabilize #endif @@ -54,7 +54,7 @@ uint8_t eeprom_wl[EEPROM_WL_BYTES]; uint8_t * eep_wl_prev_offset; uint8_t load_eeprom_wl() { - #if defined(LED_ENABLE_PIN) || defined(LED2_ENABLE_PIN) + #ifdef USE_EEP_DELAY delay_4ms(2); // wait for power to stabilize #endif @@ -83,7 +83,7 @@ uint8_t load_eeprom_wl() { } void save_eeprom_wl() { - #if defined(LED_ENABLE_PIN) || defined(LED2_ENABLE_PIN) + #ifdef USE_EEP_DELAY delay_4ms(2); // wait for power to stabilize #endif diff --git a/fsm/eeprom.h b/fsm/eeprom.h index 1e10fd2..d5f0363 100644 --- a/fsm/eeprom.h +++ b/fsm/eeprom.h @@ -55,3 +55,11 @@ // if this marker isn't found, the eeprom is assumed to be blank #define EEP_MARKER 0b10100101 +// wait a few ms before eeprom operations, to wait for power to stabilize +// (otherwise reads or writes can get corrupt data) +// (not necessary on some hardware, +// but enabled by default when there's space) +#if defined(LED_ENABLE_PIN) || defined(LED2_ENABLE_PIN) || (ROM_SIZE > 10000) + #define USE_EEP_DELAY +#endif + diff --git a/fsm/ramping.c b/fsm/ramping.c index adc8acb..743e619 100644 --- a/fsm/ramping.c +++ b/fsm/ramping.c @@ -57,6 +57,11 @@ inline void set_level_aux_rgb_leds(uint8_t level) { void set_level(uint8_t level) { + #ifdef USE_RAMP_LEVEL_HARD_LIMIT + if (ramp_level_hard_limit && (level > ramp_level_hard_limit)) + level = ramp_level_hard_limit; + #endif + #ifdef USE_JUMP_START // maybe "jump start" the engine, if it's prone to slow starts // (pulse the output high for a moment to wake up the power regulator) diff --git a/fsm/ramping.h b/fsm/ramping.h index c4b7d48..f542bd2 100644 --- a/fsm/ramping.h +++ b/fsm/ramping.h @@ -10,6 +10,9 @@ uint8_t actual_level = 0; // the level used before actual uint8_t prev_level = 0; +#ifdef USE_RAMP_LEVEL_HARD_LIMIT +uint8_t ramp_level_hard_limit = 0; +#endif void set_level(uint8_t level); //void set_level_smooth(uint8_t level); diff --git a/hw/hank/emisar-d3aa/anduril.h b/hw/hank/emisar-d3aa/anduril.h new file mode 100644 index 0000000..e4e4458 --- /dev/null +++ b/hw/hank/emisar-d3aa/anduril.h @@ -0,0 +1,118 @@ +// Emisar D3AA config options for Anduril +// Copyright (C) 2023 thefreeman, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define HWDEF_H hank/emisar-d3aa/hwdef.h +#include "hank/anduril.h" + +// HPRsense : 4.2+0.3+20 = 24.5mR +// Vsense=42.46mV, R1= 165k +// LPRsense : 3R3 +// HDR ratio: 131.5 +// transition DAC level 20, ramp level 48 +// fifth power ramp 0.02mA to 2001mA + +#define RAMP_SIZE 150 + +// 4 ramp segments: +// - low 1.024V +// - low 2.5 V +// - high 1.024V +// - high 2.5 V +// HDR ratio: 131.5 +// PWM1: DAC Data +// 131.5 * 1024 * 2.5 = 336640 total dimming ratio +// ./bin/dac-scale.py $( ./bin/level_calc.py 4.106 1 150 7135 3 0.01 1400 --pwm 336640 | grep PWM1 | cut -d : -f 2- ) +// top level for each "gear": 30 40 119 150 +#define PWM1_LEVELS \ + 3, 5, 6, 9, 12, 16, 21, 28, 35, 44, 55, 68, 83, 101, 121, 144, 170, 200, 234, 271, 313, 360, 412, 470, 534, 603, 680, 764, 855, 954, \ + 434, 482, 534, 590, 650, 715, 784, 858, 938,1023, \ + 20, 21, 23, 25, 27, 30, 32, 35, 37, 40, 43, 46, 49, 53, 56, 60, 64, 68, 73, 77, 82, 87, 93, 98, 104, 110, 116, 123, 129, 137, 144, 152, 160, 168, 177, 185, 195, 204, 214, 225, 235, 247, 258, 270, 282, 295, 308, 322, 336, 350, 365, 381, 396, 413, 430, 447, 465, 484, 503, 522, 543, 563, 585, 607, 629, 653, 677, 701, 726, 752, 779, 806, 834, 863, 892, 923, 954, 985,1018, \ + 430, 444, 459, 473, 488, 504, 520, 536, 552, 569, 587, 604, 622, 641, 660, 679, 699, 719, 740, 761, 782, 804, 827, 849, 873, 897, 921, 946, 971, 997,1023 +#define PWM2_LEVELS \ + V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, \ + V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, \ + V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, V10, \ + V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25, V25 +#define MAX_1x7135 40 +#define HDR_ENABLE_LEVEL_MIN 41 + +#define DEFAULT_LEVEL 50 + +// no PWM, so MCU clock speed can be slow +#define HALFSPEED_LEVEL 41 +#define QUARTERSPEED_LEVEL 40 // seems to run fine at 10kHz/4, try reducing more? + +// should be about 300 mA or ~100 lm, +// to avoid overloading firmware flashing adapters +#define WEAK_BATTERY_TEST_MAX_LEVEL 75 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 // 50% power +// 10 30 [50] 70 90 110 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// 10 [40] 70 100 130 +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 130 +#define SIMPLE_UI_STEPS 5 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// thermal config + +// temperature limit +#define THERM_FASTER_LEVEL 130 // stop panicking at 50%/1A +#define MIN_THERM_STEPDOWN MAX_1x7135 + + +// UI + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +#define DEFAULT_2C_STYLE 1 // enable 2 click turbo + + +// AUX + +#define USE_BUTTON_LED + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the aux LEDs are front-facing, so turn them off while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + + +// Misc + +#define PARTY_STROBE_ONTIME 1 // slow down party strobe +#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses + +// smoother candle mode with bigger oscillations +#define CANDLE_AMPLITUDE 33 + +// added for convenience +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/emisar-d3aa/arch b/hw/hank/emisar-d3aa/arch new file mode 100644 index 0000000..bcf4552 --- /dev/null +++ b/hw/hank/emisar-d3aa/arch @@ -0,0 +1 @@ +avr32dd20 diff --git a/hw/hank/emisar-d3aa/hwdef.c b/hw/hank/emisar-d3aa/hwdef.c new file mode 100644 index 0000000..be673e1 --- /dev/null +++ b/hw/hank/emisar-d3aa/hwdef.c @@ -0,0 +1,202 @@ +// Emisar D3AA helper functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "fsm/chan-rgbaux.c" +#include "fsm/ramping.h" +#include "ui/anduril/misc.h" + +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); + + // 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; + + if (dac_next == dac_now) return true; // done + + return false; // not done yet +} + + +#ifdef USE_VOLTAGE_DIVIDER +uint8_t voltage_raw2cooked(uint16_t measurement) { + // In : 65535 * BATTLVL / 1.024V + // Out: uint8_t: Vbat * 50 + // BATTLVL = Vbat * (100.0/(330+100)) = Vbat / 4.3 + // So, Out = In * 4.3 / 1280 + uint8_t result = (uint32_t)(measurement + (65535 * 4 / 1024)) + * 43 / 12800; + return result; +} +#endif + +#ifdef USE_WEAK_BATTERY_PROTECTION +uint8_t quick_volt_measurement() { + // wait for next hardware measurement + irq_adc = 0; + while (! irq_adc) {} + uint16_t m = adc_raw[0]; + return voltage_raw2cooked(m); +} + +void detect_weak_battery() { + // guess at the cell strength with a load test... + // - 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; + + uint16_t resting, loaded; + + // baseline unloaded measurement + set_level(0); + for (uint8_t i=0; i<32; i++) { delay_zero(); } // wait about 10ms + //resting = voltage_raw2cooked(adc_smooth[0]); // probably not settled yet + resting = quick_volt_measurement(); + + // set thresholds per cell type + uint8_t sag_limit, crit_voltage; + if (resting > 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_TEST_MAX_LEVEL; l++) { + set_level(l); + loaded = quick_volt_measurement(); + int16_t sag = resting - loaded; + if ( (loaded <= crit_voltage) || (sag > sag_limit) ) { + // battery empty or weak + ramp_level_hard_limit = l; + break; + } + } + set_level(0); + + // 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 += 2; + + for (uint8_t i=0; i<extra_blinks; i++) { + delay_4ms(300/4); + blink_once(); + } + + #ifdef USE_WEAK_BATTERY_PROTECTION_READOUT + // this numeric display isn't really needed by default, + // but the code remains in case anyone wants to use it + if (ramp_level_hard_limit) { + delay_4ms(255); + // not booted far enough for this to work yet + //blink_num(ramp_level_hard_limit); + uint8_t tens, ones; + tens = ramp_level_hard_limit / 10; + ones = ramp_level_hard_limit % 10; + for (uint8_t i=0; i<tens; i++) { + delay_4ms(300/4); + blink_once(); + } + delay_4ms(600/4); + for (uint8_t i=0; i<ones; i++) { + delay_4ms(300/4); + blink_once(); + } + } + #endif // ifdef USE_WEAK_BATTERY_PROTECTION_READOUT + +} +#endif // ifdef USE_WEAK_BATTERY_PROTECTION + diff --git a/hw/hank/emisar-d3aa/hwdef.h b/hw/hank/emisar-d3aa/hwdef.h new file mode 100644 index 0000000..87740ba --- /dev/null +++ b/hw/hank/emisar-d3aa/hwdef.h @@ -0,0 +1,227 @@ +// hwdef for Emisar D3AA +// Copyright (C) 2023 thefreeman, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * NiMH/li-ion 9V2A boost driver based on MP3432 + * with high dynamic range and DAC control + front aux RGB and button LED + * + * Pin Name Function + * 1 PA4 e-switch + * 2 PA5 BATT LVL (voltage divider) + * 3 PA6 EN: boost enable + * 4 PA7 A : button LED + * 5 PC1 - + * 6 PC2 - + * 7 PC3 - + * 8 VDDIO2 (BATT+ via solder jumper) + * 9 PD4 IN- NFET: absorb startup flash + * 10 PD5 HDR: high/low Rsense range + * 11 PD6 DAC: control voltage out + * 12 PD7 + * 13 VDD VCC + * 14 GND GND + * 15 PF6 RESET + * 16 PF7 UPDI + * 17 PA0 B: aux blue + * 18 PA1 + * 19 PA2 G: aux green + * 20 PA3 R: aux red + * + * BST EN enable the boost regulator and Op-Amp + * DAC sets the current, max current depends on Vset voltage divider and Rsense + * HDR FET switches between high value Rsense (low current range, pin low), + * and low value Rsense (high current range, pin high) + * IN- NFET : pull up after BST enable to eliminate startup flash, pull down otherwise + * BATT LVL : Vbat * (100.0/(330+100)) + * VDDIO2 : can be connected to BATT+ with a solder jumper for VDDIO2 voltage sensing + * + */ + +#define HWDEF_C hank/emisar-d3aa/hwdef.c + +// allow using aux LEDs as extra channel modes +#include "fsm/chan-rgbaux.h" + +// channel modes: +// * 0. main LEDs +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 + + +// DAC max is 1023, Anduril is written for 255, so regulate at 4X speed +#undef GRADUAL_ADJUST_SPEED +#define GRADUAL_ADJUST_SPEED 4 + +#define PWM_BITS 16 // 10-bit DAC +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // main LED ramp +#define PWM1_GET(l) PWM_GET16(pwm1_levels, l) +#define PWM2_DATATYPE uint8_t // DAC Vref table +#define PWM2_GET(l) PWM_GET8(pwm2_levels, l) + +// main LED outputs +// (DAC_LVL + DAC_VREF + Vref values are defined in arch/*.h) + +// BST enable +#define BST_ENABLE_PIN PIN6_bp +#define BST_ENABLE_PORT PORTA_OUT + +// HDR +// turns on HDR FET for the high current range +#define HDR_ENABLE_PIN PIN5_bp +#define HDR_ENABLE_PORT PORTD_OUT + +// IN- NFET +// pull high to force output to zero to eliminate the startup flash +#define IN_NFET_DELAY_TIME 12 // (ms) +#define IN_NFET_ENABLE_PIN PIN4_bp +#define IN_NFET_ENABLE_PORT PORTD_OUT + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PIN4_bp +#define SWITCH_PORT VPORTA.IN +#define SWITCH_ISC_REG PORTA.PIN4CTRL +#define SWITCH_VECT PORTA_PORT_vect +#define SWITCH_INTFLG VPORTA.INTFLAGS +#endif + +#define DUAL_VOLTAGE_FLOOR (21*dV) // for AA/14500 boost drivers, don't indicate low voltage if below this level +#define DUAL_VOLTAGE_LOW_LOW ( 7*dV) // the lower voltage range's danger zone 0.7 volts (NiMH) +// comment out to use VDDIO2 instead of external voltage divider +#define USE_VOLTAGE_DIVIDER +#ifdef USE_VOLTAGE_DIVIDER + // AVR datasheet table 3.1 I/O Multiplexing, PA5 ADC0 = AIN25 + #define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN25_gc + // don't use the default VDD converter + // convert BATT LVL pin readings to FSM volt units + #undef voltage_raw2cooked + uint8_t voltage_raw2cooked(uint16_t measurement); +#else + // doesn't work on this hardware in AA mode + #define USE_VOLTAGE_VDDIO2 +#endif + +// Alkaline AA can't handle the power this light wants, +// so try to detect it and limit the maximum power +// (also helps protect firmware flashing adapters from overload) +#define USE_RAMP_LEVEL_HARD_LIMIT +#define USE_WEAK_BATTERY_PROTECTION +// define this next to the ramp table instead +//#define WEAK_BATTERY_TEST_MAX_LEVEL 75 // about 300 mA +#define WEAK_BATTERY_SAG_THRESHOLD_AA (3*4) // 0.3 V +#define WEAK_BATTERY_SAG_THRESHOLD_LIION (6*4) // 0.6 V + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 0 // using a PFET so no appreciable drop +#endif + +// this driver allows for aux LEDs under the optic +#define AUXLED_R_PIN PIN3_bp +#define AUXLED_G_PIN PIN2_bp +#define AUXLED_B_PIN PIN0_bp +#define AUXLED_RGB_PORT PORTA + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// A: button LED +#ifndef BUTTON_LED_PIN +#define BUTTON_LED_PIN PIN7_bp +#define BUTTON_LED_PORT PORTA +#endif + + +inline void hwdef_setup() { + + // TODO: for this DAC controlled-light, try to decrease the clock speed + // to reduce overall system power + mcu_clock_speed(); + + VPORTA.DIR = PIN0_bm // B + | PIN2_bm // G + | PIN3_bm // R + | PIN6_bm // EN + | PIN7_bm; // A + VPORTD.DIR = PIN4_bm // IN- NFET + | PIN5_bm // HDR + | PIN6_bm; // DAC + + // enable pullups on the unused and input pins to reduce power + //PORTA.PIN0CTRL = PORT_PULLUPEN_bm; // B + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + //PORTA.PIN2CTRL = PORT_PULLUPEN_bm; // G + //PORTA.PIN3CTRL = PORT_PULLUPEN_bm; // R + PORTA.PIN4CTRL = PORT_PULLUPEN_bm + | PORT_ISC_BOTHEDGES_gc; // e-switch + //PORTA.PIN5CTRL = PORT_PULLUPEN_bm; // BATT LVL + //PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // EN + //PORTA.PIN7CTRL = PORT_PULLUPEN_bm; // A + + //PORTC.PIN0CTRL = PORT_PULLUPEN_bm; // doesn't exist + PORTC.PIN1CTRL = PORT_PULLUPEN_bm; + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; + //PORTC.PIN4CTRL = PORT_PULLUPEN_bm; // doesn't exist + //PORTC.PIN5CTRL = PORT_PULLUPEN_bm; // doesn't exist + + //PORTD.PIN0CTRL = PORT_PULLUPEN_bm; // doesn't exist + //PORTD.PIN1CTRL = PORT_PULLUPEN_bm; // doesn't exist + //PORTD.PIN2CTRL = PORT_PULLUPEN_bm; // doesn't exist + //PORTD.PIN3CTRL = PORT_PULLUPEN_bm; // doesn't exist + //PORTD.PIN4CTRL = PORT_PULLUPEN_bm; // IN- NFET + //PORTD.PIN5CTRL = PORT_PULLUPEN_bm; // EN + // AVR datasheet 34.3.1 #2, DAC pin must have input disable set + PORTD.PIN6CTRL = PORT_ISC_INPUT_DISABLE_gc; // DAC + PORTD.PIN7CTRL = PORT_PULLUPEN_bm; + + // set up the DAC + // DAC ranges from 0V to (255 * Vref) / 256 + DAC_VREF = V10; + // TODO: try DAC_RUNSTDBY_bm for extra-efficient moon + DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; + DAC_LVL = 0; // turn off output at boot + // TODO: instead of enabling the DAC at boot, pull pin down + // to generate a zero without spending power on the DAC + // (and do this in set_level_zero() too) + +} + + +// set fuses, these carry over to the ELF file +// we need this for enabling BOD in Active Mode from the factory. +// settings can be verified / dumped from the ELF file using this +// command: avr-objdump -d -S -j .fuse anduril.elf +FUSES = { + .WDTCFG = FUSE_WDTCFG_DEFAULT, // Watchdog Configuration + + // enable BOD (continuous) in active mode + .BODCFG = ACTIVE_ENABLE_gc, // BOD Configuration + + .OSCCFG = FUSE_OSCCFG_DEFAULT, // Oscillator Configuration + .SYSCFG0 = FUSE_SYSCFG0_DEFAULT, // System Configuration 0 + + // enable MVIO because VDDIO2 pin isn't connected + // set startup time to 64ms to allow power to stabilize + .SYSCFG1 = MVSYSCFG_DUAL_gc | SUT_64MS_gc, + + .CODESIZE = FUSE_CODESIZE_DEFAULT, + .BOOTSIZE = FUSE_BOOTSIZE_DEFAULT, +}; + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/emisar-d3aa/model b/hw/hank/emisar-d3aa/model new file mode 100644 index 0000000..298b713 --- /dev/null +++ b/hw/hank/emisar-d3aa/model @@ -0,0 +1 @@ +0161 diff --git a/hw/sofirn/sp10-pro/hwdef.h b/hw/sofirn/sp10-pro/hwdef.h index f220318..43da9d0 100644 --- a/hw/sofirn/sp10-pro/hwdef.h +++ b/hw/sofirn/sp10-pro/hwdef.h @@ -62,8 +62,8 @@ enum CHANNEL_MODES { // Voltage divider battLVL #define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated -#define DUAL_VOLTAGE_FLOOR (4*21) // for AA/14500 boost drivers, don't indicate low voltage if below this level -#define DUAL_VOLTAGE_LOW_LOW (4*7) // the lower voltage range's danger zone 0.7 volts (NiMH) +#define DUAL_VOLTAGE_FLOOR (21*dV) // for AA/14500 boost drivers, don't indicate low voltage if below this level +#define DUAL_VOLTAGE_LOW_LOW ( 7*dV) // the lower voltage range's danger zone 0.7 volts (NiMH) #define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN9_gc // which ADC channel to read #undef voltage_raw2cooked diff --git a/hw/thefreeman/avr32dd20-devkit/hwdef.c b/hw/thefreeman/avr32dd20-devkit/hwdef.c index 460082f..5b534d2 100644 --- a/hw/thefreeman/avr32dd20-devkit/hwdef.c +++ b/hw/thefreeman/avr32dd20-devkit/hwdef.c @@ -119,13 +119,13 @@ bool gradual_tick_main(uint8_t gt) { uint8_t voltage_raw2cooked(uint16_t measurement) { // In : 65535 * BATTLVL / 1.024V - // Out: uint8_t: Vbat * 40 + // Out: uint8_t: Vbat * 50 // BATTLVL = Vbat * (100.0/(330+100)) = Vbat / 4.3 - // So, Out = In * 4.3 / 1600 + // So, Out = In * 4.3 / 1280 // (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 / 12880; return result; } diff --git a/hw/thefreeman/avr32dd20-devkit/hwdef.h b/hw/thefreeman/avr32dd20-devkit/hwdef.h index 231f74a..5015c24 100644 --- a/hw/thefreeman/avr32dd20-devkit/hwdef.h +++ b/hw/thefreeman/avr32dd20-devkit/hwdef.h @@ -109,8 +109,8 @@ enum CHANNEL_MODES { // AVR datasheet table 3.1 I/O Multiplexing, PA6 ADC0 = AIN26 #define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated #define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN26_gc -#define DUAL_VOLTAGE_FLOOR (4*21) // for AA/14500 boost drivers, don't indicate low voltage if below this level -#define DUAL_VOLTAGE_LOW_LOW (4*7) // the lower voltage range's danger zone 0.7 volts (NiMH) +#define DUAL_VOLTAGE_FLOOR (21*dV) // for AA/14500 boost drivers, don't indicate low voltage if below this level +#define DUAL_VOLTAGE_LOW_LOW ( 7*dV) // the lower voltage range's danger zone 0.7 volts (NiMH) // don't use the default VDD converter // convert BATT LVL pin readings to FSM volt units #undef voltage_raw2cooked @@ -155,6 +155,7 @@ inline void hwdef_setup() { //PORTA.PIN2CTRL = PORT_PULLUPEN_bm; // B #ifdef USE_BST_BYPASS PORTA.PIN3CTRL = PORT_PULLUPEN_bm; // BBY + BST_BYPASS_PORT |= (1 << BST_BYPASS_PIN); #endif PORTA.PIN4CTRL = PORT_PULLUPEN_bm; PORTA.PIN5CTRL = PORT_PULLUPEN_bm; diff --git a/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h index bc1d9a7..1c6e6be 100644 --- a/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h +++ b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h @@ -94,8 +94,8 @@ enum CHANNEL_MODES { // Voltage divider battLVL #define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated #define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN2_gc // which ADC channel to read -#define DUAL_VOLTAGE_FLOOR (4*21) // for AA/14500 boost drivers, don't indicate low voltage if below this level -#define DUAL_VOLTAGE_LOW_LOW (4*7) // the lower voltage range's danger zone 0.7 volts (NiMH) +#define DUAL_VOLTAGE_FLOOR (21*dV) // for AA/14500 boost drivers, don't indicate low voltage if below this level +#define DUAL_VOLTAGE_LOW_LOW ( 7*dV) // the lower voltage range's danger zone 0.7 volts (NiMH) // don't use the default VDD converter #undef voltage_raw2cooked #define voltage_raw2cooked mcu_vdivider_raw2cooked diff --git a/ui/anduril/anduril.c b/ui/anduril/anduril.c index 1cdb8d0..a7e4199 100644 --- a/ui/anduril/anduril.c +++ b/ui/anduril/anduril.c @@ -121,7 +121,7 @@ #include "anduril/lockout-mode.h" #endif -#ifdef USE_MOMENTARY_MODE +#if (defined(USE_MOMENTARY_MODE) || defined(USE_TACTICAL_MODE)) #include "anduril/momentary-mode.h" #endif @@ -189,7 +189,7 @@ #include "anduril/lockout-mode.c" #endif -#ifdef USE_MOMENTARY_MODE +#if (defined(USE_MOMENTARY_MODE) || defined(USE_TACTICAL_MODE)) #include "anduril/momentary-mode.c" #endif @@ -225,8 +225,16 @@ void setup() { // regular e-switch light, no hard clicky power button - // blink at power-on to let user know power is connected - blink_once(); + #ifdef USE_WEAK_BATTERY_PROTECTION + // try to measure the battery strength + // (must be done *before* factory reset, + // because reset tries to use full power, + // and a weak battery can't do that) + detect_weak_battery(); + #else + // blink at power-on to let user know power is connected + blink_once(); + #endif #ifdef USE_FACTORY_RESET if (button_is_pressed()) @@ -293,10 +301,12 @@ void loop() { #ifdef USE_STROBE_STATE else if ((state == strobe_state) - #ifdef USE_MOMENTARY_MODE + #if defined(USE_MOMENTARY_MODE) || defined(USE_TACTICAL_MODE) // also handle momentary strobes - || (( - (state == momentary_state) + || ((0 + #ifdef USE_MOMENTARY_MODE + || (state == momentary_state) + #endif #ifdef USE_TACTICAL_MODE || (state == tactical_state) #endif diff --git a/ui/anduril/aux-leds.c b/ui/anduril/aux-leds.c index fd184fc..7356666 100644 --- a/ui/anduril/aux-leds.c +++ b/ui/anduril/aux-leds.c @@ -58,27 +58,27 @@ void indicator_led_update(uint8_t mode, uint8_t tick) { uint8_t voltage_to_rgb() { static const uint8_t levels[] = { // voltage, color - 0, 0, // black + 0, 0, // black #ifdef DUAL_VOLTAGE_FLOOR // AA / NiMH voltages - 4* 9, 1, // R - 4*10, 2, // R+G - 4*11, 3, // G - 4*12, 4, // G+B - 4*13, 5, // B - 4*14, 6, // R + B - 4*16, 7, // R+G+B - 4*20, 0, // black + 9*dV, 1, // R + 10*dV, 2, // R+G + 11*dV, 3, // G + 12*dV, 4, // G+B + 13*dV, 5, // B + 14*dV, 6, // R + B + 16*dV, 7, // R+G+B + 20*dV, 0, // black #endif // li-ion voltages - 4*29, 1, // R - 4*33, 2, // R+G - 4*35, 3, // G - 4*37, 4, // G+B - 4*39, 5, // B - 4*41, 6, // R + B - 4*44, 7, // R+G+B // skip; looks too similar to G+B - 255, 7, // R+G+B + 29*dV, 1, // R + 33*dV, 2, // R+G + 35*dV, 3, // G + 37*dV, 4, // G+B + 39*dV, 5, // B + 41*dV, 6, // R + B + 44*dV, 7, // R+G+B // skip; looks too similar to G+B + 255, 7, // R+G+B }; uint8_t volts = voltage; //if (volts < VOLTAGE_LOW) return 0; @@ -151,11 +151,8 @@ void rgb_led_update(uint8_t mode, uint16_t arg) { else { // voltage // show actual voltage while asleep... if (go_to_standby) { - actual_color = voltage_to_rgb(); // choose a color based on battery voltage - //if (volts >= 38) actual_color = pgm_read_byte(colors + 4); - //else if (volts >= 33) actual_color = pgm_read_byte(colors + 2); - //else actual_color = pgm_read_byte(colors + 0); + actual_color = voltage_to_rgb(); } // ... but during preview, cycle colors quickly else { diff --git a/ui/anduril/lockout-mode.c b/ui/anduril/lockout-mode.c index 6f85ca9..c3f82ea 100644 --- a/ui/anduril/lockout-mode.c +++ b/ui/anduril/lockout-mode.c @@ -26,11 +26,11 @@ uint8_t lockout_state(Event event, uint16_t arg) { if (cfg.manual_memory) lvl = cfg.manual_memory; #endif } - set_level(lvl); + off_state_set_level(lvl); } // button was released else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - set_level(0); + off_state_set_level(0); } #endif // ifdef USE_MOON_DURING_LOCKOUT_MODE diff --git a/ui/anduril/strobe-modes.c b/ui/anduril/strobe-modes.c index ccc4aa0..38e4dca 100644 --- a/ui/anduril/strobe-modes.c +++ b/ui/anduril/strobe-modes.c @@ -13,7 +13,7 @@ uint8_t strobe_state(Event event, uint16_t arg) { // 'st' reduces ROM size slightly strobe_mode_te st = current_strobe_type; - #ifdef USE_MOMENTARY_MODE + #if defined(USE_MOMENTARY_MODE) || defined(USE_TACTICAL_MODE) momentary_mode = 1; // 0 = ramping, 1 = strobes #endif diff --git a/ui/anduril/tactical-mode.c b/ui/anduril/tactical-mode.c index 5acd902..002f917 100644 --- a/ui/anduril/tactical-mode.c +++ b/ui/anduril/tactical-mode.c @@ -68,6 +68,11 @@ uint8_t tactical_state(Event event, uint16_t arg) { return lockout_state(event, arg); } + // handle 3C here to prevent changing channel modes unintentionally + if (event == EV_3clicks) { + return EVENT_HANDLED; + } + // 6 clicks: exit and turn off else if (event == EV_6clicks) { blink_once(); |
