diff options
Diffstat (limited to 'hw/hank')
| -rw-r--r-- | hw/hank/emisar-d3aa/anduril.h | 118 | ||||
| -rw-r--r-- | hw/hank/emisar-d3aa/arch | 1 | ||||
| -rw-r--r-- | hw/hank/emisar-d3aa/hwdef.c | 217 | ||||
| -rw-r--r-- | hw/hank/emisar-d3aa/hwdef.h | 227 | ||||
| -rw-r--r-- | hw/hank/emisar-d3aa/model | 1 | ||||
| -rw-r--r-- | hw/hank/lume-x1/anduril.h | 114 | ||||
| -rw-r--r-- | hw/hank/lume-x1/arch | 1 | ||||
| -rw-r--r-- | hw/hank/lume-x1/hwdef.c | 116 | ||||
| -rw-r--r-- | hw/hank/lume-x1/hwdef.h | 232 | ||||
| -rw-r--r-- | hw/hank/lume-x1/model | 1 | ||||
| -rw-r--r-- | hw/hank/lume-x1/readme.md | 34 |
11 files changed, 1062 insertions, 0 deletions
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..0ac0ddc --- /dev/null +++ b/hw/hank/emisar-d3aa/hwdef.c @@ -0,0 +1,217 @@ +// 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 +}; + + +inline void nfet_delay() { + #if IN_NFET_DELAY_TIME > 0 + delay_4ms(IN_NFET_DELAY_TIME/4); + #else + delay_zero(); + delay_zero(); + #endif +} + +void set_level_zero() { + DAC_LVL = 0; // DAC off + DAC_VREF = V10; // low Vref + HDR_ENABLE_PORT &= ~(1 << HDR_ENABLE_PIN); // HDR off + + if (actual_level) { + // prevent post-off flash + IN_NFET_ENABLE_PORT |= (1 << IN_NFET_ENABLE_PIN); + nfet_delay(); + 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); + //nfet_delay(); + } + + // 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 turning on from off, let things stabilize before enabling power + if (noflash) { nfet_delay(); } + + // BST on last, after its inputs are set and stabilized + BST_ENABLE_PORT |= (1 << BST_ENABLE_PIN); + + if (noflash) { + // wait for flash prevention to finish + nfet_delay(); + 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..889b721 --- /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 0 // (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/hank/lume-x1/anduril.h b/hw/hank/lume-x1/anduril.h new file mode 100644 index 0000000..c409e43 --- /dev/null +++ b/hw/hank/lume-x1/anduril.h @@ -0,0 +1,114 @@ +// Hank Emisar/Noctigon Lume-X1 config options for Anduril +// Copyright (C) 2018-2024 Selene ToyKeeper, Loneoceans +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// For flashlights using the Loneoceans Lume-X1-40W boost driver (AVR32DD20) +// - Same firmware for 6V, 9V, or 12V configs +// same as loneoceans lume-x1-avr32dd20 but with Hank-specific defaults + +#define HWDEF_H hank/lume-x1/hwdef.h +#include "hank/anduril.h" + + + +//*************************************** +//** RAMP TABLE AND OPERATIONS ** +//*************************************** + +#define RAMP_SIZE 150 // Change to 160 as well as table and values below if desired + +// PWM1: DAC Data +// UDR x^4 curves specifically for Lume-X1 (AVR32DD20) + +// levels 1 to 5: +// ./bin/level_calc.py 5.2 1 5 7135 100 0.01 0.1 --pwm 2500 +// (with the last 2 values divided to fit) +// levels 6 to 150: +// ./bin/dac-scale.py $( ./bin/level_calc.py 5.3 1 145 7135 17 0.2 3000 --pwm 240000 | grep PWM1 | cut -d : -f 2- ) +// top level for each "gear": 3 5 / 39 50 / 123 150 +#define PWM1_LEVELS \ + 100, 359, 790, 588,1023, \ + 17, 21, 24, 29, 34, 39, 46, 54, 62, 71, 82, 94, 107, 122, 138, 157, 177, 199, 223, 250, 279, 311, 346, 385, 426, 471, 520, 573, 630, 692, 758, 830, 907, 990, \ + 441, 480, 522, 567, 615, 666, 720, 778, 840, 905, 974, \ + 26, 28, 30, 33, 35, 37, 40, 43, 46, 49, 52, 56, 59, 63, 67, 71, 76, 80, 85, 90, 96, 101, 107, 113, 120, 126, 133, 141, 148, 156, 165, 173, 182, 192, 202, 212, 222, 234, 245, 257, 269, 282, 296, 310, 324, 339, 355, 371, 387, 405, 422, 441, 460, 480, 501, 522, 544, 567, 590, 614, 640, 665, 692, 720, 748, 778, 808, 839, 872, 905, 939, 975,1011, \ + 429, 445, 461, 478, 495, 513, 531, 550, 570, 589, 610, 631, 653, 675, 698, 721, 745, 770, 795, 821, 848, 875, 903, 932, 962, 992,1023 +#define PWM2_LEVELS \ + V10, V10, V10, 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, \ + 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, \ + 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 LED_PATH1_PIN_LEVEL_MIN 1 +#define LED_PATH2_PIN_LEVEL_MIN 6 +#define LED_PATH3_PIN_LEVEL_MIN 51 + +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 10 + +#define DEFAULT_LEVEL 50 +#define MAX_1x7135 49 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 + +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 110 // about ~12W +#define SIMPLE_UI_STEPS 5 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +//*************************************** +//** THERMAL SETTINGS ** +//*************************************** + +// stop panicking at 6W (not sure of this numbers yet since it depends on the host..) +#define THERM_FASTER_LEVEL 100 // about 6W +#define MIN_THERM_STEPDOWN 60 // similar to single amc7135 in a 3V light + +//*************************************** +//** AUX LEDs and MISC ** +//*************************************** + +#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 + +#define PARTY_STROBE_ONTIME 1 // slow down party strobe +#define STROBE_OFF_LEVEL 1 // keep the regulator chips on between pulses + +// smoother candle mode with bigger oscillations +#define CANDLE_AMPLITUDE 30 // default 25 + +// avr32dd20 has enough space to smooth out voltage readings +#define USE_VOLTAGE_LOWPASS + +// enable long-blink as negative sign +#define USE_LONG_BLINK_FOR_NEGATIVE_SIGN + diff --git a/hw/hank/lume-x1/arch b/hw/hank/lume-x1/arch new file mode 100644 index 0000000..bcf4552 --- /dev/null +++ b/hw/hank/lume-x1/arch @@ -0,0 +1 @@ +avr32dd20 diff --git a/hw/hank/lume-x1/hwdef.c b/hw/hank/lume-x1/hwdef.c new file mode 100644 index 0000000..3fe32f3 --- /dev/null +++ b/hw/hank/lume-x1/hwdef.c @@ -0,0 +1,116 @@ +// Copyright (C) 2017-2023 Selene ToyKeeper +// 2021-2024 loneoceans +// SPDX-License-Identifier: GPL-3.0-or-later + +//*********************************************** +//** HELPER FUNCTIONS FOR LUME-X1-AVR32DD20 ** +//*********************************************** + +#pragma once + +#include "fsm/chan-rgbaux.c" + +// Declare variables and functions to support UDR multiple power paths +uint8_t is_boost_currently_on = 0; // for turn-on delay during first turn on + +void set_level_zero(); +void set_level_udr(uint8_t level); +bool gradual_tick_main(uint8_t gt); +void set_power_path(uint8_t ramp_level); + +Channel channels[] = { + { // main LEDs + .set_level = set_level_udr, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + +// turn off +void set_level_zero() { + + DAC_LVL = 0; // set DAC to 0 + DAC_VREF = V10; // set DAC Vref to lowest + + // turn off DC/DC converter and amplifier + BST_ENABLE_PORT &= ~(1 << BST_ENABLE_PIN); + is_boost_currently_on = 0; + + // turn off all UDR paths + LED_PATH1_PORT &= ~LED_PATH1_PIN; + LED_PATH2_PORT &= ~LED_PATH2_PIN; + LED_PATH3_PORT &= ~LED_PATH3_PIN; +} + +// UDR for set_level, which sets the led brightness based on ramp tables. +// single set of LED(s), fully regulated boost at all levels +void set_level_udr(uint8_t level) { + if (level == actual_level - 1) return; // no-op + + // get the ramp data + PWM1_DATATYPE dac_lvl = PWM1_GET(level) << 6; // dac register is left-aligned + PWM2_DATATYPE dac_vref = PWM2_GET(level); + + if(is_boost_currently_on != 1){ + // boost is not on, enable buck and add boot-up delay + is_boost_currently_on = 1; + BST_ENABLE_PORT |= (1 << BST_ENABLE_PIN); // turn on buck and amplifier + delay_4ms(BST_ON_DELAY/4); // boot-up delay + } + // set the DAC + DAC_LVL = dac_lvl; + DAC_VREF = dac_vref; + + // set the power paths + set_power_path(level); +} + +// handles dynamic Vref used in the ramp tables +bool gradual_tick_main(uint8_t gt) { + // TODO overall smoothness can be improved due to gt using linear + // adjustments, but ramp table is non-linear. + + // if Vref is the same, make gradual adjustments. + // else, jump to the next ramp level and use set_level() to handle power paths. + PWM2_DATATYPE vref_next = PWM2_GET(gt); // DAC ramp table Vref + + // if different vref level, make a ramp level adjustment.. + if (vref_next != DAC_VREF) return true; // use set_level() to handle normally + + // .. else, same vref, adjust level gradually. + PWM1_DATATYPE dac_next = PWM1_GET(gt); // DAC ramp table data + PWM1_DATATYPE dac_curr = DAC_LVL >> 6; // register is left-aligned + + GRADUAL_ADJUST_SIMPLE(dac_next, dac_curr); + DAC_LVL = dac_curr << 6; + + if (dac_next == dac_curr) return true; // done + + return false; +} + +// handles dynamic power pathways based on threshold levels +void set_power_path(uint8_t ramp_level){ + + ramp_level ++; // convert to 1-based indexing + + if (ramp_level >= LED_PATH3_PIN_LEVEL_MIN) { + // high mode + LED_PATH1_PORT |= LED_PATH1_PIN; + LED_PATH2_PORT |= LED_PATH2_PIN; + LED_PATH3_PORT |= LED_PATH3_PIN; + } + else if (ramp_level >= LED_PATH2_PIN_LEVEL_MIN) { + // low mode + LED_PATH1_PORT |= LED_PATH1_PIN; + LED_PATH2_PORT |= LED_PATH2_PIN; + LED_PATH3_PORT &= ~LED_PATH3_PIN; + } + else if (ramp_level >= LED_PATH1_PIN_LEVEL_MIN) { + // firefly mode + LED_PATH1_PORT |= LED_PATH1_PIN; + LED_PATH2_PORT &= ~LED_PATH2_PIN; + LED_PATH3_PORT &= ~LED_PATH3_PIN; + } +} + diff --git a/hw/hank/lume-x1/hwdef.h b/hw/hank/lume-x1/hwdef.h new file mode 100644 index 0000000..250e8ee --- /dev/null +++ b/hw/hank/lume-x1/hwdef.h @@ -0,0 +1,232 @@ +// Copyright (C) 2017-2023 Selene ToyKeeper +// 2021-2024 loneoceans +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +//************************************************** +//** HARDWARE DEFINITIONS FOR LUME-X1-AVR32DD20 ** +//************************************************** + +/* Loneoceans Lume-X1 with AVR32DD20 + + 40W Boost Driver with Ultra Dynamic Range, RGB & SW Aux, Powerbank. + + The following pins are invariant (20 QFN package 3x3 0.4mm BSC) + - PD6 / PP11 - DAC OUT + - PF6 / PP15 - nRST + - PF7 / PP16 - UPDI + - xx / PP13 - VDD (read voltage from VDD, PFET RPP, no voltage drop) + - xx / PP8 - VDDIO2 + - xx / PP14 - GND + + Used Pins + - PA0 / PP17 - FET via PWM (TCA0-WO0) - N/C for Lume X1 + - PA5 / PP2 - PATH3 - High Range + - PA6 / PP3 - PATH2 - Low Range + - PA7 / PP4 - PATH1 - Moon Range + - PC1 / PP5 - Enable for Boost, Amplifier + - PC2 / PP6 - Enable for Microphone - N/C + - PC3 / PP7 - Neo Dat - N/C + - PD4 / PP9 - E-Switch + - PD5 / PP10 - Mic Output - N/C + - PD6 / PP11 - DAC Out + - PD7 / PP12 - Power Bank - N/C + - PA1 / PP18 - AUX R LED + - PA2 / PP19 - AUX G LED + - PA3 / PP20 - AUX B LED + - PA4 / PP1 - AUX SW LED + +*/ + +#define HWDEF_C hank/lume-x1/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 + +//*************************************** +//** SET UP DAC AND PWM ** +//*************************************** + +// Define DAC control +#define PWM_BITS 16 // 10-bit DAC +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t +#define PWM1_DATATYPE uint16_t // main regulated ramp 10-bit +#define PWM1_GET(x) PWM_GET16(pwm1_levels, x) +#define PWM2_DATATYPE uint8_t // DAC Vref table (4/6 options) +#define PWM2_GET(x) PWM_GET8(pwm2_levels, x) + +//*************************************** +//** PIN DEFINITIONS ** +//*************************************** + +// Boost and Amplifier Enable (PC1) +#define BST_ENABLE_PIN PIN1_bp +#define BST_ENABLE_PORT PORTC_OUT +#define BST_ON_DELAY 8 // ms delay turning on the led after enable + +// Ultra Dynamic Range (UDR) +/* + UDR makes use of the concept of multiple power paths. 3 are used in this + design to achieve extremely low moonlight levels. This is combined with + dynamic Vref for smoother brightness level transitions that would + normally be limited by the 10-bit DAC resolution. + + Lume drivers uses the internal DAC to generate a reference voltage for + a current-regulated amplifier which drives the LED. Each power path + routes current through different sense resistors, allowing for improved + current sense resolution especially at the low end. Using UDR, Lume1/X1 + is capable of ultra-low firefly / moonlight levels with a dynamic range + on the order of >10-50 million : 1. +*/ + +// For UDR Path 1 (firefly mode) - PA7 +#define LED_PATH1_PIN PIN7_bm +#define LED_PATH1_PORT PORTA_OUT + +// For UDR Path 2 (low mode) - PA6 +#define LED_PATH2_PIN PIN6_bm +#define LED_PATH2_PORT PORTA_OUT + +// For UDR Path 3 (high mode) - PA5 +#define LED_PATH3_PIN PIN5_bm +#define LED_PATH3_PORT PORTA_OUT + +// Define Aux LED Pins + +// lighted switch button aux led (PA4) +#ifndef BUTTON_LED_PIN +#define BUTTON_LED_PIN PIN4_bp +#define BUTTON_LED_PORT PORTA +#endif + +// this driver allows for aux LEDs under the optic +#define AUXLED_R_PIN PIN1_bp +#define AUXLED_G_PIN PIN2_bp +#define AUXLED_B_PIN PIN3_bp + +#define AUXLED_RGB_PORT PORTA + +/* +#define AUXLED_R_PORT PORTA +#define AUXLED_G_PORT PORTA +#define AUXLED_B_PORT PORTA + +// if aux leds are on different ports +#define AUXLED_RGB_DIFFERENT_PORTS +*/ + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// Define e-switch Pin and ISR +#ifndef SWITCH_PIN // PD4 +#define SWITCH_PIN PIN4_bp +#define SWITCH_PORT VPORTD.IN +#define SWITCH_ISC_REG PORTD.PIN4CTRL +#define SWITCH_VECT PORTD_PORT_vect +#define SWITCH_INTFLG VPORTD.INTFLAGS +#endif + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 1 // PFET for RRP, essentially 0 v-drop, but experimentally add 0.05V for better UX +#endif + +//*************************************** +//** HARDWARE INIT ** +//*************************************** + +inline void hwdef_setup() { + + // TODO: for this DAC controlled-light, try to decrease the clock speed + // to reduce overall system power + mcu_clock_speed(); + + // set output pins + VPORTA.DIR = PIN1_bm | PIN2_bm | PIN3_bm | + PIN4_bm | PIN5_bm | PIN6_bm | PIN7_bm; + VPORTC.DIR = PIN1_bm; + VPORTD.DIR = PIN6_bm; + + // now set pullups on input pins, and unused pins (reduce power) + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; // FET + //PORTA.PIN1CTRL = PORT_PULLUPEN_bm; // AUX R + //PORTA.PIN2CTRL = PORT_PULLUPEN_bm; // AUX G + //PORTA.PIN3CTRL = PORT_PULLUPEN_bm; // AUX B + //PORTA.PIN4CTRL = PORT_PULLUPEN_bm; // AUX SW + //PORTA.PIN5CTRL = PORT_PULLUPEN_bm; // PATH3 + //PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // PATH2 + //PORTA.PIN7CTRL = PORT_PULLUPEN_bm; // PATH1 + + //PORTC.PIN1CTRL = PORT_PULLUPEN_bm; // ENABLE + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; // MIC ENABLE + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; // NEO + + //PORTD.PIN4CTRL = PORT_PULLUPEN_bm; // ESW (100kR PULLUP) + PORTD.PIN5CTRL = PORT_PULLUPEN_bm; // MIC OUT + //PORTD.PIN6CTRL = PORT_PULLUPEN_bm; // DAC OUT + PORTD.PIN7CTRL = PORT_PULLUPEN_bm; // PWR BNK ENABLE + + // clear some pins we don't need for now or want to initialize as low + PORTC_OUT &= ~(1 << PIN1_bp); // ENABLE + PORTC_OUT &= ~(1 << PIN2_bp); // MIC ENABLE + PORTD_OUT &= ~(1 << PIN7_bp); // PWR BNK ENABLE + + //E-Switch (now uses external pullup) + PORTD.DIRCLR = PIN4_bm; // set ESW as input pin + PORTD.PIN4CTRL = PORT_ISC_BOTHEDGES_gc; + + // set up the DAC (used for the switching regulator) + // https://www.microchip.com/en-us/product/avr32dd20 + // DAC ranges from 0V to (255 * Vref) / 256 + + // Datasheet 34.3.1-2/2: input for DAC must be disabled + PORTD.PIN6CTRL = PORT_ISC_INPUT_DISABLE_gc; + //VREF.CTRLA |= VREF_DAC0REFSEL_2V5_gc; // also VREF_DAC0REFSEL_0V55_gc and VREF_DAC0REFSEL_1V1_gc and VREF_DAC0REFSEL_2V5_gc + //VREF.CTRLB |= VREF_DAC0REFEN_bm; // enable vref + DAC_VREF = V10; + DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; // enable DAC + DAC_LVL = 0; // lowest output during 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/lume-x1/model b/hw/hank/lume-x1/model new file mode 100644 index 0000000..5a0d2ba --- /dev/null +++ b/hw/hank/lume-x1/model @@ -0,0 +1 @@ +0281 diff --git a/hw/hank/lume-x1/readme.md b/hw/hank/lume-x1/readme.md new file mode 100644 index 0000000..eba5949 --- /dev/null +++ b/hw/hank/lume-x1/readme.md @@ -0,0 +1,34 @@ +# Hank / loneoceans Lume X1 40W + +Regulated boost driver for Hank lights (Emisar / Noctigon) by loneoceans. + +Second Hank light to use the new avr32dd20 MCU. Performs better than older +MCUs and provides more space for extra features and future firmware updates. + +Originally from https://github.com/loneoceans/anduril + +Features UDR (Ultra Dynamic Range) for smooth dimming all the way down to +*literally zero*, meaning an effectively infinite dimming ratio: + +- 3 power paths or "gears" for the regulator chip's sense resistors: + - Firefly "gear": 0 to ~0.1 lm + - Low "gear": ~0.1 to ~40 lm + - High "gear": ~40 lm to full power (2k or 3k lm?) +- Internal resolution of 2500 steps per "gear" allows very precise control over + brightness. Anduril's 150 step ramp does not fully take advantage of this. +- Constant current, no pulsing or flicker or ripple +- No preflash +- No need for jump start; regulator has a quick response at all levels + +The firefly part of the ramp is compressed to just a few levels, to avoid +making the rest of the ramp artificially low. It aims to be reasonably +consistent compared to the ramp shape of Hank's other lights, but a few levels +at the bottom are set aside for moon and firefly modes. + +The regulator can go much, much lower than the brightness of the button LEDs, +but there isn't much point because the glowing button overpowers the main LEDs. +The button and aux LEDs (on low) also use significantly less power despite +being brighter, because they do not require the MCU to be awake. So that part +of the ramp is mostly omitted. However, it can be added with a firmware +modification if desired. + |
