aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--arch/avr32dd20.c263
-rw-r--r--arch/avr32dd20.h93
-rwxr-xr-xbin/flash-avr32dd20.sh13
-rw-r--r--hw/thefreeman/avr32dd20-devkit/anduril.h138
-rw-r--r--hw/thefreeman/avr32dd20-devkit/arch1
-rw-r--r--hw/thefreeman/avr32dd20-devkit/hwdef.c105
-rw-r--r--hw/thefreeman/avr32dd20-devkit/hwdef.h208
-rw-r--r--hw/thefreeman/avr32dd20-devkit/model1
8 files changed, 822 insertions, 0 deletions
diff --git a/arch/avr32dd20.c b/arch/avr32dd20.c
new file mode 100644
index 0000000..45923a8
--- /dev/null
+++ b/arch/avr32dd20.c
@@ -0,0 +1,263 @@
+// arch/avr32dd20.h: avr32dd20 support header
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+////////// clock speed / delay stuff //////////
+
+inline void mcu_clock_speed() {
+ // TODO: allow hwdef to define a base clock speed
+ // run the internal clock at 12 MHz, not the default 4 MHz
+ _PROTECTED_WRITE( CLKCTRL.OSCHFCTRLA,
+ CLKCTRL_FRQSEL_12M_gc | CLKCTRL_AUTOTUNE_bm );
+ // (another option is to use 20 MHz / 2, like attiny1616 does)
+ // divide 20 MHz to run at 10 MHz to match attiny1616
+ //_PROTECTED_WRITE( CLKCTRL.MCLKCTRLB,
+ // CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
+}
+
+///// clock dividers
+// this should work, but needs further validation
+inline void clock_prescale_set(uint8_t n) {
+ cli();
+ _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, n); // Set the prescaler
+ while (CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm) {} // wait for clock change to finish
+ sei();
+}
+
+
+////////// ADC voltage / temperature //////////
+
+// ADC0.CTRLA bits:
+// RUNSTDBY, -, CONVMODE, LEFTADJ, RESSEL[1:0], FREERUN, ENABLE
+// CTRLB bits: -, -, -, -, -, SAMPNUM[2:0]
+// CTRLC bits: -, -, -, -, PRESC[3:0]
+// CTRLD bits: INITDLY[2:0], -, SAMPDLY[3:0]
+// CTRLE bits: -, -, -, -, -, WINCM[2:0]
+// SAMPCTRL: 8 bits
+// MUXPOS, MUXNEG: 7 bits each
+// COMMAND: -, -, -, -, -, -, SPCONV, STCONV
+
+inline void mcu_set_admux_therm() {
+ // ADC init: Datasheet section 33.3.2
+ // Temperature mode: Datasheet section 33.3.3.8
+ // measurements should be 12-bit right-adjusted single-ended conversion
+ // set Vref to 2.048V for temperature mode
+ VREF.ADC0REF = VREF_REFSEL_2V048_gc;
+ // set temp sensor as input
+ ADC0.MUXPOS = ADC_MUXPOS_TEMPSENSE_gc;
+ // configure init delay to >= 25 us * Fclk_adc (no sample delay needed)
+ ADC0.CTRLD = ADC_INITDLY_DLY32_gc | ADC_SAMPDLY_DLY0_gc;
+ // configure ADC sample length to >= 28 us * Fclk_adc
+ ADC0.SAMPCTRL = 32;
+ // set single-ended or differential
+ // set resolution to 12 bits
+ // set left- or right-adjust (right)
+ // set free-running mode or not (yes)
+ ADC0.CTRLA = ADC_CONVMODE_SINGLEENDED_gc
+ | ADC_RESSEL_12BIT_gc
+ //| ADC_LEFTADJ_bm // not in temperature mode
+ | ADC_FREERUN_bm;
+ // set number of samples (requires adjustment in formula too)
+ ADC0.CTRLB = ADC_SAMPNUM_NONE_gc;
+ // TODO: accumulate more samples for more resolution
+ // (and probably set the prescale faster too)
+ //ADC0.CTRLB = ADC_SAMPNUM_ACC16_gc; // 16 samples per result
+ // set a clock prescaler
+ ADC0.CTRLC = ADC_PRESC_DIV64_gc;
+ // enable the ADC
+ ADC0.CTRLA |= ADC_ENABLE_bm;
+ // actually start measuring (happens in another function)
+ //ADC0.COMMAND |= ADC_STCONV_bm;
+ // for each measurement:
+ // process according to sigrow data + formula
+}
+
+inline void mcu_set_admux_voltage() {
+ // ADC init: Datasheet section 33.3.2
+ // set Vref
+ VREF.ADC0REF = VREF_REFSEL_1V024_gc;
+ // set single-ended or differential
+ // set resolution to 12 bits
+ // set left- or right-adjust (right)
+ // set free-running mode or not (yes)
+ ADC0.CTRLA = ADC_CONVMODE_SINGLEENDED_gc
+ | ADC_RESSEL_12BIT_gc
+ | ADC_LEFTADJ_bm // has no effect when 16+ samples taken
+ | ADC_FREERUN_bm
+ | ADC_RUNSTBY_bm // allow voltage sense in standby mode
+ ;
+ // set number of samples
+ ADC0.CTRLB = ADC_SAMPNUM_ACC16_gc; // 16 samples per result
+ // set a clock prescaler
+ ADC0.CTRLC = ADC_PRESC_DIV16_gc; // not too fast, not too slow
+ // select the positive ADC input with MUXPOS
+ #ifdef USE_VOLTAGE_DIVIDER // external voltage divider
+ // ADC input pin / Vref
+ ADC0.MUXPOS = ADMUX_VOLTAGE_DIVIDER; // external pin
+ #elif defined (USE_VOLTAGE_VDDIO2) // internal voltage divider
+ // (Vbat / 10) / Vref
+ ADC0.MUXPOS = ADC_MUXPOS_VDDIO2DIV10_gc;
+ #else // measure directly on VDD/VCC pin
+ // (Vbat / 10) / Vref
+ ADC0.MUXPOS = ADC_MUXPOS_VDDDIV10_gc;
+ #endif
+ // enable the ADC
+ ADC0.CTRLA |= ADC_ENABLE_bm;
+ // actually start measuring (happens in another function)
+ //ADC0.COMMAND |= ADC_STCONV_bm;
+}
+
+inline void mcu_adc_sleep_mode() {
+ set_sleep_mode(SLEEP_MODE_STANDBY);
+}
+
+inline void mcu_adc_start_measurement() {
+ // FIXME: enable this after getting ADC stuff fixed
+ #if 0
+ ADC0.INTCTRL |= ADC_RESRDY_bm; // enable interrupt
+ ADC0.COMMAND |= ADC_STCONV_bm; // actually start measuring
+ #endif
+}
+
+/*
+void mcu_adc_on() {
+ hwdef_set_admux_voltage();
+ mcu_adc_start_measurement();
+}
+*/
+
+inline void mcu_adc_off() {
+ ADC0.CTRLA &= ~(ADC_ENABLE_bm); // disable the ADC
+}
+
+inline void mcu_adc_vect_clear() {
+ ADC0.INTFLAGS = ADC_RESRDY_bm; // clear the interrupt
+}
+
+inline uint16_t mcu_adc_result_temp() {
+ // FIXME: better math, higher precision
+ // Use the factory calibrated values in SIGROW.TEMPSENSE0 and
+ // SIGROW.TEMPSENSE1 to calculate a temperature reading in Kelvin, then
+ // left-align it.
+ int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed value from signature row
+ uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // Read unsigned value from signature row
+ uint32_t temp = ADC0.RES - sigrow_offset;
+ temp *= sigrow_gain; // Result might overflow 16 bit variable (10bit+8bit)
+ temp += 0x80; // Add 1/2 to get correct rounding on division below
+ //temp >>= 8; // Divide result to get Kelvin
+ //return temp << 6; // left align it
+ return temp >> 2; // left-aligned uint16_t
+}
+
+inline uint16_t mcu_adc_result_volts() {
+ // voltage is 12-bit right-aligned + 16x oversampling = 16 bits total
+ return ADC0.RES;
+}
+
+inline uint8_t mcu_adc_lsb() {
+ // temp is right-aligned, voltage is 16-bit, both have a useful LSB
+ return ADC0_RESL;
+}
+
+#ifdef USE_VOLTAGE_VDD
+uint8_t calc_voltage(uint16_t measurement) {
+ // calculate actual voltage: volts * 10
+ // FIXME
+ // ADC = 1.1 * 1024 / volts
+ // volts = 1.1 * 1024 / ADC
+ result = ((uint16_t)(2*1.1*1024*10)/(measurement>>6)
+ + VOLTAGE_FUDGE_FACTOR
+ #ifdef USE_VOLTAGE_CORRECTION
+ + VOLT_CORR - 7
+ #endif
+ ) >> 1;
+ return result;
+}
+#elif defined(USE_VOLTAGE_VDDIO2)
+#elif defined(USE_VOLTAGE_VDDIO2)
+#else
+// hwdef must supply its own function
+#endif
+
+////////// WDT //////////
+// this uses the RTC PIT interrupt instead of WDT,
+// as recommended on AVR 0/1-series and later:
+// https://github.com/SpenceKonde/DxCore/blob/master/megaavr/extras/PowerSave.md#using-the-real-time-counter-rtcpit
+// The PIT runs even in power-down mode, unlike RTC,
+// and its cycles are relative to the AVR's internal 32768 Hz ULP oscillator
+// AVR datasheet sections 26, 26.5, 26.9, 26.12, 26.13.12
+
+// PIT tick speeds:
+// 0 (none)
+// 1 8192 Hz (CYC4)
+// 2 4096 Hz
+// 3 2048 Hz
+// 4 1024 Hz (CYC32)
+// 5 512 Hz
+// 6 256 Hz
+// 7 128 Hz
+// 8 64 Hz (default) (CYC512)
+inline void mcu_wdt_active() {
+ RTC.PITINTCTRL = RTC_PI_bm; // enable the Periodic Interrupt
+ while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated
+ // Period = 16ms (64 Hz), enable the PI Timer
+ RTC.PITCTRLA = RTC_PERIOD_CYC512_gc | RTC_PITEN_bm;
+}
+
+// STANDBY_TICK_SPEED:
+// 0 64 Hz
+// 1 32 Hz
+// 2 16 Hz
+// 3 8 Hz (default)
+// 4 4 Hz
+// 5 2 Hz
+// 6 1 Hz
+inline void mcu_wdt_standby() {
+ RTC.PITINTCTRL = RTC_PI_bm; // enable the Periodic Interrupt
+ while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated
+ // Set period (64 Hz / STANDBY_TICK_SPEED = 8 Hz), enable the PI Timer
+ RTC.PITCTRLA = ((8+STANDBY_TICK_SPEED)<<3) | RTC_PITEN_bm;
+}
+
+inline void mcu_wdt_stop() {
+ while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated
+ RTC.PITCTRLA = 0; // Disable the PI Timer
+}
+
+inline void mcu_wdt_vect_clear() {
+ RTC.PITINTFLAGS = RTC_PI_bm; // clear the PIT interrupt flag
+}
+
+
+////////// PCINT - pin change interrupt (e-switch) //////////
+
+inline void mcu_switch_vect_clear() {
+ // Write a '1' to clear the interrupt flag
+ SWITCH_INTFLG |= (1 << SWITCH_PIN);
+}
+
+inline void mcu_pcint_on() {
+ SWITCH_ISC_REG |= PORT_ISC_BOTHEDGES_gc;
+}
+
+inline void mcu_pcint_off() {
+ SWITCH_ISC_REG &= ~(PORT_ISC_gm);
+}
+
+
+////////// misc //////////
+
+void reboot() {
+ // request a reboot (software reset)
+ _PROTECTED_WRITE(RSTCTRL.SWRR, RSTCTRL_SWRST_bm);
+}
+
+inline void prevent_reboot_loop() {
+ // if previous reset was a crash (WDT time-out),
+ // prevent it from happening again immediately
+ //RSTCTRL.RSTFR &= ~(RSTCTRL_WDRF_bm); // reset wdt flag only
+ RSTCTRL.RSTFR = 0; // reset all reset status flags (maybe unneeded?)
+ wdt_disable(); // from avr/wdt.h
+}
+
diff --git a/arch/avr32dd20.h b/arch/avr32dd20.h
new file mode 100644
index 0000000..7a6b8f1
--- /dev/null
+++ b/arch/avr32dd20.h
@@ -0,0 +1,93 @@
+// arch/avr32dd20.h: avr32dd20 support header
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+////////// clock speed / delay stuff //////////
+
+#define F_CPU 12000000UL
+#define BOGOMIPS (F_CPU/3800)
+#define DELAY_ZERO_TIME 1020
+
+inline void mcu_clock_speed();
+
+///// clock dividers
+// this should work, but needs further validation
+inline void clock_prescale_set(uint8_t n);
+
+// TODO: allow hwdef to define a base clock speed,
+// and adjust these values accordingly
+typedef enum
+{
+ // Actual clock is 12 MHz
+ clock_div_1 = (0), // 12 MHz
+ clock_div_2 = (CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm), // 6 MHz
+ clock_div_4 = (CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm), // 3 MHz
+ clock_div_8 = (CLKCTRL_PDIV_8X_gc | CLKCTRL_PEN_bm), // 1.5 MHz
+ clock_div_16 = (CLKCTRL_PDIV_16X_gc | CLKCTRL_PEN_bm), // 0.75 MHz
+ clock_div_32 = (CLKCTRL_PDIV_32X_gc | CLKCTRL_PEN_bm), // 375 kHz
+ clock_div_64 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 187.5 kHz
+} clock_div_t;
+
+
+////////// ADC voltage / temperature //////////
+
+#define hwdef_set_admux_therm mcu_set_admux_therm
+inline void mcu_set_admux_therm();
+
+#define hwdef_set_admux_voltage mcu_set_admux_voltage
+inline void mcu_set_admux_voltage();
+
+inline void mcu_adc_sleep_mode();
+
+inline void mcu_adc_start_measurement();
+
+//#define mcu_adc_on hwdef_set_admux_voltage
+//void mcu_adc_on();
+
+inline void mcu_adc_off();
+
+#define ADC_vect ADC0_RESRDY_vect
+inline void mcu_adc_vect_clear();
+
+// read ADC differently for temperature and voltage
+#define MCU_ADC_RESULT_PER_TYPE
+
+inline uint16_t mcu_adc_result_temp();
+
+inline uint16_t mcu_adc_result_volts();
+
+inline uint8_t mcu_adc_lsb();
+
+
+////////// WDT //////////
+
+inline void mcu_wdt_active();
+
+inline void mcu_wdt_standby();
+
+inline void mcu_wdt_stop();
+
+#define WDT_vect RTC_PIT_vect
+inline void mcu_wdt_vect_clear();
+
+
+////////// PCINT - pin change interrupt (e-switch) //////////
+
+// set these in hwdef
+//#define SWITCH_PORT VPORTD.IN
+//#define SWITCH_VECT PORTD_PORT_vect
+
+inline void mcu_switch_vect_clear();
+
+inline void mcu_pcint_on();
+
+inline void mcu_pcint_off();
+
+
+////////// misc //////////
+
+void reboot();
+
+inline void prevent_reboot_loop();
+
diff --git a/bin/flash-avr32dd20.sh b/bin/flash-avr32dd20.sh
new file mode 100755
index 0000000..ef8b0f3
--- /dev/null
+++ b/bin/flash-avr32dd20.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -e
+
+[[ -z "$1" ]] && echo "No .hex file specified." && exit 1
+HEX="$1"
+
+#pymcuprog ping -t uart -u /dev/ttyUSB2 -d avr32dd20
+#pymcuprog erase -t uart -u /dev/ttyUSB2 -d avr32dd20
+#pymcuprog write verify -t uart -u /dev/ttyUSB2 -d avr32dd20 -f "$HEX"
+#pymcuprog verify -t uart -u /dev/ttyUSB2 -d avr32dd20 -f "$HEX"
+
+pymcuprog write --erase --verify --timing -t uart -u /dev/ttyUSB2 -d avr32dd20 -f "$HEX"
diff --git a/hw/thefreeman/avr32dd20-devkit/anduril.h b/hw/thefreeman/avr32dd20-devkit/anduril.h
new file mode 100644
index 0000000..97e9e10
--- /dev/null
+++ b/hw/thefreeman/avr32dd20-devkit/anduril.h
@@ -0,0 +1,138 @@
+// thefreeman's BST21 BST20-FWxA (no button LED)
+// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define HWDEF_H thefreeman/avr32dd20-devkit/hwdef.h
+
+// HPRsense : 1.7+0.3+5 = 7mR (DMN22M5UFG+trace resistance+5mR)
+// Vsense=42.46mV, R1= 191k
+// LPRsense : 1R
+// transition DAC level 8, ramp level 45
+// fifth power ramp 0.1mA to 6066mA
+
+#define RAMP_SIZE 150
+
+// 4 ramp segments:
+// - low 1.024V
+// - low 2.5 V
+// - high 1.024V
+// - high 2.5 V
+// HDR ratio: 160
+// PWM1: DAC Data
+#if 1
+// 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 \
+ 6, 7, 9, 11, 14, 18, 23, 30, 37, 46, 56, 69, 83, 100, 120, 142, 168, 196, 229, 266, 307, 353, 403, 460, 522, 591, 667, 750, 840, 939, \
+ 428, 476, 528, 584, 645, 710, 780, 856, 937,1023, \
+ 17, 18, 20, 21, 23, 25, 27, 29, 32, 34, 37, 40, 42, 46, 49, 52, 56, 60, 64, 68, 72, 77, 81, 86, 92, 97, 103, 109, 115, 122, 128, 136, 143, 151, 159, 167, 176, 185, 194, 204, 214, 224, 235, 246, 258, 270, 283, 295, 309, 323, 337, 352, 367, 383, 399, 416, 434, 452, 470, 489, 509, 529, 550, 572, 594, 617, 640, 664, 689, 715, 741, 768, 796, 824, 854, 884, 915, 947, 979,1013, \
+ 429, 443, 458, 473, 489, 505, 521, 538, 555, 572, 590, 609, 628, 647, 667, 687, 708, 729, 751, 773, 796, 819, 843, 867, 891, 917, 943, 969, 996,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, 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
+#define MAX_1x7135 40
+#define HDR_ENABLE_LEVEL_MIN 41
+#define DEFAULT_LEVEL 50
+#else
+// level_calc.py 9.21 1 150 7135 5 0.2 1400 --pwm 400000
+// (plus dac-scale.py post-processing to get values for HDR+Vref ranges)
+// top level for each "gear": 35 48 127 150
+#define PWM1_LEVELS \
+ 5, 11, 18, 25, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141, 158, 177, 198, 220, 244, 269, 297, 326, 358, 392, 429, 469, 511, 556, 605, 657, 713, 772, 836, 904, 976, \
+ 431, 465, 501, 539, 580, 624, 670, 720, 772, 828, 887, 950,1017, \
+ 16, 17, 18, 20, 21, 23, 24, 26, 27, 29, 31, 33, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 64, 68, 72, 76, 80, 85, 90, 95, 100, 106, 112, 118, 124, 131, 138, 145, 153, 161, 170, 179, 188, 198, 208, 219, 230, 242, 254, 266, 280, 294, 308, 323, 339, 355, 373, 391, 409, 429, 449, 470, 492, 515, 539, 564, 589, 616, 644, 673, 704, 735, 768, 802, 837, 874, 912, 952, 993, \
+ 424, 442, 461, 480, 501, 522, 544, 566, 590, 614, 640, 666, 693, 721, 750, 780, 811, 844, 877, 912, 948, 985,1023
+// Vref selector (V10, V20, V25, V40 = 1.024V, 2.048V, 2.5V, 4.096V)
+#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, \
+ V25, V25, 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, 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
+
+#define MAX_1x7135 48
+#define DEFAULT_LEVEL 48
+#define HDR_ENABLE_LEVEL_MIN 49 // when HDR FET turns ON
+#endif
+
+// 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 130 // 50% / 3A / 1000 lm
+// 1 22 [44] 65 87 108 130
+#define RAMP_DISCRETE_FLOOR 1
+#define RAMP_DISCRETE_CEIL 130
+#define RAMP_DISCRETE_STEPS 7
+
+// 20 [45] 70 95 120
+#define SIMPLE_UI_FLOOR 20
+#define SIMPLE_UI_CEIL 120 // ~2.25A / ~750 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 130 // stop panicking at 50%/3A
+#define MIN_THERM_STEPDOWN MAX_1x7135
+
+
+// UI
+
+//#define SIMPLE_UI_ACTIVE 0 // advanced UI by default
+
+// 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/thefreeman/avr32dd20-devkit/arch b/hw/thefreeman/avr32dd20-devkit/arch
new file mode 100644
index 0000000..bcf4552
--- /dev/null
+++ b/hw/thefreeman/avr32dd20-devkit/arch
@@ -0,0 +1 @@
+avr32dd20
diff --git a/hw/thefreeman/avr32dd20-devkit/hwdef.c b/hw/thefreeman/avr32dd20-devkit/hwdef.c
new file mode 100644
index 0000000..e5b347f
--- /dev/null
+++ b/hw/thefreeman/avr32dd20-devkit/hwdef.c
@@ -0,0 +1,105 @@
+// thefreeman boost driver 2.1 output 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
+}
+
diff --git a/hw/thefreeman/avr32dd20-devkit/hwdef.h b/hw/thefreeman/avr32dd20-devkit/hwdef.h
new file mode 100644
index 0000000..7e093f9
--- /dev/null
+++ b/hw/thefreeman/avr32dd20-devkit/hwdef.h
@@ -0,0 +1,208 @@
+// hwdef for thefreeman's avr32dd20 dev kit
+// Copyright (C) 2023 thefreeman, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/* thefreeman’s avr32dd20 dev kit
+ * Boost driver based on MP3431(?)
+ * with high dynamic range and DAC control + aux RGB
+ *
+ * Pin Name Function
+ * 1 PA4 -
+ * 2 PA5 LVB (unused)
+ * 3 PA6 BATT LVL (voltage divider)
+ * 4 PA7 HDR: high/low Rsense range
+ * 5 PC1 -
+ * 6 PC2 -
+ * 7 PC3 -
+ * 8 VDDIO2 VCC2 (unused)
+ * 9 PD4 e-switch
+ * 10 PD5 EN: boost enable
+ * 11 PD6 DAC: control voltage out
+ * 12 PD7 IN- NFET: absorb startup flash
+ * 13 VDD VCC
+ * 14 GND GND
+ * 15 PF6 RESET
+ * 16 PF7 UPDI
+ * 17 PA0 R: aux red
+ * 18 PA1 G: aux green
+ * 19 PA2 B: aux blue
+ * 20 PA3 CH: detect charging
+ *
+ * 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
+ * CH senses the status of the onboard charger
+ * LVL : ??? (unused?)
+ * LVB is for OTSM firmware, not used here
+ */
+
+#define HWDEF_C thefreeman/avr32dd20-devkit/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
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#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
+#define DAC_LVL DAC0_DATA // 0 to 255, for 0V to Vref
+#define DAC_VREF VREF_DAC0REF // 1.024V, 2.048V, 4.096V, or 2.5V
+//#define DAC_VREF VREF.ADC0REF // 1.024V, 2.048V, 4.096V, or 2.5V
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp (unused?)
+// Vref values
+#define V10 VREF_REFSEL_1V024_gc
+#define V20 VREF_REFSEL_2V048_gc
+#define V25 VREF_REFSEL_2V500_gc
+#define V40 VREF_REFSEL_4V096_gc
+
+// BST enable
+#define BST_ENABLE_PIN PIN5_bp
+#define BST_ENABLE_PORT PORTD_OUT
+
+// HDR
+// turns on HDR FET for the high current range
+#define HDR_ENABLE_PIN PIN7_bp
+#define HDR_ENABLE_PORT PORTA_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 PIN7_bp
+#define IN_NFET_ENABLE_PORT PORTD_OUT
+
+// e-switch
+#ifndef SWITCH_PIN
+#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
+
+// TODO: define stuff for the voltage divider
+// 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
+
+// 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 PIN0_bp
+#define AUXLED_G_PIN PIN1_bp
+#define AUXLED_B_PIN PIN2_bp
+#define AUXLED_RGB_PORT PORTA
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+
+
+inline void hwdef_setup() {
+
+ // TODO? for this DAC controlled-light, try to decrease the clock speed
+ mcu_clock_speed();
+
+ VPORTA.DIR = PIN0_bm // R
+ | PIN1_bm // G
+ | PIN2_bm // B
+ //| PIN3_bm // CH
+ | PIN7_bm; // HDR
+ VPORTD.DIR = PIN5_bm // EN
+ | PIN6_bm // DAC
+ | PIN7_bm; // IN- NFET
+
+ // enable pullups on the unused and input pins to reduce power
+ //PORTA.PIN0CTRL = PORT_PULLUPEN_bm; // R
+ //PORTA.PIN1CTRL = PORT_PULLUPEN_bm; // G
+ //PORTA.PIN2CTRL = PORT_PULLUPEN_bm; // B
+ //PORTA.PIN3CTRL = PORT_PULLUPEN_bm; // CH
+ PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN5CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
+ //PORTA.PIN7CTRL = PORT_PULLUPEN_bm; // HDR
+
+ //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
+ | PORT_ISC_BOTHEDGES_gc; // e-switch
+ //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; // IN- NFET
+
+ // 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; // set the output voltage (off 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/thefreeman/avr32dd20-devkit/model b/hw/thefreeman/avr32dd20-devkit/model
new file mode 100644
index 0000000..216403c
--- /dev/null
+++ b/hw/thefreeman/avr32dd20-devkit/model
@@ -0,0 +1 @@
+1632==20
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.