diff --git a/MODELS b/MODELS index 17536eb3..a0541f27 100644 --- a/MODELS +++ b/MODELS @@ -71,6 +71,7 @@ Model MCU Name 0623 attiny1616 sofirn-lt1s-pro 0631 attiny1616 sofirn-sp10-pro 0632 attiny1616 sofirn-sc21-pro +0633 attiny1616 sofirn-sc13 0712 attiny1616 wurkkos-ts10-rgbaux-lowfet 0713 attiny1616 wurkkos-ts10-rgbaux 0714 attiny1616 wurkkos-ts10 diff --git a/fsm/wdt.c b/fsm/wdt.c index 1095d448..bca81292 100644 --- a/fsm/wdt.c +++ b/fsm/wdt.c @@ -36,6 +36,45 @@ void WDT_inner() { // cache again, in case the value changed ticks_since_last = ticks_since_last_event; + // For lights that have the charging circuit controlled by the MCU, + // detect if charging is active. If so, keep the aux turned off for now + #ifdef CHARGE_IND_CTRL + /* + inline uint8_t roundToNextPowerOf2(uint8_t n) { + if (n == 0) return 1; // Handle the case of 0, next power of 2 is 1 + n--; // Decrement to handle cases where n is already a power of 2 + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n++; + return n; + } + uint8_t tps = roundToNextPowerOf2((go_to_standby) ? SLEEP_TICKS_PER_SECOND : TICKS_PER_SECOND); + */ + if(is_plugged_in) { + if(fully_charged) { + CHARGE_IND_PORT.OUTSET = (1 << CHARGE_IND_GRN); // set the green LED to high + CHARGE_IND_PORT.OUTCLR = (1 << CHARGE_IND_RED); // set the red LED to low + } + //else if (ticks_since_last & tps) { + else { + CHARGE_IND_PORT.OUTCLR = (1 << CHARGE_IND_GRN); // set the green LED to low + CHARGE_IND_PORT.OUTSET = (1 << CHARGE_IND_RED); // set the red LED to high + } + /* + else { + CHARGE_IND_PORT.OUTCLR = (1 << CHARGE_IND_GRN); // set the green LED to low + CHARGE_IND_PORT.OUTCLR = (1 << CHARGE_IND_RED); // set the red LED to low + } + */ + } + else { + CHARGE_IND_PORT.OUTCLR = (1 << CHARGE_IND_GRN); // set the green LED to low + CHARGE_IND_PORT.OUTCLR = (1 << CHARGE_IND_RED); // set the red LED to low + } + + #endif + #ifdef TICK_DURING_STANDBY // handle standby mode specially if (go_to_standby) { diff --git a/hw/sofirn/sc13/anduril.h b/hw/sofirn/sc13/anduril.h new file mode 100644 index 00000000..7313791b --- /dev/null +++ b/hw/sofirn/sc13/anduril.h @@ -0,0 +1,92 @@ +// Sofirn SC13, modelled after the TS25 and TS10 with RGB Aux, but with a buck regulator +// Copyright (C) 2024 +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define HWDEF_H sofirn/sc13/hwdef.h +#include "wurkkos/anduril.h" + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// don't turn on the aux LEDs while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +#define RAMP_SIZE 150 + +// Use 4/4096 as the lowest level (~3 lm) ... anything lower than that seems to flicker +// level_calc.py 5.8 1 150 7135 4 3 1800 --pwm dyn:74:4096:255 +#define PWM1_LEVELS 4,5,5,5,6,6,7,9,9,10,11,12,13,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,30,31,32,33,34,35,37,38,38,39,40,41,42,44,45,46,47,48,48,49,50,51,51,52,53,53,53,53,53,53,53,53,52,52,51,50,49,47,46,44,41,39,36,33,30,27,28,29,30,31,32,33,34,35,37,38,39,40,42,43,45,46,48,49,51,52,54,56,58,59,61,63,65,67,70,72,74,76,79,81,84,86,89,91,94,97,100,103,106,109,112,116,119,123,126,130,134,137,141,145,149,154,158,162,167,171,176,181,186,191,196,202,207,213,218,224,230,236,242,249,255 +#define PWM_TOPS 4095,2941,3765,2556,3480,2623,3210,3517,2950,3192,3334,3414,3453,3088,3125,3137,3130,3110,3080,3042,2998,2948,2896,2840,2783,2723,2663,2603,2542,2584,2518,2453,2388,2325,2263,2273,2210,2148,2088,2030,1973,1918,1912,1858,1805,1754,1704,1619,1575,1531,1489,1418,1380,1343,1280,1222,1166,1114,1064,1017,973,912,872,818,767,719,658,617,563,500,455,399,348,300,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + + +#define MAX_1x7135 75 +#define DEFAULT_LEVEL 50 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 5 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +// 20 38 56 [75] 93 111 130 +#define RAMP_DISCRETE_FLOOR 5 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// 25 50 [75] 100 125 +#define SIMPLE_UI_FLOOR 5 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// and finally, set the default ramp style to Stepped +#undef RAMP_STYLE +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped + +#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses + +// use the brightest setting for strobe +#define STROBE_BRIGHTNESS MAX_LEVEL +// slow down party strobe; this driver can't pulse very fast +#define PARTY_STROBE_ONTIME 12 +#define BIKE_STROBE_ONTIME 8 + +#define BLINK_BRIGHTNESS 30 +#define BLINK_ONCE_TIME 48 // longer blink, since LEDs are slow +#define BLIP_LEVEL 10 +#define BUZZ_SPEED 36 // default buzz of 16ms is too fast to turn LEDs on, slow it down to 36ms + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 42 + +// stop panicking at ~50% power +#define THERM_FASTER_LEVEL 120 // throttle back faster when high +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 50 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default +// (so battcheck will be visible while charging) +#define DEFAULT_BLINK_CHANNEL CM_MAIN +// blink numbers on the aux LEDs by default +//#define DEFAULT_BLINK_CHANNEL CM_AUXWHT + +// Just blink volts plus tenths, not the extra digit +#ifdef USE_EXTRA_BATTCHECK_DIGIT +#undef USE_EXTRA_BATTCHECK_DIGIT +#endif + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// Barry isn't a fan of the voltage-based aux for fear of red LEDs drawing customer concerns, so override the default +#ifdef RGB_LED_OFF_DEFAULT +#undef RGB_LED_OFF_DEFAULT +#endif +#define RGB_LED_OFF_DEFAULT 0x13 // 0x19 = low, voltage; 0x13 = low, cyan; 0x12 = low, green; 0x14 = low, blue +//#define RGB_LED_LOCKOUT_DEFAULT 0x39 // blinking, voltage \ No newline at end of file diff --git a/hw/sofirn/sc13/arch b/hw/sofirn/sc13/arch new file mode 100644 index 00000000..4caf6c04 --- /dev/null +++ b/hw/sofirn/sc13/arch @@ -0,0 +1 @@ +attiny1616 \ No newline at end of file diff --git a/hw/sofirn/sc13/hwdef.c b/hw/sofirn/sc13/hwdef.c new file mode 100644 index 00000000..e5e0f66d --- /dev/null +++ b/hw/sofirn/sc13/hwdef.c @@ -0,0 +1,58 @@ +// Sofirn SC13 helper functions +// Copyright (C) 2024 +// 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[] = { + { // channel 1 only + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + PWM_CNT = 0; // reset phase + BCK_ENABLE_PORT &= ~(1 << BCK_ENABLE_PIN); // disable buck regulator +} + +// single set of LEDs with 1 regulated power channels +void set_level_main(uint8_t level) { + + BCK_ENABLE_PORT |= (1 << BCK_ENABLE_PIN); // enable buck regulator + + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET16(pwm_tops, level); + + CH1_PWM = ch1_pwm; + // wait to sync the counter and avoid flashes + // (unnecessary w/ buffered registers) + //while(actual_level && (PWM_CNT > (top - 32))) {} + PWM_TOP = top; + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + + //GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_SIMPLE (pwm1, CH1_PWM); + + if (pwm1 == CH1_PWM) return true; // done + + return false; // not done yet +} \ No newline at end of file diff --git a/hw/sofirn/sc13/hwdef.h b/hw/sofirn/sc13/hwdef.h new file mode 100644 index 00000000..fdeb99e8 --- /dev/null +++ b/hw/sofirn/sc13/hwdef.h @@ -0,0 +1,191 @@ +// Sofirn SC13 driver layout +// Copyright (C) 2024 +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Driver pinout: + * eSwitch: PA5 + * PWM: PB0 (TCA0 WO0) + * Reg enable: PB2 + * Voltage: PA1 (resistors: 1M and 330K) + * Aux Red: PC3 + * Aux Green: PC2 + * Aux Blue: PC1 + + Potentially... the battery charger (PL7203) signals get routed through the attiny + * PB3 - CH_IN: goes high when USB power is present, otherwise pulled low + * PA6 - CH_OK (STDBY): goes low when charging is complete + * PB4 - green charging indicator, set high to turn on, otherwise set to low + * PB5 - red charging indicator, set to high to turn on, otherwise set to low + + When CH_IN is HIGH (plugged in): + * If CH_OK (PA6) is high (from internal pull-up), then light red indicator and turn off the green + * If CH_OK (PA6) is low (pulled down from PL7203), then light the green indicator and turn off the red + * turn off any aux LEDs from Anduril... what function?? + + When CH_IN goes low (from being high): + * turn off the red and green indicators + * re-enabled Anduril's aux lights + + */ + +#define HWDEF_C sofirn/sc13/hwdef.c + +// allow using aux LEDs as extra channel modes +#include "fsm/chan-rgbaux.h" + +// channel modes: +// * 0. main LED(s) +// * 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 2 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +//#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // main output ramp +//#define PWM2_DATATYPE uint8_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +// not necessary when double-buffered "BUF" registers are used +#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment + +// main output channel +#define CH1_PIN PB0 +#define CH1_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0 + +// Buck regulator enable +#define BCK_ENABLE_PIN PIN2_bp +#define BCK_ENABLE_PORT PORTB_OUT + +// e-switch +#define SWITCH_PIN PIN5_bp +#define SWITCH_PORT VPORTA.IN +#define SWITCH_ISC_REG PORTA.PIN5CTRL +#define SWITCH_VECT PORTA_PORT_vect +#define SWITCH_INTFLG VPORTA.INTFLAGS + +// charging circuit stuff +#define CHARGE_IND_CTRL +#define is_plugged_in (VPORTB.IN & PIN3_bm) +#define fully_charged (~VPORTA.IN & PIN6_bm) +#define CHARGE_IND_PORT PORTB +#define CHARGE_IND_RED PIN5_bp +#define CHARGE_IND_GRN PIN4_bp + +// Voltage divider battLVL +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated +#define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN1_gc // which ADC channel to read + +#undef voltage_raw2cooked +#define voltage_raw2cooked mcu_vdivider_raw2cooked + +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// (voltage - D1) * (R2/(R2+R1) * 1024 / 1.1) +// Resistors are 1,000,000 and 300,000 +#ifndef ADC_44 +#define ADC_44 (4*964) // raw value at 4.40V +#endif +#ifndef ADC_22 +#define ADC_22 (4*460) // raw value at 2.20V // was 473 +#endif + +// this driver allows for aux LEDs under the optic +#define AUXLED_R_PIN PIN3_bp // pin 3 +#define AUXLED_G_PIN PIN2_bp // pin 2 +#define AUXLED_B_PIN PIN1_bp // pin 1 +#define AUXLED_RGB_PORT PORTC // PORTA or PORTB or PORTC + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + + +inline void hwdef_setup() { + + // set up the system clock to run at 10 MHz instead of the default 3.33 MHz + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + //VPORTA.DIR = ...; + // Outputs: PWM and Buck Enable, charging LEDs + VPORTB.DIR = PIN0_bm // PWM output + | PIN2_bm // Buck enable + | PIN4_bm // charging LED: green + | PIN5_bm; // charging LED; red + // RGB aux LEDs + VPORTC.DIR = PIN1_bm + | PIN2_bm + | PIN3_bm; + + // enable pullups on the unused pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + //PORTA.PIN1CTRL = PORT_PULLUPEN_bm; // voltage divider + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch + PORTA.PIN6CTRL = PORT_PULLUPEN_bm; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // PWM output + PORTB.PIN1CTRL = PORT_PULLUPEN_bm; + //PORTB.PIN2CTRL = PORT_PULLUPEN_bm; // Buck Enable + //PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // CH_IN is externally pulled low + //PORTB.PIN4CTRL = PORT_PULLUPEN_bm; // charging LED: green + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // charging LED: green + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + //PORTC.PIN1CTRL = PORT_PULLUPEN_bm; // RGB Aux + //PORTC.PIN2CTRL = PORT_PULLUPEN_bm; // RGB Aux + //PORTC.PIN3CTRL = PORT_PULLUPEN_bm; // RGB Aux + + // set up the PWM + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm + // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm + // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm + // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc + // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc + // See the manual for other pins, clocks, configs, portmux, etc + TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm + | TCA_SINGLE_WGMODE_DSBOTTOM_gc; + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc + | TCA_SINGLE_ENABLE_bm; + + PWM_TOP = PWM_TOP_INIT; + +} + +// 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 + .BODCFG = FUSE_ACTIVE0, // BOD Configuration + .OSCCFG = FUSE_OSCCFG_DEFAULT, // Oscillator Configuration + .TCD0CFG = FUSE_TCD0CFG_DEFAULT, // TCD0 Configuration + .SYSCFG0 = FUSE_SYSCFG0_DEFAULT, // System Configuration 0 + .SYSCFG1 = FUSE_SYSCFG1_DEFAULT, // System Configuration 1 + .APPEND = FUSE_APPEND_DEFAULT, // Application Code Section End + .BOOTEND = FUSE_BOOTEND_DEFAULT, // Boot Section End +}; + +#define LAYOUT_DEFINED \ No newline at end of file diff --git a/hw/sofirn/sc13/model b/hw/sofirn/sc13/model new file mode 100644 index 00000000..d81a3e52 --- /dev/null +++ b/hw/sofirn/sc13/model @@ -0,0 +1 @@ +0633 \ No newline at end of file diff --git a/ui/anduril/aux-leds.c b/ui/anduril/aux-leds.c index 50ce5c51..9f62ed64 100644 --- a/ui/anduril/aux-leds.c +++ b/ui/anduril/aux-leds.c @@ -95,7 +95,17 @@ uint8_t voltage_to_rgb() { void rgb_led_update(uint8_t mode, uint16_t arg) { static uint8_t rainbow = 0; // track state of rainbow mode static uint8_t frame = 0; // track state of animation mode - + + // For lights that have the charging indicator circuit controlled by the MCU, + // detect if charging is active. If so, keep the aux turned off for now. + #ifdef CHARGE_IND_CTRL + // Charging aux is handled in WDT_inner(), so the only action we need to do is to turn off the lights and exit + if (is_plugged_in) { + rgb_led_set(0); + return; + } + #endif + // turn off aux LEDs when battery is empty // (but if voltage==0, that means we just booted and don't know yet) uint8_t volts = voltage; // save a few bytes by caching volatile value diff --git a/ui/anduril/version-check-mode.c b/ui/anduril/version-check-mode.c index 1cd69688..61b00460 100644 --- a/ui/anduril/version-check-mode.c +++ b/ui/anduril/version-check-mode.c @@ -22,7 +22,7 @@ inline void version_check_iter() { else { // "buzz" for non-numeric characters for(uint8_t frame=0; frame<25; frame++) { set_level((frame&1) << 5); - nice_delay_ms(16); + nice_delay_ms(BUZZ_SPEED); } nice_delay_ms(BLINK_SPEED * 8 / 12); } diff --git a/ui/anduril/version-check-mode.h b/ui/anduril/version-check-mode.h index 4d92dfe9..87f24c24 100644 --- a/ui/anduril/version-check-mode.h +++ b/ui/anduril/version-check-mode.h @@ -12,6 +12,10 @@ #error MODEL_NUMBER undefined #endif +#ifndef BUZZ_SPEED +#define BUZZ_SPEED 16 +#endif + #include "anduril/version.h" const PROGMEM uint8_t version_number[] = MODEL_NUMBER "." VERSION_NUMBER; uint8_t version_check_state(Event event, uint16_t arg);