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