diff options
83 files changed, 2633 insertions, 1296 deletions
diff --git a/arch/attiny1616.c b/arch/attiny1616.c new file mode 100644 index 0000000..c5499dd --- /dev/null +++ b/arch/attiny1616.c @@ -0,0 +1,225 @@ +// arch/attiny1616.c: attiny1616 support functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "arch/attiny1616.h" + +////////// clock speed / delay stuff ////////// + +inline void mcu_clock_speed() { + // TODO: allow hwdef to define a base clock speed + // 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 ); +} + +///// 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 ////////// + +inline void mcu_set_admux_therm() { + // put the ADC in temperature mode + // attiny1616 datasheet section 30.3.2.6 + mcu_set_adc0_vref(VREF_ADC0REFSEL_1V1_gc); // Set Vbg ref to 1.1V + ADC0.MUXPOS = ADC_MUXPOS_TEMPSENSE_gc; // read temperature + ADC0.CTRLB = ADC_SAMPNUM_ACC4_gc; // 10-bit result + 4x oversampling + ADC0.CTRLC = ADC_SAMPCAP_bm + | ADC_PRESC_DIV16_gc + | ADC_REFSEL_INTREF_gc; // Internal ADC reference +} + +inline void mcu_set_admux_voltage() { + // Enabled, free-running (aka, auto-retrigger), run in standby + ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm | ADC_RUNSTBY_bm; + // set a INITDLY value because the AVR manual says so (section 30.3.5) + // (delay 1st reading until Vref is stable) + ADC0.CTRLD |= ADC_INITDLY_DLY16_gc; + #ifdef USE_VOLTAGE_DIVIDER // measure an arbitrary pin + // result = resolution * Vdiv / 1.1V + mcu_set_adc0_vref(VREF_ADC0REFSEL_1V1_gc); // Set Vbg ref to 1.1V + ADC0.MUXPOS = ADMUX_VOLTAGE_DIVIDER; // read the requested ADC pin + ADC0.CTRLB = ADC_SAMPNUM_ACC4_gc; // 12-bit result, 4x oversampling + ADC0.CTRLC = ADC_SAMPCAP_bm + | ADC_PRESC_DIV16_gc + | ADC_REFSEL_INTREF_gc; // Use internal ADC reference + #else // measure VDD pin + // result = resolution * 1.5V / Vbat + mcu_set_adc0_vref(VREF_ADC0REFSEL_1V5_gc); // Set Vbg ref to 1.5V + ADC0.MUXPOS = ADC_MUXPOS_INTREF_gc; // read internal reference + ADC0.CTRLB = ADC_SAMPNUM_ACC4_gc; // 12-bit result, 4x oversampling + ADC0.CTRLC = ADC_SAMPCAP_bm + | ADC_PRESC_DIV16_gc + | ADC_REFSEL_VDDREF_gc; // Vdd (Vcc) be ADC reference + #endif +} + +inline void mcu_adc_sleep_mode() { + set_sleep_mode(SLEEP_MODE_STANDBY); +} + +inline void mcu_adc_start_measurement() { + ADC0.INTCTRL |= ADC_RESRDY_bm; // enable interrupt + ADC0.COMMAND |= ADC_STCONV_bm; // actually start measuring +} + +/* +inline void mcu_adc_on() { + VREF.CTRLA |= VREF_ADC0REFSEL_1V1_gc; // Set Vbg ref to 1.1V + // Enabled, free-running (aka, auto-retrigger), run in standby + ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm | ADC_RUNSTBY_bm; + // set a INITDLY value because the AVR manual says so (section 30.3.5) + // (delay 1st reading until Vref is stable) + ADC0.CTRLD |= ADC_INITDLY_DLY16_gc; + hwdef_set_admux_voltage(); +} +*/ + +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() { + // just return left-aligned ADC result, don't convert to calibrated units + //return ADC0.RES << 6; + return ADC0.RES << 4; +} + +inline uint16_t mcu_adc_result_volts() { + // ADC has no left-aligned mode, so left-align it manually + return ADC0.RES << 4; +} + +inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) { + // In : 65535 * 1.5 / Vbat + // Out: uint8_t: Vbat * 40 + // 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); + #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); + #endif + return vbat40; +} + +#if 0 // fine voltage, 0 to 10.24V in 1/6400th V steps +inline uint16_t mcu_vdd_raw2fine(uint16_t measurement) { + // In : 65535 * 1.5 / Vbat + // Out: 65535 * (Vbat / 10) / 1.024V + uint16_t voltage = ((uint32_t)(1.5 * 4096 * 100 * 64 * 16) / measurement; + return voltage; +} +#endif + +#ifdef USE_VOLTAGE_DIVIDER +inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement) { + // In : 4095 * Vdiv / 1.1V + // Out: uint8_t: Vbat * 40 + // 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)); + uint8_t result = measurement / adc_per_volt; + return result; +} +#endif + +inline uint16_t mcu_temp_raw2cooked(uint16_t measurement) { + // convert raw ADC values to calibrated temperature + // In: ADC raw temperature (16-bit, or 12-bit left-aligned) + // Out: Kelvin << 6 + // Precision: 1/64th Kelvin (but noisy) + // attiny1616 datasheet section 30.3.2.6 + uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // factory calibration data + int8_t sigrow_offset = SIGROW.TEMPSENSE1; + const uint32_t scaling_factor = 65536; // use all 16 bits of ADC data + uint32_t temp = measurement - (sigrow_offset << 6); + temp *= sigrow_gain; // 24-bit result + temp += scaling_factor / 8; // Add 1/8th K to get correct rounding on later divisions + temp = temp >> 8; // change (K << 14) to (K << 6) + return temp; // left-aligned uint16_t, 0 to 1023.98 Kelvin +} + +inline uint8_t mcu_adc_lsb() { + //return (ADCL >> 6) + (ADCH << 2); + return ADC0.RESL; // right aligned, not left... so should be equivalent? +} + + +////////// WDT ////////// + +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; +} + +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 = (1<<6) | (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() { + // put the WDT in hard reset mode, then trigger it + cli(); + // Enable, timeout 8ms + _PROTECTED_WRITE(WDT.CTRLA, WDT_PERIOD_8CLK_gc); + sei(); + wdt_reset(); + while (1) {} +} + +inline void prevent_reboot_loop() { + // prevent WDT from rebooting MCU again + RSTCTRL.RSTFR &= ~(RSTCTRL_WDRF_bm); // reset status flag + wdt_disable(); // from avr/wdt.h +} + diff --git a/arch/attiny1616.h b/arch/attiny1616.h new file mode 100644 index 0000000..940973e --- /dev/null +++ b/arch/attiny1616.h @@ -0,0 +1,134 @@ +// arch/attiny1616.h: attiny1616 support header +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// FIXME: remove this +#define AVRXMEGA3 + +////////// clock speed / delay stuff ////////// + +#define F_CPU 10000000UL +#define BOGOMIPS (F_CPU/4350) +#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 20 MHz, but assume that 10 MHz is the top speed and work from there + // TODO: measure PWM speed and power use at 1.25/2.5/5/10 MHz, to determine which speeds are optimal + clock_div_1 = (CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm), // 10 MHz + clock_div_2 = (CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm), // 5 MHz + clock_div_4 = (CLKCTRL_PDIV_8X_gc | CLKCTRL_PEN_bm), // 2.5 MHz + clock_div_8 = (CLKCTRL_PDIV_16X_gc | CLKCTRL_PEN_bm), // 1.25 MHz + clock_div_16 = (CLKCTRL_PDIV_32X_gc | CLKCTRL_PEN_bm), // 625 kHz + clock_div_32 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 312 kHz, max without changing to the 32 kHz ULP + clock_div_64 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 312 kHz + clock_div_128 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 312 kHz + clock_div_256 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm) // 312 kHz +} clock_div_t; + + +////////// DAC controls ////////// + +#define DAC_LVL DAC0.DATA // 0 to 255, for 0V to Vref +#define DAC_VREF VREF.CTRLA // 0.55V, 1.1V, 1.5V, 2.5V, or 4.3V + +// set only the relevant bits of the Vref register +#define mcu_set_dac_vref(x) \ + VREF.CTRLA = x | (VREF.CTRLA & (~VREF_DAC0REFSEL_gm)); + +// Vref values +// (for the DAC bits, not the ADC bits) +#define V05 V055 +#define V055 VREF_DAC0REFSEL_0V55_gc +#define V11 VREF_DAC0REFSEL_1V1_gc +#define V25 VREF_DAC0REFSEL_2V5_gc +#define V43 VREF_DAC0REFSEL_4V34_gc +#define V15 VREF_DAC0REFSEL_1V5_gc + +////////// ADC voltage / temperature ////////// + +// set only the relevant bits of the Vref register +#define mcu_set_adc0_vref(x) \ + VREF.CTRLA = x | (VREF.CTRLA & (~VREF_ADC0REFSEL_gm)); + +#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(); + +//inline void mcu_adc_on(); + +inline void mcu_adc_off(); + +#define ADC_vect ADC0_RESRDY_vect +inline void mcu_adc_vect_clear(); + +//// both readings are left-aligned +//inline uint16_t mcu_adc_result(); + +// 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(); + +// return Volts * 40, range 0 to 6.375V +#define voltage_raw2cooked mcu_vdd_raw2cooked +inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement); +inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement); + +// return (temp in Kelvin << 6) +#define temp_raw2cooked mcu_temp_raw2cooked +inline uint16_t mcu_temp_raw2cooked(uint16_t measurement); + +inline uint8_t mcu_adc_lsb(); + + +////////// WDT ////////// + +inline void mcu_wdt_active(); + +inline void mcu_wdt_standby(); + +inline void mcu_wdt_stop(); + +// *** Note for the AVRXMEGA3 (1-Series, eg 816 and 817), the WDT +// is not used for time-based interrupts. A new peripheral, the +// Periodic Interrupt Timer ("PIT") is used for this purpose. + +#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 PINA +//#define SWITCH_VECT PCINT0_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/arch/attiny1634.c b/arch/attiny1634.c new file mode 100644 index 0000000..e29d1c3 --- /dev/null +++ b/arch/attiny1634.c @@ -0,0 +1,214 @@ +// arch/attiny1634.c: attiny85 support functions +// Copyright (C) 2014-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "arch/attiny1634.h" + +////////// clock speed / delay stuff ////////// + +inline void mcu_clock_speed() { + // TODO? + // (or not; clock speed is set by the fuses) + // attiny1634 datasheet 6.5 + // CLKSR = [OSCRDY, CSTR, CKOUT_IO, SUT, CKSEL3/2/1/0] + // default 8MHz calibrated internal clock: SUT=0, CKSEL=0b0010 + #if 0 + cli(); + CCP = 0xD8; + CLKSR = 0b01000010; + CCP = 0xD8; + CLKPR = 0; // CLK / 1 = full speed, 8 MHz + sei(); + #endif +} + +///// clock dividers +inline void clock_prescale_set(uint8_t n) { + cli(); + // _PROTECTED_WRITE(CLKPR, n); + CCP = 0xD8; + CLKPR = n; + sei(); +} + +////////// default hw_setup() ////////// + + +////////// ADC voltage / temperature ////////// + +inline void mcu_set_admux_therm() { + // put the ADC in temperature mode + // DS table 19-3, 19-4, internal sensor / 1.1V ref + // [refs1, refs0, refen, adc0en, mux3, mux2, mux1, mux0] + // refs=0b10 : internal 1.1V ref + // mux=0b1110 : internal temperature sensor + //#define ADMUX_THERM 0b10001110 + ADMUX = ADMUX_THERM; + // other stuff is already set, so no need to re-set it +} + +inline void mcu_set_admux_voltage() { + // put the ADC in battery voltage measurement mode + // TODO: avr datasheet references + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER; + // disable digital input on divider pin to reduce power consumption + // TODO: this should be in hwdef init, not here + VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); + #else // VCC / 1.1V reference + ADMUX = ADMUX_VCC; + // disable digital input on VCC pin to reduce power consumption + //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin + #endif + //ACSRA |= (1 << ACD); // turn off analog comparator to save power + // ADCSRB: [VDEN, VDPD, -, -, ADLAR, ADTS2, ADTS1, ADTS0] + ADCSRB = (1 << ADLAR); // left-adjust, free-running + //ADCSRB = 0; // right-adjust, free-running +} + +inline void mcu_adc_sleep_mode() { + set_sleep_mode(SLEEP_MODE_ADC); +} + +inline void mcu_adc_start_measurement() { + // [ADEN, ADSC, ADATE, adif, ADIE, ADPS2, ADPS1, ADPS0] + ADCSRA = (1 << ADEN) // enable + | (1 << ADSC) // start + | (1 << ADATE) // auto-retrigger + | (1 << ADIE) // interrupt enable + | ADC_PRSCL; // prescale +} + +inline void mcu_adc_off() { + ADCSRA &= ~(1<<ADEN); //ADC off +} + +// left-adjusted mode: +inline uint16_t mcu_adc_result() { return ADC; } +//inline uint16_t mcu_adc_result() { return (uint16_t)(ADCL | (ADCH << 8)); } +// right-adjusted mode: +/* +inline uint16_t mcu_adc_result() { + uint16_t result = (ADCL | (ADCH << 8)) << 6; + return result; +} +*/ + +inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) { + // In : 65535 * 1.1 / Vbat + // Out: uint8_t: Vbat * 40 + // 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); + #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); + #endif + return vbat40; +} + + +#ifdef USE_VOLTAGE_DIVIDER +inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement) { + // In : 4095 * Vdiv / 1.1V + // Out: uint8_t: Vbat * 40 + // 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)); + uint8_t result = measurement / adc_per_volt; + return result; +} +#endif + +inline uint16_t mcu_temp_raw2cooked(uint16_t measurement) { + // convert raw ADC values to calibrated temperature + // In: ADC raw temperature (16-bit, or left-aligned) + // Out: Kelvin << 6 + // Precision: 1/64th Kelvin (but noisy) + // attiny1634 datasheet section 19.12 + // nothing to do; input value is already "cooked" + return measurement; +} + +inline uint8_t mcu_adc_lsb() { + // left-adjusted mode: + return (ADCL >> 6) + (ADCH << 2); + // right-adjusted mode: + // datasheet 19.13.3.2: + // "When ADCL is read, the ADC Data Register is not updated + // until ADCH is read. ... ADCL must be read first, then ADCH." + // So... gotta read it even if not needed? + // (the value doesn't matter here, the lower bits are only used + // for generating some random seed data) + //return ADCL + ADCH; +} + + +////////// WDT ////////// + +inline void mcu_wdt_active() { + wdt_reset(); // Reset the WDT + WDTCSR = (1<<WDIE); // Enable interrupt every 16ms +} + +inline void mcu_wdt_standby() { + wdt_reset(); // Reset the WDT + WDTCSR = (1<<WDIE) | STANDBY_TICK_SPEED; +} + +inline void mcu_wdt_stop() { + cli(); // needed because CCP, below + wdt_reset(); // Reset the WDT + MCUSR &= ~(1<<WDRF); // clear watchdog reset flag + // _PROTECTED_WRITE(WDTCSR, 0); + CCP = 0xD8; // enable config changes + WDTCSR = 0; // disable and clear all WDT settings + sei(); +} + + +////////// PCINT - pin change interrupt (e-switch) ////////// + +inline void mcu_pcint_on() { + // enable pin change interrupt + #ifdef SWITCH2_PCIE + GIMSK |= ((1 << SWITCH_PCIE) | (1 << SWITCH2_PCIE)); + #else + GIMSK |= (1 << SWITCH_PCIE); + #endif +} + +inline void mcu_pcint_off() { + // disable all pin-change interrupts + GIMSK &= ~(1 << SWITCH_PCIE); +} + + +////////// misc ////////// + +void reboot() { + // put the WDT in hard reset mode, then trigger it + cli(); + // _PROTECTED_WRITE(WDTCSR, 0b10001000); + // allow protected configuration changes for next 4 clock cycles + CCP = 0xD8; // magic number + // reset (WDIF + WDE), no WDIE, fastest (16ms) timing (0000) + // (DS section 8.5.2 and table 8-4) + WDTCSR = 0b10001000; + sei(); + wdt_reset(); + while (1) {} +} + +inline void prevent_reboot_loop() { + // prevent WDT from rebooting MCU again + MCUSR &= ~(1<<WDRF); // reset status flag + wdt_disable(); // from avr/wdt.h +} + diff --git a/arch/attiny1634.h b/arch/attiny1634.h new file mode 100644 index 0000000..559d04e --- /dev/null +++ b/arch/attiny1634.h @@ -0,0 +1,112 @@ +// arch/attiny1634.h: attiny1634 support header +// Copyright (C) 2014-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// fill in missing values from Atmel's headers +#define PROGMEM_SIZE 16384 +#define EEPROM_SIZE 256 + +////////// clock speed / delay stuff ////////// + +#define F_CPU 8000000UL +#define BOGOMIPS (F_CPU/4000) +#define DELAY_ZERO_TIME 1020 + +inline void mcu_clock_speed(); + +///// clock dividers +inline void clock_prescale_set(uint8_t n); + +// TODO? allow hwdef to define a base clock speed, +// and adjust these values accordingly +typedef enum +{ + // datasheet 6.5.2, CLKPR - Clock Prescale Register + clock_div_1 = 0, // 8 MHz + clock_div_2 = 1, // 4 MHz + clock_div_4 = 2, // 2 MHz + clock_div_8 = 3, // 1 MHz + clock_div_16 = 4, // 500 kHz + clock_div_32 = 5, // 250 kHz + clock_div_64 = 6, // 125 kHz + clock_div_128 = 7, // 62.5 kHz + clock_div_256 = 8, // 31.75 kHz +} clock_div_t; + + +////////// ADC voltage / temperature ////////// + +#define V_REF REFS1 +//#define VOLTAGE_ADC_DIDR DIDR0 // set this in hwdef + +// DS table 19-3, 19-4, 1.1V ref / VCC +#define ADMUX_VCC 0b00001101 +// (1 << V_REF) | (THERM_CHANNEL) +// DS table 19-3, 19-4, internal sensor / 1.1V ref +// [refs1, refs0, refen, adc0en, mux3, mux2, mux1, mux0] +// refs=0b10 : internal 1.1V ref +// mux=0b1110 : internal temperature sensor +#define ADMUX_THERM 0b10001110 + +#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(); + +inline void mcu_adc_off(); + +// NOP because interrupt flag clears itself +#define mcu_adc_vect_clear() + +inline uint16_t mcu_adc_result(); + +// return Volts * 40, range 0 to 6.375V +#define voltage_raw2cooked mcu_vdd_raw2cooked +inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement); +inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement); + +// return (temp in Kelvin << 6) +#define temp_raw2cooked mcu_temp_raw2cooked +inline uint16_t mcu_temp_raw2cooked(uint16_t measurement); + +inline uint8_t mcu_adc_lsb(); + + +////////// WDT ////////// + +inline void mcu_wdt_active(); + +inline void mcu_wdt_standby(); + +inline void mcu_wdt_stop(); + +// NOP because interrupt flag clears itself +#define mcu_wdt_vect_clear() + + +////////// PCINT - pin change interrupt (e-switch) ////////// + +// set these in hwdef +//#define SWITCH_PORT PINA +//#define SWITCH_VECT PCINT0_vect + +// NOP because interrupt flag clears itself +#define 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/arch/attiny85.c b/arch/attiny85.c new file mode 100644 index 0000000..9e298cc --- /dev/null +++ b/arch/attiny85.c @@ -0,0 +1,225 @@ +// arch/attiny85.c: attiny85 support functions +// Copyright (C) 2014-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "arch/attiny85.h" + +////////// clock speed / delay stuff ////////// + +///// clock dividers + +////////// default hw_setup() ////////// + +// FIXME: fsm/main should call hwdef_setup(), not hw_setup, +// and this function should be hwdef_setup +#ifdef USE_GENERIC_HWDEF_SETUP +static inline void hwdef_setup() { + // configure PWM channels + #if PWM_CHANNELS >= 1 + DDRB |= (1 << PWM1_PIN); + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + #if (PWM1_PIN == PB4) // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum + #endif + #endif + // tint ramping needs second channel enabled, + // despite PWM_CHANNELS being only 1 + #if (PWM_CHANNELS >= 2) || defined(USE_TINT_RAMPING) + DDRB |= (1 << PWM2_PIN); + #if (PWM2_PIN == PB4) // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum + #endif + #endif + #if PWM_CHANNELS >= 3 + DDRB |= (1 << PWM3_PIN); + #if (PWM3_PIN == PB4) // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum + #endif + #endif + #if PWM_CHANNELS >= 4 + // 4th PWM channel is ... not actually supported in hardware :( + DDRB |= (1 << PWM4_PIN); + //OCR1C = 255; // Set ceiling value to maximum + TCCR1 = 1<<CTC1 | 1<<PWM1A | 3<<COM1A0 | 2<<CS10; + GTCCR = (2<<COM1B0) | (1<<PWM1B); + // set up an interrupt to control PWM4 pin + TIMSK |= (1<<OCIE1A) | (1<<TOIE1); + #endif + + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin +} +#endif // #ifdef USE_GENERIC_HWDEF_SETUP + + +////////// ADC voltage / temperature ////////// + +inline void mcu_set_admux_therm() { + // put the ADC in temperature mode + ADMUX = ADMUX_THERM | (1 << ADLAR); +} + +inline void mcu_set_admux_voltage() { + // put the ADC in battery voltage measurement mode + // TODO: avr datasheet references + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER | (1 << ADLAR); + // disable digital input on divider pin to reduce power consumption + VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); + #else // VCC / 1.1V reference + ADMUX = ADMUX_VCC | (1 << ADLAR); + // disable digital input on VCC pin to reduce power consumption + //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin + #endif +} + +inline void mcu_adc_sleep_mode() { + set_sleep_mode(SLEEP_MODE_ADC); +} + +inline void mcu_adc_start_measurement() { + ADCSRA = (1 << ADEN) // enable + | (1 << ADSC) // start + | (1 << ADATE) // auto-retrigger + | (1 << ADIE) // interrupt enable + | ADC_PRSCL; // prescale +} + +inline void mcu_adc_off() { + ADCSRA &= ~(1<<ADEN); //ADC off +} + +// left-adjusted mode: +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 + // 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; +} + + +#ifdef USE_VOLTAGE_DIVIDER +inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement) { + // In : 4095 * Vdiv / 1.1V + // Out: uint8_t: Vbat * 40 + // 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)); + uint8_t result = measurement / adc_per_volt; + return result; +} +#endif + +inline uint16_t mcu_temp_raw2cooked(uint16_t measurement) { + // convert raw ADC values to calibrated temperature + // In: ADC raw temperature (16-bit, or left-aligned) + // Out: Kelvin << 6 + // Precision: 1/64th Kelvin (but noisy) + // attiny1634 datasheet section 19.12 + // nothing to do; input value is already "cooked" + return measurement; +} + +inline uint8_t mcu_adc_lsb() { + // left-adjusted mode: + return (ADCL >> 6) + (ADCH << 2); +} + + +////////// WDT ////////// + +inline void mcu_wdt_active() { + // interrupt every 16ms + //cli(); // Disable interrupts + wdt_reset(); // Reset the WDT + WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence + WDTCR = (1<<WDIE); // Enable interrupt every 16ms + //sei(); // Enable interrupts +} + +inline void mcu_wdt_standby() { + // interrupt slower + //cli(); // Disable interrupts + wdt_reset(); // Reset the WDT + WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence + WDTCR = (1<<WDIE) | STANDBY_TICK_SPEED; // Enable interrupt every so often + //sei(); // Enable interrupts +} + +inline void mcu_wdt_stop() { + //cli(); // Disable interrupts + wdt_reset(); // Reset the WDT + MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag + WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence + WDTCR = 0x00; // Disable WDT + //sei(); // Enable interrupts +} + + +////////// PCINT - pin change interrupt (e-switch) ////////// + +inline void mcu_pcint_on() { + // enable pin change interrupt + GIMSK |= (1 << PCIE); + // only pay attention to the e-switch pin + #if 0 // this is redundant; was already done in main() + PCMSK = (1 << SWITCH_PCINT); + #endif + // set bits 1:0 to 0b01 (interrupt on rising *and* falling edge) (default) + // MCUCR &= 0b11111101; MCUCR |= 0b00000001; +} + +inline void mcu_pcint_off() { + // disable all pin-change interrupts + GIMSK &= ~(1 << PCIE); +} + + +////////// misc ////////// + +void reboot() { + // put the WDT in hard reset mode, then trigger it + cli(); + WDTCR = 0xD8 | WDTO_15MS; + sei(); + wdt_reset(); + while (1) {} +} + +inline void prevent_reboot_loop() { + // prevent WDT from rebooting MCU again + MCUSR &= ~(1<<WDRF); // reset status flag + wdt_disable(); +} + + +#if 0 // example for one way of creating a 4th PWM channel +// 4th PWM channel requires manually turning the pin on/off via interrupt :( +ISR(TIMER1_OVF_vect) { + //bitClear(PORTB, 3); + PORTB &= 0b11110111; + //PORTB |= 0b00001000; +} +ISR(TIMER1_COMPA_vect) { + //if (!bitRead(TIFR,TOV1)) bitSet(PORTB, 3); + if (! (TIFR & (1<<TOV1))) PORTB |= 0b00001000; + //if (! (TIFR & (1<<TOV1))) PORTB &= 0b11110111; +} +#endif + diff --git a/arch/attiny85.h b/arch/attiny85.h new file mode 100644 index 0000000..3f6ffcb --- /dev/null +++ b/arch/attiny85.h @@ -0,0 +1,98 @@ +// arch/attiny85.h: attiny85 support header +// Copyright (C) 2014-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// fill in missing values from Atmel's headers +#define PROGMEM_SIZE 8192 +#define EEPROM_SIZE 512 + +////////// clock speed / delay stuff ////////// + +// TODO: Use 6.4 MHz instead of 8 MHz? +#define F_CPU 8000000UL +#define BOGOMIPS (F_CPU/4000) +#define DELAY_ZERO_TIME 1020 + +///// clock dividers +// use clock_prescale_set(n) instead; it's safer +//#define CLOCK_DIVIDER_SET(n) {CLKPR = 1<<CLKPCE; CLKPR = n;} + + +////////// default hw_setup() ////////// + +#ifdef USE_GENERIC_HWDEF_SETUP +static inline void hwdef_setup(); +#endif + + +////////// ADC voltage / temperature ////////// + +#define V_REF REFS1 +#define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR + +// (1 << V_REF) | (0 << ADLAR) | (VCC_CHANNEL) +#define ADMUX_VCC 0b00001100 +// (1 << V_REF) | (0 << ADLAR) | (THERM_CHANNEL) +#define ADMUX_THERM 0b10001111 + +#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(); + +inline void mcu_adc_off(); + +// NOP because interrupt flag clears itself +#define mcu_adc_vect_clear() + +inline uint16_t mcu_adc_result(); + +// return Volts * 40, range 0 to 6.375V +#define voltage_raw2cooked mcu_vdd_raw2cooked +inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement); +inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement); + +// return (temp in Kelvin << 6) +#define temp_raw2cooked mcu_temp_raw2cooked +inline uint16_t mcu_temp_raw2cooked(uint16_t measurement); + +inline uint8_t mcu_adc_lsb(); + + +////////// WDT ////////// + +inline void mcu_wdt_active(); + +inline void mcu_wdt_standby(); + +inline void mcu_wdt_stop(); + +// NOP because interrupt flag clears itself +#define mcu_wdt_vect_clear() + + +////////// PCINT - pin change interrupt (e-switch) ////////// + +#define SWITCH_PORT PINB // PINA or PINB or PINC +#define SWITCH_VECT PCINT0_vect + +// NOP because interrupt flag clears itself +#define 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/arch/avr32dd20.c b/arch/avr32dd20.c new file mode 100644 index 0000000..2ac3526 --- /dev/null +++ b/arch/avr32dd20.c @@ -0,0 +1,264 @@ +// arch/avr32dd20.h: avr32dd20 support functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "arch/avr32dd20.h" + +////////// 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 + // set free-running mode or not (yes) + ADC0.CTRLA = ADC_CONVMODE_SINGLEENDED_gc + | ADC_RESSEL_12BIT_gc + | ADC_LEFTADJ_bm + | ADC_FREERUN_bm; + // set number of samples (requires adjustment in formula too) + ADC0.CTRLB = ADC_SAMPNUM_NONE_gc; + // accumulate more samples for more resolution + ADC0.CTRLB = ADC_SAMPNUM_ACC16_gc; // 16 samples per result + // set a clock prescaler + //ADC0.CTRLC = ADC_PRESC_DIV64_gc; // use this when no accumulation + ADC0.CTRLC = ADC_PRESC_DIV4_gc; // measure faster when oversampling + // 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_DIV4_gc; // measure faster when oversampling + // 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() { + ADC0.INTCTRL |= ADC_RESRDY_bm; // enable interrupt + ADC0.COMMAND |= ADC_STCONV_bm; // actually start measuring +} + +/* +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() { + // value is 12-bit left-aligned + 16x oversampling = 16 bits total + return ADC0.RES; +} + +inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) { + // In : 65535 * (Vbat / 10) / 1.024V + // Out: uint8_t: Vbat * 40 + // (add 80 to round up near a boundary) + uint8_t vbat40 = (uint16_t)(measurement + 80) / 160; + return vbat40; +} + +#if 0 +inline uint16_t mcu_vdd_raw2fine(uint16_t measurement) { + // In : 65535 * (Vbat / 10) / 1.024V + // Out: 65535 * (Vbat / 10) / 1.024V + // This MCU's native format is already correct + return measurement; +} +#endif + +inline uint16_t mcu_temp_raw2cooked(uint16_t measurement) { + // convert raw ADC values to calibrated temperature + // In: ADC raw temperature (16-bit, or 12-bit left-aligned) + // Out: Kelvin << 6 + // Precision: 1/64th Kelvin (but noisy) + // AVR DD datasheet section 33.3.3.8 + uint16_t sigrow_slope = SIGROW.TEMPSENSE0; // factory calibration data + uint16_t sigrow_offset = SIGROW.TEMPSENSE1; // 12-bit value + //const uint32_t scaling_factor = 4096; // use top 12 bits of ADC data + //uint32_t temp = sigrow_offset - (measurement >> 4); + const uint32_t scaling_factor = 65536; // use all 16 bits of ADC data + uint32_t temp = (sigrow_offset << 4) - measurement; + temp *= sigrow_slope; // 24-bit result + temp += scaling_factor / 8; // Add 1/8th K to get correct rounding on later divisions + //temp = temp >> 6; // change (K << 12) to (K << 6) + temp = temp >> 10; // change (K << 16) to (K << 6) + return temp; // left-aligned uint16_t, 0 to 1023.98 Kelvin +} + +inline uint8_t mcu_adc_lsb() { + // volts and temp are both 16-bit, so the LSB is useful as-is + return ADC0_RESL; +} + + +////////// 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..09b4096 --- /dev/null +++ b/arch/avr32dd20.h @@ -0,0 +1,115 @@ +// 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; + + +////////// DAC controls ////////// + +// main LED outputs +#define DAC_LVL DAC0_DATA // 0 to 1023, for 0V to Vref +#define DAC_VREF VREF_DAC0REF // 1.024V, 2.048V, 4.096V, or 2.5V + +// Vref values (suitable for DAC and ADC0) +#define V10 VREF_REFSEL_1V024_gc +#define V20 VREF_REFSEL_2V048_gc +#define V25 VREF_REFSEL_2V500_gc +#define V40 VREF_REFSEL_4V096_gc + + +////////// 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(); + +// both readings are left-aligned +inline uint16_t mcu_adc_result(); + +// 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(); + +// return Volts * 40, range 0 to 6.375V +#define voltage_raw2cooked mcu_vdd_raw2cooked +inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement); + +// return (temp in Kelvin << 6) +#define temp_raw2cooked mcu_temp_raw2cooked +inline uint16_t mcu_temp_raw2cooked(uint16_t measurement); + +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/arch/mcu.c b/arch/mcu.c new file mode 100644 index 0000000..8881c16 --- /dev/null +++ b/arch/mcu.c @@ -0,0 +1,10 @@ +// arch/mcu.c: Attiny portability header. +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "arch/mcu.h" + +#define MCU_C arch/MCUNAME.c +#include incfile(MCU_C) + @@ -5,145 +5,20 @@ // This helps abstract away the differences between various attiny MCUs. -// auto-detect eeprom size from avr-libc headers -#ifndef EEPROM_SIZE - #ifdef E2SIZE - #define EEPROM_SIZE E2SIZE - #elif defined(E2END) - #define EEPROM_SIZE (E2END+1) - #endif -#endif - -/******************** hardware-specific values **************************/ -#if (ATTINY == 13) - #define F_CPU 4800000UL - #define V_REF REFS0 - #define BOGOMIPS 950 - #define ADMUX_VCC 0b00001100 - #define DELAY_ZERO_TIME 252 - #define SWITCH_PORT PINB // PINA or PINB or PINC - #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR -#elif (ATTINY == 25) - // TODO: Use 6.4 MHz instead of 8 MHz? - #define F_CPU 8000000UL - #define V_REF REFS1 - #define BOGOMIPS (F_CPU/4000) - #define ADMUX_VCC 0b00001100 - #define ADMUX_THERM 0b10001111 - #define DELAY_ZERO_TIME 1020 - #define SWITCH_PORT PINB // PINA or PINB or PINC - #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR -#elif (ATTINY == 85) - // TODO: Use 6.4 MHz instead of 8 MHz? - #define F_CPU 8000000UL - #define V_REF REFS1 - #define BOGOMIPS (F_CPU/4000) - // (1 << V_REF) | (0 << ADLAR) | (VCC_CHANNEL) - #define ADMUX_VCC 0b00001100 - // (1 << V_REF) | (0 << ADLAR) | (THERM_CHANNEL) - #define ADMUX_THERM 0b10001111 - #define DELAY_ZERO_TIME 1020 - #define SWITCH_PORT PINB // PINA or PINB or PINC - #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR -#elif (ATTINY == 1634) - #define F_CPU 8000000UL - #define V_REF REFS1 - #define BOGOMIPS (F_CPU/4000) - // DS table 19-3, 19-4, 1.1V ref / VCC - #define ADMUX_VCC 0b00001101 - // (1 << V_REF) | (THERM_CHANNEL) - // DS table 19-3, 19-4, internal sensor / 1.1V ref - #define ADMUX_THERM 0b10001110 - #define DELAY_ZERO_TIME 1020 - //#define SWITCH_PORT PINA // set this in hwdef - //#define VOLTAGE_ADC_DIDR DIDR0 // set this in hwdef -#elif (ATTINY == 412) || (ATTINY == 416) || (ATTINY == 417) || (ATTINY == 816) || (ATTINY == 817) || (ATTINY == 1616) || (ATTINY == 1617) || (ATTINY == 3216) || (ATTINY == 3217) - #define AVRXMEGA3 - #define F_CPU 10000000UL - #define BOGOMIPS (F_CPU/4700) - #define DELAY_ZERO_TIME 1020 -#else - #error Hey, you need to define ATTINY. -#endif - - +#include <avr/eeprom.h> #include <avr/interrupt.h> +#include <avr/io.h> +#include <avr/power.h> +#include <avr/sleep.h> +#include <avr/wdt.h> -/******************** I/O pin and register layout ************************/ -#ifdef HWDEFFILE -#include "fsm/tk.h" -#include incfile(HWDEFFILE) -#endif - -#if 0 // placeholder - -#elif defined(NANJG_LAYOUT) -#include "hwdef-nanjg.h" -#elif defined(FET_7135_LAYOUT) -#include "hwdef-FET_7135.h" +// for consistency, ROM_SIZE + EEPROM_SIZE +#define ROM_SIZE PROGMEM_SIZE -#elif defined(TRIPLEDOWN_LAYOUT) -#include "hwdef-Tripledown.h" - -#elif defined(FERRERO_ROCHER_LAYOUT) -#include "hwdef-Ferrero_Rocher.h" - -#endif // no more recognized driver types - -#ifndef LAYOUT_DEFINED -#error Hey, you need to define an I/O pin layout. -#endif - -#if (ATTINY==13) - // no changes needed -#elif (ATTINY==25) || (ATTINY==45) || (ATTINY==85) - // use clock_prescale_set(n) instead; it's safer - //#define CLOCK_DIVIDER_SET(n) {CLKPR = 1<<CLKPCE; CLKPR = n;} -#elif (ATTINY==1634) - // make it a NOP for now - // FIXME - //#define clock_prescale_set(x) ((void)0) - //#define clock_prescale_set(n) {cli(); CCP = 0xD8; CLKPR = n; sei();} - //#define clock_prescale_set(n) {cli(); CCP = 0xD8; CLKPR = n; sei();} - inline void clock_prescale_set(uint8_t n) {cli(); CCP = 0xD8; CLKPR = n; sei();} - typedef enum - { - clock_div_1 = 0, - clock_div_2 = 1, - clock_div_4 = 2, - clock_div_8 = 3, - clock_div_16 = 4, - clock_div_32 = 5, - clock_div_64 = 6, - clock_div_128 = 7, - clock_div_256 = 8 - } clock_div_t; +#include "fsm/tk.h" -#elif defined(AVRXMEGA3) // ATTINY816, 817, etc - // this should work, but needs further validation - inline void clock_prescale_set(uint8_t n) { - cli(); - CCP = CCP_IOREG_gc; // temporarily disable clock change protection - CLKCTRL.MCLKCTRLB = n; // Set the prescaler - while (CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm) {} // wait for clock change to finish - sei(); - } - typedef enum - { - // Actual clock is 20 MHz, but assume that 10 MHz is the top speed and work from there - // TODO: measure PWM speed and power use at 1.25/2.5/5/10 MHz, to determine which speeds are optimal - clock_div_1 = (CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm), // 10 MHz - clock_div_2 = (CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm), // 5 MHz - clock_div_4 = (CLKCTRL_PDIV_8X_gc | CLKCTRL_PEN_bm), // 2.5 MHz - clock_div_8 = (CLKCTRL_PDIV_16X_gc | CLKCTRL_PEN_bm), // 1.25 MHz - clock_div_16 = (CLKCTRL_PDIV_32X_gc | CLKCTRL_PEN_bm), // 625 kHz - clock_div_32 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 312 kHz, max without changing to the 32 kHz ULP - clock_div_64 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 312 kHz - clock_div_128 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 312 kHz - clock_div_256 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm) // 312 kHz - } clock_div_t; -#else -#error Unable to define MCU macros. -#endif +#define MCU_H arch/MCUNAME.h +#define MCU_C arch/MCUNAME.c +#include incfile(MCU_H) diff --git a/bin/dac-scale.py b/bin/dac-scale.py new file mode 100755 index 0000000..d57f8c9 --- /dev/null +++ b/bin/dac-scale.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +# +# Calculates DAC values and Vref values from a list of raw intensity values +# Usage: dac-scale.py 5,11,18,25,...,370474,384985,400000 +# Output: #defines suitable for use in hw/*/anduril.h +# Assumptions: +# - DAC data range is 0 to 1023 +# - DAC Vrefs are 1.024V and 2.5V +# - HDR is available and has 2 steps +# - HDR channel ratio is "highest_ramp_value / 2500" +# +# Output values thus have 4 "engine gears": +# - HDR low , 1.024V Vref +# - HDR low , 2.5 V Vref +# - HDR high, 1.024V Vref +# - HDR high, 2.5 V Vref +# + +def main(args): + max_pwm = 1023 + raw_pwm = [int(x) for x in args[0].split(',')] + ratio = raw_pwm[-1] / 2500.0 + cooked = [[]] + + def limit(p): + return min(max_pwm, int(p)) + + phase = 0 + for raw in raw_pwm: + if 0 == phase: + if raw <= 1023: + cooked[-1].append(limit(raw)) + else: + phase += 1 + cooked.append([]) + if 1 == phase: + if raw <= int(2500): + cooked[-1].append(limit(raw * 1.024 / 2.5)) + else: + phase += 1 + cooked.append([]) + if 2 == phase: + if raw <= int(1024 * ratio): + cooked[-1].append(limit(raw / ratio)) + else: + phase += 1 + cooked.append([]) + if 3 == phase: + cooked[-1].append(limit(raw * 1.024 / 2.5 / ratio)) + + # "gear change" boundaries + b1 = len(cooked[0]) + b2 = b1 + len(cooked[1]) + b3 = b2 + len(cooked[2]) + b4 = b3 + len(cooked[3]) + + #print(','.join(['%4i' % n for n in cooked])) + + def fmt_pwms(l): + return ','.join(['%4i' % n for n in l]) + + def fmt_tops(v, l): + return ','.join([' %s' % v for n in l]) + + lines = [] + + lines.append('// top level for each "gear": %i %i %i %i' % (b1, b2, b3, b4)) + + lines.append('#define PWM1_LEVELS \\') + lines.append(' ' + fmt_pwms(cooked[0]) + ', \\') + lines.append(' ' + fmt_pwms(cooked[1]) + ', \\') + lines.append(' ' + fmt_pwms(cooked[2]) + ', \\') + lines.append(' ' + fmt_pwms(cooked[3])) + lines.append('#define PWM2_LEVELS \\') + lines.append(' ' + fmt_tops('V10', cooked[0]) + ', \\') + lines.append(' ' + fmt_tops('V25', cooked[1]) + ', \\') + lines.append(' ' + fmt_tops('V10', cooked[2]) + ', \\') + lines.append(' ' + fmt_tops('V25', cooked[3])) + + lines.append('#define MAX_1x7135 %3i' % b2) + lines.append('#define HDR_ENABLE_LEVEL_MIN %3i' % (b2+1)) + + print('\n'.join(lines)) + +if __name__ == "__main__": + import sys + main(sys.argv[1:]) + diff --git a/bin/detect-mcu.sh b/bin/detect-mcu.sh index 57670e2..93b624b 100755 --- a/bin/detect-mcu.sh +++ b/bin/detect-mcu.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash # Anduril / FSM MCU type detection script # Copyright (C) 2014-2023 Selene ToyKeeper # SPDX-License-Identifier: GPL-3.0-or-later @@ -30,7 +30,7 @@ while [ -n "$TARGET" ]; do NUM=$( echo "$MCU" | sed 's/^avr//; s/^attiny//;' ) echo "export MCUNAME=${MCU}" echo "export MCU=0x${NUM}" - echo "export ATTINY=${NUM}" + [[ "$MCU" =~ "attiny" ]] && echo "export ATTINY=${NUM}" echo "export MCUFLAGS=\"-DMCUNAME=${MCU} -DMCU=0x${NUM} -DATTINY=${NUM}\"" exit fi diff --git a/bin/flash-avr32dd20.sh b/bin/flash-avr32dd20.sh new file mode 100755 index 0000000..dcd4967 --- /dev/null +++ b/bin/flash-avr32dd20.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# AVR firmware flashing script, a wrapper for other tools +# Copyright (C) 2023 Selene ToyKeeper +# SPDX-License-Identifier: GPL-3.0-or-later + +# Usage: flash-MCUTYPE.sh hex/foo.hex +# (where the script name specifies the type of MCU chip, +# and the first parameter is the path to a .hex file) +# Example: +# ./bin/flash-avr32dd20.sh hex/anduril.thefreeman-avr32dd20-devkit.hex + +set -e + +# Get the path to a .hex file +[[ -z "$1" ]] && echo "No .hex file specified." && exit 1 +HEX="$1" + +# assume the highest-numbered USB serial device is the correct one, +# since it was probably the most recently plugged in +TTYUSB=$(ls -1 /dev/tty* | grep -i usb | tail -1) + +# figure out the MCU type... +# TODO: find the relevant hw/*/arch file and use that to get MCU type +# use $2 if it exists, and use the name of this script maybe +MCUTYPE="unset" +[[ -n "$2" ]] && MCUTYPE="$2" +#MCUTYPE=$(echo "$0" | sed 's/.*flash-\(.*\).sh.*/\1/') +[[ "$0" =~ flash-(.*).sh ]] && MCUTYPE="${BASH_REMATCH[1]}" + +# Do the actual flashing +echo "Flashing $MCUTYPE MCU on port $TTYUSB: $HEX" +echo pymcuprog write \ + --erase \ + --verify \ + --timing \ + -t uart \ + -u "$TTYUSB" \ + -d "$MCUTYPE" \ + -f "$HEX" + @@ -15,139 +15,22 @@ #include <avr/sleep.h> -static inline void set_admux_therm() { - #if (ATTINY == 1634) - ADMUX = ADMUX_THERM; - #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - ADMUX = ADMUX_THERM | (1 << ADLAR); - #elif (ATTINY == 841) // FIXME: not tested - ADMUXA = ADMUXA_THERM; - ADMUXB = ADMUXB_THERM; - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - ADC0.MUXPOS = ADC_MUXPOS_TEMPSENSE_gc; // read temperature - ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_INTREF_gc; // Internal ADC reference - #else - #error Unrecognized MCU type - #endif +static inline void adc_therm_mode() { + hwdef_set_admux_therm(); adc_channel = 1; adc_sample_count = 0; // first result is unstable ADC_start_measurement(); } -inline void set_admux_voltage() { - #if (ATTINY == 1634) - #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 - ADMUX = ADMUX_VOLTAGE_DIVIDER; - #else // VCC / 1.1V reference - ADMUX = ADMUX_VCC; - #endif - #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 - ADMUX = ADMUX_VOLTAGE_DIVIDER | (1 << ADLAR); - #else // VCC / 1.1V reference - ADMUX = ADMUX_VCC | (1 << ADLAR); - #endif - #elif (ATTINY == 841) // FIXME: not tested - #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 - ADMUXA = ADMUXA_VOLTAGE_DIVIDER; - ADMUXB = ADMUXB_VOLTAGE_DIVIDER; - #else // VCC / 1.1V reference - ADMUXA = ADMUXA_VCC; - ADMUXB = ADMUXB_VCC; - #endif - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - #ifdef USE_VOLTAGE_DIVIDER // 1.1V / ADC input pin - // verify that this is correct!!! untested - ADC0.MUXPOS = ADMUX_VOLTAGE_DIVIDER; // read the requested ADC pin - ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_INTREF_gc; // Use internal ADC reference - #else // VCC / 1.1V reference - ADC0.MUXPOS = ADC_MUXPOS_INTREF_gc; // read internal reference - ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_VDDREF_gc; // Vdd (Vcc) be ADC reference - #endif - #else - #error Unrecognized MCU type - #endif +void adc_voltage_mode() { + hwdef_set_admux_voltage(); adc_channel = 0; adc_sample_count = 0; // first result is unstable ADC_start_measurement(); } -#ifdef TICK_DURING_STANDBY -inline void adc_sleep_mode() { - // needs a special sleep mode to get accurate measurements quickly - // ... full power-down ends up using more power overall, and causes - // some weird issues when the MCU doesn't stay awake enough cycles - // to complete a reading - #ifdef SLEEP_MODE_ADC - // attiny1634 - set_sleep_mode(SLEEP_MODE_ADC); - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - set_sleep_mode(SLEEP_MODE_STANDBY); - #else - #error No ADC sleep mode defined for this hardware. - #endif -} -#endif - -inline void ADC_start_measurement() { - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 841) || (ATTINY == 1634) - ADCSRA |= (1 << ADSC) | (1 << ADIE); - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - ADC0.INTCTRL |= ADC_RESRDY_bm; // enable interrupt - ADC0.COMMAND |= ADC_STCONV_bm; // Start the ADC conversions - #else - #error unrecognized MCU type - #endif -} - -// set up ADC for reading battery voltage -inline void ADC_on() -{ - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) - set_admux_voltage(); - #ifdef USE_VOLTAGE_DIVIDER - // disable digital input on divider pin to reduce power consumption - VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); - #else - // disable digital input on VCC pin to reduce power consumption - //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin - #endif - #if (ATTINY == 1634) - //ACSRA |= (1 << ACD); // turn off analog comparator to save power - ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX - #endif - // enable, start, auto-retrigger, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; - // end tiny25/45/85 - #elif (ATTINY == 841) // FIXME: not tested, missing left-adjust - ADCSRB = 0; // Right adjusted, auto trigger bits cleared. - //ADCSRA = (1 << ADEN ) | 0b011; // ADC on, prescaler division factor 8. - set_admux_voltage(); - // enable, start, auto-retrigger, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; - //ADCSRA |= (1 << ADSC); // start measuring - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - VREF.CTRLA |= VREF_ADC0REFSEL_1V1_gc; // Set Vbg ref to 1.1V - // Enabled, free-running (aka, auto-retrigger), run in standby - ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm | ADC_RUNSTBY_bm; - // set a INITDLY value because the AVR manual says so (section 30.3.5) - // (delay 1st reading until Vref is stable) - ADC0.CTRLD |= ADC_INITDLY_DLY16_gc; - set_admux_voltage(); - #else - #error Unrecognized MCU type - #endif -} - -inline void ADC_off() { - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - ADC0.CTRLA &= ~(ADC_ENABLE_bm); // disable the ADC - #else - ADCSRA &= ~(1<<ADEN); //ADC off - #endif -} - +#if 0 #ifdef USE_VOLTAGE_DIVIDER static inline uint8_t calc_voltage_divider(uint16_t value) { // use 9.7 fixed-point to get sufficient precision @@ -162,6 +45,7 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { return result; } #endif +#endif // Each full cycle runs ~2X per second with just voltage enabled, // or ~1X per second with voltage and temperature. @@ -171,15 +55,11 @@ static inline uint8_t calc_voltage_divider(uint16_t value) { #define ADC_CYCLES_PER_SECOND 2 #endif -#ifdef AVRXMEGA3 // ATTINY816, 817, etc -#define ADC_vect ADC0_RESRDY_vect -#endif // happens every time the ADC sampler finishes a measurement ISR(ADC_vect) { - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - ADC0.INTFLAGS = ADC_RESRDY_bm; // clear the interrupt - #endif + // clear the interrupt flag + mcu_adc_vect_clear(); if (adc_sample_count) { @@ -188,22 +68,11 @@ ISR(ADC_vect) { uint8_t channel = adc_channel; // update the latest value - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - // Use the factory calibrated values in SIGROW.TEMPSENSE0 and SIGROW.TEMPSENSE1 - // to calculate a temperature reading in Kelvin, then left-align it. - if (channel == 1) { // thermal, convert ADC reading to left-aligned Kelvin - 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 - m = (temp << 6); // left align it - } - else { m = (ADC0.RES << 6); } // voltage, force left-alignment - + #ifdef MCU_ADC_RESULT_PER_TYPE + if (channel) m = mcu_adc_result_temp(); + else m = mcu_adc_result_volts(); #else - m = ADC; + m = mcu_adc_result(); #endif adc_raw[channel] = m; @@ -235,11 +104,7 @@ void adc_deferred() { // real-world entropy makes this a true random, not pseudo // Why here instead of the ISR? Because it makes the time-critical ISR // code a few cycles faster and we don't need crypto-grade randomness. - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - pseudo_rand_seed += ADC0.RESL; // right aligned, not left... so should be equivalent? - #else - pseudo_rand_seed += (ADCL >> 6) + (ADCH << 2); - #endif + pseudo_rand_seed += mcu_adc_lsb(); #endif // the ADC triggers repeatedly when it's on, but we only need to run the @@ -281,7 +146,7 @@ void adc_deferred() { ADC_voltage_handler(); #ifdef USE_THERMAL_REGULATION // set the correct type of measurement for next time - if (! go_to_standby) set_admux_therm(); + if (! go_to_standby) adc_therm_mode(); #endif } #endif @@ -291,7 +156,7 @@ void adc_deferred() { ADC_temperature_handler(); #ifdef USE_LVP // set the correct type of measurement for next time - set_admux_voltage(); + adc_voltage_mode(); #endif } #endif @@ -301,7 +166,7 @@ void adc_deferred() { #ifdef USE_LVP -static inline void ADC_voltage_handler() { +static void ADC_voltage_handler() { // rate-limit low-voltage warnings to a max of 1 per N seconds static uint8_t lvp_timer = 0; #define LVP_TIMER_START (VOLTAGE_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between LVP warnings @@ -344,38 +209,23 @@ static inline void ADC_voltage_handler() { #endif else measurement = adc_smooth[0]; - // values stair-step between intervals of 64, with random variations - // of 1 or 2 in either direction, so if we chop off the last 6 bits - // it'll flap between N and N-1... but if we add half an interval, - // the values should be really stable after right-alignment - // (instead of 99.98, 100.00, and 100.02, it'll hit values like - // 100.48, 100.50, and 100.52... which are stable when truncated) - //measurement += 32; - //measurement = (measurement + 16) >> 5; - measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000 - - #ifdef USE_VOLTAGE_DIVIDER - voltage = calc_voltage_divider(measurement); - #else - // calculate actual voltage: volts * 10 - // ADC = 1.1 * 1024 / volts - // volts = 1.1 * 1024 / ADC - voltage = ((uint16_t)(2*1.1*1024*10)/(measurement>>6) - + VOLTAGE_FUDGE_FACTOR - #ifdef USE_VOLTAGE_CORRECTION - + VOLT_CORR - 7 - #endif - ) >> 1; - #endif + // convert raw ADC value to FSM voltage units: Volts * 40 + // 0 .. 200 = 0.0V .. 5.0V + voltage = voltage_raw2cooked(measurement) + + (VOLTAGE_FUDGE_FACTOR << 1) + #ifdef USE_VOLTAGE_CORRECTION + + ((VOLT_CORR - 7) << 1) + #endif + ; // if low, callback EV_voltage_low / EV_voltage_critical // (but only if it has been more than N seconds since last call) if (lvp_timer) { lvp_timer --; } else { // it has been long enough since the last warning - #ifdef DUAL_VOLTAGE_FLOOR - if (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) || (voltage < DUAL_VOLTAGE_LOW_LOW)) { - #else + #ifdef DUAL_VOLTAGE_FLOOR + if (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) || (voltage < DUAL_VOLTAGE_LOW_LOW)) { + #else if (voltage < VOLTAGE_LOW) { #endif // send out a warning @@ -390,7 +240,7 @@ static inline void ADC_voltage_handler() { #ifdef USE_THERMAL_REGULATION // generally happens once per second while awake -static inline void ADC_temperature_handler() { +static void ADC_temperature_handler() { // coarse adjustment #ifndef THERM_LOOKAHEAD #define THERM_LOOKAHEAD 4 @@ -415,19 +265,24 @@ static inline void ADC_temperature_handler() { static uint16_t temperature_history[NUM_TEMP_HISTORY_STEPS]; static int8_t warning_threshold = 0; - if (adc_reset) { // wipe out old data - // ignore average, use latest sample - uint16_t foo = adc_raw[1]; - adc_smooth[1] = foo; - - // forget any past measurements - for(uint8_t i=0; i<NUM_TEMP_HISTORY_STEPS; i++) - temperature_history[i] = (foo + 16) >> 5; - } + if (adc_reset) adc_smooth[1] = adc_raw[1]; // latest 16-bit ADC reading - uint16_t measurement = adc_smooth[1]; + // convert raw ADC value to Kelvin << 6 + // 0 .. 65535 = 0 K .. 1024 K + uint16_t measurement = temp_raw2cooked(adc_smooth[1]); + // let the UI see the current temperature in C + // (Kelvin << 6) to Celsius + // Why 275? Because Atmel's docs use 275 instead of 273. + temperature = (measurement>>6) + THERM_CAL_OFFSET + (int16_t)TH_CAL - 275; + + // instead of (K << 6), use (K << 1) now + // TODO: use more precision, if it can be done without overflow in 16 bits + // (and still work on attiny85 without increasing ROM size) + #if 1 + measurement = measurement >> 5; + #else // TODO: is this still needed? // values stair-step between intervals of 64, with random variations // of 1 or 2 in either direction, so if we chop off the last 6 bits // it'll flap between N and N-1... but if we add half an interval, @@ -437,17 +292,14 @@ static inline void ADC_temperature_handler() { //measurement += 32; measurement = (measurement + 16) >> 5; //measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000 - - // let the UI see the current temperature in C - // Convert ADC units to Celsius (ish) - #ifndef USE_EXTERNAL_TEMP_SENSOR - // onboard sensor for attiny25/45/85/1634 - temperature = (measurement>>1) + THERM_CAL_OFFSET + (int16_t)TH_CAL - 275; - #else - // external sensor - temperature = EXTERN_TEMP_FORMULA(measurement>>1) + THERM_CAL_OFFSET + (int16_t)TH_CAL; #endif + if (adc_reset) { // wipe out old data after waking up + // forget any past measurements + for(uint8_t i=0; i<NUM_TEMP_HISTORY_STEPS; i++) + temperature_history[i] = measurement; + } + // how much has the temperature changed between now and a few seconds ago? int16_t diff; diff = measurement - temperature_history[history_step]; @@ -540,33 +392,58 @@ static inline void ADC_temperature_handler() { #ifdef USE_BATTCHECK #ifdef BATTCHECK_4bars PROGMEM const uint8_t voltage_blinks[] = { - 30, 35, 38, 40, 42, 99, + 4*30, + 4*35, + 4*38, + 4*40, + 4*42, + 255, }; #endif #ifdef BATTCHECK_6bars PROGMEM const uint8_t voltage_blinks[] = { - 30, 34, 36, 38, 40, 41, 43, 99, + 4*30, + 4*34, + 4*36, + 4*38, + 4*40, + 4*41, + 4*43, + 255, }; #endif #ifdef BATTCHECK_8bars PROGMEM const uint8_t voltage_blinks[] = { - 30, 33, 35, 37, 38, 39, 40, 41, 42, 99, + 4*30, + 4*33, + 4*35, + 4*37, + 4*38, + 4*39, + 4*40, + 4*41, + 4*42, + 255, }; #endif void battcheck() { #ifdef BATTCHECK_VpT - blink_num(voltage); - #else - uint8_t i; - for(i=0; - voltage >= pgm_read_byte(voltage_blinks + i); - i++) {} - #ifdef DONT_DELAY_AFTER_BATTCHECK - blink_digit(i); + blink_num(voltage / 4); + #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)); + #endif #else - if (blink_digit(i)) - nice_delay_ms(1000); - #endif + uint8_t i; + for(i=0; + voltage >= pgm_read_byte(voltage_blinks + i); + i++) {} + #ifdef DONT_DELAY_AFTER_BATTCHECK + blink_digit(i); + #else + if (blink_digit(i)) + nice_delay_ms(1000); + #endif #endif } #endif @@ -17,11 +17,11 @@ volatile uint8_t adc_reset = 2; #endif // low-battery threshold in volts * 10 #ifndef VOLTAGE_LOW -#define VOLTAGE_LOW 29 +#define VOLTAGE_LOW (4*29) #endif // battery is low but not critical #ifndef VOLTAGE_RED -#define VOLTAGE_RED 33 +#define VOLTAGE_RED (4*33) #endif // MCU sees voltage 0.X volts lower than actual, add X/2 to readings #ifndef VOLTAGE_FUDGE_FACTOR @@ -32,6 +32,9 @@ volatile uint8_t adc_reset = 2; #endif #endif + +void adc_voltage_mode(); + #ifdef TICK_DURING_STANDBY volatile uint8_t adc_active_now = 0; // sleep LVP needs a different sleep mode #endif @@ -46,7 +49,7 @@ uint16_t adc_smooth[2]; // lowpassed ADC measurements (0=voltage, 1=temperature uint8_t adc_deferred_enable = 0; // stop waiting and run the deferred code void adc_deferred(); // do the actual ADC-related calculations -static inline void ADC_voltage_handler(); +static void ADC_voltage_handler(); uint8_t voltage = 0; #ifdef USE_VOLTAGE_CORRECTION #ifdef USE_CFG @@ -98,15 +101,21 @@ int16_t temperature; uint8_t therm_ceil = DEFAULT_THERM_CEIL; int8_t therm_cal_offset = 0; #endif -static inline void ADC_temperature_handler(); +static void ADC_temperature_handler(); #endif // ifdef USE_THERMAL_REGULATION -inline void ADC_on(); -inline void ADC_off(); -inline void ADC_start_measurement(); +//inline void ADC_on(); +#define ADC_on adc_voltage_mode +//inline void ADC_off(); +#define ADC_off mcu_adc_off +//inline void ADC_start_measurement(); +#define ADC_start_measurement mcu_adc_start_measurement -#ifdef TICK_DURING_STANDBY -inline void adc_sleep_mode(); -#endif +// needs a special sleep mode to get accurate measurements quickly +// ... full power-down ends up using more power overall, and causes +// some weird issues when the MCU doesn't stay awake enough cycles +// to complete a reading +//inline void adc_sleep_mode(); +#define adc_sleep_mode mcu_adc_sleep_mode @@ -6,108 +6,16 @@ #include "fsm/main.h" -#if PWM_CHANNELS == 4 -#ifdef AVRXMEGA3 // ATTINY816, 817, etc -#error 4-channel PWM not currently set up for the AVR 1-Series -#endif -// 4th PWM channel requires manually turning the pin on/off via interrupt :( -ISR(TIMER1_OVF_vect) { - //bitClear(PORTB, 3); - PORTB &= 0b11110111; - //PORTB |= 0b00001000; -} -ISR(TIMER1_COMPA_vect) { - //if (!bitRead(TIFR,TOV1)) bitSet(PORTB, 3); - if (! (TIFR & (1<<TOV1))) PORTB |= 0b00001000; - //if (! (TIFR & (1<<TOV1))) PORTB &= 0b11110111; -} -#endif - -// FIXME: hw_setup() shouldn't be here ... move it entirely to hwdef files -#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) -static inline void hw_setup() { - #if !defined(USE_GENERIC_HWDEF_SETUP) - hwdef_setup(); - #else - // configure PWM channels - #if PWM_CHANNELS >= 1 - DDRB |= (1 << PWM1_PIN); - TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) - TCCR0A = PHASE; - #if (PWM1_PIN == PB4) // Second PWM counter is ... weird - TCCR1 = _BV (CS10); - GTCCR = _BV (COM1B1) | _BV (PWM1B); - OCR1C = 255; // Set ceiling value to maximum - #endif - #endif - // tint ramping needs second channel enabled, - // despite PWM_CHANNELS being only 1 - #if (PWM_CHANNELS >= 2) || defined(USE_TINT_RAMPING) - DDRB |= (1 << PWM2_PIN); - #if (PWM2_PIN == PB4) // Second PWM counter is ... weird - TCCR1 = _BV (CS10); - GTCCR = _BV (COM1B1) | _BV (PWM1B); - OCR1C = 255; // Set ceiling value to maximum - #endif - #endif - #if PWM_CHANNELS >= 3 - DDRB |= (1 << PWM3_PIN); - #if (PWM3_PIN == PB4) // Second PWM counter is ... weird - TCCR1 = _BV (CS10); - GTCCR = _BV (COM1B1) | _BV (PWM1B); - OCR1C = 255; // Set ceiling value to maximum - #endif - #endif - #if PWM_CHANNELS >= 4 - // 4th PWM channel is ... not actually supported in hardware :( - DDRB |= (1 << PWM4_PIN); - //OCR1C = 255; // Set ceiling value to maximum - TCCR1 = 1<<CTC1 | 1<<PWM1A | 3<<COM1A0 | 2<<CS10; - GTCCR = (2<<COM1B0) | (1<<PWM1B); - // set up an interrupt to control PWM4 pin - TIMSK |= (1<<OCIE1A) | (1<<TOIE1); - #endif - - // configure e-switch - PORTB = (1 << SWITCH_PIN); // e-switch is the only input - PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin - #endif // ifdef USE_GENERIC_HWDEF_SETUP -} -#elif (ATTINY == 1634) || defined(AVRXMEGA3) // ATTINY816, 817, etc -static inline void hw_setup() { - // this gets tricky with so many pins... - // ... so punt it to the hwdef file - hwdef_setup(); -} -#else - #error Unrecognized MCU type -#endif - - -//#ifdef USE_REBOOT -static inline void prevent_reboot_loop() { - // prevent WDT from rebooting MCU again - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - RSTCTRL.RSTFR &= ~(RSTCTRL_WDRF_bm); // reset status flag - #else - MCUSR &= ~(1<<WDRF); // reset status flag - #endif - wdt_disable(); -} -//#endif - int main() { // Don't allow interrupts while booting cli(); - //#ifdef USE_REBOOT // prevents cycling after a crash, // whether intentional (like factory reset) or not (bugs) prevent_reboot_loop(); - //#endif - hw_setup(); + hwdef_setup(); #if 0 #ifdef HALFSPEED @@ -124,6 +32,9 @@ int main() { // all booted -- turn interrupts back on PCINT_on(); + // FIXME: support both WDT *and* RTC PIT on newer devices + // (WDT to recover from crashes, PIT for task scheduling) + // (old AVR had only WDT, newer ones split it into WDT, RTC, and PIT) WDT_on(); ADC_on(); sei(); @@ -160,22 +71,7 @@ int main() { // enter standby mode if requested // (works better if deferred like this) if (go_to_standby) { - #ifdef USE_RAMPING set_level(0); - #else - #if PWM_CHANNELS >= 1 - PWM1_LVL = 0; - #endif - #if PWM_CHANNELS >= 2 - PWM2_LVL = 0; - #endif - #if PWM_CHANNELS >= 3 - PWM3_LVL = 0; - #endif - #if PWM_CHANNELS >= 4 - PWM4_LVL = 255; // inverted :( - #endif - #endif standby_mode(); } @@ -118,7 +118,8 @@ uint8_t blink_num(uint8_t num) { #ifdef USE_INDICATOR_LED void indicator_led(uint8_t lvl) { switch (lvl) { - #ifdef AVRXMEGA3 // ATTINY816, 817, etc + // FIXME: move this logic to arch/* + #if (MCU==0x1616) || (MCU==0x32dd20) // ATTINY816, 817, etc case 0: // indicator off AUXLED_PORT.DIRSET = (1 << AUXLED_PIN); // set as output @@ -192,7 +193,8 @@ void indicator_led_auto() { void button_led_set(uint8_t lvl) { switch (lvl) { - #ifdef AVRXMEGA3 // ATTINY816, 817, etc + // FIXME: move this logic to arch/* + #if (MCU==0x1616) || (MCU==0x32dd20) // ATTINY816, 817, etc case 0: // LED off BUTTON_LED_PORT.DIRSET = (1 << BUTTON_LED_PIN); // set as output @@ -240,7 +242,8 @@ void rgb_led_set(uint8_t value) { uint8_t pin = pins[i]; switch (lvl) { - #ifdef AVRXMEGA3 // ATTINY816, 817, etc + // FIXME: move this logic to arch/* + #if (MCU==0x1616) || (MCU==0x32dd20) // ATTINY816, 817, etc case 0: // LED off AUXLED_RGB_PORT.DIRSET = (1 << pin); // set as output @@ -288,25 +291,3 @@ uint8_t triangle_wave(uint8_t phase) { } #endif -#ifdef USE_REBOOT -void reboot() { - // put the WDT in hard reset mode, then trigger it - cli(); - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - WDTCR = 0xD8 | WDTO_15MS; - #elif (ATTINY == 1634) - // allow protected configuration changes for next 4 clock cycles - CCP = 0xD8; // magic number - // reset (WDIF + WDE), no WDIE, fastest (16ms) timing (0000) - // (DS section 8.5.2 and table 8-4) - WDTCSR = 0b10001000; - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - CCP = CCP_IOREG_gc; // temporarily disable change protection - WDT.CTRLA = WDT_PERIOD_8CLK_gc; // Enable, timeout 8ms - #endif - sei(); - wdt_reset(); - while (1) {} -} -#endif - @@ -62,7 +62,3 @@ void rgb_led_set(uint8_t value); uint8_t triangle_wave(uint8_t phase); #endif -#ifdef USE_REBOOT -void reboot(); -#endif - diff --git a/fsm/pcint.c b/fsm/pcint.c index 131d0c3..d00b51d 100644 --- a/fsm/pcint.c +++ b/fsm/pcint.c @@ -13,58 +13,8 @@ uint8_t button_is_pressed() { return value; } -inline void PCINT_on() { - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - // enable pin change interrupt - GIMSK |= (1 << PCIE); - // only pay attention to the e-switch pin - #if 0 // this is redundant; was already done in main() - PCMSK = (1 << SWITCH_PCINT); - #endif - // set bits 1:0 to 0b01 (interrupt on rising *and* falling edge) (default) - // MCUCR &= 0b11111101; MCUCR |= 0b00000001; - #elif (ATTINY == 1634) - // enable pin change interrupt - #ifdef SWITCH2_PCIE - GIMSK |= ((1 << SWITCH_PCIE) | (1 << SWITCH2_PCIE)); - #else - GIMSK |= (1 << SWITCH_PCIE); - #endif - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc) - SWITCH_ISC_REG |= PORT_ISC_BOTHEDGES_gc; - #else - #error Unrecognized MCU type - #endif -} - -inline void PCINT_off() { - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - // disable all pin-change interrupts - GIMSK &= ~(1 << PCIE); - #elif (ATTINY == 1634) - // disable all pin-change interrupts - GIMSK &= ~(1 << SWITCH_PCIE); - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc) - SWITCH_ISC_REG &= ~(PORT_ISC_gm); - #else - #error Unrecognized MCU type - #endif -} - -//void button_change_interrupt() { -#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) - #ifdef PCINT_vect - ISR(PCINT_vect) { - #else - ISR(PCINT0_vect) { - #endif -#elif defined(AVRXMEGA3) // ATTINY816, 817, etc) - ISR(SWITCH_VECT) { - // Write a '1' to clear the interrupt flag - SWITCH_INTFLG |= (1 << SWITCH_PIN); -#else - #error Unrecognized MCU type -#endif +ISR(SWITCH_VECT) { + mcu_switch_vect_clear(); irq_pcint = 1; // let deferred code know an interrupt happened diff --git a/fsm/pcint.h b/fsm/pcint.h index cd7ba02..62f93ab 100644 --- a/fsm/pcint.h +++ b/fsm/pcint.h @@ -9,7 +9,9 @@ volatile uint8_t irq_pcint = 0; // pin change interrupt happened? #define BP_SAMPLES 32 volatile uint8_t button_last_state; uint8_t button_is_pressed(); -inline void PCINT_on(); -inline void PCINT_off(); +//inline void PCINT_on(); +//inline void PCINT_off(); +#define PCINT_on mcu_pcint_on +#define PCINT_off mcu_pcint_off void PCINT_inner(uint8_t pressed); diff --git a/fsm/spaghetti-monster.h b/fsm/spaghetti-monster.h index c035d5b..7084ad4 100644 --- a/fsm/spaghetti-monster.h +++ b/fsm/spaghetti-monster.h @@ -13,10 +13,9 @@ * - ... */ -#include "arch/mcu.h" +////////// include all the .h files ////////// -#include <avr/eeprom.h> -#include <avr/power.h> +#include "arch/mcu.h" // include project definitions to help with recognizing symbols #include "fsm/events.h" @@ -39,6 +38,10 @@ #include "arch/delay.h" #endif +////////// include all the .c files ////////// + +#include "arch/mcu.c" + #ifdef USE_DEBUG_BLINK #define DEBUG_FLASH PWM1_LVL = 64; delay_4ms(2); PWM1_LVL = 0; void debug_blink(uint8_t num) { @@ -7,85 +7,9 @@ #include <avr/interrupt.h> #include <avr/wdt.h> -// *** Note for the AVRXMEGA3 (1-Series, eg 816 and 817), the WDT -// is not used for time-based interrupts. A new peripheral, the -// Periodic Interrupt Timer ("PIT") is used for this purpose. - -void WDT_on() -{ - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - // interrupt every 16ms - //cli(); // Disable interrupts - wdt_reset(); // Reset the WDT - WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence - WDTCR = (1<<WDIE); // Enable interrupt every 16ms - //sei(); // Enable interrupts - #elif (ATTINY == 1634) - wdt_reset(); // Reset the WDT - WDTCSR = (1<<WDIE); // Enable interrupt every 16ms - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - RTC.PITINTCTRL = RTC_PI_bm; // enable the Periodic Interrupt - while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated - RTC.PITCTRLA = RTC_PERIOD_CYC512_gc | RTC_PITEN_bm; // Period = 16ms, enable the PI Timer - #else - #error Unrecognized MCU type - #endif -} - -#ifdef TICK_DURING_STANDBY -inline void WDT_slow() -{ - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - // interrupt slower - //cli(); // Disable interrupts - wdt_reset(); // Reset the WDT - WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence - WDTCR = (1<<WDIE) | STANDBY_TICK_SPEED; // Enable interrupt every so often - //sei(); // Enable interrupts - #elif (ATTINY == 1634) - wdt_reset(); // Reset the WDT - WDTCSR = (1<<WDIE) | STANDBY_TICK_SPEED; - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - RTC.PITINTCTRL = RTC_PI_bm; // enable the Periodic Interrupt - while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated - RTC.PITCTRLA = (1<<6) | (STANDBY_TICK_SPEED<<3) | RTC_PITEN_bm; // Set period, enable the PI Timer - #else - #error Unrecognized MCU type - #endif -} -#endif - -inline void WDT_off() -{ - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - //cli(); // Disable interrupts - wdt_reset(); // Reset the WDT - MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag - WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence - WDTCR = 0x00; // Disable WDT - //sei(); // Enable interrupts - #elif (ATTINY == 1634) - cli(); // needed because CCP, below - wdt_reset(); // Reset the WDT - MCUSR &= ~(1<<WDRF); // clear watchdog reset flag - CCP = 0xD8; // enable config changes - WDTCSR = 0; // disable and clear all WDT settings - sei(); - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated - RTC.PITCTRLA = 0; // Disable the PI Timer - #else - #error Unrecognized MCU type - #endif -} - // clock tick -- this runs every 16ms (62.5 fps) -#ifdef AVRXMEGA3 // ATTINY816, 817, etc -ISR(RTC_PIT_vect) { - RTC.PITINTFLAGS = RTC_PI_bm; // clear the PIT interrupt flag -#else ISR(WDT_vect) { -#endif + mcu_wdt_vect_clear(); irq_wdt = 1; // WDT event happened } @@ -6,8 +6,11 @@ #define TICKS_PER_SECOND 62 -void WDT_on(); -inline void WDT_off(); +//void WDT_on(); +//inline void WDT_off(); +#define WDT_on mcu_wdt_active +#define WDT_slow mcu_wdt_standby +#define WDT_off mcu_wdt_stop volatile uint8_t irq_wdt = 0; // WDT interrupt happened? diff --git a/hw/gchart/fet1-t1616/hwdef.h b/hw/gchart/fet1-t1616/hwdef.h index ac4fd53..40083fc 100644 --- a/hw/gchart/fet1-t1616/hwdef.h +++ b/hw/gchart/fet1-t1616/hwdef.h @@ -11,11 +11,9 @@ * Read voltage from VCC pin, has diode with ~0.4v drop */ -#include <avr/io.h> - // nearly all t1616-based FET+1 drivers work pretty much the same // (this one has single-color aux like the TS10) -#define HWDEF_C_FILE wurkkos/ts10/hwdef.c +#define HWDEF_C wurkkos/ts10/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-aux.h" diff --git a/hw/hank/emisar-2ch/fet/hwdef.h b/hw/hank/emisar-2ch/fet/hwdef.h index 0778e10..33eef7e 100644 --- a/hw/hank/emisar-2ch/fet/hwdef.h +++ b/hw/hank/emisar-2ch/fet/hwdef.h @@ -32,9 +32,7 @@ * The first channel also has a direct-drive FET for turbo. */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/emisar-2ch/fet/hwdef.c +#define HWDEF_C hank/emisar-2ch/fet/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -106,38 +104,10 @@ enum channel_modes_e { #define SWITCH_PCMSK PCMSK0 // PCMSK1 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] #endif -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#include "hank/vdivider-1634.h" // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 diff --git a/hw/hank/emisar-2ch/hwdef.h b/hw/hank/emisar-2ch/hwdef.h index e3707c7..28d1b45 100644 --- a/hw/hank/emisar-2ch/hwdef.h +++ b/hw/hank/emisar-2ch/hwdef.h @@ -31,9 +31,7 @@ * and one pin to control the Opamp power level. */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/emisar-2ch/hwdef.c +#define HWDEF_C hank/emisar-2ch/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -103,38 +101,10 @@ enum channel_modes_e { #define SWITCH_PCMSK PCMSK0 // PCMSK1 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] #endif -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#include "hank/vdivider-1634.h" // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 diff --git a/hw/hank/emisar-d18/hwdef.h b/hw/hank/emisar-d18/hwdef.h index 86c97c2..a0d3cd0 100644 --- a/hw/hank/emisar-d18/hwdef.h +++ b/hw/hank/emisar-d18/hwdef.h @@ -12,9 +12,7 @@ * ---- */ -#include <avr/io.h> - -#define HWDEF_C_FILE lumintop/fw3a/hwdef.c +#define HWDEF_C lumintop/fw3a/hwdef.c // channel modes // * 0. FET+N+1 stacked diff --git a/hw/hank/emisar-d4/hwdef.h b/hw/hank/emisar-d4/hwdef.h index 55ef72e..6257ddb 100644 --- a/hw/hank/emisar-d4/hwdef.h +++ b/hw/hank/emisar-d4/hwdef.h @@ -12,9 +12,7 @@ * ---- */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/emisar-d4/hwdef.c +#define HWDEF_C hank/emisar-d4/hwdef.c // allow using aux LEDs as extra channel modes (when they exist) //#ifdef AUXLED_PIN diff --git a/hw/hank/emisar-d4k-3ch/hwdef.h b/hw/hank/emisar-d4k-3ch/hwdef.h index 7cfe699..81206c2 100644 --- a/hw/hank/emisar-d4k-3ch/hwdef.h +++ b/hw/hank/emisar-d4k-3ch/hwdef.h @@ -35,9 +35,7 @@ * So this code should support both wire layouts. */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/emisar-d4k-3ch/hwdef.c +#define HWDEF_C hank/emisar-d4k-3ch/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -132,38 +130,10 @@ uint8_t led4_pwm, led4_dsm; #define SWITCH_PCMSK PCMSK0 // PCMSK1 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] #endif -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#include "hank/vdivider-1634.h" // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 diff --git a/hw/hank/emisar-d4sv2/hwdef.h b/hw/hank/emisar-d4sv2/hwdef.h index d1e0452..121593a 100644 --- a/hw/hank/emisar-d4sv2/hwdef.h +++ b/hw/hank/emisar-d4sv2/hwdef.h @@ -30,9 +30,7 @@ * ADC12 thermal sensor */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/emisar-d4sv2/hwdef.c +#define HWDEF_C hank/emisar-d4sv2/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -89,7 +87,7 @@ enum CHANNEL_MODES { #define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] #define ADC_PRSCL 0x07 // clk/128 diff --git a/hw/hank/emisar-d4v2/hwdef.h b/hw/hank/emisar-d4v2/hwdef.h index 9e3f755..1c10004 100644 --- a/hw/hank/emisar-d4v2/hwdef.h +++ b/hw/hank/emisar-d4v2/hwdef.h @@ -28,10 +28,8 @@ * ADC12 thermal sensor */ -#include <avr/io.h> - -#ifndef HWDEF_C_FILE -#define HWDEF_C_FILE hank/emisar-d4v2/hwdef.c +#ifndef HWDEF_C +#define HWDEF_C hank/emisar-d4v2/hwdef.c #endif // allow using aux LEDs as extra channel modes @@ -84,7 +82,7 @@ enum CHANNEL_MODES { #define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] #define ADC_PRSCL 0x07 // clk/128 diff --git a/hw/hank/emisar-d4v2/nofet/anduril.h b/hw/hank/emisar-d4v2/nofet/anduril.h index b5f9304..e05fb2e 100644 --- a/hw/hank/emisar-d4v2/nofet/anduril.h +++ b/hw/hank/emisar-d4v2/nofet/anduril.h @@ -4,7 +4,7 @@ #pragma once // switch to 1-channel support functions -#define HWDEF_C_FILE hank/emisar-d4v2/nofet/hwdef.c +#define HWDEF_C hank/emisar-d4v2/nofet/hwdef.c #include "hank/emisar-d4v2/anduril.h" diff --git a/hw/hank/noctigon-dm11/boost/hwdef.h b/hw/hank/noctigon-dm11/boost/hwdef.h index 5ac2daf..789eac9 100644 --- a/hw/hank/noctigon-dm11/boost/hwdef.h +++ b/hw/hank/noctigon-dm11/boost/hwdef.h @@ -36,9 +36,7 @@ * not to change brightness. */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/noctigon-dm11/boost/hwdef.c +#define HWDEF_C hank/noctigon-dm11/boost/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -101,37 +99,9 @@ uint8_t ch1_pwm, ch1_dsm; #define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] - -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] + +#include "hank/vdivider-1634.h" // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 diff --git a/hw/hank/noctigon-dm11/hwdef.h b/hw/hank/noctigon-dm11/hwdef.h index cd21eae..3e3d426 100644 --- a/hw/hank/noctigon-dm11/hwdef.h +++ b/hw/hank/noctigon-dm11/hwdef.h @@ -37,10 +37,8 @@ * Some models also have a direct-drive FET for turbo. */ -#include <avr/io.h> - -#ifndef HWDEF_C_FILE -#define HWDEF_C_FILE hank/noctigon-kr4/hwdef.c +#ifndef HWDEF_C +#define HWDEF_C hank/noctigon-kr4/hwdef.c #endif // allow using aux LEDs as extra channel modes @@ -95,38 +93,10 @@ enum CHANNEL_MODES { #define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] - - -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] + + +#include "hank/vdivider-1634.h" // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 diff --git a/hw/hank/noctigon-dm11/nofet/anduril.h b/hw/hank/noctigon-dm11/nofet/anduril.h index 12336f1..c13f4ab 100644 --- a/hw/hank/noctigon-dm11/nofet/anduril.h +++ b/hw/hank/noctigon-dm11/nofet/anduril.h @@ -4,7 +4,7 @@ #pragma once // same support functions as a KR4 -#define HWDEF_C_FILE hank/noctigon-kr4/nofet/hwdef.c +#define HWDEF_C hank/noctigon-kr4/nofet/hwdef.c #include "hank/noctigon-dm11/anduril.h" // turn off the DD FET diff --git a/hw/hank/noctigon-k1/boost/hwdef.h b/hw/hank/noctigon-k1/boost/hwdef.h index 951932a..db1584c 100644 --- a/hw/hank/noctigon-k1/boost/hwdef.h +++ b/hw/hank/noctigon-k1/boost/hwdef.h @@ -34,9 +34,7 @@ * not to change brightness. */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/noctigon-dm11/boost/hwdef.c +#define HWDEF_C hank/noctigon-dm11/boost/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -99,37 +97,9 @@ uint8_t ch1_pwm, ch1_dsm; #define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] - -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] + +#include "hank/vdivider-1634.h" // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 diff --git a/hw/hank/noctigon-k1/hwdef.h b/hw/hank/noctigon-k1/hwdef.h index 9a68401..0d20871 100644 --- a/hw/hank/noctigon-k1/hwdef.h +++ b/hw/hank/noctigon-k1/hwdef.h @@ -36,10 +36,8 @@ * not to change brightness. */ -#include <avr/io.h> - -#ifndef HWDEF_C_FILE -#define HWDEF_C_FILE hank/noctigon-k1/hwdef.c +#ifndef HWDEF_C +#define HWDEF_C hank/noctigon-k1/hwdef.c #endif // allow using aux LEDs as extra channel modes @@ -88,38 +86,10 @@ enum CHANNEL_MODES { #define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] - - -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] + + +#include "hank/vdivider-1634.h" // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 diff --git a/hw/hank/noctigon-k1/sbt90/hwdef.h b/hw/hank/noctigon-k1/sbt90/hwdef.h index 8186b49..e2d04a9 100644 --- a/hw/hank/noctigon-k1/sbt90/hwdef.h +++ b/hw/hank/noctigon-k1/sbt90/hwdef.h @@ -37,9 +37,7 @@ * Also has a direct-drive FET for turbo. */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/noctigon-kr4/hwdef.c +#define HWDEF_C hank/noctigon-kr4/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -93,38 +91,9 @@ enum CHANNEL_MODES { #define SWITCH_PCMSK PCMSK1 // PCMSK1 is for PCINT[11:8] #define SWITCH_PORT PINB // PINA or PINB or PINC #define SWITCH_PUE PUEB // pullup group B -#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8] - -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// TODO: calibrate this -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#define SWITCH_VECT PCINT1_vect // ISR for PCINT[11:8] + +#include "hank/vdivider-1634.h" #define TEMP_CHANNEL 0b00001111 diff --git a/hw/hank/noctigon-kr4/2ch/hwdef.h b/hw/hank/noctigon-kr4/2ch/hwdef.h index b23c7cc..28a686d 100644 --- a/hw/hank/noctigon-kr4/2ch/hwdef.h +++ b/hw/hank/noctigon-kr4/2ch/hwdef.h @@ -30,8 +30,6 @@ * ADC12 thermal sensor */ -#include <avr/io.h> - // move the switch to a different pin #define SWITCH_PIN PB2 // pin 17 #define SWITCH_PCINT PCINT10 // pin 17 pin change interrupt @@ -39,7 +37,7 @@ #define SWITCH_PCMSK PCMSK1 // PCMSK1 is for PCINT[11:8] #define SWITCH_PORT PINB // PINA or PINB or PINC #define SWITCH_PUE PUEB // pullup group B -#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8] +#define SWITCH_VECT PCINT1_vect // ISR for PCINT[11:8] // the rest of the config is the same as the generic Emisar 2ch build #include "hank/emisar-2ch/hwdef.h" diff --git a/hw/hank/noctigon-kr4/boost/hwdef.h b/hw/hank/noctigon-kr4/boost/hwdef.h index f17d263..b923b30 100644 --- a/hw/hank/noctigon-kr4/boost/hwdef.h +++ b/hw/hank/noctigon-kr4/boost/hwdef.h @@ -46,12 +46,12 @@ #undef SWITCH_PCMSK #undef SWITCH_PORT #undef SWITCH_PUE -#undef PCINT_vect +#undef SWITCH_VECT #define SWITCH_PIN PB2 // pin 17 #define SWITCH_PCINT PCINT10 // pin 17 pin change interrupt #define SWITCH_PCIE PCIE1 // PCIE1 is for PCINT[11:8] #define SWITCH_PCMSK PCMSK1 // PCMSK1 is for PCINT[11:8] #define SWITCH_PORT PINB // PINA or PINB or PINC #define SWITCH_PUE PUEB // pullup group B -#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8] +#define SWITCH_VECT PCINT1_vect // ISR for PCINT[11:8] diff --git a/hw/hank/noctigon-kr4/hwdef.h b/hw/hank/noctigon-kr4/hwdef.h index 586f848..49e71fa 100644 --- a/hw/hank/noctigon-kr4/hwdef.h +++ b/hw/hank/noctigon-kr4/hwdef.h @@ -35,10 +35,8 @@ * Some models also have a direct-drive FET for turbo. */ -#include <avr/io.h> - -#ifndef HWDEF_C_FILE -#define HWDEF_C_FILE hank/noctigon-kr4/hwdef.c +#ifndef HWDEF_C +#define HWDEF_C hank/noctigon-kr4/hwdef.c #endif // allow using aux LEDs as extra channel modes @@ -93,41 +91,13 @@ enum CHANNEL_MODES { #define SWITCH_PCMSK PCMSK1 // PCMSK1 is for PCINT[11:8] #define SWITCH_PORT PINB // PINA or PINB or PINC #define SWITCH_PUE PUEB // pullup group B -#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8] +#define SWITCH_VECT PCINT1_vect // ISR for PCINT[11:8] // the button tends to short out the voltage divider, // so ignore voltage while the button is being held //#define NO_LVP_WHILE_BUTTON_PRESSED -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#include "hank/vdivider-1634.h" // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 diff --git a/hw/hank/noctigon-kr4/nofet/anduril.h b/hw/hank/noctigon-kr4/nofet/anduril.h index 4522cde..ad3f012 100644 --- a/hw/hank/noctigon-kr4/nofet/anduril.h +++ b/hw/hank/noctigon-kr4/nofet/anduril.h @@ -5,7 +5,7 @@ // (and Noctigon KR1) // (and Emisar D4v2 E21A, a.k.a. "D4v2.5") -#define HWDEF_C_FILE hank/noctigon-kr4/nofet/hwdef.c +#define HWDEF_C hank/noctigon-kr4/nofet/hwdef.c #include "hank/noctigon-kr4/anduril.h" // brightness w/ SST-20 4000K LEDs: diff --git a/hw/hank/noctigon-m44/hwdef.h b/hw/hank/noctigon-m44/hwdef.h index af942d9..a397212 100644 --- a/hw/hank/noctigon-m44/hwdef.h +++ b/hw/hank/noctigon-m44/hwdef.h @@ -28,9 +28,7 @@ * ADC12 thermal sensor */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/noctigon-m44/hwdef.c +#define HWDEF_C hank/noctigon-m44/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -110,37 +108,9 @@ uint8_t ch2_pwm, ch2_dsm; #define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] - -#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened -#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 -// pin to ADC mappings are in DS table 19-4 -#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 -// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D -// DS tables 19-3, 19-4 -// Bit 7 6 5 4 3 2 1 0 -// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 -// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 -// divided by ... -// REFS[1:0] = 1, 0 for internal 1.1V reference -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b10000110 -#define ADC_PRSCL 0x07 // clk/128 - -// 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) -// D1, R1, R2 = 0, 330, 100 -#ifndef ADC_44 -//#define ADC_44 981 // raw value at 4.40V -#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 -#endif -#ifndef ADC_22 -//#define ADC_22 489 // raw value at 2.20V -#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 -#endif +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] + +#include "hank/vdivider-1634.h" // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 diff --git a/hw/hank/vdivider-1634.h b/hw/hank/vdivider-1634.h new file mode 100644 index 0000000..171267d --- /dev/null +++ b/hw/hank/vdivider-1634.h @@ -0,0 +1,39 @@ +// attiny1634 voltage divider common defs +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened +#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 +// pin to ADC mappings are in DS table 19-4 +#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 +// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 +#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D +// DS tables 19-3, 19-4 +// Bit 7 6 5 4 3 2 1 0 +// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 +// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 +// divided by ... +// REFS[1:0] = 1, 0 for internal 1.1V reference +// other bits reserved +#define ADMUX_VOLTAGE_DIVIDER 0b10000110 +#undef voltage_raw2cooked +#define voltage_raw2cooked mcu_vdivider_raw2cooked + +#define ADC_PRSCL 0x07 // clk/128 + +// 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) +// D1, R1, R2 = 0, 330, 100 +#ifndef ADC_44 +//#define ADC_44 (4*981) // raw value at 4.40V +#define ADC_44 (4*967) // manually tweaked so 4.16V will blink out 4.2 +#endif +#ifndef ADC_22 +//#define ADC_22 (4*489) // raw value at 2.20V +#define ADC_22 (4*482) // manually tweaked so 4.16V will blink out 4.2 +#endif + + diff --git a/hw/lumintop/blf-gt/anduril.h b/hw/lumintop/blf-gt/anduril.h index 2fc359e..cc2f940 100644 --- a/hw/lumintop/blf-gt/anduril.h +++ b/hw/lumintop/blf-gt/anduril.h @@ -54,7 +54,14 @@ #undef BLINK_AT_RAMP_MIDDLE #undef BLINK_AT_RAMP_FLOOR +#define USE_SMOOTH_STEPS + // too big, turn off extra features //#undef USE_TACTICAL_MODE #undef USE_SOS_MODE +#undef USE_BEACON_MODE +#undef USE_RAMP_AFTER_MOON_CONFIG +//#undef USE_RAMP_SPEED_CONFIG +//#undef USE_VOLTAGE_CORRECTION +#undef USE_2C_STYLE_CONFIG diff --git a/hw/lumintop/blf-gt/hwdef.h b/hw/lumintop/blf-gt/hwdef.h index bf3790d..68197ac 100644 --- a/hw/lumintop/blf-gt/hwdef.h +++ b/hw/lumintop/blf-gt/hwdef.h @@ -16,9 +16,7 @@ * and its output gets PWM'd by pin 5. */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/emisar-d4/hwdef.c +#define HWDEF_C hank/emisar-d4/hwdef.c // channel modes // * 0. main LEDs @@ -69,14 +67,18 @@ enum CHANNEL_MODES { //#define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | (1 << ADLAR) | VOLTAGE_CHANNEL) // 1.1V reference, no left-adjust, ADC1/PB2 #define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | VOLTAGE_CHANNEL) + +#undef voltage_raw2cooked +#define voltage_raw2cooked mcu_vdivider_raw2cooked + #define ADC_PRSCL 0x07 // clk/128 // Raw ADC readings at 4.4V and 2.2V (in-between, we assume values form a straight line) #ifndef ADC_44 -#define ADC_44 (184*4) +#define ADC_44 (184*4*4) #endif #ifndef ADC_22 -#define ADC_22 (92*4) +#define ADC_22 (92*4*4) #endif #define FAST 0xA3 // fast PWM both channels diff --git a/hw/lumintop/fw3a/hwdef.h b/hw/lumintop/fw3a/hwdef.h index 649dc19..7809fa9 100644 --- a/hw/lumintop/fw3a/hwdef.h +++ b/hw/lumintop/fw3a/hwdef.h @@ -12,9 +12,7 @@ * ---- */ -#include <avr/io.h> - -#define HWDEF_C_FILE lumintop/fw3a/hwdef.c +#define HWDEF_C lumintop/fw3a/hwdef.c // channel modes // * 0. FET+7+1 stacked diff --git a/hw/lumintop/fw3x-lume1/README.md b/hw/lumintop/fw3x-lume1/README.md new file mode 100644 index 0000000..8609c49 --- /dev/null +++ b/hw/lumintop/fw3x-lume1/README.md @@ -0,0 +1,26 @@ +# Lumintop FW3X Lume1 + +A BLF FW3A with a new driver from LoneOceans. The new driver adds efficient +constant-current power regulation, RGB aux LEDs, and an upgraded temperature +sensor. + +## Notes of interest + +**Flashing firmware**: The MOSI and MISO pin are swapped, compared to a Hanklight. +LoneOceans sent a fixed driver design to Lumintop, but the new design didn't +get produced. So to flash firmware, swap the wires for those two pins first. + +**RGB mixup**: Lumintop seems to have swapped the wires for aux R and aux B. +This was fixed in firmware in 2023-12, but some lights were fixed in hardware +before that, so the firmware fix might cause the colors to be swapped again. + +**Turbo**: The driver uses regulation for levels 1 to 149, and level 150 is a +direct-drive FET. This is by design, and the FET cannot be ramped smoothly up +and down. Turbo is a single level at the top of the ramp, with a big sudden +drop to the next level. + +**Moon**: This light has a pretty bright preflash at moon level, and the +output is unstable so there is very visible ripple. The user can either raise +the ramp floor to a level high enough to avoid these issues, or learn to live +with the ripple and preflash. + diff --git a/hw/lumintop/fw3x-lume1/anduril.h b/hw/lumintop/fw3x-lume1/anduril.h index b208fbc..cd06c6a 100644 --- a/hw/lumintop/fw3x-lume1/anduril.h +++ b/hw/lumintop/fw3x-lume1/anduril.h @@ -8,10 +8,9 @@ * For more information: www.loneoceans.com/labs/ * Datasheets: * - 1634: http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8303-8-bit-AVR-Microcontroller-tinyAVR-ATtiny1634_Datasheet.pdf - * - 85: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf */ -#include "lumintop/fw3x-lume1/hwdef.h" +#define HWDEF_H lumintop/fw3x-lume1/hwdef.h // set this light for 50C thermal ceiling #undef DEFAULT_THERM_CEIL @@ -20,60 +19,61 @@ // this light has three aux LED channels: R, G, B #define USE_AUX_RGB_LEDS -// it has no independent LED in the button unlike emisar d4 -//#define USE_BUTTON_LED - // 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 -// ../../bin/level_calc.py cube 1 149 7135 0 0.5 1000, with 0 appended to the end. -// for FET PWM (PWM2), all values are 0, except for last value of 1023 -// (with max_pwm set to 1023) #define RAMP_SIZE 150 -//#define PWM1_LEVELS 0,0,0,0,1,1,1,1,2,2,2,3,3,4,4,5,5,6,7,7,8,9,10,11,12,13,14,15,16,17,19,20,22,23,25,26,28,30,32,34,36,38,40,42,45,47,49,52,55,58,60,63,66,70,73,76,80,83,87,91,94,98,102,107,111,115,120,124,129,134,139,144,150,155,160,166,172,178,184,190,196,203,209,216,223,230,237,244,252,259,267,275,283,291,299,308,316,325,334,343,353,362,372,382,392,402,412,423,433,444,455,466,478,489,501,513,525,538,550,563,576,589,602,616,630,644,658,672,687,701,716,731,747,762,778,794,810,827,844,861,878,895,913,930,948,967,985,1004,1023,0 -#define PWM1_LEVELS 1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,56,58,61,64,67,70,74,77,80,84,88,91,95,99,103,108,112,116,121,125,130,135,140,145,150,156,161,167,173,178,184,191,197,203,210,217,223,230,238,245,252,260,268,275,283,292,300,308,317,326,335,344,353,363,372,382,392,402,413,423,434,445,456,467,478,490,502,514,526,538,551,563,576,589,603,616,630,644,658,672,687,702,717,732,747,763,778,794,811,827,844,861,878,895,913,931,949,967,985,1004,1023,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1023 + +// level_calc.py 5.01 1 149 7135 0 0.2 2000 --pwm 32640 +// regulated channel: 16-bit PWM+DSM for low lows and very smooth ramp +#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,29,33,37,41,46,51,57,63,70,77,85,93,103,112,123,134,146,159,172,187,203,219,237,256,276,297,319,343,368,394,422,452,483,516,551,587,625,666,708,753,799,848,900,954,1010,1069,1131,1195,1263,1333,1407,1483,1563,1647,1734,1825,1919,2017,2119,2226,2336,2451,2571,2694,2823,2957,3095,3239,3388,3542,3702,3868,4040,4217,4401,4591,4788,4992,5202,5419,5644,5876,6115,6362,6617,6880,7152,7432,7721,8018,8325,8641,8967,9302,9647,10003,10369,10745,11133,11531,11941,12363,12796,13241,13699,14169,14652,15148,15657,16180,16717,17268,17834,18414,19009,19620,20246,20888,21546,22221,22913,23621,24348,25091,25853,26634,27433,28251,29089,29946,30823,31721,32640,0 +// DD FET: 8-bit PWM (but hardware can only handle 0 and MAX) +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255 #define DEFAULT_LEVEL 56 #define MAX_1x7135 149 -// TODO: test if underclocking works on lume1 -//#define HALFSPEED_LEVEL 14 -//#define QUARTERSPEED_LEVEL 5 -// don't slow down at low levels; this isn't that sort of light -// (it needs to stay at full speed for the 10-bit PWM to work) -#ifdef USE_DYNAMIC_UNDERCLOCKING -#undef USE_DYNAMIC_UNDERCLOCKING -#endif - -// the entire ramp is regulated except turbo; don't blink halfway up -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif +#define MIN_THERM_STEPDOWN 50 +#define HALFSPEED_LEVEL 21 +#define QUARTERSPEED_LEVEL 11 #define RAMP_SMOOTH_FLOOR 1 #define RAMP_SMOOTH_CEIL 149 -// turn on BuckBoost from level 1 to 149, but not 150 -// Level 150 is when BuckBoost is off and FET is ON 100% -#define LED_ENABLE_PIN_LEVEL_MIN 1 -#define LED_ENABLE_PIN_LEVEL_MAX 149 // 10 33 56 79 102 125 [149] #define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL #define RAMP_DISCRETE_STEPS 7 #define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR #define SIMPLE_UI_CEIL 120 #define SIMPLE_UI_STEPS 5 +// 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 + // slow down party strobe; this driver can't pulse for too short a time -#define PARTY_STROBE_ONTIME 4 +#define PARTY_STROBE_ONTIME 1 + +// 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 // stop panicking at ~85% regulated power or ~750 lm #define THERM_FASTER_LEVEL 140 #define THERM_CAL_OFFSET 0 // not needed due to external sensor +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + + // can't reset the normal way because power is connected before the button #define USE_SOFT_FACTORY_RESET diff --git a/hw/lumintop/fw3x-lume1/hwdef.c b/hw/lumintop/fw3x-lume1/hwdef.c index 71cd799..306a58c 100644 --- a/hw/lumintop/fw3x-lume1/hwdef.c +++ b/hw/lumintop/fw3x-lume1/hwdef.c @@ -1,10 +1,11 @@ -// FW3X Lume1 PWM helper functions +// FW3X Lume1 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); @@ -21,6 +22,12 @@ Channel channels[] = { void set_level_zero() { + // disable timer overflow interrupt + // (helps improve button press handling from Off state) + DSM_INTCTRL &= ~DSM_OVF_bm; + + // turn off all LEDs + ch1_dsm_lvl = 0; CH1_PWM = 0; CH2_PWM = 0; PWM_CNT = 0; // reset phase @@ -29,32 +36,109 @@ void set_level_zero() { // single set of LEDs with 2 stacked power channels, regulated + DD FET void set_level_main(uint8_t level) { - CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable regulator + if (level == actual_level - 1) return; // prevent flicker on no-op - PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); - PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + PWM1_DATATYPE ch1 = PWM1_GET(level); + PWM2_DATATYPE ch2 = PWM2_GET(level); - CH1_PWM = ch1_pwm; - CH2_PWM = ch2_pwm; + // set delta-sigma soft levels + ch1_dsm_lvl = ch1; + + // set hardware PWM levels and init dsm loop + CH1_PWM = ch1_pwm = ch1 >> 7; + CH2_PWM = ch2; + + // enable timer overflow interrupt so DSM can work + DSM_INTCTRL |= DSM_OVF_bm; // force reset phase when turning on from zero // (for faster, more consistent initial response) if (! actual_level) PWM_CNT = 0; + + // don't enable ch1 and ch2 at the same time + if (ch2) CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable regulator + else CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable regulator } +// delta-sigma modulation of PWM outputs +// happens on each Timer overflow (every 512 cpu clock cycles) +// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD) +ISR(DSM_vect) { + // set new hardware values first, + // for best timing (reduce effect of interrupt jitter) + CH1_PWM = ch1_pwm; + + // calculate next values, now that timing matters less + + // accumulate error + ch1_dsm += (ch1_dsm_lvl & 0x007f); + // next PWM = base PWM value + carry bit + ch1_pwm = (ch1_dsm_lvl >> 7) + (ch1_dsm > 0x7f); + // clear carry bit + ch1_dsm &= 0x7f; +} + + bool gradual_tick_main(uint8_t gt) { // 150/150 is full FET + zero regulated, // 149/150 is zero FET + full regulated, // so don't try to gradually adjust between - if ((RAMP_SIZE == actual_level) || (gt >= RAMP_SIZE-1)) { - set_level(gt + 1); - return true; - } + // if target is in the top 2 levels, just let the parent handle it + if (gt >= RAMP_SIZE-2) return true; - PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); - GRADUAL_ADJUST_SIMPLE(pwm1, CH1_PWM); + PWM1_DATATYPE ch1 = PWM1_GET(gt); - if (pwm1 == CH1_PWM) return true; // done + // adjust multiple times based on current brightness + // (so it adjusts faster/coarser when bright, slower/finer when dim) + + // higher shift = slower/finer adjustments + const uint8_t shift = 9; // ((255 << 7) >> 9) = 63 max + uint8_t steps; + + steps = ch1_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(ch1, ch1_dsm_lvl); + + if ((ch1 == ch1_dsm_lvl) + ) { + return true; // done + } return false; // not done yet } +////////// external temperature sensor ////////// + +#ifdef ADMUX_THERM_EXTERNAL_SENSOR + +void hwdef_set_admux_therm() { + // put the ADC in temperature mode + // ADCSRB: [VDEN, VDPD, -, -, ADLAR, ADTS2, ADTS1, ADTS0] + ADCSRB = (1 << ADLAR); // left-adjust, free-running + // DS table 19-3, 19-4 + // [refs1, refs0, refen, adc0en, mux3, mux2, mux1, mux0] + // refs=0b00 : VCC (2.5V) + // mux=0b1011 : ADC11 (pin PC2) + ADMUX = ADMUX_THERM_EXTERNAL_SENSOR; + // other stuff is already set, so no need to re-set it +} + +uint16_t temp_raw2cooked(uint16_t measurement) { + // In: (raw ADC average) << 6 + // Out: Kelvin << 6 + /* notes from old versions of the code... + // Used for Lume1 Driver: MCP9700 - T_Celsius = 100*(VOUT - 0.5V) + // ADC is 2.5V reference, 0 to 1023 + // due to floating point, this calculation takes 916 extra bytes + // (should use an integer equivalent instead) + //#define EXTERN_TEMP_FORMULA(m) (((m)-205)/4.09) + //int16_t celsius = (((measurement-205)/4.09)) + THERM_CAL_OFFSET + (int16_t)therm_cal_offset; + */ + + // this formula could probably be simplified... but meh, it works + uint16_t k6 = ((uint32_t)(measurement - (205 << 6)) * 100 / 409) + + (273 << 6); // convert back from C to K + return k6; +} + +#endif + diff --git a/hw/lumintop/fw3x-lume1/hwdef.h b/hw/lumintop/fw3x-lume1/hwdef.h index 943921f..021c4cf 100644 --- a/hw/lumintop/fw3x-lume1/hwdef.h +++ b/hw/lumintop/fw3x-lume1/hwdef.h @@ -8,9 +8,9 @@ * * Pin / Name / Function in Lume1 Rev B * 1 PA6 Regulated PWM (PWM1B) - * 2 PA5 R: red aux LED (PWM0B) + * 2 PA5 B: blue aux LED (PWM0B) (or red) * 3 PA4 G: green aux LED - * 4 PA3 B: blue aux LED + * 4 PA3 R: red aux LED (or blue) * 5 PA2 e-switch (PCINT2) * 6 PA1 Jumper 1 * 7 PA0 Jumper 2 @@ -34,9 +34,7 @@ * Another pin is used for DD FET control. */ -#include <avr/io.h> - -#define HWDEF_C_FILE lumintop/fw3x-lume1/hwdef.c +#define HWDEF_C lumintop/fw3x-lume1/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -56,29 +54,38 @@ enum CHANNEL_MODES { #define CHANNEL_MODES_ENABLED 0b0000000000000001 -#define PWM_CHANNELS 2 // old, remove this - -// Added for Lume1 Buck Boost Driver -#define PWM_BITS 16 // 0 to 1023 at 3.9 kHz, not 0 to 255 at 15.6 kHz -#define PWM_GET PWM_GET16 +#define PWM_BITS 16 // 0 to 32640 (0 to 255 PWM + 0 to 127 DSM) at constant kHz #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 // regulated ramp -#define PWM2_DATATYPE uint16_t // DD FET ramp +#define PWM1_GET(x) PWM_GET16(pwm1_levels, x) +#define PWM2_DATATYPE uint8_t // DD FET ramp +#define PWM2_GET(x) PWM_GET8(pwm2_levels, x) // PWM parameters of both channels are tied together because they share a counter #define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM -#define PWM_TOP_INIT 1023 -#define PWM_CNT TCNT1 // for dynamic PWM, reset phase +#define PWM_TOP_INIT 255 +#define PWM_CNT TCNT1 // for checking / resetting phase +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define DSM_TOP (255<<7) // 15-bit resolution leaves 1 bit for carry + +// timer interrupt for DSM +#define DSM_vect TIMER1_OVF_vect +#define DSM_INTCTRL TIMSK +#define DSM_OVF_bm (1<<TOIE1) + +#define DELAY_FACTOR 85 // less time in delay() because more time spent in interrupts -// regulated channel +// regulated channel uses PWM+DSM +uint16_t ch1_dsm_lvl; +uint8_t ch1_pwm, ch1_dsm; #define CH1_PIN PA6 // pin 1, Buck Boost CTRL pin or 7135-eqv PWM #define CH1_PWM OCR1B // OCR1B is the output compare register for PA6 #define CH1_ENABLE_PIN PA7 // pin 20, BuckBoost Enable #define CH1_ENABLE_PORT PORTA // control port for PA7 -// DD FET channel -#define CH2_PIN PB3 // pin 16, FET PWM Pin, but only used as on (1023) or off (0) +// DD FET channel is on/off only (PWM=0, or PWM=MAX) +#define CH2_PIN PB3 // pin 16, FET PWM Pin, but only used as on (255) or off (0) #define CH2_PWM OCR1A // OCR1A is the output compare register for PB3 /* // For Jumpers X1 to X4, no SW support yet @@ -95,7 +102,7 @@ enum CHANNEL_MODES { #define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] #define SWITCH_PORT PINA // PINA or PINB or PINC #define SWITCH_PUE PUEA // pullup group A -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] +#define SWITCH_VECT PCINT0_vect // ISR for PCINT[7:0] #define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened #define VOLTAGE_PIN PB0 // Pin 19 PB0 ADC5 @@ -113,16 +120,19 @@ enum CHANNEL_MODES { #define ADMUX_VOLTAGE_DIVIDER 0b00000101 #define ADC_PRSCL 0x06 // clk/64 +#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: // [(Vbatt)*(R2/(R2+R1)) / 2.5V] * 1023 // R1 = R2 = 100kR #ifndef ADC_44 -#define ADC_44 900 +#define ADC_44 (4*900) #endif #ifndef ADC_22 -#define ADC_22 450 +#define ADC_22 (4*450) #endif // Default ADMUX_THERM for Temperature is: 0b10001110 in arch/mcu.h @@ -132,18 +142,26 @@ enum CHANNEL_MODES { // Modified fsm-adc.c to use different ADMUX and ADC_temperature_handler() // based on USE_EXTERNAL_TEMP_SENSOR // See line 34 and line 209 -#define USE_EXTERNAL_TEMP_SENSOR -#define ADMUX_THERM_EXTERNAL_SENSOR 0b00001011 // VCC reference (2.5V), Channel PC2 -// Used for Lume1 Driver: MCP9700 - T_Celsius = 100*(VOUT - 0.5V) -// ADC is 2.5V reference, 0 to 1023 -// FIXME: due to floating point, this calculation takes 916 extra bytes -// (should use an integer equivalent instead) -#define EXTERN_TEMP_FORMULA(m) (((m)-205)/4.09) +//#define USE_EXTERNAL_TEMP_SENSOR + +// override the default temperature sensor code +#undef hwdef_set_admux_therm +void hwdef_set_admux_therm(); +#undef temp_raw2cooked +uint16_t temp_raw2cooked(uint16_t measurement); +// VCC reference (2.5V), Channel PC2 +#define ADMUX_THERM_EXTERNAL_SENSOR 0b00001011 // this driver allows for aux LEDs under the optic -#define AUXLED_R_PIN PA5 // pin 2 -#define AUXLED_G_PIN PA4 // pin 3 -#define AUXLED_B_PIN PA3 // pin 4 +#ifdef FW3X_RGB_SWAP // wiring fixed by end user + #define AUXLED_R_PIN PA5 // pin 2 + #define AUXLED_G_PIN PA4 // pin 3 + #define AUXLED_B_PIN PA3 // pin 4 +#else // Lumintop's factory wiring + #define AUXLED_R_PIN PA3 // pin 4 + #define AUXLED_G_PIN PA4 // pin 3 + #define AUXLED_B_PIN PA5 // pin 2 +#endif #define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC #define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC #define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC @@ -173,24 +191,31 @@ inline void hwdef_setup() { PUEC = (1 << JUMPER4_PIN); */ - // configure PWM for 10 bit at 3.9kHz + // configure PWM // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter // pre-scale for timer: N = 1 - // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (DS table 12-5) + // PWM for both channels + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (DS table 12-5) + // WGM1[3:0]: 1,1,1,0: PWM, Fast, adjustable (DS table 12-5) // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6) // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4) // COM1B[1:0]: 1,0: PWM OC1B in the normal direction (DS table 12-4) - TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5) - | (1<<COM1A1) | (0<<COM1A0) // PWM 1A Clear OC1A on Compare Match - | (1<<COM1B1) | (0<<COM1B0) // PWM 1B Clear OC1B on Compare Match - ; - TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) - | (0<<WGM13) | (0<<WGM12) // PWM, Phase Correct, 10-bit - ; + TCCR1A = (1<<WGM11) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (DS table 12-5) + | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4) + | (1<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4) + ; + TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) + //| (1<<WGM13) | (1<<WGM12) // fast adjustable PWM (DS table 12-5) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; // set PWM resolution PWM_TOP = PWM_TOP_INIT; + // set up interrupt for delta-sigma modulation + // (moved to hwdef.c functions so it can be enabled/disabled based on ramp level) + //DSM_INTCTRL |= DSM_OVF_bm; // interrupt once for each timer cycle + // set up e-switch SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt diff --git a/hw/lumintop/fw3x-lume1/rgbswap/anduril.h b/hw/lumintop/fw3x-lume1/rgbswap/anduril.h new file mode 100644 index 0000000..2fba7ff --- /dev/null +++ b/hw/lumintop/fw3x-lume1/rgbswap/anduril.h @@ -0,0 +1,11 @@ +// FW3X (RGB aux swapped) config options for Anduril +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// Lumintop swapped red and blue wires. Some people fixed the wiring. +// Then the firmware fixed the pinouts to match Lumintop's wiring. +// This firmware exists for people who fixed their wiring. +#define FW3X_RGB_SWAP +#include "lumintop/fw3x-lume1/anduril.h" + diff --git a/hw/lumintop/fw3x-lume1/rgbswap/model b/hw/lumintop/fw3x-lume1/rgbswap/model new file mode 100644 index 0000000..44505ce --- /dev/null +++ b/hw/lumintop/fw3x-lume1/rgbswap/model @@ -0,0 +1 @@ +0315 diff --git a/hw/mateminco/mf01-mini/hwdef.h b/hw/mateminco/mf01-mini/hwdef.h index f245042..82dafb2 100644 --- a/hw/mateminco/mf01-mini/hwdef.h +++ b/hw/mateminco/mf01-mini/hwdef.h @@ -12,9 +12,7 @@ * ---- */ -#include <avr/io.h> - -#define HWDEF_C_FILE lumintop/fw3a/hwdef.c +#define HWDEF_C lumintop/fw3a/hwdef.c // channel modes // * 0. FET+N+1 stacked diff --git a/hw/mateminco/mf01s/hwdef.h b/hw/mateminco/mf01s/hwdef.h index 12451d7..21ea1fd 100644 --- a/hw/mateminco/mf01s/hwdef.h +++ b/hw/mateminco/mf01s/hwdef.h @@ -12,9 +12,7 @@ * ---- */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/emisar-d4/hwdef.c +#define HWDEF_C hank/emisar-d4/hwdef.c // channel modes // * 0. small FET + big FET stacked @@ -69,14 +67,18 @@ enum CHANNEL_MODES { // 1.1V reference, no left-adjust, ADC1/PB2 #define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | VOLTAGE_CHANNEL) #endif + +#undef voltage_raw2cooked +#define voltage_raw2cooked mcu_vdivider_raw2cooked + #define ADC_PRSCL 0x07 // clk/128 // Raw ADC readings at 4.4V and 2.2V (in-between, we assume values form a straight line) #ifndef ADC_44 -#define ADC_44 (234*4) +#define ADC_44 (234*4*4) #endif #ifndef ADC_22 -#define ADC_22 (117*4) +#define ADC_22 (117*4*4) #endif #define FAST 0xA3 // fast PWM both channels diff --git a/hw/mateminco/mt35-mini/hwdef.h b/hw/mateminco/mt35-mini/hwdef.h index aec4eaf..ac7bf07 100644 --- a/hw/mateminco/mt35-mini/hwdef.h +++ b/hw/mateminco/mt35-mini/hwdef.h @@ -12,9 +12,7 @@ * ---- */ -#include <avr/io.h> - -#define HWDEF_C_FILE hank/emisar-d4/hwdef.c +#define HWDEF_C hank/emisar-d4/hwdef.c // channel modes // * 0. FET+7135 stacked diff --git a/hw/sofirn/blf-lt1-t1616/hwdef.h b/hw/sofirn/blf-lt1-t1616/hwdef.h index 66575de..c9ae1ea 100644 --- a/hw/sofirn/blf-lt1-t1616/hwdef.h +++ b/hw/sofirn/blf-lt1-t1616/hwdef.h @@ -12,9 +12,7 @@ * Voltage: VCC */ -#include <avr/io.h> - -#define HWDEF_C_FILE sofirn/blf-lt1-t1616/hwdef.c +#define HWDEF_C sofirn/blf-lt1-t1616/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-aux.h" diff --git a/hw/sofirn/blf-lt1/hwdef.h b/hw/sofirn/blf-lt1/hwdef.h index 03f3568..a57b1c4 100644 --- a/hw/sofirn/blf-lt1/hwdef.h +++ b/hw/sofirn/blf-lt1/hwdef.h @@ -12,9 +12,7 @@ * ---- */ -#include <avr/io.h> - -#define HWDEF_C_FILE sofirn/blf-lt1/hwdef.c +#define HWDEF_C sofirn/blf-lt1/hwdef.c // channel modes: // * 0. channel 1 only diff --git a/hw/sofirn/blf-q8-t1616/hwdef.h b/hw/sofirn/blf-q8-t1616/hwdef.h index 29c2ffa..638a2c1 100644 --- a/hw/sofirn/blf-q8-t1616/hwdef.h +++ b/hw/sofirn/blf-q8-t1616/hwdef.h @@ -15,11 +15,9 @@ * Voltage: VCC */ -#include <avr/io.h> - // nearly all t1616-based FET+1 drivers work pretty much the same // (this one has single-color aux like the TS10) -#define HWDEF_C_FILE wurkkos/ts10/hwdef.c +#define HWDEF_C wurkkos/ts10/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-aux.h" diff --git a/hw/sofirn/lt1s-pro/hwdef.h b/hw/sofirn/lt1s-pro/hwdef.h index cd4dd4e..7708631 100644 --- a/hw/sofirn/lt1s-pro/hwdef.h +++ b/hw/sofirn/lt1s-pro/hwdef.h @@ -13,9 +13,7 @@ * Voltage: VCC */ -#include <avr/io.h> - -#define HWDEF_C_FILE sofirn/lt1s-pro/hwdef.c +#define HWDEF_C sofirn/lt1s-pro/hwdef.c // channel modes: // * 0. warm/cool white blend diff --git a/hw/sofirn/sp10-pro/hwdef.h b/hw/sofirn/sp10-pro/hwdef.h index a52166d..f220318 100644 --- a/hw/sofirn/sp10-pro/hwdef.h +++ b/hw/sofirn/sp10-pro/hwdef.h @@ -12,9 +12,7 @@ * PA1 : Boost Enable */ -#include <avr/io.h> - -#define HWDEF_C_FILE sofirn/sp10-pro/hwdef.c +#define HWDEF_C sofirn/sp10-pro/hwdef.c // channel modes: // * 0. low+high PWM stacked @@ -61,25 +59,26 @@ enum CHANNEL_MODES { #define SWITCH_ISC_REG PORTB.PIN3CTRL #define SWITCH_VECT PORTB_PORT_vect #define SWITCH_INTFLG VPORTB.INTFLAGS -#define SWITCH_PCINT PCINT0 -#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] // Voltage divider battLVL #define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated -#define DUAL_VOLTAGE_FLOOR 21 // for AA/14500 boost drivers, don't indicate low voltage if below this level -#define DUAL_VOLTAGE_LOW_LOW 7 // the lower voltage range's danger zone 0.7 volts (NiMH) +#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 ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN9_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 300,000 and 100,000 #ifndef ADC_44 -#define ADC_44 1023 // raw value at 4.40V +#define ADC_44 (4*1023) // raw value at 4.40V #endif #ifndef ADC_22 -#define ADC_22 512 // raw value at 2.20V +#define ADC_22 (4*512) // raw value at 2.20V #endif diff --git a/hw/thefreeman/avr32dd20-devkit/anduril.h b/hw/thefreeman/avr32dd20-devkit/anduril.h new file mode 100644 index 0000000..02d5de3 --- /dev/null +++ b/hw/thefreeman/avr32dd20-devkit/anduril.h @@ -0,0 +1,121 @@ +// thefreeman's avr32dd20 devkit board +// Copyright (C) 2023 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 +// 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 + +// 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, because it's a dev board + +// 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 +// turn on the aux LEDs while main LEDs are on +// because this is a dev board and it's useful to see that +#define USE_AUX_RGB_LEDS_WHILE_ON 20 +#define USE_INDICATOR_LED_WHILE_RAMPING + +// 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..3e5dd79 --- /dev/null +++ b/hw/thefreeman/avr32dd20-devkit/hwdef.c @@ -0,0 +1,118 @@ +// 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 +} + + +uint8_t voltage_raw2cooked(uint16_t measurement) { + // In : 65535 * BATTLVL / 1.024V + // Out: uint8_t: Vbat * 40 + // BATTLVL = Vbat * (100.0/(330+100)) = Vbat / 4.3 + // So, Out = In * 4.3 / 1600 + // (plus a bit of fudging to fix the slope and offset, + // based on measuring actual hardware) + uint8_t result = (uint32_t)(measurement + (65535 * 4 / 1024)) + * 43 / 16128; + return result; +} + diff --git a/hw/thefreeman/avr32dd20-devkit/hwdef.h b/hw/thefreeman/avr32dd20-devkit/hwdef.h new file mode 100644 index 0000000..f1b6095 --- /dev/null +++ b/hw/thefreeman/avr32dd20-devkit/hwdef.h @@ -0,0 +1,206 @@ +// 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 + * BATT LVL : Vbat * (100.0/(330+100)) + * 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 + + +#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 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 +#define DUAL_VOLTAGE_FLOOR (4*21) // for AA/14500 boost drivers, don't indicate low voltage if below this level +#define DUAL_VOLTAGE_LOW_LOW (4*7) // the lower voltage range's danger zone 0.7 volts (NiMH) +// don't use the default VDD converter +// convert BATT LVL pin readings to FSM volt units +#undef voltage_raw2cooked +uint8_t voltage_raw2cooked(uint16_t measurement); + + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 0 // using a PFET so no appreciable drop +#endif + +// this driver allows for aux LEDs under the optic +#define AUXLED_R_PIN 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 + // to reduce overall system power + 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; // BATT LVL + //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; // 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/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 diff --git a/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/anduril.h b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/anduril.h index eea8887..d1b1661 100644 --- a/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/anduril.h +++ b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/anduril.h @@ -19,15 +19,17 @@ // - high 0.55V // - high 2.5V // PWM1: DAC Data -#define PWM1_LEVELS 2, 3, 4, 5, 6, 8, 9, 11, 14, 16, 19, 23, 26, 31, 35, 41, 47, 54, 61, 69, 78, 89,100,112,125,140,155,173,191,212,234, \ - 56, 62, 68, 74, 82, 89, 97,106,115,125,136,147,159,172,186,200,215,232,249, \ - 14, 15, 17, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 38, 40, 43, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81, 86, 90, 95, 99,104,109,114,120,126,131,138,144,150,157,164,171,179,187,195,203,212,221,230,239,249, \ - 57, 59, 61, 64, 66, 69, 72, 74, 77, 80, 83, 86, 90, 93, 96,100,103,107,111,115,119,123,127,132,136,141,145,150,155,160,166,171,176,182,188,194,200,206,213,219,226,233,240,247,255 -// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) -#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 +#define PWM1_LEVELS \ + 2, 3, 4, 5, 6, 8, 9, 11, 14, 16, 19, 23, 26, 31, 35, 41, 47, 54, 61, 69, 78, 89,100,112,125,140,155,173,191,212,234, \ + 56, 62, 68, 74, 82, 89, 97,106,115,125,136,147,159,172,186,200,215,232,249, \ + 14, 15, 17, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 38, 40, 43, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81, 86, 90, 95, 99,104,109,114,120,126,131,138,144,150,157,164,171,179,187,195,203,212,221,230,239,249, \ + 57, 59, 61, 64, 66, 69, 72, 74, 77, 80, 83, 86, 90, 93, 96,100,103,107,111,115,119,123,127,132,136,141,145,150,155,160,166,171,176,182,188,194,200,206,213,219,226,233,240,247,255 +// PWM2: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) +#define PWM2_LEVELS \ + V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05, \ + V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25, \ + V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05, \ + 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,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25 #define MAX_1x7135 50 #define DEFAULT_LEVEL 44 @@ -39,6 +41,7 @@ #define RAMP_SMOOTH_FLOOR 1 #define RAMP_SMOOTH_CEIL 130 // ~50% power, ~??? mA / ??? lm +// 1 22 [44] 65 87 108 130 #define RAMP_DISCRETE_FLOOR 1 #define RAMP_DISCRETE_CEIL 130 #define RAMP_DISCRETE_STEPS 7 @@ -70,7 +73,7 @@ //#define SIMPLE_UI_ACTIVE 0 // advanced UI by default // allow Aux Config and Strobe Modes in Simple UI -#define USE_EXTENDED_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 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 cd883fa..bc1d9a7 100644 --- a/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h +++ b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h @@ -36,9 +36,7 @@ * IN- NFET : pull up after BST enable to eliminate startup flash, pull down otherwise */ -#include <avr/io.h> - -#define HWDEF_C_FILE thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c +#define HWDEF_C thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -58,24 +56,16 @@ enum CHANNEL_MODES { #define CHANNEL_MODES_ENABLED 0b0000000000000001 -#define PWM_CHANNELS 1 // old, remove this - #define PWM_BITS 8 // 8-bit DAC -#define PWM_GET PWM_GET8 #define PWM_DATATYPE uint8_t #define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 #define PWM1_DATATYPE uint8_t // main LED ramp +#define PWM1_GET(l) PWM_GET8(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.CTRLA // 0.55V or 2.5V -#define PWM_TOP_INIT 255 // highest value used in top half of ramp (unused?) -// Vref values -#define V055 16 -#define V11 17 -#define V25 18 -#define V43 19 -#define V15 20 +// (DAC_LVL + DAC_VREF + Vref values are defined in arch/*.h) // BST enable #define BST_ENABLE_PIN PIN4_bp @@ -99,27 +89,25 @@ enum CHANNEL_MODES { #define SWITCH_ISC_REG PORTC.PIN3CTRL #define SWITCH_VECT PORTC_PORT_vect #define SWITCH_INTFLG VPORTC.INTFLAGS -#define SWITCH_PCINT PCINT0 -#define PCINT_vect PCINT0_vect #endif // Voltage divider battLVL #define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated -#define DUAL_VOLTAGE_FLOOR 21 // for AA/14500 boost drivers, don't indicate low voltage if below this level -#define DUAL_VOLTAGE_LOW_LOW 7 // the lower voltage range's danger zone 0.7 volts (NiMH) #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) +// don't use the default VDD converter +#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) +// (voltage - D1) * (R2/(R2+R1) * 4096 / 1.1) // Resistors are 330k and 100k -#ifndef ADC_44 -#define ADC_44 951 // raw value at 4.40V -#endif -#ifndef ADC_22 -#define ADC_22 476 // raw value at 2.20V -#endif +#define ADC_44 3810 // raw value at 4.40V +#define ADC_22 1905 // raw value at 2.20V // this driver allows for aux LEDs under the optic #define AUXLED_R_PIN PIN3_bp @@ -133,10 +121,9 @@ enum CHANNEL_MODES { inline void hwdef_setup() { - // TODO: for this DAC controlled-light, try to decrease the clock speed or use the ULP - // set up the system clock to run at 10 MHz to match other attiny1616 lights - _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, - CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + // TODO: for this DAC controlled-light, try to decrease the clock speed + // to reduce overall system power + mcu_clock_speed(); VPORTA.DIR = PIN4_bm // BST EN | PIN5_bm // HDR @@ -173,11 +160,10 @@ inline void hwdef_setup() { // set up the DAC // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf // DAC ranges from 0V to (255 * Vref) / 256 - // also VREF_DAC0REFSEL_0V55_gc and VREF_DAC0REFSEL_1V1_gc and VREF_DAC0REFSEL_2V5_gc - VREF.CTRLA |= VREF_DAC0REFSEL_2V5_gc; - VREF.CTRLB |= VREF_DAC0REFEN_bm; - DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; - DAC0.DATA = 255; // set the output voltage + mcu_set_dac_vref(V05); // boot at lowest Vref setting + VREF.CTRLB |= VREF_DAC0REFEN_bm; // enable DAC Vref + DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; // enable DAC + DAC_LVL = 0; // turn off output at boot } diff --git a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/anduril.h b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/anduril.h index 64da638..8319d13 100644 --- a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/anduril.h +++ b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/anduril.h @@ -3,7 +3,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #pragma once -#include "thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h" +#define HWDEF_H thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h // HPRsense : 1.7+0.3+5 = 7mR (DMN22M5UFG+trace resistance+5mR) // Vsense=42.46mV, R1= 191k @@ -19,15 +19,17 @@ // - high 0.55V // - high 2.5V // PWM1: DAC Data -#define PWM1_LEVELS 2, 3, 4, 5, 7, 9, 11, 13, 16, 19, 23, 28, 33, 39, 45, 53, 61, 71, 81, 93,106,121,137,155,175,196,220,246, \ - 60, 67, 74, 82, 91,100,110,121,133,146,159,174,190,207,224,244, \ - 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 23, 24, 26, 27, 29, 31, 33, 35, 37, 40, 42, 45, 47, 50, 53, 56, 59, 62, 66, 69, 73, 77, 81, 85, 90, 94, 99,104,109,114,120,126,132,138,144,151,158,165,173,180,188,196,205,214,223,232,242,252, \ - 57, 60, 62, 65, 67, 70, 73, 76, 78, 82, 85, 88, 91, 95, 98,102,105,109,113,117,121,126,130,135,139,144,149,154,159,164,170,175,181,187,193,199,206,212,219,225,232,240,247,255 -// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) -#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 +#define PWM1_LEVELS \ + 2, 3, 4, 5, 7, 9, 11, 13, 16, 19, 23, 28, 33, 39, 45, 53, 61, 71, 81, 93,106,121,137,155,175,196,220,246, \ + 60, 67, 74, 82, 91,100,110,121,133,146,159,174,190,207,224,244, \ + 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 23, 24, 26, 27, 29, 31, 33, 35, 37, 40, 42, 45, 47, 50, 53, 56, 59, 62, 66, 69, 73, 77, 81, 85, 90, 94, 99,104,109,114,120,126,132,138,144,151,158,165,173,180,188,196,205,214,223,232,242,252, \ + 57, 60, 62, 65, 67, 70, 73, 76, 78, 82, 85, 88, 91, 95, 98,102,105,109,113,117,121,126,130,135,139,144,149,154,159,164,170,175,181,187,193,199,206,212,219,225,232,240,247,255 +// PWM2: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) +#define PWM2_LEVELS \ + V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05, \ + V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25, \ + V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05, \ + 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,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25 #define MAX_1x7135 44 #define DEFAULT_LEVEL 44 @@ -71,7 +73,7 @@ //#define SIMPLE_UI_ACTIVE 0 // advanced UI by default // allow Aux Config and Strobe Modes in Simple UI -#define USE_EXTENDED_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 diff --git a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c index 97f5fc9..573ec2f 100644 --- a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c +++ b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c @@ -22,7 +22,7 @@ Channel channels[] = { void set_level_zero() { DAC_LVL = 0; // DAC off - DAC_VREF = V055; // low Vref + mcu_set_dac_vref(V055); // low Vref HDR_ENABLE_PORT &= ~(1 << HDR_ENABLE_PIN); // HDR off // prevent post-off flash @@ -49,8 +49,8 @@ void set_level_main(uint8_t level) { BST_ENABLE_PORT |= (1 << BST_ENABLE_PIN); // pre-load ramp data so it can be assigned faster later - PWM_DATATYPE dac_lvl = PWM_GET(pwm1_levels, level); - PWM_DATATYPE dac_vref = PWM_GET(pwm_tops, level); + PWM1_DATATYPE dac_lvl = PWM1_GET(level); + PWM2_DATATYPE dac_vref = PWM2_GET(level); // enable HDR on top half of ramp if (level >= (HDR_ENABLE_LEVEL_MIN-1)) @@ -61,7 +61,7 @@ void set_level_main(uint8_t level) { // 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; + mcu_set_dac_vref(dac_vref); if (noflash) { // wait for flash prevention to finish @@ -75,11 +75,11 @@ bool gradual_tick_main(uint8_t gt) { // otherwise, simply jump to the next ramp level // and let set_level() handle any gear changes - PWM_DATATYPE dac_next = PWM_GET(pwm1_levels, gt); - PWM_DATATYPE vref_next = PWM_GET(pwm_tops, gt); + PWM1_DATATYPE dac_next = PWM1_GET(gt); + PWM2_DATATYPE vref_next = PWM2_GET(gt); // different gear = full adjustment - if (vref_next != DAC_VREF) return true; // let parent set_level() for us + if (vref_next != (DAC_VREF & VREF_DAC0REFSEL_gm)) return true; // let parent set_level() for us // same gear = small adjustment GRADUAL_ADJUST_SIMPLE(dac_next, DAC_LVL); diff --git a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h index 3f64287..d83aa46 100644 --- a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h +++ b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h @@ -37,9 +37,7 @@ * IN- NFET : pull up after BST enable to eliminate startup flash, pull down otherwise */ -#include <avr/io.h> - -#define HWDEF_C_FILE thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c +#define HWDEF_C thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -59,24 +57,16 @@ enum CHANNEL_MODES { #define CHANNEL_MODES_ENABLED 0b0000000000000001 -#define PWM_CHANNELS 1 // old, remove this - #define PWM_BITS 8 // 8-bit DAC -#define PWM_GET PWM_GET8 #define PWM_DATATYPE uint8_t #define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 #define PWM1_DATATYPE uint8_t // main LED ramp +#define PWM1_GET(l) PWM_GET8(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.CTRLA // 0.55V or 2.5V -#define PWM_TOP_INIT 255 // highest value used in top half of ramp (unused?) -// Vref values -#define V055 16 -#define V11 17 -#define V25 18 -#define V43 19 -#define V15 20 +// (DAC_LVL + DAC_VREF + Vref values are defined in arch/*.h) // BST enable #define BST_ENABLE_PIN PIN0_bp @@ -100,8 +90,6 @@ enum CHANNEL_MODES { #define SWITCH_ISC_REG PORTC.PIN2CTRL #define SWITCH_VECT PORTC_PORT_vect #define SWITCH_INTFLG VPORTC.INTFLAGS -#define SWITCH_PCINT PCINT0 -#define PCINT_vect PCINT0_vect #endif // average drop across diode on this hardware @@ -127,10 +115,9 @@ enum CHANNEL_MODES { inline void hwdef_setup() { - // TODO: for this DAC controlled-light, try to decrease the clock speed or use the ULP - // set up the system clock to run at 10 MHz to match other attiny1616 lights - _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, - CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + // TODO: for this DAC controlled-light, try to decrease the clock speed + // to reduce overall system power + mcu_clock_speed(); VPORTA.DIR = PIN6_bm; // DAC VPORTB.DIR = PIN1_bm // R @@ -167,11 +154,10 @@ inline void hwdef_setup() { // set up the DAC // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf // DAC ranges from 0V to (255 * Vref) / 256 - // also VREF_DAC0REFSEL_0V55_gc and VREF_DAC0REFSEL_1V1_gc and VREF_DAC0REFSEL_2V5_gc - VREF.CTRLA |= VREF_DAC0REFSEL_2V5_gc; - VREF.CTRLB |= VREF_DAC0REFEN_bm; - DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; - DAC0.DATA = 255; // set the output voltage + mcu_set_dac_vref(V05); // boot at lowest Vref setting + VREF.CTRLB |= VREF_DAC0REFEN_bm; // enable DAC Vref + DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; // enable DAC + DAC_LVL = 0; // turn off output at boot } diff --git a/hw/thefreeman/lin16dac/anduril.h b/hw/thefreeman/lin16dac/anduril.h index 8ca8b9f..8f9d6e4 100644 --- a/hw/thefreeman/lin16dac/anduril.h +++ b/hw/thefreeman/lin16dac/anduril.h @@ -3,7 +3,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #pragma once -#include "thefreeman/lin16dac/hwdef.h" +#define HWDEF_H thefreeman/lin16dac/hwdef.h // the button lights up #define USE_INDICATOR_LED @@ -24,15 +24,17 @@ // PWM1: DAC Data // FIXME: ramp stalls with 8 duplicate levels in a row // (maybe use 1.1V Vref during that part of the ramp?) -#define PWM1_LEVELS 25, 25, 33, 41, 41, 50, 58, 66, 75, 83, 92,108,117,133,150,167,192,209,234, \ - 58, 64, 71, 80, 90, 99,110,121,134,149,163,180,198,218,241, \ - 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 39, 41, 44, 47, 50, 53, 56, 59, 63, 67, 71, 75, 79, 84, 89, 94,100,105,112,118,124,131,139,146,154,163,172,181,191,201,212,223,234,246, \ - 57, 60, 63, 66, 69, 73, 76, 80, 84, 88, 93, 97,102,107,112,117,123,129,135,141,147,154,161,169,176,184,193,201,210,220,229,239,250,255 -// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) -#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 +#define PWM1_LEVELS \ + 25, 25, 33, 41, 41, 50, 58, 66, 75, 83, 92,108,117,133,150,167,192,209,234, \ + 58, 64, 71, 80, 90, 99,110,121,134,149,163,180,198,218,241, \ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 39, 41, 44, 47, 50, 53, 56, 59, 63, 67, 71, 75, 79, 84, 89, 94,100,105,112,118,124,131,139,146,154,163,172,181,191,201,212,223,234,246, \ + 57, 60, 63, 66, 69, 73, 76, 80, 84, 88, 93, 97,102,107,112,117,123,129,135,141,147,154,161,169,176,184,193,201,210,220,229,239,250,255 +// PWM2: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) +#define PWM2_LEVELS \ + V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05, \ + V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25,V25, \ + V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05,V05, \ + 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,V25,V25,V25 #define MAX_1x7135 34 #define HDR_ENABLE_LEVEL_MIN 35 // bottom level of top half of the ramp diff --git a/hw/thefreeman/lin16dac/hwdef.c b/hw/thefreeman/lin16dac/hwdef.c index 191444e..89700d7 100644 --- a/hw/thefreeman/lin16dac/hwdef.c +++ b/hw/thefreeman/lin16dac/hwdef.c @@ -22,7 +22,7 @@ Channel channels[] = { void set_level_zero() { DAC_LVL = 0; // DAC off - DAC_VREF = V055; // low Vref + mcu_set_dac_vref(V055); // low Vref HDR_ENABLE_PORT &= ~(1 << HDR_ENABLE_PIN); // HDR off // prevent post-off flash @@ -50,8 +50,8 @@ void set_level_main(uint8_t level) { OPAMP_ENABLE_PORT |= (1 << OPAMP_ENABLE_PIN); // pre-load ramp data so it can be assigned faster later - PWM_DATATYPE dac_lvl = PWM_GET(pwm1_levels, level); - PWM_DATATYPE dac_vref = PWM_GET(pwm_tops, level); + PWM1_DATATYPE dac_lvl = PWM1_GET(level); + PWM2_DATATYPE dac_vref = PWM2_GET(level); // enable HDR on top half of ramp if (level >= (HDR_ENABLE_LEVEL_MIN-1)) @@ -69,7 +69,8 @@ void set_level_main(uint8_t level) { // 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; + mcu_set_dac_vref(dac_vref); + } bool gradual_tick_main(uint8_t gt) { @@ -77,11 +78,11 @@ bool gradual_tick_main(uint8_t gt) { // otherwise, simply jump to the next ramp level // and let set_level() handle any gear changes - PWM_DATATYPE dac_next = PWM_GET(pwm1_levels, gt); - PWM_DATATYPE vref_next = PWM_GET(pwm_tops, gt); + PWM1_DATATYPE dac_next = PWM1_GET(gt); + PWM2_DATATYPE vref_next = PWM2_GET(gt); // different gear = full adjustment - if (vref_next != DAC_VREF) return true; // let parent set_level() for us + if (vref_next != (DAC_VREF & VREF_DAC0REFSEL_gm)) return true; // let parent set_level() for us // same gear = small adjustment GRADUAL_ADJUST_SIMPLE(dac_next, DAC_LVL); diff --git a/hw/thefreeman/lin16dac/hwdef.h b/hw/thefreeman/lin16dac/hwdef.h index 2066d04..a68d9c2 100644 --- a/hw/thefreeman/lin16dac/hwdef.h +++ b/hw/thefreeman/lin16dac/hwdef.h @@ -12,9 +12,7 @@ * Read voltage from VCC pin, has PFET so no drop */ -#include <avr/io.h> - -#define HWDEF_C_FILE thefreeman/lin16dac/hwdef.c +#define HWDEF_C thefreeman/lin16dac/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-aux.h" @@ -34,24 +32,16 @@ enum CHANNEL_MODES { #define CHANNEL_MODES_ENABLED 0b0000000000000001 -#define PWM_CHANNELS 1 // old, remove this - #define PWM_BITS 8 // 8-bit DAC -#define PWM_GET PWM_GET8 #define PWM_DATATYPE uint8_t #define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 #define PWM1_DATATYPE uint8_t // main LED ramp +#define PWM1_GET(l) PWM_GET8(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.CTRLA // 0.55V or 2.5V -#define PWM_TOP_INIT 255 // highest value used in top half of ramp (unused?) -// Vref values -#define V055 16 -#define V11 17 -#define V25 18 -#define V43 19 -#define V15 20 +// (DAC_LVL + DAC_VREF + Vref values are defined in arch/*.h) // Opamp enable // For turning on and off the op-amp @@ -86,11 +76,9 @@ enum CHANNEL_MODES { inline void hwdef_setup() { - // set up the system clock to run at 10 MHz instead of the default 3.33 MHz - // (it'll get underclocked to 2.5 MHz later) - // TODO: maybe run even slower? - _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, - CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + // TODO: for this DAC controlled-light, try to decrease the clock speed + // to reduce overall system power + mcu_clock_speed(); VPORTA.DIR = PIN6_bm // DAC | PIN7_bm; // Opamp @@ -123,11 +111,10 @@ inline void hwdef_setup() { // set up the DAC // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf // DAC ranges from 0V to (255 * Vref) / 256 - // also VREF_DAC0REFSEL_0V55_gc and VREF_DAC0REFSEL_1V1_gc and VREF_DAC0REFSEL_2V5_gc - VREF.CTRLA |= VREF_DAC0REFSEL_2V5_gc; - VREF.CTRLB |= VREF_DAC0REFEN_bm; - DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; - DAC0.DATA = 255; // set the output voltage + mcu_set_dac_vref(V05); // boot at lowest Vref setting + VREF.CTRLB |= VREF_DAC0REFEN_bm; // enable DAC Vref + DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; // enable DAC + DAC_LVL = 0; // turn off output at boot } diff --git a/hw/wurkkos/ts10/hwdef.h b/hw/wurkkos/ts10/hwdef.h index b1239b8..92898fb 100644 --- a/hw/wurkkos/ts10/hwdef.h +++ b/hw/wurkkos/ts10/hwdef.h @@ -13,9 +13,7 @@ * Voltage: VCC */ -#include <avr/io.h> - -#define HWDEF_C_FILE wurkkos/ts10/hwdef.c +#define HWDEF_C wurkkos/ts10/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-aux.h" @@ -60,12 +58,10 @@ enum CHANNEL_MODES { // e-switch #define SWITCH_PIN PIN5_bp -//#define SWITCH_PCINT PCINT0 #define SWITCH_PORT VPORTA.IN #define SWITCH_ISC_REG PORTA.PIN2CTRL #define SWITCH_VECT PORTA_PORT_vect #define SWITCH_INTFLG VPORTA.INTFLAGS -//#define PCINT_vect PCINT0_vect // average drop across diode on this hardware #ifndef VOLTAGE_FUDGE_FACTOR diff --git a/hw/wurkkos/ts25/anduril.h b/hw/wurkkos/ts25/anduril.h index 722bc5d..ecf0db1 100644 --- a/hw/wurkkos/ts25/anduril.h +++ b/hw/wurkkos/ts25/anduril.h @@ -3,7 +3,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later #pragma once -#include "wurkkos/ts25/hwdef.h" +//#include "wurkkos/ts25/hwdef.h" +#define HWDEF_H wurkkos/ts25/hwdef.h #include "wurkkos/anduril.h" // this light has three aux LED channels: R, G, B diff --git a/hw/wurkkos/ts25/hwdef.h b/hw/wurkkos/ts25/hwdef.h index 024a18d..ac1e574 100644 --- a/hw/wurkkos/ts25/hwdef.h +++ b/hw/wurkkos/ts25/hwdef.h @@ -14,9 +14,7 @@ * Aux Blue: PC1 */ -#include <avr/io.h> - -#define HWDEF_C_FILE wurkkos/ts25/hwdef.c +#define HWDEF_C wurkkos/ts25/hwdef.c // allow using aux LEDs as extra channel modes #include "fsm/chan-rgbaux.h" @@ -61,12 +59,10 @@ enum CHANNEL_MODES { // e-switch #define SWITCH_PIN PIN5_bp -//#define SWITCH_PCINT PCINT0 #define SWITCH_PORT VPORTA.IN #define SWITCH_ISC_REG PORTA.PIN2CTRL #define SWITCH_VECT PORTA_PORT_vect #define SWITCH_INTFLG VPORTA.INTFLAGS -//#define PCINT_vect PCINT0_vect // average drop across diode on this hardware #ifndef VOLTAGE_FUDGE_FACTOR diff --git a/ui/anduril/anduril.c b/ui/anduril/anduril.c index e72c3b5..1cdb8d0 100644 --- a/ui/anduril/anduril.c +++ b/ui/anduril/anduril.c @@ -34,16 +34,19 @@ * as possible. These are mostly "USE" flags. */ +/********* load up MCU info, like ROM size and such *********/ +#include "arch/mcu.h" + /********* User-configurable options *********/ #include "anduril/config-default.h" /********* specific settings for known driver types *********/ -// Anduril config file name (set it here or define it at the gcc command line) -//#define CFG_H cfg-blf-q8.h -#include "fsm/tk.h" #include incfile(CFG_H) +#ifdef HWDEF_H +#include incfile(HWDEF_H) +#endif /********* Include headers which need to be before FSM *********/ @@ -77,11 +80,12 @@ #include "fsm/spaghetti-monster.h" /********* does this build target have special code to include? *********/ -#ifdef HWDEF_C_FILE -#include incfile(HWDEF_C_FILE) +#ifdef CFG_C +#include incfile(CFG_C) #endif -#ifdef CFG_C_FILE -#include incfile(CFG_C_FILE) + +#ifdef HWDEF_C +#include incfile(HWDEF_C) #endif @@ -312,6 +316,7 @@ void loop() { #ifdef USE_BATTCHECK else if (state == battcheck_state) { + nice_delay_ms(1000); // wait a moment for a more accurate reading battcheck(); #ifdef USE_SIMPLE_UI // in simple mode, turn off after one readout diff --git a/ui/anduril/aux-leds.c b/ui/anduril/aux-leds.c index 1b30d34..fd184fc 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 - 9, 1, // R - 10, 2, // R+G - 11, 3, // G - 12, 4, // G+B - 13, 5, // B - 14, 6, // R + B - 15, 7, // R+G+B - 20, 0, // black + 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 #endif // li-ion voltages - 29, 1, // R - 33, 2, // R+G - 35, 3, // G - 37, 4, // G+B - 39, 5, // B - 41, 6, // R + B - 44, 7, // R+G+B // skip; looks too similar to G+B - 255, 7, // R+G+B + 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 }; uint8_t volts = voltage; //if (volts < VOLTAGE_LOW) return 0; diff --git a/ui/anduril/config-default.h b/ui/anduril/config-default.h index 899bc4a..1b34e8c 100644 --- a/ui/anduril/config-default.h +++ b/ui/anduril/config-default.h @@ -20,7 +20,7 @@ // overheat protection #define USE_THERMAL_REGULATION -#if (ATTINY==85) || (ATTINY==1634) +#if (MCU==0x85) || (MCU==0x1634) // sloppy temperature sensor needs bigger error margin #define DEFAULT_THERM_CEIL 45 // try not to get hotter than this (in C) #else @@ -113,6 +113,12 @@ // timer is available in regular ramp mode and candle mode #define USE_SUNSET_TIMER +// optionally make gradual ticks happen faster +// Affects: thermal regulation speed, sunset mode, maybe other features +// (default is calibrated for 8-bit PWM, +// but 10-bit should set this value to 4 instead of 1) +#define GRADUAL_ADJUST_SPEED 1 + ///// What to do when power is connected ///// // factory reset function erases user's runtime configuration in eeprom @@ -137,6 +143,10 @@ #define BATTCHECK_VpT //#define BATTCHECK_8bars // FIXME: breaks build //#define BATTCHECK_4bars // FIXME: breaks build +#if ROM_SIZE > 10000 + // battcheck displays 1.25V instead of 1.2V + #define USE_EXTRA_BATTCHECK_DIGIT +#endif // allow the user to calibrate the voltage readings? // (adjust in 0.05V increments from -0.30V to +0.30V) // (1 = -0.30V, 2 = -0.25V, ... 7 = 0V, ... 13 = +0.30V) @@ -185,7 +195,7 @@ // if the aux LEDs oscillate between "full battery" and "empty battery" // while in "voltage" mode, enable this to reduce the amplitude of // those oscillations -#if (ATTINY==1616) || (ATTINY==1634) +#if (ROM_SIZE > 10000) #define USE_LOWPASS_WHILE_ASLEEP #endif @@ -195,7 +205,7 @@ // Use "smooth steps" to soften on/off and step changes // on MCUs with enough room for extra stuff like this -#if (ATTINY==1616) || (ATTINY==1634) +#if (ROM_SIZE > 10000) #define USE_SMOOTH_STEPS #endif // 0 = none, 1 = smooth, 2+ = undefined diff --git a/ui/anduril/ramp-mode.c b/ui/anduril/ramp-mode.c index 9e19025..4c535b4 100644 --- a/ui/anduril/ramp-mode.c +++ b/ui/anduril/ramp-mode.c @@ -287,7 +287,7 @@ uint8_t steady_state(Event event, uint16_t arg) { static uint16_t ticks_since_adjust = 0; ticks_since_adjust++; if (diff) { - uint16_t ticks_per_adjust = 256; + uint16_t ticks_per_adjust = 256 / GRADUAL_ADJUST_SPEED; if (diff < 0) { //diff = -diff; if (actual_level > THERM_FASTER_LEVEL) { diff --git a/ui/anduril/ramp-mode.h b/ui/anduril/ramp-mode.h index 59c8db0..1392981 100644 --- a/ui/anduril/ramp-mode.h +++ b/ui/anduril/ramp-mode.h @@ -4,14 +4,15 @@ #pragma once -#ifndef RAMP_LENGTH -#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file +#ifndef RAMP_SIZE +#define RAMP_SIZE 150 // default, if not overridden in a driver cfg file #endif +// TODO: Replace MAX_Xx7135 with MAX_CH1, MAX_CH2, MAX_REGULATED, etc + // thermal properties, if not defined per-driver #ifndef MIN_THERM_STEPDOWN -// TODO: Replace MAX_Xx7135 with MAX_CH1, MAX_CH2, MAX_REGULATED, etc -#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to +#define MIN_THERM_STEPDOWN (RAMP_SIZE/3) // lowest value it'll step down to #endif #ifndef THERM_FASTER_LEVEL #ifdef MAX_Nx7135 diff --git a/ui/anduril/version-check-mode.c b/ui/anduril/version-check-mode.c index eebe59b..1cd6968 100644 --- a/ui/anduril/version-check-mode.c +++ b/ui/anduril/version-check-mode.c @@ -15,7 +15,10 @@ uint8_t version_check_state(Event event, uint16_t arg) { inline void version_check_iter() { for (uint8_t i=0; i<sizeof(version_number)-1; i++) { uint8_t digit = pgm_read_byte(version_number + i) - '0'; - if (digit < 10) blink_digit(digit); + // digits: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + // hex digits: 0 1 2 3 4 5 6 7 8 9 a b c d e f + // 'model' file: 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + if (digit < 16) blink_digit(digit); else { // "buzz" for non-numeric characters for(uint8_t frame=0; frame<25; frame++) { set_level((frame&1) << 5); |
