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/anduril.h | 118 +++++++++++++++++++++++ hw/hank/emisar-d3aa/arch | 1 + hw/hank/emisar-d3aa/hwdef.c | 118 +++++++++++++++++++++++ hw/hank/emisar-d3aa/hwdef.h | 212 ++++++++++++++++++++++++++++++++++++++++++ hw/hank/emisar-d3aa/model | 1 + 5 files changed, 450 insertions(+) create mode 100644 hw/hank/emisar-d3aa/anduril.h create mode 100644 hw/hank/emisar-d3aa/arch create mode 100644 hw/hank/emisar-d3aa/hwdef.c create mode 100644 hw/hank/emisar-d3aa/hwdef.h create mode 100644 hw/hank/emisar-d3aa/model diff --git a/hw/hank/emisar-d3aa/anduril.h b/hw/hank/emisar-d3aa/anduril.h new file mode 100644 index 0000000..3d65dc5 --- /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 + +// HPRsense : 4.2+0.3+20 = 24.5mR +// Vsense=42.46mV, R1= 191k +// LPRsense : 2R +// 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 +// (level_calc.py 4.3287 1 150 7135 5 0.01 1400 --pwm 400000) +// (top level for each "gear": 30 40 120 150) +#define PWM1_LEVELS \ + 3,4,5,7,9,12,15,19,24,29,35,43,51,61,72,84,99,115,133,153,176,202,230,262,297,335,377,424,475,531,592,658,730,809,894,986, \ + 444,488,535,586,641,700,763,830,903,980,1023, \ + 20,22,24,26,28,30,32,35,37,40,43,46,49,52,56,60,63,68,72,77,81,86,92,97,103,109,115,122,129,136,143,151,159,168,177,186,196,206,216,227,239,250,263,275,289,302,316,331,346,362,379,396,413,432,450,470,490,511,532,555,578,602,626,651,678,705,733,761,791,821,853,885,919,953,989, \ + 420,435,451,467,484,501,519,537,556,575,595,615,636,657,679,702,725,749,773,798,824,850,877,905,933,962,992,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,V10,V10,V10,V10,V10,V10, \ + V25,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, \ + 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 47 +#define HDR_ENABLE_LEVEL_MIN 48 +#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 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 119 // 35% / 0.7A / 700 lm +// 1 22 [44] 65 87 108 130 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL 119 +#define RAMP_DISCRETE_STEPS 7 + +// 20 [45] 70 95 120 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 129 // 50% / ~1A / ~860 lm +#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 129 // stop panicking at 50%/1A +#define MIN_THERM_STEPDOWN MAX_1x7135 + + +// UI + +#define SIMPLE_UI_ACTIVE 0 // advanced UI by default, because prototype + +// allow Aux Config and Strobe Modes in Simple UI +//#define USE_EXTENDED_SIMPLE_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 +#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 40 + +// enable 13H factory reset so it can be used on tail e-switch lights +#define USE_SOFT_FACTORY_RESET + +// TODO: disable lowpass while asleep; the MCU oversamples + 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..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; +} + diff --git a/hw/hank/emisar-d3aa/hwdef.h b/hw/hank/emisar-d3aa/hwdef.h new file mode 100644 index 0000000..8677609 --- /dev/null +++ b/hw/hank/emisar-d3aa/hwdef.h @@ -0,0 +1,212 @@ +// 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 + + +#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 + +// TODO: define stuff for the voltage divider +// AVR datasheet table 3.1 I/O Multiplexing, PA5 ADC0 = AIN25 +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated +#define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN25_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) +// 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); + + +// 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..1d00063 --- /dev/null +++ b/hw/hank/emisar-d3aa/model @@ -0,0 +1 @@ +0144 -- cgit v1.2.3 From 4e72ee28f892b2f473545bc783b7efaf3a4503b8 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 30 Nov 2023 13:57:28 -0700 Subject: emisar-d3aa: new model number, since this is a new product line The 0144 model number is reserved for the successor to the Meteor M44. This is Hank's first AA light, so it's assigned as 0161: - 01: Emisar - 6: product line 6 - 1: model 1 --- hw/hank/emisar-d3aa/model | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/hank/emisar-d3aa/model b/hw/hank/emisar-d3aa/model index 1d00063..298b713 100644 --- a/hw/hank/emisar-d3aa/model +++ b/hw/hank/emisar-d3aa/model @@ -1 +1 @@ -0144 +0161 -- 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 ++ hw/hank/emisar-d3aa/hwdef.h | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) 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 diff --git a/hw/hank/emisar-d3aa/hwdef.h b/hw/hank/emisar-d3aa/hwdef.h index 8677609..1001e5d 100644 --- a/hw/hank/emisar-d3aa/hwdef.h +++ b/hw/hank/emisar-d3aa/hwdef.h @@ -97,17 +97,20 @@ enum CHANNEL_MODES { #define SWITCH_INTFLG VPORTA.INTFLAGS #endif -// TODO: define stuff for the voltage divider -// AVR datasheet table 3.1 I/O Multiplexing, PA5 ADC0 = AIN25 -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated -#define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN25_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) -// 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); - +// 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 + #define USE_VOLTAGE_VDDIO2 +#endif // average drop across diode on this hardware #ifndef VOLTAGE_FUDGE_FACTOR -- cgit v1.2.3 From 737062e5d0e47344ceff3930a27a3fb409aa8cca Mon Sep 17 00:00:00 2001 From: jim-p Date: Sun, 21 Jan 2024 20:43:35 -0500 Subject: Add Fireflies PL47G2-219 variant. Same as PL47G2 but with FET PWM levels safe for 219 emitters. Same as difference between PL47 and PL47-219. PL47G2 base allows low mode for aux LEDs, PL47 does not. --- MODELS | 1 + hw/fireflies/pl47g2/219/anduril.h | 13 +++++++++++++ hw/fireflies/pl47g2/219/model | 1 + 3 files changed, 15 insertions(+) create mode 100644 hw/fireflies/pl47g2/219/anduril.h create mode 100644 hw/fireflies/pl47g2/219/model diff --git a/MODELS b/MODELS index 0a8c244..6dcbc0a 100644 --- a/MODELS +++ b/MODELS @@ -55,6 +55,7 @@ Model MCU Name 0421 attiny85 fireflies-pl47 0422 attiny85 fireflies-pl47-219 0423 attiny85 fireflies-pl47g2 +0424 attiny85 fireflies-pl47g2-219 0441 attiny85 fireflies-e01 0511 attiny85 mateminco-mf01s 0521 attiny85 mateminco-mf01-mini diff --git a/hw/fireflies/pl47g2/219/anduril.h b/hw/fireflies/pl47g2/219/anduril.h new file mode 100644 index 0000000..98cd6ea --- /dev/null +++ b/hw/fireflies/pl47g2/219/anduril.h @@ -0,0 +1,13 @@ +// Fireflies PL47G2-219B config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as PL47G2 but with FET modes limited to 67% power +// to avoid destroying the LEDs +#include "fireflies/pl47g2/anduril.h" + +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,19,20,22,23,25,27,28,30,31,33,35,37,39,41,43,45,47,50,52,55,57,60,63,65,68,71,74,77,80,83,87,90,93,97,101,105,108,112,116,121,125,129,134,139,143,148,153,159,164,169 diff --git a/hw/fireflies/pl47g2/219/model b/hw/fireflies/pl47g2/219/model new file mode 100644 index 0000000..6e6161f --- /dev/null +++ b/hw/fireflies/pl47g2/219/model @@ -0,0 +1 @@ +0424 -- cgit v1.2.3 From afcacfb02c59b2ce2c9a9eeb6ec38aa08588f6dd Mon Sep 17 00:00:00 2001 From: SiteRelEnby Date: Fri, 2 Feb 2024 18:06:03 -0600 Subject: Bugfix: Tactical mode has a dependency on momentary mode Use case 1: Updating a t85 light, where tactical mode is useful but takes some squeezing, and momentary is less useful Use case 2: Custom builds for people who don't like/want momentary mode --- ui/anduril/anduril.c | 4 ++-- ui/anduril/strobe-modes.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/anduril/anduril.c b/ui/anduril/anduril.c index 1cdb8d0..7557bf7 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 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 -- cgit v1.2.3 From cc16e398a990ab8a086095ef42a72c268b5fa474 Mon Sep 17 00:00:00 2001 From: SiteRelEnby Date: Wed, 14 Feb 2024 14:33:46 -0600 Subject: Bugfix: Prevent switching channel modes when in tactical mode Fixes issue https://github.com/ToyKeeper/anduril/issues/40 --- ui/anduril/tactical-mode.c | 5 +++++ 1 file changed, 5 insertions(+) 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(); -- cgit v1.2.3 From cad2501b91a6879d51e7f6fb715d1c6c459665f9 Mon Sep 17 00:00:00 2001 From: SiteRelEnby Date: Thu, 29 Feb 2024 19:10:52 -0600 Subject: Allow manually running GitHub actions workflows https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow --- .github/workflows/compile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" ] -- 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/anduril.h | 4 ++-- hw/hank/emisar-d3aa/hwdef.c | 2 +- hw/hank/emisar-d3aa/hwdef.h | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/hank/emisar-d3aa/anduril.h b/hw/hank/emisar-d3aa/anduril.h index 3d65dc5..ca2ed65 100644 --- a/hw/hank/emisar-d3aa/anduril.h +++ b/hw/hank/emisar-d3aa/anduril.h @@ -6,8 +6,8 @@ #define HWDEF_H hank/emisar-d3aa/hwdef.h // HPRsense : 4.2+0.3+20 = 24.5mR -// Vsense=42.46mV, R1= 191k -// LPRsense : 2R +// 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 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 diff --git a/hw/hank/emisar-d3aa/hwdef.h b/hw/hank/emisar-d3aa/hwdef.h index 1001e5d..a104fc2 100644 --- a/hw/hank/emisar-d3aa/hwdef.h +++ b/hw/hank/emisar-d3aa/hwdef.h @@ -100,7 +100,7 @@ enum CHANNEL_MODES { #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) // comment out to use VDDIO2 instead of external voltage divider -//#define USE_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 @@ -109,6 +109,7 @@ enum CHANNEL_MODES { #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 -- 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/anduril.h | 60 ++++++++++++++++++++----------------------- hw/hank/emisar-d3aa/hwdef.c | 14 +++------- hw/hank/emisar-d3aa/hwdef.h | 7 ++--- 3 files changed, 35 insertions(+), 46 deletions(-) diff --git a/hw/hank/emisar-d3aa/anduril.h b/hw/hank/emisar-d3aa/anduril.h index ca2ed65..a80557d 100644 --- a/hw/hank/emisar-d3aa/anduril.h +++ b/hw/hank/emisar-d3aa/anduril.h @@ -1,9 +1,10 @@ -// Emisar D3aa config options for Anduril +// 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 @@ -21,36 +22,38 @@ // - high 2.5 V // HDR ratio: 131.5 // PWM1: DAC Data -// (level_calc.py 4.3287 1 150 7135 5 0.01 1400 --pwm 400000) -// (top level for each "gear": 30 40 120 150) +// 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,4,5,7,9,12,15,19,24,29,35,43,51,61,72,84,99,115,133,153,176,202,230,262,297,335,377,424,475,531,592,658,730,809,894,986, \ - 444,488,535,586,641,700,763,830,903,980,1023, \ - 20,22,24,26,28,30,32,35,37,40,43,46,49,52,56,60,63,68,72,77,81,86,92,97,103,109,115,122,129,136,143,151,159,168,177,186,196,206,216,227,239,250,263,275,289,302,316,331,346,362,379,396,413,432,450,470,490,511,532,555,578,602,626,651,678,705,733,761,791,821,853,885,919,953,989, \ - 420,435,451,467,484,501,519,537,556,575,595,615,636,657,679,702,725,749,773,798,824,850,877,905,933,962,992,1023 + 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,V10,V10,V10,V10,V10,V10, \ - V25,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, \ - 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 47 -#define HDR_ENABLE_LEVEL_MIN 48 + 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 +#define QUARTERSPEED_LEVEL 40 // seems to run fine at 10kHz/4, try reducing more? #define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 119 // 35% / 0.7A / 700 lm -// 1 22 [44] 65 87 108 130 -#define RAMP_DISCRETE_FLOOR 1 -#define RAMP_DISCRETE_CEIL 119 +#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 -// 20 [45] 70 95 120 -#define SIMPLE_UI_FLOOR 20 -#define SIMPLE_UI_CEIL 129 // 50% / ~1A / ~860 lm +// 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 @@ -61,17 +64,12 @@ // thermal config // temperature limit -#define THERM_FASTER_LEVEL 129 // stop panicking at 50%/1A +#define THERM_FASTER_LEVEL 130 // stop panicking at 50%/1A #define MIN_THERM_STEPDOWN MAX_1x7135 // UI -#define SIMPLE_UI_ACTIVE 0 // advanced UI by default, because prototype - -// allow Aux Config and Strobe Modes in Simple UI -//#define USE_EXTENDED_SIMPLE_UI - // Allow 3C in Simple UI for switching between smooth and stepped ramping #define USE_SIMPLE_UI_RAMPING_TOGGLE @@ -88,7 +86,7 @@ // show each channel while it scroll by in the menu #define USE_CONFIG_COLORS -// blink numbers on the main LEDs by default +// 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 @@ -109,10 +107,8 @@ #define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses // smoother candle mode with bigger oscillations -#define CANDLE_AMPLITUDE 40 +#define CANDLE_AMPLITUDE 33 -// enable 13H factory reset so it can be used on tail e-switch lights +// added for convenience #define USE_SOFT_FACTORY_RESET -// TODO: disable lowpass while asleep; the MCU oversamples - 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; diff --git a/hw/hank/emisar-d3aa/hwdef.h b/hw/hank/emisar-d3aa/hwdef.h index a104fc2..56dd061 100644 --- a/hw/hank/emisar-d3aa/hwdef.h +++ b/hw/hank/emisar-d3aa/hwdef.h @@ -3,7 +3,7 @@ // 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 * @@ -26,7 +26,7 @@ * 16 PF7 UPDI * 17 PA0 B: aux blue * 18 PA1 - * 19 PA2 G: aux green + * 19 PA2 G: aux green * 20 PA3 R: aux red * * BST EN enable the boost regulator and Op-Amp @@ -36,7 +36,7 @@ * 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 @@ -59,6 +59,7 @@ enum CHANNEL_MODES { #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 -- cgit v1.2.3 From ef7581e825a851de85918d4c3a3146ba767403a4 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sat, 23 Mar 2024 22:30:05 -0600 Subject: fixed inaccurate comment (thanks to xikteny for spotting it) --- fsm/adc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsm/adc.h b/fsm/adc.h index 02e33f8..f66bf34 100644 --- a/fsm/adc.h +++ b/fsm/adc.h @@ -15,7 +15,7 @@ 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 * 40 #ifndef VOLTAGE_LOW #define VOLTAGE_LOW (4*29) #endif -- 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) --- fsm/ramping.c | 5 ++++ fsm/ramping.h | 3 +++ hw/hank/emisar-d3aa/hwdef.c | 64 +++++++++++++++++++++++++++++++++++++++++++++ hw/hank/emisar-d3aa/hwdef.h | 9 +++++++ ui/anduril/anduril.c | 12 +++++++-- 5 files changed, 91 insertions(+), 2 deletions(-) diff --git a/fsm/ramping.c b/fsm/ramping.c index adc8acb..63ab399 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 (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..6fe87fe 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 = RAMP_SIZE; +#endif void set_level(uint8_t level); //void set_level_smooth(uint8_t level); 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 ramp_level_hard_limit) + if (ramp_level_hard_limit && (level > ramp_level_hard_limit)) level = ramp_level_hard_limit; #endif diff --git a/fsm/ramping.h b/fsm/ramping.h index 6fe87fe..f542bd2 100644 --- a/fsm/ramping.h +++ b/fsm/ramping.h @@ -11,7 +11,7 @@ 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 = RAMP_SIZE; +uint8_t ramp_level_hard_limit = 0; #endif void set_level(uint8_t level); diff --git a/hw/hank/emisar-d3aa/hwdef.c b/hw/hank/emisar-d3aa/hwdef.c index f6cf94e..d6cb2fd 100644 --- a/hw/hank/emisar-d3aa/hwdef.c +++ b/hw/hank/emisar-d3aa/hwdef.c @@ -114,16 +114,11 @@ uint8_t voltage_raw2cooked(uint16_t measurement) { #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; + // 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() { @@ -133,44 +128,65 @@ void detect_weak_battery() { // - determine how much to limit power // - 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_level(WEAK_BATTERY_CHECK_LEVEL); - loaded = quick_volt_measurement(); + // 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; + } + } 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); diff --git a/hw/hank/emisar-d3aa/hwdef.h b/hw/hank/emisar-d3aa/hwdef.h index 7cd2849..7fbffbe 100644 --- a/hw/hank/emisar-d3aa/hwdef.h +++ b/hw/hank/emisar-d3aa/hwdef.h @@ -119,8 +119,9 @@ enum CHANNEL_MODES { // (also helps protect firmware flashing adapters from overload) #define USE_RAMP_LEVEL_HARD_LIMIT #define USE_WEAK_BATTERY_PROTECTION -#define WEAK_BATTERY_TEST_MAX_LEVEL 75 // about 300 mA -#define WEAK_BATTERY_SAG_THRESHOLD (5*4) // 0.5 V +#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 -- cgit v1.2.3 From 7b49797be1da97f54e9df3a1b0342ab20d7f02d5 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Wed, 27 Mar 2024 07:13:26 -0600 Subject: increased voltage precision from 0.025V to 0.02V (so 0 to 255 now goes from 0.00V to 5.10V) --- arch/attiny1616.c | 12 ++--- arch/attiny1616.h | 2 +- arch/attiny1634.c | 12 ++--- arch/attiny1634.h | 2 +- arch/attiny85.c | 10 ++-- arch/attiny85.h | 2 +- arch/avr32dd20.c | 6 +-- arch/avr32dd20.h | 2 +- fsm/adc.c | 58 +++++++++++----------- fsm/adc.h | 10 ++-- hw/sofirn/sp10-pro/hwdef.h | 4 +- hw/thefreeman/avr32dd20-devkit/hwdef.c | 6 +-- hw/thefreeman/avr32dd20-devkit/hwdef.h | 4 +- .../boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h | 4 +- ui/anduril/aux-leds.c | 39 +++++++-------- 15 files changed, 87 insertions(+), 86 deletions(-) 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/fsm/adc.c b/fsm/adc.c index fbe84a1..e0bacb6 100644 --- a/fsm/adc.c +++ b/fsm/adc.c @@ -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; diff --git a/fsm/adc.h b/fsm/adc.h index f66bf34..5dec6c5 100644 --- a/fsm/adc.h +++ b/fsm/adc.h @@ -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 * 40 +// 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/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 38b508d..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 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/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 { -- cgit v1.2.3 From 3cabd5acdddb7ace17b12fe1df9b6281f63b7905 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 28 Mar 2024 08:19:17 -0600 Subject: fixed Tactical Mode's strobes when Momentary Mode not enabled --- ui/anduril/anduril.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/anduril/anduril.c b/ui/anduril/anduril.c index 7557bf7..4378816 100644 --- a/ui/anduril/anduril.c +++ b/ui/anduril/anduril.c @@ -293,10 +293,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 -- cgit v1.2.3 From cc228bd519d95a24a4ca17f491885f5539b6630b Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 29 Mar 2024 07:54:24 -0600 Subject: use smooth steps in lockout mode, if enabled Based on [SammysHP's patch](https://github.com/ToyKeeper/anduril/pull/18), but only for lockout mode, not tactical. --- ui/anduril/lockout-mode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 -- 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/anduril.h | 4 ++++ hw/hank/emisar-d3aa/hwdef.c | 16 ++++++++++++---- hw/hank/emisar-d3aa/hwdef.h | 3 ++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/hw/hank/emisar-d3aa/anduril.h b/hw/hank/emisar-d3aa/anduril.h index a80557d..e4e4458 100644 --- a/hw/hank/emisar-d3aa/anduril.h +++ b/hw/hank/emisar-d3aa/anduril.h @@ -44,6 +44,10 @@ #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 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 0) or low } bool gradual_tick_null(uint8_t gt) { return true; } // do nothing diff --git a/fsm/chan-rgbaux.c b/fsm/chan-rgbaux.c index 19d18a6..a66c29e 100644 --- a/fsm/chan-rgbaux.c +++ b/fsm/chan-rgbaux.c @@ -4,31 +4,31 @@ #pragma once void set_level_auxred(uint8_t level) { - rgb_led_set(!(!(level)) * 0b000010); // red, high (or off) + rgb_led_set(0b000001 << !(!(level))); // red, high (level > 0) or low } void set_level_auxyel(uint8_t level) { - rgb_led_set(!(!(level)) * 0b001010); // red+green, high (or off) + rgb_led_set(0b000101 << !(!(level))); // red+green, high (level > 0) or low } void set_level_auxgrn(uint8_t level) { - rgb_led_set(!(!(level)) * 0b001000); // green, high (or off) + rgb_led_set(0b000100 << !(!(level))); // green, high (level > 0) or low } void set_level_auxcyn(uint8_t level) { - rgb_led_set(!(!(level)) * 0b101000); // green+blue, high (or off) + rgb_led_set(0b010100 << !(!(level))); // green+blue, high (level > 0) or low } void set_level_auxblu(uint8_t level) { - rgb_led_set(!(!(level)) * 0b100000); // blue, high (or off) + rgb_led_set(0b010000 << !(!(level))); // blue, high (level > 0) or low } void set_level_auxprp(uint8_t level) { - rgb_led_set(!(!(level)) * 0b100010); // red+blue, high (or off) + rgb_led_set(0b010001 << !(!(level))); // red+blue, high (level > 0) or low } void set_level_auxwht(uint8_t level) { - rgb_led_set(!(!(level)) * 0b101010); // red+green+blue, high (or off) + rgb_led_set(0b010101 << !(!(level))); // red+green+blue, high (level > 0) or low } bool gradual_tick_null(uint8_t gt) { return true; } // do nothing -- cgit v1.2.3 From 03f263e7e0fe2ed07b90a40ed4c8a6008cbad2c0 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 19 Apr 2024 03:42:45 -0600 Subject: Forgot to update model count after the last couple additions --- MODELS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODELS b/MODELS index 781d308..2a6e114 100644 --- a/MODELS +++ b/MODELS @@ -1,4 +1,4 @@ -Models: 75 +Models: 78 Model MCU Name ----- --- ---- -- cgit v1.2.3 From 5dc58cdce0ca8dbae1c986ff91b04316a6ac7df1 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sat, 20 Apr 2024 08:03:58 -0600 Subject: added change log for 2024-04-20 release --- ChangeLog.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index a70a398..4a71e4e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -14,6 +14,39 @@ formats: # Next +# 2024-04-20 + +General: + +- Smooth steps now work in Lockout Mode, if enabled. +- Made eeprom access more reliable, by waiting for power to stabilize before + reads and writes. +- Increased voltage resolution to 0.02V. It can, for example, read out 1.20V, + 1.22V, 1.24V, 1.26V, 1.28V, or 1.30V. +- Added weak battery detection, to limit power on alkaline, on empty cells, and + while powered by a flashing adapter. Should prevent cell overload and magic + smoke. Weak battery mode blinks 3X at boot. (d3aa only, so far) +- Made dark "blip"s work better on some types of regulators. +- Fixed bug: 3C in Tactical Mode would change the channel when it shouldn't. +- Fixed bug: Aux channels were off/off/high for levels 0/1/2. Now uses + off/low/high. +- Misc improvements to the build process. Can build with Tactical Mode without + Momentary Mode. Can build with newer avr-libc. Version strings calculated + better now. Github actions can be run manually. +- Documentation updates. + +New lights: + +- Added &hank-emisar-2ch-fet-joined, for the lighted-switch variant of the D4S. + It uses a 2-channel driver with only 1 channel of LEDs. (0137) +- Added &hank-emisar-d3aa, the first "3rd generation" torch (avr32dd20, + thefreeman HDR driver). (0161) +- Added &fireflies-pl47g2-219, a reduced-power version of the PL47G2. + +Hardware-specific changes: + +- &lumintop-fw3x-lume1: Reduced visible pulsing on low modes. + # 2023-12-03 This release is somewhat higher risk than usual, because so many large things -- cgit v1.2.3 From 72128b62aae51c5747c8cc21e5113e145c66b9c7 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sat, 20 Apr 2024 08:19:58 -0600 Subject: include hardware-specific readme files in the release .zip --- bin/make-release.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/make-release.sh b/bin/make-release.sh index 66216b5..6618005 100755 --- a/bin/make-release.sh +++ b/bin/make-release.sh @@ -40,6 +40,12 @@ cp -a \ docs/which-hex-file.md \ "$RELDIR" +# add hardware-specific docs +for f in $(find hw -name '*.md') ; do + d=$(echo "$f" | sed 's/^hw.//; s|/readme||i; s/^/readme./; s|/|-|g;') + cp -a "$f" "$RELDIR/$d" +done + # add the .hex files rename -f 's|hex/anduril.|'"$RELDIR/hex/$RELNAME"'.|;' hex/*.hex -- cgit v1.2.3 From 4f7f90df760f1bd215ce1b2ec671d62b332690c6 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sun, 4 Aug 2024 06:03:38 -0600 Subject: removed "Off -> 3H" strobe/mood mode access from Extended Simple UI fixes #85 The Simple UI is meant to be relatively child-safe, and the strobe modes are not. Users who want unsafe features enabled should use the full UI instead... and the strobe + mood modes should also have some safety features added, on drivers which have enough ROM to hold extra code. --- docs/anduril-manual.md | 12 ++++-------- ui/anduril/off-mode.c | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/docs/anduril-manual.md b/docs/anduril-manual.md index 68c577c..cdf0dd1 100644 --- a/docs/anduril-manual.md +++ b/docs/anduril-manual.md @@ -142,17 +142,13 @@ manufacturer's request. This typically includes: - `Ramp -> 3C`: Toggle smooth or stepped ramp shape. - `Ramp -> 5H`: Sunset timer. - - `Off -> 3H`: Access the strobe/mood modes. - `Off -> 7C/7H`: Change the aux LED pattern. - `Lockout -> 7C/7H`: Change the aux LED pattern. -If your light uses Extended Simple UI, *think twice about letting kids use it*, -because the strobe/mood modes were not intended to be used in simple mode, and -can reach full power with no thermal regulation. - -It is likely that strobe/mood modes will be removed from Extended Simple UI in -the future, for safety reasons, or maybe have already been removed. But that -doesn't help with older firmware, so be careful. +Old versions (before 2024-08) also allowed access to strobe/mood modes, which +can be dangerous, so if you have one of those, *think twice about letting kids +use it*. Those modes were never intended to be child-safe, and can reach full +power with no thermal regulation. ### Configuring Simple UI diff --git a/ui/anduril/off-mode.c b/ui/anduril/off-mode.c index 36d771c..f699c89 100644 --- a/ui/anduril/off-mode.c +++ b/ui/anduril/off-mode.c @@ -264,19 +264,6 @@ uint8_t off_state(Event event, uint16_t arg) { #endif // ifndef USE_EXTENDED_SIMPLE_UI #endif // ifdef USE_SIMPLE_UI - // click, click, long-click: strobe mode - #ifdef USE_STROBE_STATE - else if (event == EV_click3_hold) { - set_state(strobe_state, 0); - return EVENT_HANDLED; - } - #elif defined(USE_BORING_STROBE_STATE) - else if (event == EV_click3_hold) { - set_state(boring_strobe_state, 0); - return EVENT_HANDLED; - } - #endif - #ifdef USE_INDICATOR_LED // 7 clicks: change indicator LED mode else if (event == EV_7clicks) { @@ -334,6 +321,19 @@ uint8_t off_state(Event event, uint16_t arg) { } #endif // ifdef USE_EXTENDED_SIMPLE_UI + // click, click, long-click: strobe mode + #ifdef USE_STROBE_STATE + else if (event == EV_click3_hold) { + set_state(strobe_state, 0); + return EVENT_HANDLED; + } + #elif defined(USE_BORING_STROBE_STATE) + else if (event == EV_click3_hold) { + set_state(boring_strobe_state, 0); + return EVENT_HANDLED; + } + #endif + // 10 clicks: enable simple UI else if (event == EV_10clicks) { blink_once(); -- cgit v1.2.3